summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/SCsub26
-rw-r--r--platform/android/SCsub76
-rw-r--r--platform/android/android_keys_utils.cpp2
-rw-r--r--platform/android/android_keys_utils.h1
-rw-r--r--platform/android/api/api.cpp12
-rw-r--r--platform/android/api/api.h5
-rw-r--r--platform/android/api/java_class_wrapper.h40
-rw-r--r--platform/android/api/jni_singleton.h223
-rw-r--r--platform/android/audio_driver_jandroid.cpp34
-rw-r--r--platform/android/audio_driver_jandroid.h1
-rw-r--r--platform/android/audio_driver_opensl.cpp29
-rw-r--r--platform/android/audio_driver_opensl.h1
-rw-r--r--platform/android/detect.py241
-rw-r--r--platform/android/dir_access_jandroid.cpp32
-rw-r--r--platform/android/dir_access_jandroid.h1
-rw-r--r--platform/android/display_server_android.cpp668
-rw-r--r--platform/android/display_server_android.h175
-rw-r--r--platform/android/export/export.cpp393
-rw-r--r--platform/android/export/export.h5
-rw-r--r--platform/android/file_access_android.cpp24
-rw-r--r--platform/android/file_access_android.h1
-rw-r--r--platform/android/file_access_jandroid.cpp19
-rw-r--r--platform/android/file_access_jandroid.h1
-rw-r--r--platform/android/java/app/AndroidManifest.xml4
-rw-r--r--platform/android/java/app/build.gradle38
-rw-r--r--platform/android/java/app/config.gradle85
-rw-r--r--platform/android/java/app/settings.gradle2
-rw-r--r--platform/android/java/build.gradle3
-rw-r--r--platform/android/java/gradle.properties3
-rw-r--r--platform/android/java/lib/AndroidManifest.xml2
-rw-r--r--platform/android/java/lib/build.gradle6
-rw-r--r--platform/android/java/lib/res/drawable-hdpi/notify_panel_notification_icon_bg.pngbin1116 -> 0 bytes
-rw-r--r--platform/android/java/lib/res/drawable-mdpi/notify_panel_notification_icon_bg.pngbin388 -> 0 bytes
-rw-r--r--platform/android/java/lib/res/drawable-xhdpi/notify_panel_notification_icon_bg.pngbin462 -> 0 bytes
-rw-r--r--platform/android/java/lib/res/drawable-xxhdpi/notify_panel_notification_icon_bg.pngbin1556 -> 0 bytes
-rw-r--r--platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Dictionary.java6
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java103
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java (renamed from platform/android/java/lib/src/org/godotengine/godot/GodotView.java)56
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotIO.java85
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java20
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java (renamed from platform/iphone/platform_refcount.h)24
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java13
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java142
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java74
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java16
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java52
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java36
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java97
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java24
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java99
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java17
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt36
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt6
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java7
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java7
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java5
-rw-r--r--platform/android/java/plugins/godotpayment/build.gradle1
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java4
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java21
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java2
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java1
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java8
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java4
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java7
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java8
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java (renamed from platform/android/java/lib/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java)5
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java (renamed from platform/android/java/lib/src/org/godotengine/godot/utils/HttpRequester.java)7
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/RequestParams.java (renamed from platform/android/java/lib/src/org/godotengine/godot/utils/RequestParams.java)5
-rw-r--r--platform/android/java_class_wrapper.cpp112
-rw-r--r--platform/android/java_godot_io_wrapper.cpp47
-rw-r--r--platform/android/java_godot_io_wrapper.h12
-rw-r--r--platform/android/java_godot_lib_jni.cpp147
-rw-r--r--platform/android/java_godot_lib_jni.h8
-rw-r--r--platform/android/java_godot_wrapper.cpp16
-rw-r--r--platform/android/java_godot_wrapper.h10
-rw-r--r--platform/android/jni_utils.cpp42
-rw-r--r--platform/android/jni_utils.h189
-rw-r--r--platform/android/net_socket_android.cpp6
-rw-r--r--platform/android/net_socket_android.h7
-rw-r--r--platform/android/os_android.cpp544
-rw-r--r--platform/android/os_android.h116
-rw-r--r--platform/android/plugin/godot_plugin_config.h251
-rw-r--r--platform/android/plugin/godot_plugin_jni.cpp50
-rw-r--r--platform/android/plugin/godot_plugin_jni.h2
-rw-r--r--platform/android/string_android.h4
-rw-r--r--platform/android/thread_jandroid.cpp27
-rw-r--r--platform/android/thread_jandroid.h1
-rw-r--r--platform/android/vulkan/vk_renderer_jni.h46
-rw-r--r--platform/android/vulkan/vulkan_context_android.cpp (renamed from platform/android/vulkan/vk_renderer_jni.cpp)40
-rw-r--r--platform/android/vulkan/vulkan_context_android.h48
-rw-r--r--platform/haiku/SCsub27
-rw-r--r--platform/haiku/audio_driver_media_kit.cpp12
-rw-r--r--platform/haiku/detect.py140
-rw-r--r--platform/haiku/haiku_direct_window.cpp12
-rw-r--r--platform/haiku/haiku_direct_window.h2
-rw-r--r--platform/haiku/key_mapping_haiku.h2
-rw-r--r--platform/haiku/os_haiku.cpp32
-rw-r--r--platform/haiku/os_haiku.h6
-rw-r--r--platform/iphone/SCsub38
-rw-r--r--platform/iphone/app_delegate.mm74
-rw-r--r--platform/iphone/detect.py257
-rw-r--r--platform/iphone/export/export.cpp89
-rw-r--r--platform/iphone/export/export.h5
-rw-r--r--platform/iphone/game_center.h1
-rw-r--r--platform/iphone/game_center.mm15
-rw-r--r--platform/iphone/gl_view.mm21
-rw-r--r--platform/iphone/godot_iphone.cpp9
-rw-r--r--platform/iphone/icloud.h1
-rw-r--r--platform/iphone/icloud.mm6
-rw-r--r--platform/iphone/in_app_store.h1
-rw-r--r--platform/iphone/in_app_store.mm17
-rw-r--r--platform/iphone/ios.h1
-rw-r--r--platform/iphone/ios.mm3
-rw-r--r--platform/iphone/os_iphone.cpp76
-rw-r--r--platform/iphone/os_iphone.h14
-rw-r--r--platform/iphone/view_controller.mm4
-rw-r--r--platform/iphone/vulkan_context_iphone.h3
-rw-r--r--platform/iphone/vulkan_context_iphone.mm3
-rw-r--r--platform/javascript/SCsub77
-rw-r--r--platform/javascript/api/api.cpp10
-rw-r--r--platform/javascript/api/api.h5
-rw-r--r--platform/javascript/audio_driver_javascript.cpp94
-rw-r--r--platform/javascript/audio_driver_javascript.h3
-rw-r--r--platform/javascript/detect.py159
-rw-r--r--platform/javascript/display_server_javascript.cpp1193
-rw-r--r--platform/javascript/display_server_javascript.h187
-rw-r--r--platform/javascript/dom_keys.inc151
-rw-r--r--platform/javascript/emscripten_helpers.py25
-rw-r--r--platform/javascript/engine/engine.js175
-rw-r--r--platform/javascript/engine/loader.js33
-rw-r--r--platform/javascript/engine/utils.js18
-rw-r--r--platform/javascript/export/export.cpp70
-rw-r--r--platform/javascript/http_client.h.inc16
-rw-r--r--platform/javascript/http_client_javascript.cpp36
-rw-r--r--platform/javascript/http_request.h2
-rw-r--r--platform/javascript/javascript_eval.cpp2
-rw-r--r--platform/javascript/javascript_main.cpp49
-rw-r--r--platform/javascript/native/http_request.js (renamed from platform/javascript/http_request.js)0
-rw-r--r--platform/javascript/native/id_handler.js (renamed from platform/javascript/id_handler.js)0
-rw-r--r--platform/javascript/native/utils.js204
-rw-r--r--platform/javascript/os_javascript.cpp1159
-rw-r--r--platform/javascript/os_javascript.h101
-rw-r--r--platform/linuxbsd/SCsub22
-rw-r--r--platform/linuxbsd/context_gl_x11.cpp (renamed from platform/x11/context_gl_x11.cpp)22
-rw-r--r--platform/linuxbsd/context_gl_x11.h (renamed from platform/x11/context_gl_x11.h)1
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.cpp (renamed from platform/x11/crash_handler_x11.cpp)25
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.h (renamed from platform/x11/crash_handler_x11.h)3
-rw-r--r--platform/linuxbsd/detect.py379
-rw-r--r--platform/linuxbsd/detect_prime_x11.cpp (renamed from platform/x11/detect_prime.cpp)15
-rw-r--r--platform/linuxbsd/detect_prime_x11.h (renamed from platform/x11/detect_prime.h)2
-rw-r--r--platform/linuxbsd/display_server_x11.cpp (renamed from platform/x11/os_x11.cpp)4327
-rw-r--r--platform/linuxbsd/display_server_x11.h354
-rw-r--r--platform/linuxbsd/export/export.cpp (renamed from platform/x11/export/export.cpp)9
-rw-r--r--platform/linuxbsd/export/export.h (renamed from platform/x11/export/export.h)7
-rw-r--r--platform/linuxbsd/godot_linuxbsd.cpp (renamed from platform/x11/godot_x11.cpp)10
-rw-r--r--platform/linuxbsd/joypad_linux.cpp (renamed from platform/x11/joypad_linux.cpp)96
-rw-r--r--platform/linuxbsd/joypad_linux.h (renamed from platform/x11/joypad_linux.h)10
-rw-r--r--platform/linuxbsd/key_mapping_x11.cpp (renamed from platform/x11/key_mapping_x11.cpp)58
-rw-r--r--platform/linuxbsd/key_mapping_x11.h (renamed from platform/x11/key_mapping_x11.h)2
-rw-r--r--platform/linuxbsd/logo.png (renamed from platform/x11/logo.png)bin1679 -> 1679 bytes
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp363
-rw-r--r--platform/linuxbsd/os_linuxbsd.h105
-rw-r--r--platform/linuxbsd/pck_embed.ld (renamed from platform/x11/pck_embed.ld)0
-rw-r--r--platform/linuxbsd/pck_embed.legacy.ld (renamed from platform/x11/pck_embed.legacy.ld)0
-rw-r--r--platform/linuxbsd/platform_config.h (renamed from platform/x11/platform_config.h)0
-rw-r--r--platform/linuxbsd/platform_linuxbsd_builders.py (renamed from platform/x11/platform_x11_builders.py)10
-rw-r--r--platform/linuxbsd/vulkan_context_x11.cpp (renamed from platform/x11/vulkan_context_x11.cpp)11
-rw-r--r--platform/linuxbsd/vulkan_context_x11.h (renamed from platform/x11/vulkan_context_x11.h)3
-rw-r--r--platform/osx/SCsub20
-rw-r--r--platform/osx/context_gl_osx.h1
-rw-r--r--platform/osx/context_gl_osx.mm11
-rw-r--r--platform/osx/crash_handler_osx.h1
-rw-r--r--platform/osx/detect.py208
-rw-r--r--platform/osx/dir_access_osx.mm1
-rw-r--r--platform/osx/display_server_osx.h308
-rw-r--r--platform/osx/display_server_osx.mm3664
-rw-r--r--platform/osx/export/export.cpp371
-rw-r--r--platform/osx/export/export.h5
-rw-r--r--platform/osx/godot_main_osx.mm1
-rw-r--r--platform/osx/joypad_osx.cpp76
-rw-r--r--platform/osx/joypad_osx.h7
-rw-r--r--platform/osx/os_osx.h244
-rw-r--r--platform/osx/os_osx.mm2877
-rw-r--r--platform/osx/platform_osx_builders.py10
-rw-r--r--platform/osx/vulkan_context_osx.h3
-rw-r--r--platform/osx/vulkan_context_osx.mm7
-rw-r--r--platform/server/SCsub12
-rw-r--r--platform/server/detect.py265
-rw-r--r--platform/server/godot_server.cpp1
-rw-r--r--platform/server/os_server.cpp51
-rw-r--r--platform/server/os_server.h11
-rw-r--r--platform/uwp/SCsub20
-rw-r--r--platform/uwp/app.cpp48
-rw-r--r--platform/uwp/app.h16
-rw-r--r--platform/uwp/context_egl_uwp.cpp12
-rw-r--r--platform/uwp/context_egl_uwp.h1
-rw-r--r--platform/uwp/detect.py172
-rw-r--r--platform/uwp/export/export.cpp236
-rw-r--r--platform/uwp/export/export.h5
-rw-r--r--platform/uwp/joypad_uwp.cpp28
-rw-r--r--platform/uwp/joypad_uwp.h4
-rw-r--r--platform/uwp/os_uwp.cpp117
-rw-r--r--platform/uwp/os_uwp.h15
-rw-r--r--platform/uwp/thread_uwp.cpp12
-rw-r--r--platform/uwp/thread_uwp.h9
-rw-r--r--platform/windows/SCsub11
-rw-r--r--platform/windows/context_gl_windows.cpp19
-rw-r--r--platform/windows/context_gl_windows.h1
-rw-r--r--platform/windows/crash_handler_windows.cpp10
-rw-r--r--platform/windows/crash_handler_windows.h1
-rw-r--r--platform/windows/detect.py425
-rw-r--r--platform/windows/display_server_windows.cpp3136
-rw-r--r--platform/windows/display_server_windows.h552
-rw-r--r--platform/windows/export/export.cpp6
-rw-r--r--platform/windows/godot_windows.cpp15
-rw-r--r--platform/windows/joypad_windows.cpp112
-rw-r--r--platform/windows/joypad_windows.h12
-rw-r--r--platform/windows/key_mapping_windows.cpp19
-rw-r--r--platform/windows/key_mapping_windows.h4
-rw-r--r--platform/windows/os_windows.cpp2788
-rw-r--r--platform/windows/os_windows.h307
-rw-r--r--platform/windows/platform_windows_builders.py10
-rw-r--r--platform/windows/vulkan_context_win.cpp9
-rw-r--r--platform/windows/vulkan_context_win.h3
-rw-r--r--platform/windows/windows_terminal_logger.cpp51
-rw-r--r--platform/x11/SCsub21
-rw-r--r--platform/x11/detect.py368
-rw-r--r--platform/x11/os_x11.h339
241 files changed, 18159 insertions, 14288 deletions
diff --git a/platform/SCsub b/platform/SCsub
index 38bab59d74..5194a19518 100644
--- a/platform/SCsub
+++ b/platform/SCsub
@@ -1,32 +1,30 @@
#!/usr/bin/env python
-from compat import open_utf8
-
-Import('env')
+Import("env")
env.platform_sources = []
# Register platform-exclusive APIs
reg_apis_inc = '#include "register_platform_apis.h"\n'
-reg_apis = 'void register_platform_apis() {\n'
-unreg_apis = 'void unregister_platform_apis() {\n'
+reg_apis = "void register_platform_apis() {\n"
+unreg_apis = "void unregister_platform_apis() {\n"
for platform in env.platform_apis:
platform_dir = env.Dir(platform)
- env.add_source_files(env.platform_sources, platform + '/api/api.cpp')
- reg_apis += '\tregister_' + platform + '_api();\n'
- unreg_apis += '\tunregister_' + platform + '_api();\n'
+ env.add_source_files(env.platform_sources, platform + "/api/api.cpp")
+ reg_apis += "\tregister_" + platform + "_api();\n"
+ unreg_apis += "\tunregister_" + platform + "_api();\n"
reg_apis_inc += '#include "' + platform + '/api/api.h"\n'
-reg_apis_inc += '\n'
-reg_apis += '}\n\n'
-unreg_apis += '}\n'
+reg_apis_inc += "\n"
+reg_apis += "}\n\n"
+unreg_apis += "}\n"
# NOTE: It is safe to generate this file here, since this is still execute serially
-with open_utf8('register_platform_apis.gen.cpp', 'w') as f:
+with open("register_platform_apis.gen.cpp", "w", encoding="utf-8") as f:
f.write(reg_apis_inc)
f.write(reg_apis)
f.write(unreg_apis)
-env.add_source_files(env.platform_sources, 'register_platform_apis.gen.cpp')
+env.add_source_files(env.platform_sources, "register_platform_apis.gen.cpp")
-lib = env.add_library('platform', env.platform_sources)
+lib = env.add_library("platform", env.platform_sources)
env.Prepend(LIBS=[lib])
diff --git a/platform/android/SCsub b/platform/android/SCsub
index 5ca5ce548c..ec42bc42b5 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -1,24 +1,24 @@
#!/usr/bin/env python
-Import('env')
+Import("env")
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',
- 'audio_driver_jandroid.cpp',
- 'java_godot_lib_jni.cpp',
- 'java_class_wrapper.cpp',
- 'java_godot_wrapper.cpp',
- 'java_godot_io_wrapper.cpp',
- 'jni_utils.cpp',
- 'android_keys_utils.cpp',
- 'vulkan/vk_renderer_jni.cpp',
- 'plugin/godot_plugin_jni.cpp'
+ "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",
+ "audio_driver_jandroid.cpp",
+ "java_godot_lib_jni.cpp",
+ "java_class_wrapper.cpp",
+ "java_godot_wrapper.cpp",
+ "java_godot_io_wrapper.cpp",
+ "jni_utils.cpp",
+ "android_keys_utils.cpp",
+ "display_server_android.cpp",
+ "vulkan/vulkan_context_android.cpp",
]
env_android = env.Clone()
@@ -29,30 +29,34 @@ 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'))
+android_objects.append(env_thirdparty.SharedObject("#thirdparty/misc/ifaddrs-android.cc"))
lib = env_android.add_shared_library("#bin/libgodot", [android_objects], SHLIBSUFFIX=env["SHLIBSUFFIX"])
-lib_arch_dir = ''
-if env['android_arch'] == 'armv7':
- lib_arch_dir = 'armeabi-v7a'
-elif env['android_arch'] == 'arm64v8':
- lib_arch_dir = 'arm64-v8a'
-elif env['android_arch'] == 'x86':
- lib_arch_dir = 'x86'
-elif env['android_arch'] == 'x86_64':
- lib_arch_dir = 'x86_64'
+lib_arch_dir = ""
+if env["android_arch"] == "armv7":
+ lib_arch_dir = "armeabi-v7a"
+elif env["android_arch"] == "arm64v8":
+ lib_arch_dir = "arm64-v8a"
+elif env["android_arch"] == "x86":
+ lib_arch_dir = "x86"
+elif env["android_arch"] == "x86_64":
+ lib_arch_dir = "x86_64"
else:
- print('WARN: Architecture not suitable for embedding into APK; keeping .so at \\bin')
+ print("WARN: Architecture not suitable for embedding into APK; keeping .so at \\bin")
-if lib_arch_dir != '':
- if env['target'] == 'release':
- lib_type_dir = 'release'
+if lib_arch_dir != "":
+ if env["target"] == "release":
+ lib_type_dir = "release"
else: # release_debug, debug
- lib_type_dir = 'debug'
+ lib_type_dir = "debug"
- out_dir = '#platform/android/java/lib/libs/' + lib_type_dir + '/' + lib_arch_dir
- env_android.Command(out_dir + '/libgodot_android.so', '#bin/libgodot' + env['SHLIBSUFFIX'], Move("$TARGET", "$SOURCE"))
+ out_dir = "#platform/android/java/lib/libs/" + lib_type_dir + "/" + lib_arch_dir
+ env_android.Command(
+ out_dir + "/libgodot_android.so", "#bin/libgodot" + env["SHLIBSUFFIX"], Move("$TARGET", "$SOURCE")
+ )
- stl_lib_path = str(env['ANDROID_NDK_ROOT']) + '/sources/cxx-stl/llvm-libc++/libs/' + lib_arch_dir + '/libc++_shared.so'
- env_android.Command(out_dir + '/libc++_shared.so', stl_lib_path, Copy("$TARGET", "$SOURCE"))
+ stl_lib_path = (
+ str(env["ANDROID_NDK_ROOT"]) + "/sources/cxx-stl/llvm-libc++/libs/" + lib_arch_dir + "/libc++_shared.so"
+ )
+ env_android.Command(out_dir + "/libc++_shared.so", stl_lib_path, Copy("$TARGET", "$SOURCE"))
diff --git a/platform/android/android_keys_utils.cpp b/platform/android/android_keys_utils.cpp
index 88874ba2c7..b5b4fb9a4b 100644
--- a/platform/android/android_keys_utils.cpp
+++ b/platform/android/android_keys_utils.cpp
@@ -32,9 +32,7 @@
unsigned int android_get_keysym(unsigned int p_code) {
for (int i = 0; _ak_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
-
if (_ak_to_keycode[i].keycode == p_code) {
-
return _ak_to_keycode[i].keysym;
}
}
diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h
index f076688ac8..fb442f4c54 100644
--- a/platform/android/android_keys_utils.h
+++ b/platform/android/android_keys_utils.h
@@ -154,7 +154,6 @@ enum {
};
struct _WinTranslatePair {
-
unsigned int keysym;
unsigned int keycode;
};
diff --git a/platform/android/api/api.cpp b/platform/android/api/api.cpp
index 7efb545524..1f140f7119 100644
--- a/platform/android/api/api.cpp
+++ b/platform/android/api/api.cpp
@@ -32,15 +32,19 @@
#include "core/engine.h"
#include "java_class_wrapper.h"
+#include "jni_singleton.h"
#if !defined(ANDROID_ENABLED)
-static JavaClassWrapper *java_class_wrapper = NULL;
+static JavaClassWrapper *java_class_wrapper = nullptr;
#endif
void register_android_api() {
-
#if !defined(ANDROID_ENABLED)
+ // On Android platforms, the `java_class_wrapper` instantiation and the
+ // `JNISingleton` registration occurs in
+ // `platform/android/java_godot_lib_jni.cpp#Java_org_godotengine_godot_GodotLib_setup`
java_class_wrapper = memnew(JavaClassWrapper); // Dummy
+ ClassDB::register_class<JNISingleton>();
#endif
ClassDB::register_class<JavaClass>();
@@ -49,14 +53,12 @@ void register_android_api() {
}
void unregister_android_api() {
-
#if !defined(ANDROID_ENABLED)
memdelete(java_class_wrapper);
#endif
}
void JavaClassWrapper::_bind_methods() {
-
ClassDB::bind_method(D_METHOD("wrap", "name"), &JavaClassWrapper::wrap);
}
@@ -73,7 +75,7 @@ Variant JavaObject::call(const StringName &, const Variant **, int, Callable::Ca
return Variant();
}
-JavaClassWrapper *JavaClassWrapper::singleton = NULL;
+JavaClassWrapper *JavaClassWrapper::singleton = nullptr;
Ref<JavaClass> JavaClassWrapper::wrap(const String &) {
return Ref<JavaClass>();
diff --git a/platform/android/api/api.h b/platform/android/api/api.h
index c7296d92a7..5e951b9c88 100644
--- a/platform/android/api/api.h
+++ b/platform/android/api/api.h
@@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef ANDROID_API_H
+#define ANDROID_API_H
+
void register_android_api();
void unregister_android_api();
+
+#endif // ANDROID_API_H
diff --git a/platform/android/api/java_class_wrapper.h b/platform/android/api/java_class_wrapper.h
index d7322deb81..e34f2a9f69 100644
--- a/platform/android/api/java_class_wrapper.h
+++ b/platform/android/api/java_class_wrapper.h
@@ -43,7 +43,6 @@ class JavaObject;
#endif
class JavaClass : public Reference {
-
GDCLASS(JavaClass, Reference);
#ifdef ANDROID_ENABLED
@@ -68,7 +67,6 @@ class JavaClass : public Reference {
Map<StringName, Variant> constant_map;
struct MethodInfo {
-
bool _static;
Vector<uint32_t> param_types;
Vector<StringName> param_sigs;
@@ -77,15 +75,17 @@ class JavaClass : public Reference {
};
_FORCE_INLINE_ static void _convert_to_variant_type(int p_sig, Variant::Type &r_type, float &likelihood) {
-
likelihood = 1.0;
r_type = Variant::NIL;
switch (p_sig) {
-
- case ARG_TYPE_VOID: r_type = Variant::NIL; break;
+ case ARG_TYPE_VOID:
+ r_type = Variant::NIL;
+ break;
case ARG_TYPE_BOOLEAN | ARG_NUMBER_CLASS_BIT:
- case ARG_TYPE_BOOLEAN: r_type = Variant::BOOL; break;
+ case ARG_TYPE_BOOLEAN:
+ r_type = Variant::BOOL;
+ break;
case ARG_TYPE_BYTE | ARG_NUMBER_CLASS_BIT:
case ARG_TYPE_BYTE:
r_type = Variant::INT;
@@ -121,10 +121,18 @@ class JavaClass : public Reference {
r_type = Variant::FLOAT;
likelihood = 0.5;
break;
- case ARG_TYPE_STRING: r_type = Variant::STRING; break;
- case ARG_TYPE_CLASS: r_type = Variant::OBJECT; break;
- case ARG_ARRAY_BIT | ARG_TYPE_VOID: r_type = Variant::NIL; break;
- case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: r_type = Variant::ARRAY; break;
+ case ARG_TYPE_STRING:
+ r_type = Variant::STRING;
+ break;
+ case ARG_TYPE_CLASS:
+ r_type = Variant::OBJECT;
+ break;
+ case ARG_ARRAY_BIT | ARG_TYPE_VOID:
+ r_type = Variant::NIL;
+ break;
+ case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN:
+ r_type = Variant::ARRAY;
+ break;
case ARG_ARRAY_BIT | ARG_TYPE_BYTE:
r_type = Variant::PACKED_BYTE_ARRAY;
likelihood = 1.0;
@@ -153,8 +161,12 @@ class JavaClass : public Reference {
r_type = Variant::PACKED_FLOAT32_ARRAY;
likelihood = 0.5;
break;
- case ARG_ARRAY_BIT | ARG_TYPE_STRING: r_type = Variant::PACKED_STRING_ARRAY; break;
- case ARG_ARRAY_BIT | ARG_TYPE_CLASS: r_type = Variant::ARRAY; break;
+ case ARG_ARRAY_BIT | ARG_TYPE_STRING:
+ r_type = Variant::PACKED_STRING_ARRAY;
+ break;
+ case ARG_ARRAY_BIT | ARG_TYPE_CLASS:
+ r_type = Variant::ARRAY;
+ break;
}
}
@@ -174,7 +186,6 @@ public:
};
class JavaObject : public Reference {
-
GDCLASS(JavaObject, Reference);
#ifdef ANDROID_ENABLED
@@ -194,7 +205,6 @@ public:
};
class JavaClassWrapper : public Object {
-
GDCLASS(JavaClassWrapper, Object);
#ifdef ANDROID_ENABLED
@@ -236,7 +246,7 @@ public:
Ref<JavaClass> wrap(const String &p_class);
#ifdef ANDROID_ENABLED
- JavaClassWrapper(jobject p_activity = NULL);
+ JavaClassWrapper(jobject p_activity = nullptr);
#else
JavaClassWrapper();
#endif
diff --git a/platform/android/api/jni_singleton.h b/platform/android/api/jni_singleton.h
new file mode 100644
index 0000000000..ed69f8d6e4
--- /dev/null
+++ b/platform/android/api/jni_singleton.h
@@ -0,0 +1,223 @@
+/*************************************************************************/
+/* jni_singleton.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 JNI_SINGLETON_H
+#define JNI_SINGLETON_H
+
+#include <core/engine.h>
+#include <core/variant.h>
+#ifdef ANDROID_ENABLED
+#include <platform/android/jni_utils.h>
+#endif
+
+class JNISingleton : public Object {
+ GDCLASS(JNISingleton, Object);
+
+#ifdef ANDROID_ENABLED
+ struct MethodData {
+ jmethodID method;
+ Variant::Type ret_type;
+ Vector<Variant::Type> argtypes;
+ };
+
+ jobject instance;
+ Map<StringName, MethodData> method_map;
+#endif
+
+public:
+ virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+#ifdef ANDROID_ENABLED
+ Map<StringName, MethodData>::Element *E = method_map.find(p_method);
+
+ // Check the method we're looking for is in the JNISingleton map and that
+ // the arguments match.
+ bool call_error = !E || E->get().argtypes.size() != p_argcount;
+ if (!call_error) {
+ for (int i = 0; i < p_argcount; i++) {
+ if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) {
+ call_error = true;
+ break;
+ }
+ }
+ }
+
+ if (call_error) {
+ // The method is not in this map, defaulting to the regular instance calls.
+ return Object::call(p_method, p_args, p_argcount, r_error);
+ }
+
+ ERR_FAIL_COND_V(!instance, Variant());
+
+ r_error.error = Callable::CallError::CALL_OK;
+
+ jvalue *v = nullptr;
+
+ if (p_argcount) {
+ v = (jvalue *)alloca(sizeof(jvalue) * p_argcount);
+ }
+
+ JNIEnv *env = ThreadAndroid::get_env();
+
+ int res = env->PushLocalFrame(16);
+
+ ERR_FAIL_COND_V(res != 0, Variant());
+
+ List<jobject> to_erase;
+ for (int i = 0; i < p_argcount; i++) {
+ jvalret vr = _variant_to_jvalue(env, E->get().argtypes[i], p_args[i]);
+ v[i] = vr.val;
+ if (vr.obj)
+ to_erase.push_back(vr.obj);
+ }
+
+ Variant ret;
+
+ switch (E->get().ret_type) {
+ case Variant::NIL: {
+ env->CallVoidMethodA(instance, E->get().method, v);
+ } break;
+ case Variant::BOOL: {
+ ret = env->CallBooleanMethodA(instance, E->get().method, v) == JNI_TRUE;
+ } break;
+ case Variant::INT: {
+ ret = env->CallIntMethodA(instance, E->get().method, v);
+ } break;
+ case Variant::FLOAT: {
+ ret = env->CallFloatMethodA(instance, E->get().method, v);
+ } break;
+ case Variant::STRING: {
+ jobject o = env->CallObjectMethodA(instance, E->get().method, v);
+ ret = jstring_to_string((jstring)o, env);
+ env->DeleteLocalRef(o);
+ } break;
+ case Variant::PACKED_STRING_ARRAY: {
+ jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v);
+
+ ret = _jobject_to_variant(env, arr);
+
+ env->DeleteLocalRef(arr);
+ } break;
+ case Variant::PACKED_INT32_ARRAY: {
+ jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v);
+
+ int fCount = env->GetArrayLength(arr);
+ Vector<int> sarr;
+ sarr.resize(fCount);
+
+ int *w = sarr.ptrw();
+ env->GetIntArrayRegion(arr, 0, fCount, w);
+ ret = sarr;
+ env->DeleteLocalRef(arr);
+ } break;
+ case Variant::PACKED_FLOAT32_ARRAY: {
+ jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v);
+
+ int fCount = env->GetArrayLength(arr);
+ Vector<float> sarr;
+ sarr.resize(fCount);
+
+ float *w = sarr.ptrw();
+ env->GetFloatArrayRegion(arr, 0, fCount, w);
+ ret = sarr;
+ env->DeleteLocalRef(arr);
+ } break;
+
+#ifndef _MSC_VER
+#warning This is missing 64 bits arrays, I have no idea how to do it in JNI
+#endif
+ case Variant::DICTIONARY: {
+ jobject obj = env->CallObjectMethodA(instance, E->get().method, v);
+ ret = _jobject_to_variant(env, obj);
+ env->DeleteLocalRef(obj);
+
+ } break;
+ default: {
+ env->PopLocalFrame(nullptr);
+ ERR_FAIL_V(Variant());
+ } break;
+ }
+
+ while (to_erase.size()) {
+ env->DeleteLocalRef(to_erase.front()->get());
+ to_erase.pop_front();
+ }
+
+ env->PopLocalFrame(nullptr);
+
+ return ret;
+#else // ANDROID_ENABLED
+
+ // Defaulting to the regular instance calls.
+ return Object::call(p_method, p_args, p_argcount, r_error);
+#endif
+ }
+
+#ifdef ANDROID_ENABLED
+ jobject get_instance() const {
+ return instance;
+ }
+
+ void set_instance(jobject p_instance) {
+ instance = p_instance;
+ }
+
+ void add_method(const StringName &p_name, jmethodID p_method, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) {
+ MethodData md;
+ md.method = p_method;
+ md.argtypes = p_args;
+ md.ret_type = p_ret_type;
+ method_map[p_name] = md;
+ }
+
+ void add_signal(const StringName &p_name, const Vector<Variant::Type> &p_args) {
+ if (p_args.size() == 0)
+ ADD_SIGNAL(MethodInfo(p_name));
+ else if (p_args.size() == 1)
+ ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1")));
+ else if (p_args.size() == 2)
+ ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2")));
+ else if (p_args.size() == 3)
+ ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3")));
+ else if (p_args.size() == 4)
+ ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"), PropertyInfo(p_args[3], "arg4")));
+ else if (p_args.size() == 5)
+ ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"), PropertyInfo(p_args[3], "arg4"), PropertyInfo(p_args[4], "arg5")));
+ }
+
+#endif
+
+ JNISingleton() {
+#ifdef ANDROID_ENABLED
+ instance = nullptr;
+#endif
+ }
+};
+
+#endif // JNI_SINGLETON_H
diff --git a/platform/android/audio_driver_jandroid.cpp b/platform/android/audio_driver_jandroid.cpp
index e94dad9ac6..09c981b3fa 100644
--- a/platform/android/audio_driver_jandroid.cpp
+++ b/platform/android/audio_driver_jandroid.cpp
@@ -34,7 +34,7 @@
#include "core/project_settings.h"
#include "thread_jandroid.h"
-AudioDriverAndroid *AudioDriverAndroid::s_ad = NULL;
+AudioDriverAndroid *AudioDriverAndroid::s_ad = nullptr;
jobject AudioDriverAndroid::io;
jmethodID AudioDriverAndroid::_init_audio;
@@ -46,18 +46,16 @@ jclass AudioDriverAndroid::cls;
int AudioDriverAndroid::audioBufferFrames = 0;
int AudioDriverAndroid::mix_rate = 44100;
bool AudioDriverAndroid::quit = false;
-jobject AudioDriverAndroid::audioBuffer = NULL;
-void *AudioDriverAndroid::audioBufferPinned = NULL;
+jobject AudioDriverAndroid::audioBuffer = nullptr;
+void *AudioDriverAndroid::audioBufferPinned = nullptr;
Mutex AudioDriverAndroid::mutex;
-int32_t *AudioDriverAndroid::audioBuffer32 = NULL;
+int32_t *AudioDriverAndroid::audioBuffer32 = nullptr;
const char *AudioDriverAndroid::get_name() const {
-
return "Android";
}
Error AudioDriverAndroid::init() {
-
/*
// TODO: pass in/return a (Java) device ID, also whether we're opening for input or output
this->spec.samples = Android_JNI_OpenAudioDevice(this->spec.freq, this->spec.format == AUDIO_U8 ? 0 : 1, this->spec.channels, this->spec.samples);
@@ -75,15 +73,15 @@ Error AudioDriverAndroid::init() {
// __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
JNIEnv *env = ThreadAndroid::get_env();
- int mix_rate = GLOBAL_DEF_RST("audio/mix_rate", 44100);
+ int mix_rate = GLOBAL_GET("audio/mix_rate");
- int latency = GLOBAL_DEF_RST("audio/output_latency", 25);
+ int latency = GLOBAL_GET("audio/output_latency");
unsigned int buffer_size = next_power_of_2(latency * mix_rate / 1000);
print_verbose("Audio buffer size: " + itos(buffer_size));
audioBuffer = env->CallObjectMethod(io, _init_audio, mix_rate, buffer_size);
- ERR_FAIL_COND_V(audioBuffer == NULL, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(audioBuffer == nullptr, ERR_INVALID_PARAMETER);
audioBuffer = env->NewGlobalRef(audioBuffer);
@@ -100,7 +98,6 @@ void AudioDriverAndroid::start() {
}
void AudioDriverAndroid::setup(jobject p_io) {
-
JNIEnv *env = ThreadAndroid::get_env();
io = p_io;
@@ -114,10 +111,8 @@ void AudioDriverAndroid::setup(jobject p_io) {
}
void AudioDriverAndroid::thread_func(JNIEnv *env) {
-
jclass cls = env->FindClass("org/godotengine/godot/Godot");
if (cls) {
-
cls = (jclass)env->NewGlobalRef(cls);
}
jfieldID fid = env->GetStaticFieldID(cls, "io", "Lorg/godotengine/godot/GodotIO;");
@@ -128,24 +123,20 @@ void AudioDriverAndroid::thread_func(JNIEnv *env) {
_write_buffer = env->GetMethodID(lcls, "audioWriteShortBuffer", "([S)V");
while (!quit) {
-
int16_t *ptr = (int16_t *)audioBufferPinned;
int fc = audioBufferFrames;
if (!s_ad->active || mutex.try_lock() != OK) {
-
for (int i = 0; i < fc; i++) {
ptr[i] = 0;
}
} else {
-
s_ad->audio_server_process(fc / 2, audioBuffer32);
mutex.unlock();
for (int i = 0; i < fc; i++) {
-
ptr[i] = audioBuffer32[i] >> 16;
}
}
@@ -155,47 +146,40 @@ void AudioDriverAndroid::thread_func(JNIEnv *env) {
}
int AudioDriverAndroid::get_mix_rate() const {
-
return mix_rate;
}
AudioDriver::SpeakerMode AudioDriverAndroid::get_speaker_mode() const {
-
return SPEAKER_MODE_STEREO;
}
void AudioDriverAndroid::lock() {
-
mutex.lock();
}
void AudioDriverAndroid::unlock() {
-
mutex.unlock();
}
void AudioDriverAndroid::finish() {
-
JNIEnv *env = ThreadAndroid::get_env();
env->CallVoidMethod(io, _quit);
if (audioBuffer) {
env->DeleteGlobalRef(audioBuffer);
- audioBuffer = NULL;
- audioBufferPinned = NULL;
+ audioBuffer = nullptr;
+ audioBufferPinned = nullptr;
}
active = false;
}
void AudioDriverAndroid::set_pause(bool p_pause) {
-
JNIEnv *env = ThreadAndroid::get_env();
env->CallVoidMethod(io, _pause, p_pause);
}
AudioDriverAndroid::AudioDriverAndroid() {
-
s_ad = this;
active = false;
}
diff --git a/platform/android/audio_driver_jandroid.h b/platform/android/audio_driver_jandroid.h
index b1cc3f9aa0..953ade9311 100644
--- a/platform/android/audio_driver_jandroid.h
+++ b/platform/android/audio_driver_jandroid.h
@@ -36,7 +36,6 @@
#include "java_godot_lib_jni.h"
class AudioDriverAndroid : public AudioDriver {
-
static Mutex mutex;
static AudioDriverAndroid *s_ad;
static jobject io;
diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp
index 222120f81f..740e9a3132 100644
--- a/platform/android/audio_driver_opensl.cpp
+++ b/platform/android/audio_driver_opensl.cpp
@@ -39,7 +39,6 @@
void AudioDriverOpenSL::_buffer_callback(
SLAndroidSimpleBufferQueueItf queueItf) {
-
bool mix = true;
if (pause) {
@@ -51,7 +50,6 @@ void AudioDriverOpenSL::_buffer_callback(
if (mix) {
audio_server_process(buffer_size, mixdown_buffer);
} else {
-
int32_t *src_buff = mixdown_buffer;
for (unsigned int i = 0; i < buffer_size * 2; i++) {
src_buff[i] = 0;
@@ -67,7 +65,6 @@ void AudioDriverOpenSL::_buffer_callback(
last_free = (last_free + 1) % BUFFER_COUNT;
for (unsigned int i = 0; i < buffer_size * 2; i++) {
-
ptr[i] = src_buff[i] >> 16;
}
@@ -77,26 +74,23 @@ void AudioDriverOpenSL::_buffer_callback(
void AudioDriverOpenSL::_buffer_callbacks(
SLAndroidSimpleBufferQueueItf queueItf,
void *pContext) {
-
AudioDriverOpenSL *ad = (AudioDriverOpenSL *)pContext;
ad->_buffer_callback(queueItf);
}
-AudioDriverOpenSL *AudioDriverOpenSL::s_ad = NULL;
+AudioDriverOpenSL *AudioDriverOpenSL::s_ad = nullptr;
const char *AudioDriverOpenSL::get_name() const {
-
return "Android";
}
Error AudioDriverOpenSL::init() {
-
SLresult res;
SLEngineOption EngineOption[] = {
{ (SLuint32)SL_ENGINEOPTION_THREADSAFE, (SLuint32)SL_BOOLEAN_TRUE }
};
- res = slCreateEngine(&sl, 1, EngineOption, 0, NULL, NULL);
+ res = slCreateEngine(&sl, 1, EngineOption, 0, nullptr, nullptr);
ERR_FAIL_COND_V_MSG(res != SL_RESULT_SUCCESS, ERR_INVALID_PARAMETER, "Could not initialize OpenSL.");
res = (*sl)->Realize(sl, SL_BOOLEAN_FALSE);
@@ -106,7 +100,6 @@ Error AudioDriverOpenSL::init() {
}
void AudioDriverOpenSL::start() {
-
active = false;
SLresult res;
@@ -114,7 +107,6 @@ void AudioDriverOpenSL::start() {
buffer_size = 1024;
for (int i = 0; i < BUFFER_COUNT; i++) {
-
buffers[i] = memnew_arr(int16_t, buffer_size * 2);
memset(buffers[i], 0, buffer_size * 4);
}
@@ -161,7 +153,7 @@ void AudioDriverOpenSL::start() {
locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
locator_outputmix.outputMix = OutputMix;
audioSink.pLocator = (void *)&locator_outputmix;
- audioSink.pFormat = NULL;
+ audioSink.pFormat = nullptr;
/* Initialize the context for Buffer queue callbacks */
//cntxt.pDataBase = (void*)&pcmData;
//cntxt.pData = cntxt.pDataBase;
@@ -204,7 +196,6 @@ void AudioDriverOpenSL::start() {
}
void AudioDriverOpenSL::_record_buffer_callback(SLAndroidSimpleBufferQueueItf queueItf) {
-
for (int i = 0; i < rec_buffer.size(); i++) {
int32_t sample = rec_buffer[i] << 16;
input_buffer_write(sample);
@@ -216,21 +207,19 @@ void AudioDriverOpenSL::_record_buffer_callback(SLAndroidSimpleBufferQueueItf qu
}
void AudioDriverOpenSL::_record_buffer_callbacks(SLAndroidSimpleBufferQueueItf queueItf, void *pContext) {
-
AudioDriverOpenSL *ad = (AudioDriverOpenSL *)pContext;
ad->_record_buffer_callback(queueItf);
}
Error AudioDriverOpenSL::capture_init_device() {
-
SLDataLocator_IODevice loc_dev = {
SL_DATALOCATOR_IODEVICE,
SL_IODEVICE_AUDIOINPUT,
SL_DEFAULTDEVICEID_AUDIOINPUT,
- NULL
+ nullptr
};
- SLDataSource recSource = { &loc_dev, NULL };
+ SLDataSource recSource = { &loc_dev, nullptr };
SLDataLocator_AndroidSimpleBufferQueue loc_bq = {
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
@@ -291,7 +280,6 @@ Error AudioDriverOpenSL::capture_init_device() {
}
Error AudioDriverOpenSL::capture_start() {
-
if (OS::get_singleton()->request_permission("RECORD_AUDIO")) {
return capture_init_device();
}
@@ -300,7 +288,6 @@ Error AudioDriverOpenSL::capture_start() {
}
Error AudioDriverOpenSL::capture_stop() {
-
SLuint32 state;
SLresult res = (*recordItf)->GetRecordState(recordItf, &state);
ERR_FAIL_COND_V(res != SL_RESULT_SUCCESS, ERR_CANT_OPEN);
@@ -317,34 +304,28 @@ Error AudioDriverOpenSL::capture_stop() {
}
int AudioDriverOpenSL::get_mix_rate() const {
-
return 44100; // hardcoded for Android, as selected by SL_SAMPLINGRATE_44_1
}
AudioDriver::SpeakerMode AudioDriverOpenSL::get_speaker_mode() const {
-
return SPEAKER_MODE_STEREO;
}
void AudioDriverOpenSL::lock() {
-
if (active)
mutex.lock();
}
void AudioDriverOpenSL::unlock() {
-
if (active)
mutex.unlock();
}
void AudioDriverOpenSL::finish() {
-
(*sl)->Destroy(sl);
}
void AudioDriverOpenSL::set_pause(bool p_pause) {
-
pause = p_pause;
if (active) {
diff --git a/platform/android/audio_driver_opensl.h b/platform/android/audio_driver_opensl.h
index 569e2aa54b..9858a40822 100644
--- a/platform/android/audio_driver_opensl.h
+++ b/platform/android/audio_driver_opensl.h
@@ -38,7 +38,6 @@
#include <SLES/OpenSLES_Android.h>
class AudioDriverOpenSL : public AudioDriver {
-
bool active;
Mutex mutex;
diff --git a/platform/android/detect.py b/platform/android/detect.py
index ff3ca0706c..6da1e5f3d6 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_NDK_ROOT" in os.environ
def get_platform(platform):
@@ -24,33 +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)),
- ('ndk_platform', 'Target platform (android-<api>, e.g. "android-18")', "android-18"),
- EnumVariable('android_arch', 'Target architecture', "armv7", ('armv7', 'arm64v8', 'x86', 'x86_64')),
- BoolVariable('android_neon', 'Enable NEON support (armv7 only)', True),
+ ("ANDROID_NDK_ROOT", "Path to the Android NDK", os.environ.get("ANDROID_NDK_ROOT", 0)),
+ ("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),
]
def get_flags():
return [
- ('tools', False),
+ ("tools", False),
]
def create(env):
- tools = env['TOOLS']
+ tools = env["TOOLS"]
if "mingw" in tools:
- tools.remove('mingw')
+ tools.remove("mingw")
if "applelink" in tools:
tools.remove("applelink")
- env.Tool('gcc')
+ env.Tool("gcc")
return env.Clone(tools=tools)
def configure(env):
# Workaround for MinGW. See:
# http://www.scons.org/wiki/LongCmdLinesOnWin32
- if (os.name == "nt"):
+ if os.name == "nt":
import subprocess
@@ -58,8 +58,15 @@ def configure(env):
# print("SPAWNED : " + cmdline)
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
- proc = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE, startupinfo=startupinfo, shell=False, env=env)
+ proc = subprocess.Popen(
+ cmdline,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ startupinfo=startupinfo,
+ shell=False,
+ env=env,
+ )
data, err = proc.communicate()
rv = proc.wait()
if rv:
@@ -70,7 +77,7 @@ def configure(env):
def mySpawn(sh, escape, cmd, args, env):
- newargs = ' '.join(args[1:])
+ newargs = " ".join(args[1:])
cmdline = cmd + " " + newargs
rv = 0
@@ -85,50 +92,54 @@ def configure(env):
return rv
- env['SPAWN'] = mySpawn
+ env["SPAWN"] = mySpawn
# Architecture
- if env['android_arch'] not in ['armv7', 'arm64v8', 'x86', 'x86_64']:
- env['android_arch'] = 'armv7'
+ if env["android_arch"] not in ["armv7", "arm64v8", "x86", "x86_64"]:
+ env["android_arch"] = "armv7"
neon_text = ""
- if env["android_arch"] == "armv7" and env['android_neon']:
+ if env["android_arch"] == "armv7" and env["android_neon"]:
neon_text = " (with NEON)"
- print("Building for Android (" + env['android_arch'] + ")" + neon_text)
+ print("Building for Android, platform " + env["ndk_platform"] + " (" + env["android_arch"] + ")" + neon_text)
can_vectorize = True
- if env['android_arch'] == 'x86':
- env['ARCH'] = 'arch-x86'
+ if env["android_arch"] == "x86":
+ env["ARCH"] = "arch-x86"
env.extra_suffix = ".x86" + env.extra_suffix
target_subpath = "x86-4.9"
abi_subpath = "i686-linux-android"
arch_subpath = "x86"
env["x86_libtheora_opt_gcc"] = True
- if env['android_arch'] == 'x86_64':
+ 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")
+ print(
+ "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'
+ env["ARCH"] = "arch-x86_64"
env.extra_suffix = ".x86_64" + env.extra_suffix
target_subpath = "x86_64-4.9"
abi_subpath = "x86_64-linux-android"
arch_subpath = "x86_64"
env["x86_libtheora_opt_gcc"] = True
elif env["android_arch"] == "armv7":
- env['ARCH'] = 'arch-arm'
+ env["ARCH"] = "arch-arm"
target_subpath = "arm-linux-androideabi-4.9"
abi_subpath = "arm-linux-androideabi"
arch_subpath = "armeabi-v7a"
- if env['android_neon']:
+ if env["android_neon"]:
env.extra_suffix = ".armv7.neon" + env.extra_suffix
else:
env.extra_suffix = ".armv7" + env.extra_suffix
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")
+ print(
+ "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'
+ env["ARCH"] = "arch-arm64"
target_subpath = "aarch64-linux-android-4.9"
abi_subpath = "aarch64-linux-android"
arch_subpath = "arm64-v8a"
@@ -136,40 +147,40 @@ def configure(env):
# Build type
- if (env["target"].startswith("release")):
- if (env["optimize"] == "speed"): # optimize for speed (default)
- env.Append(LINKFLAGS=['-O2'])
- env.Append(CCFLAGS=['-O2', '-fomit-frame-pointer'])
- env.Append(CPPDEFINES=['NDEBUG'])
+ if env["target"].startswith("release"):
+ if env["optimize"] == "speed": # optimize for speed (default)
+ env.Append(LINKFLAGS=["-O2"])
+ env.Append(CCFLAGS=["-O2", "-fomit-frame-pointer"])
+ env.Append(CPPDEFINES=["NDEBUG"])
else: # optimize for size
- env.Append(CCFLAGS=['-Os'])
- env.Append(CPPDEFINES=['NDEBUG'])
- env.Append(LINKFLAGS=['-Os'])
-
- if (can_vectorize):
- env.Append(CCFLAGS=['-ftree-vectorize'])
- if (env["target"] == "release_debug"):
- env.Append(CPPDEFINES=['DEBUG_ENABLED'])
- 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(CPPFLAGS=['-UNDEBUG'])
+ env.Append(CCFLAGS=["-Os"])
+ env.Append(CPPDEFINES=["NDEBUG"])
+ env.Append(LINKFLAGS=["-Os"])
+
+ if can_vectorize:
+ env.Append(CCFLAGS=["-ftree-vectorize"])
+ if env["target"] == "release_debug":
+ env.Append(CPPDEFINES=["DEBUG_ENABLED"])
+ 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(CPPFLAGS=["-UNDEBUG"])
# Compiler configuration
- env['SHLIBSUFFIX'] = '.so'
+ env["SHLIBSUFFIX"] = ".so"
- if env['PLATFORM'] == 'win32':
- env.Tool('gcc')
+ if env["PLATFORM"] == "win32":
+ env.Tool("gcc")
env.use_windows_spawn_fix()
- if (sys.platform.startswith("linux")):
+ if sys.platform.startswith("linux"):
host_subpath = "linux-x86_64"
- elif (sys.platform.startswith("darwin")):
+ elif sys.platform.startswith("darwin"):
host_subpath = "darwin-x86_64"
- elif (sys.platform.startswith('win')):
- if (platform.machine().endswith('64')):
+ elif sys.platform.startswith("win"):
+ if platform.machine().endswith("64"):
host_subpath = "windows-x86_64"
else:
host_subpath = "windows"
@@ -179,22 +190,22 @@ def configure(env):
tools_path = gcc_toolchain_path + "/" + abi_subpath + "/bin"
# For Clang to find NDK tools in preference of those system-wide
- env.PrependENVPath('PATH', tools_path)
+ env.PrependENVPath("PATH", tools_path)
ccache_path = os.environ.get("CCACHE")
if ccache_path is None:
- env['CC'] = compiler_path + '/clang'
- env['CXX'] = compiler_path + '/clang++'
+ env["CC"] = compiler_path + "/clang"
+ env["CXX"] = compiler_path + "/clang++"
else:
# there aren't any ccache wrappers available for Android,
# to enable caching we need to prepend the path to the ccache binary
- env['CC'] = ccache_path + ' ' + compiler_path + '/clang'
- env['CXX'] = ccache_path + ' ' + compiler_path + '/clang++'
- env['AR'] = tools_path + "/ar"
- env['RANLIB'] = tools_path + "/ranlib"
- env['AS'] = tools_path + "/as"
+ env["CC"] = ccache_path + " " + compiler_path + "/clang"
+ env["CXX"] = ccache_path + " " + compiler_path + "/clang++"
+ env["AR"] = tools_path + "/ar"
+ env["RANLIB"] = tools_path + "/ranlib"
+ env["AS"] = tools_path + "/as"
- common_opts = ['-fno-integrated-as', '-gcc-toolchain', gcc_toolchain_path]
+ common_opts = ["-fno-integrated-as", "-gcc-toolchain", gcc_toolchain_path]
# Compile flags
@@ -202,14 +213,14 @@ 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']:
- env.Append(CXXFLAGS=['-frtti'])
+ if env["tools"]:
+ env.Append(CXXFLAGS=["-frtti"])
else:
- env.Append(CXXFLAGS=['-fno-rtti', '-fno-exceptions'])
+ env.Append(CXXFLAGS=["-fno-rtti", "-fno-exceptions"])
# Don't use dynamic_cast, necessary with no-rtti.
- env.Append(CPPDEFINES=['NO_SAFE_CAST'])
+ env.Append(CPPDEFINES=["NO_SAFE_CAST"])
- lib_sysroot = env["ANDROID_NDK_ROOT"] + "/platforms/" + env['ndk_platform'] + "/" + env['ARCH']
+ lib_sysroot = env["ANDROID_NDK_ROOT"] + "/platforms/" + env["ndk_platform"] + "/" + env["ARCH"]
# Using NDK unified headers (NDK r15+)
sysroot = env["ANDROID_NDK_ROOT"] + "/sysroot"
@@ -217,35 +228,37 @@ def configure(env):
env.Append(CPPFLAGS=["-isystem", sysroot + "/usr/include/" + abi_subpath])
env.Append(CPPFLAGS=["-isystem", env["ANDROID_NDK_ROOT"] + "/sources/android/support/include"])
# For unified headers this define has to be set manually
- env.Append(CPPDEFINES=[('__ANDROID_API__', str(get_platform(env['ndk_platform'])))])
+ 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())
- env.Append(CPPDEFINES=['NO_STATVFS', 'GLES_ENABLED'])
+ env.Append(
+ CCFLAGS="-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing".split()
+ )
+ env.Append(CPPDEFINES=["NO_STATVFS", "GLES_ENABLED"])
- env['neon_enabled'] = False
- if env['android_arch'] == 'x86':
- target_opts = ['-target', 'i686-none-linux-android']
+ env["neon_enabled"] = False
+ if env["android_arch"] == "x86":
+ target_opts = ["-target", "i686-none-linux-android"]
# The NDK adds this if targeting API < 21, so we can drop it when Godot targets it at least
- env.Append(CCFLAGS=['-mstackrealign'])
+ env.Append(CCFLAGS=["-mstackrealign"])
- elif env['android_arch'] == 'x86_64':
- target_opts = ['-target', 'x86_64-none-linux-android']
+ elif env["android_arch"] == "x86_64":
+ target_opts = ["-target", "x86_64-none-linux-android"]
elif env["android_arch"] == "armv7":
- target_opts = ['-target', 'armv7-none-linux-androideabi']
- env.Append(CCFLAGS='-march=armv7-a -mfloat-abi=softfp'.split())
- env.Append(CPPDEFINES=['__ARM_ARCH_7__', '__ARM_ARCH_7A__'])
- if env['android_neon']:
- env['neon_enabled'] = True
- env.Append(CCFLAGS=['-mfpu=neon'])
- env.Append(CPPDEFINES=['__ARM_NEON__'])
+ target_opts = ["-target", "armv7-none-linux-androideabi"]
+ env.Append(CCFLAGS="-march=armv7-a -mfloat-abi=softfp".split())
+ env.Append(CPPDEFINES=["__ARM_ARCH_7__", "__ARM_ARCH_7A__"])
+ if env["android_neon"]:
+ env["neon_enabled"] = True
+ env.Append(CCFLAGS=["-mfpu=neon"])
+ env.Append(CPPDEFINES=["__ARM_NEON__"])
else:
- env.Append(CCFLAGS=['-mfpu=vfpv3-d16'])
+ env.Append(CCFLAGS=["-mfpu=vfpv3-d16"])
elif env["android_arch"] == "arm64v8":
- target_opts = ['-target', 'aarch64-none-linux-android']
- env.Append(CCFLAGS=['-mfix-cortex-a53-835769'])
- env.Append(CPPDEFINES=['__ARM_ARCH_8A__'])
+ target_opts = ["-target", "aarch64-none-linux-android"]
+ env.Append(CCFLAGS=["-mfix-cortex-a53-835769"])
+ env.Append(CPPDEFINES=["__ARM_ARCH_8A__"])
env.Append(CCFLAGS=target_opts)
env.Append(CCFLAGS=common_opts)
@@ -254,29 +267,55 @@ def configure(env):
ndk_version = get_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++'])
+ env.Append(LINKFLAGS=["-Wl,--exclude-libs,libgcc.a", "-Wl,--exclude-libs,libatomic.a", "-nostdlib++"])
else:
- env.Append(LINKFLAGS=[env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++/libs/" + arch_subpath + "/libandroid_support.a"])
- env.Append(LINKFLAGS=['-shared', '--sysroot=' + lib_sysroot, '-Wl,--warn-shared-textrel'])
+ env.Append(
+ LINKFLAGS=[
+ env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++/libs/" + arch_subpath + "/libandroid_support.a"
+ ]
+ )
+ env.Append(LINKFLAGS=["-shared", "--sysroot=" + lib_sysroot, "-Wl,--warn-shared-textrel"])
env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++/libs/" + arch_subpath + "/"])
- env.Append(LINKFLAGS=[env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++/libs/" + arch_subpath + "/libc++_shared.so"])
+ env.Append(
+ LINKFLAGS=[env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++/libs/" + arch_subpath + "/libc++_shared.so"]
+ )
if env["android_arch"] == "armv7":
- env.Append(LINKFLAGS='-Wl,--fix-cortex-a8'.split())
- env.Append(LINKFLAGS='-Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now'.split())
- env.Append(LINKFLAGS='-Wl,-soname,libgodot_android.so -Wl,--gc-sections'.split())
+ env.Append(LINKFLAGS="-Wl,--fix-cortex-a8".split())
+ env.Append(LINKFLAGS="-Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now".split())
+ env.Append(LINKFLAGS="-Wl,-soname,libgodot_android.so -Wl,--gc-sections".split())
env.Append(LINKFLAGS=target_opts)
env.Append(LINKFLAGS=common_opts)
- env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"] + '/toolchains/' + target_subpath + '/prebuilt/' +
- host_subpath + '/lib/gcc/' + abi_subpath + '/4.9.x'])
- env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"] +
- '/toolchains/' + target_subpath + '/prebuilt/' + host_subpath + '/' + abi_subpath + '/lib'])
-
- env.Prepend(CPPPATH=['#platform/android'])
- env.Append(CPPDEFINES=['ANDROID_ENABLED', 'UNIX_ENABLED', 'NO_FCNTL'])
- env.Append(LIBS=['OpenSLES', 'EGL', 'GLESv3', 'GLESv2', 'android', 'log', 'z', 'dl'])
+ env.Append(
+ LIBPATH=[
+ env["ANDROID_NDK_ROOT"]
+ + "/toolchains/"
+ + target_subpath
+ + "/prebuilt/"
+ + host_subpath
+ + "/lib/gcc/"
+ + abi_subpath
+ + "/4.9.x"
+ ]
+ )
+ env.Append(
+ LIBPATH=[
+ env["ANDROID_NDK_ROOT"]
+ + "/toolchains/"
+ + target_subpath
+ + "/prebuilt/"
+ + host_subpath
+ + "/"
+ + abi_subpath
+ + "/lib"
+ ]
+ )
+
+ env.Prepend(CPPPATH=["#platform/android"])
+ env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED", "VULKAN_ENABLED", "NO_FCNTL"])
+ env.Append(LIBS=["OpenSLES", "EGL", "GLESv2", "vulkan", "android", "log", "z", "dl"])
# Return NDK version string in source.properties (adapted from the Chromium project).
diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp
index ebcac884db..ca312b427f 100644
--- a/platform/android/dir_access_jandroid.cpp
+++ b/platform/android/dir_access_jandroid.cpp
@@ -34,20 +34,18 @@
#include "string_android.h"
#include "thread_jandroid.h"
-jobject DirAccessJAndroid::io = NULL;
-jclass DirAccessJAndroid::cls = NULL;
-jmethodID DirAccessJAndroid::_dir_open = NULL;
-jmethodID DirAccessJAndroid::_dir_next = NULL;
-jmethodID DirAccessJAndroid::_dir_close = NULL;
-jmethodID DirAccessJAndroid::_dir_is_dir = NULL;
+jobject DirAccessJAndroid::io = nullptr;
+jclass DirAccessJAndroid::cls = nullptr;
+jmethodID DirAccessJAndroid::_dir_open = nullptr;
+jmethodID DirAccessJAndroid::_dir_next = nullptr;
+jmethodID DirAccessJAndroid::_dir_close = nullptr;
+jmethodID DirAccessJAndroid::_dir_is_dir = nullptr;
DirAccess *DirAccessJAndroid::create_fs() {
-
return memnew(DirAccessJAndroid);
}
Error DirAccessJAndroid::list_dir_begin() {
-
list_dir_end();
JNIEnv *env = ThreadAndroid::get_env();
@@ -62,7 +60,6 @@ Error DirAccessJAndroid::list_dir_begin() {
}
String DirAccessJAndroid::get_next() {
-
ERR_FAIL_COND_V(id == 0, "");
JNIEnv *env = ThreadAndroid::get_env();
@@ -76,19 +73,16 @@ String DirAccessJAndroid::get_next() {
}
bool DirAccessJAndroid::current_is_dir() const {
-
JNIEnv *env = ThreadAndroid::get_env();
return env->CallBooleanMethod(io, _dir_is_dir, id);
}
bool DirAccessJAndroid::current_is_hidden() const {
-
return current != "." && current != ".." && current.begins_with(".");
}
void DirAccessJAndroid::list_dir_end() {
-
if (id == 0)
return;
@@ -98,17 +92,14 @@ void DirAccessJAndroid::list_dir_end() {
}
int DirAccessJAndroid::get_drive_count() {
-
return 0;
}
String DirAccessJAndroid::get_drive(int p_drive) {
-
return "";
}
Error DirAccessJAndroid::change_dir(String p_dir) {
-
JNIEnv *env = ThreadAndroid::get_env();
if (p_dir == "" || p_dir == "." || (p_dir == ".." && current_dir == ""))
@@ -145,12 +136,10 @@ Error DirAccessJAndroid::change_dir(String p_dir) {
}
String DirAccessJAndroid::get_current_dir(bool p_include_drive) {
-
return "res://" + current_dir;
}
bool DirAccessJAndroid::file_exists(String p_file) {
-
String sd;
if (current_dir == "")
sd = p_file;
@@ -165,7 +154,6 @@ bool DirAccessJAndroid::file_exists(String p_file) {
}
bool DirAccessJAndroid::dir_exists(String p_dir) {
-
JNIEnv *env = ThreadAndroid::get_env();
String sd;
@@ -198,33 +186,27 @@ bool DirAccessJAndroid::dir_exists(String p_dir) {
}
Error DirAccessJAndroid::make_dir(String p_dir) {
-
ERR_FAIL_V(ERR_UNAVAILABLE);
}
Error DirAccessJAndroid::rename(String p_from, String p_to) {
-
ERR_FAIL_V(ERR_UNAVAILABLE);
}
Error DirAccessJAndroid::remove(String p_name) {
-
ERR_FAIL_V(ERR_UNAVAILABLE);
}
String DirAccessJAndroid::get_filesystem_type() const {
-
return "APK";
}
//FileType get_file_type() const;
size_t DirAccessJAndroid::get_space_left() {
-
return 0;
}
void DirAccessJAndroid::setup(jobject p_io) {
-
JNIEnv *env = ThreadAndroid::get_env();
io = p_io;
@@ -240,11 +222,9 @@ void DirAccessJAndroid::setup(jobject p_io) {
}
DirAccessJAndroid::DirAccessJAndroid() {
-
id = 0;
}
DirAccessJAndroid::~DirAccessJAndroid() {
-
list_dir_end();
}
diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h
index 8dab3e50ce..7d0def137a 100644
--- a/platform/android/dir_access_jandroid.h
+++ b/platform/android/dir_access_jandroid.h
@@ -36,7 +36,6 @@
#include <stdio.h>
class DirAccessJAndroid : public DirAccess {
-
//AAssetDir* aad;
static jobject io;
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
new file mode 100644
index 0000000000..1436d832de
--- /dev/null
+++ b/platform/android/display_server_android.cpp
@@ -0,0 +1,668 @@
+/*************************************************************************/
+/* display_server_android.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 "display_server_android.h"
+
+#include "android_keys_utils.h"
+#include "core/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
+#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"
+#endif
+
+DisplayServerAndroid *DisplayServerAndroid::get_singleton() {
+ return (DisplayServerAndroid *)DisplayServer::get_singleton();
+}
+
+bool DisplayServerAndroid::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 DisplayServerAndroid::get_name() const {
+ return "Android";
+}
+
+void DisplayServerAndroid::clipboard_set(const String &p_text) {
+ GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
+ ERR_FAIL_COND(!godot_java);
+
+ if (godot_java->has_set_clipboard()) {
+ godot_java->set_clipboard(p_text);
+ } else {
+ DisplayServer::clipboard_set(p_text);
+ }
+}
+
+String DisplayServerAndroid::clipboard_get() const {
+ GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
+ ERR_FAIL_COND_V(!godot_java, String());
+
+ if (godot_java->has_get_clipboard()) {
+ return godot_java->get_clipboard();
+ } else {
+ return DisplayServer::clipboard_get();
+ }
+}
+
+void DisplayServerAndroid::screen_set_keep_on(bool p_enable) {
+ GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
+ ERR_FAIL_COND(!godot_java);
+
+ godot_java->set_keep_screen_on(p_enable);
+ keep_screen_on = p_enable;
+}
+
+bool DisplayServerAndroid::screen_is_kept_on() const {
+ return keep_screen_on;
+}
+
+void DisplayServerAndroid::screen_set_orientation(DisplayServer::ScreenOrientation p_orientation, int p_screen) {
+ GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
+ ERR_FAIL_COND(!godot_io_java);
+
+ godot_io_java->set_screen_orientation(p_orientation);
+}
+
+DisplayServer::ScreenOrientation DisplayServerAndroid::screen_get_orientation(int p_screen) const {
+ GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
+ ERR_FAIL_COND_V(!godot_io_java, SCREEN_LANDSCAPE);
+
+ return (ScreenOrientation)godot_io_java->get_screen_orientation();
+}
+
+int DisplayServerAndroid::get_screen_count() const {
+ return 1;
+}
+
+Point2i DisplayServerAndroid::screen_get_position(int p_screen) const {
+ return Point2i(0, 0);
+}
+
+Size2i DisplayServerAndroid::screen_get_size(int p_screen) const {
+ return OS_Android::get_singleton()->get_display_size();
+}
+
+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);
+}
+
+int DisplayServerAndroid::screen_get_dpi(int p_screen) const {
+ GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
+ ERR_FAIL_COND_V(!godot_io_java, 0);
+
+ return godot_io_java->get_screen_dpi();
+}
+
+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) {
+ 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);
+ } else {
+ ERR_PRINT("Virtual keyboard not available");
+ }
+}
+
+void DisplayServerAndroid::virtual_keyboard_hide() {
+ 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->hide_vk();
+ } else {
+ ERR_PRINT("Virtual keyboard not available");
+ }
+}
+
+int DisplayServerAndroid::virtual_keyboard_get_height() const {
+ GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
+ ERR_FAIL_COND_V(!godot_io_java, 0);
+
+ return godot_io_java->get_vk_height();
+}
+
+void DisplayServerAndroid::window_set_window_event_callback(const Callable &p_callable, DisplayServer::WindowID p_window) {
+ window_event_callback = p_callable;
+}
+
+void DisplayServerAndroid::window_set_input_event_callback(const Callable &p_callable, DisplayServer::WindowID p_window) {
+ input_event_callback = p_callable;
+}
+
+void DisplayServerAndroid::window_set_input_text_callback(const Callable &p_callable, DisplayServer::WindowID p_window) {
+ input_text_callback = p_callable;
+}
+
+void DisplayServerAndroid::window_set_rect_changed_callback(const Callable &p_callable, DisplayServer::WindowID p_window) {
+ // Not supported on Android.
+}
+
+void DisplayServerAndroid::window_set_drop_files_callback(const Callable &p_callable, DisplayServer::WindowID p_window) {
+ // Not supported on Android.
+}
+
+void DisplayServerAndroid::_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);
+ }
+}
+
+void DisplayServerAndroid::send_window_event(DisplayServer::WindowEvent p_event) const {
+ _window_callback(window_event_callback, int(p_event));
+}
+
+void DisplayServerAndroid::send_input_event(const Ref<InputEvent> &p_event) const {
+ _window_callback(input_event_callback, p_event);
+}
+
+void DisplayServerAndroid::send_input_text(const String &p_text) const {
+ _window_callback(input_text_callback, p_text);
+}
+
+void DisplayServerAndroid::_dispatch_input_events(const Ref<InputEvent> &p_event) {
+ DisplayServerAndroid::get_singleton()->send_input_event(p_event);
+}
+
+Vector<DisplayServer::WindowID> DisplayServerAndroid::get_window_list() const {
+ Vector<WindowID> ret;
+ ret.push_back(MAIN_WINDOW_ID);
+ return ret;
+}
+
+DisplayServer::WindowID DisplayServerAndroid::get_window_at_screen_position(const Point2i &p_position) const {
+ return MAIN_WINDOW_ID;
+}
+
+void DisplayServerAndroid::window_attach_instance_id(ObjectID p_instance, DisplayServer::WindowID p_window) {
+ window_attached_instance_id = p_instance;
+}
+
+ObjectID DisplayServerAndroid::window_get_attached_instance_id(DisplayServer::WindowID p_window) const {
+ return window_attached_instance_id;
+}
+
+void DisplayServerAndroid::window_set_title(const String &p_title, DisplayServer::WindowID p_window) {
+ // Not supported on Android.
+}
+
+int DisplayServerAndroid::window_get_current_screen(DisplayServer::WindowID p_window) const {
+ return SCREEN_OF_MAIN_WINDOW;
+}
+
+void DisplayServerAndroid::window_set_current_screen(int p_screen, DisplayServer::WindowID p_window) {
+ // Not supported on Android.
+}
+
+Point2i DisplayServerAndroid::window_get_position(DisplayServer::WindowID p_window) const {
+ return Point2i();
+}
+
+void DisplayServerAndroid::window_set_position(const Point2i &p_position, DisplayServer::WindowID p_window) {
+ // Not supported on Android.
+}
+
+void DisplayServerAndroid::window_set_transient(DisplayServer::WindowID p_window, DisplayServer::WindowID p_parent) {
+ // Not supported on Android.
+}
+
+void DisplayServerAndroid::window_set_max_size(const Size2i p_size, DisplayServer::WindowID p_window) {
+ // Not supported on Android.
+}
+
+Size2i DisplayServerAndroid::window_get_max_size(DisplayServer::WindowID p_window) const {
+ return Size2i();
+}
+
+void DisplayServerAndroid::window_set_min_size(const Size2i p_size, DisplayServer::WindowID p_window) {
+ // Not supported on Android.
+}
+
+Size2i DisplayServerAndroid::window_get_min_size(DisplayServer::WindowID p_window) const {
+ return Size2i();
+}
+
+void DisplayServerAndroid::window_set_size(const Size2i p_size, DisplayServer::WindowID p_window) {
+ // Not supported on Android.
+}
+
+Size2i DisplayServerAndroid::window_get_size(DisplayServer::WindowID p_window) const {
+ return OS_Android::get_singleton()->get_display_size();
+}
+
+Size2i DisplayServerAndroid::window_get_real_size(DisplayServer::WindowID p_window) const {
+ return OS_Android::get_singleton()->get_display_size();
+}
+
+void DisplayServerAndroid::window_set_mode(DisplayServer::WindowMode p_mode, DisplayServer::WindowID p_window) {
+ // Not supported on Android.
+}
+
+DisplayServer::WindowMode DisplayServerAndroid::window_get_mode(DisplayServer::WindowID p_window) const {
+ return WINDOW_MODE_FULLSCREEN;
+}
+
+bool DisplayServerAndroid::window_is_maximize_allowed(DisplayServer::WindowID p_window) const {
+ return false;
+}
+
+void DisplayServerAndroid::window_set_flag(DisplayServer::WindowFlags p_flag, bool p_enabled, DisplayServer::WindowID p_window) {
+ // Not supported on Android.
+}
+
+bool DisplayServerAndroid::window_get_flag(DisplayServer::WindowFlags p_flag, DisplayServer::WindowID p_window) const {
+ return false;
+}
+
+void DisplayServerAndroid::window_request_attention(DisplayServer::WindowID p_window) {
+ // Not supported on Android.
+}
+
+void DisplayServerAndroid::window_move_to_foreground(DisplayServer::WindowID p_window) {
+ // Not supported on Android.
+}
+
+bool DisplayServerAndroid::window_can_draw(DisplayServer::WindowID p_window) const {
+ return true;
+}
+
+bool DisplayServerAndroid::can_any_window_draw() const {
+ return true;
+}
+
+void DisplayServerAndroid::alert(const String &p_alert, const String &p_title) {
+ GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
+ ERR_FAIL_COND(!godot_java);
+
+ godot_java->alert(p_alert, p_title);
+}
+
+void DisplayServerAndroid::process_events() {
+ // Nothing to do
+}
+
+Vector<String> DisplayServerAndroid::get_rendering_drivers_func() {
+ Vector<String> drivers;
+
+#ifdef OPENGL_ENABLED
+ drivers.push_back("opengl");
+#endif
+#ifdef VULKAN_ENABLED
+ drivers.push_back("vulkan");
+#endif
+
+ return drivers;
+}
+
+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));
+}
+
+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;
+
+ // TODO: rendering_driver is broken, change when different drivers are supported again
+ rendering_driver = "vulkan";
+
+ keep_screen_on = GLOBAL_GET("display/window/energy_saving/keep_screen_on");
+
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver == "opengl") {
+ bool gl_initialization_error = false;
+
+ 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.\n"
+ "Please try updating your Android version.",
+ "Unable to initialize video driver");
+ return;
+ }
+ }
+#endif
+
+#if defined(VULKAN_ENABLED)
+ context_vulkan = nullptr;
+ rendering_device_vulkan = nullptr;
+
+ if (rendering_driver == "vulkan") {
+ ANativeWindow *native_window = OS_Android::get_singleton()->get_native_window();
+ ERR_FAIL_COND(!native_window);
+
+ context_vulkan = memnew(VulkanContextAndroid);
+ if (context_vulkan->initialize() != OK) {
+ memdelete(context_vulkan);
+ context_vulkan = nullptr;
+ ERR_FAIL_MSG("Failed to initialize Vulkan context");
+ }
+
+ 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 create Vulkan window.");
+ }
+
+ rendering_device_vulkan = memnew(RenderingDeviceVulkan);
+ rendering_device_vulkan->initialize(context_vulkan);
+
+ RasterizerRD::make_current();
+ }
+#endif
+
+ Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
+}
+
+DisplayServerAndroid::~DisplayServerAndroid() {
+#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
+}
+
+void DisplayServerAndroid::process_joy_event(DisplayServerAndroid::JoypadEvent p_event) {
+ switch (p_event.type) {
+ case JOY_EVENT_BUTTON:
+ Input::get_singleton()->joy_button(p_event.device, p_event.index, p_event.pressed);
+ break;
+ case JOY_EVENT_AXIS:
+ Input::JoyAxis value;
+ value.min = -1;
+ value.value = p_event.value;
+ Input::get_singleton()->joy_axis(p_event.device, p_event.index, value);
+ break;
+ case JOY_EVENT_HAT:
+ Input::get_singleton()->joy_hat(p_event.device, p_event.hat);
+ break;
+ default:
+ return;
+ }
+}
+
+void DisplayServerAndroid::process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed) {
+ Ref<InputEventKey> ev;
+ ev.instance();
+ int val = p_unicode_char;
+ int keycode = android_get_keysym(p_keycode);
+ int phy_keycode = android_get_keysym(p_scancode);
+ ev->set_keycode(keycode);
+ ev->set_physical_keycode(phy_keycode);
+ ev->set_unicode(val);
+ ev->set_pressed(p_pressed);
+
+ if (val == '\n') {
+ ev->set_keycode(KEY_ENTER);
+ } else if (val == 61448) {
+ ev->set_keycode(KEY_BACKSPACE);
+ ev->set_unicode(KEY_BACKSPACE);
+ } else if (val == 61453) {
+ ev->set_keycode(KEY_ENTER);
+ ev->set_unicode(KEY_ENTER);
+ } else if (p_keycode == 4) {
+ OS_Android::get_singleton()->main_loop_request_go_back();
+ }
+
+ Input::get_singleton()->parse_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
+ if (touch.size()) {
+ //end all if exist
+ for (int i = 0; i < touch.size(); i++) {
+ Ref<InputEventScreenTouch> ev;
+ ev.instance();
+ ev->set_index(touch[i].id);
+ ev->set_pressed(false);
+ ev->set_position(touch[i].pos);
+ Input::get_singleton()->parse_input_event(ev);
+ }
+ }
+
+ touch.resize(p_points.size());
+ for (int i = 0; i < p_points.size(); i++) {
+ touch.write[i].id = p_points[i].id;
+ touch.write[i].pos = p_points[i].pos;
+ }
+
+ //send touch
+ for (int i = 0; i < touch.size(); i++) {
+ Ref<InputEventScreenTouch> ev;
+ ev.instance();
+ ev->set_index(touch[i].id);
+ ev->set_pressed(true);
+ ev->set_position(touch[i].pos);
+ Input::get_singleton()->parse_input_event(ev);
+ }
+
+ } break;
+ case 1: { //motion
+ ERR_FAIL_COND(touch.size() != p_points.size());
+
+ for (int i = 0; i < touch.size(); i++) {
+ int idx = -1;
+ for (int j = 0; j < p_points.size(); j++) {
+ if (touch[i].id == p_points[j].id) {
+ idx = j;
+ break;
+ }
+ }
+
+ ERR_CONTINUE(idx == -1);
+
+ if (touch[i].pos == p_points[idx].pos)
+ continue; //no move unncesearily
+
+ Ref<InputEventScreenDrag> ev;
+ ev.instance();
+ 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);
+ touch.write[i].pos = p_points[idx].pos;
+ }
+
+ } break;
+ case 2: { //release
+ if (touch.size()) {
+ //end all if exist
+ for (int i = 0; i < touch.size(); i++) {
+ Ref<InputEventScreenTouch> ev;
+ ev.instance();
+ ev->set_index(touch[i].id);
+ ev->set_pressed(false);
+ ev->set_position(touch[i].pos);
+ Input::get_singleton()->parse_input_event(ev);
+ }
+ touch.clear();
+ }
+ } break;
+ case 3: { // add touch
+ for (int i = 0; i < p_points.size(); i++) {
+ if (p_points[i].id == p_pointer) {
+ TouchPos tp = p_points[i];
+ touch.push_back(tp);
+
+ Ref<InputEventScreenTouch> ev;
+ ev.instance();
+
+ ev->set_index(tp.id);
+ ev->set_pressed(true);
+ ev->set_position(tp.pos);
+ Input::get_singleton()->parse_input_event(ev);
+
+ break;
+ }
+ }
+ } break;
+ case 4: { // remove touch
+ for (int i = 0; i < touch.size(); i++) {
+ if (touch[i].id == p_pointer) {
+ Ref<InputEventScreenTouch> ev;
+ ev.instance();
+ ev->set_index(touch[i].id);
+ ev->set_pressed(false);
+ ev->set_position(touch[i].pos);
+ Input::get_singleton()->parse_input_event(ev);
+ touch.remove(i);
+
+ break;
+ }
+ }
+ } break;
+ }
+}
+
+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
+ Ref<InputEventMouseMotion> ev;
+ ev.instance();
+ 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);
+ hover_prev_pos = p_pos;
+ } break;
+ }
+}
+
+void DisplayServerAndroid::process_double_tap(Point2 p_pos) {
+ Ref<InputEventMouseButton> ev;
+ ev.instance();
+ ev->set_position(p_pos);
+ ev->set_global_position(p_pos);
+ ev->set_pressed(false);
+ ev->set_doubleclick(true);
+ Input::get_singleton()->parse_input_event(ev);
+}
+
+void DisplayServerAndroid::process_scroll(Point2 p_pos) {
+ Ref<InputEventPanGesture> ev;
+ ev.instance();
+ ev->set_position(p_pos);
+ ev->set_delta(p_pos - scroll_prev_pos);
+ Input::get_singleton()->parse_input_event(ev);
+ scroll_prev_pos = p_pos;
+}
+
+void DisplayServerAndroid::process_accelerometer(const Vector3 &p_accelerometer) {
+ Input::get_singleton()->set_accelerometer(p_accelerometer);
+}
+
+void DisplayServerAndroid::process_gravity(const Vector3 &p_gravity) {
+ Input::get_singleton()->set_gravity(p_gravity);
+}
+
+void DisplayServerAndroid::process_magnetometer(const Vector3 &p_magnetometer) {
+ Input::get_singleton()->set_magnetometer(p_magnetometer);
+}
+
+void DisplayServerAndroid::process_gyroscope(const Vector3 &p_gyroscope) {
+ Input::get_singleton()->set_gyroscope(p_gyroscope);
+}
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
new file mode 100644
index 0000000000..d64542df58
--- /dev/null
+++ b/platform/android/display_server_android.h
@@ -0,0 +1,175 @@
+/*************************************************************************/
+/* display_server_android.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 DISPLAY_SERVER_ANDROID_H
+#define DISPLAY_SERVER_ANDROID_H
+
+#include "servers/display_server.h"
+
+#if defined(VULKAN_ENABLED)
+class VulkanContextAndroid;
+class RenderingDeviceVulkan;
+#endif
+
+class DisplayServerAndroid : public DisplayServer {
+public:
+ struct TouchPos {
+ int id;
+ Point2 pos;
+ };
+
+ enum {
+ JOY_EVENT_BUTTON = 0,
+ JOY_EVENT_AXIS = 1,
+ JOY_EVENT_HAT = 2
+ };
+
+ struct JoypadEvent {
+ int device;
+ int type;
+ int index;
+ bool pressed;
+ float value;
+ int hat;
+ };
+
+private:
+ String rendering_driver;
+
+ bool keep_screen_on;
+
+ Vector<TouchPos> touch;
+ Point2 hover_prev_pos; // needed to calculate the relative position on hover events
+ Point2 scroll_prev_pos; // needed to calculate the relative position on scroll events
+
+#if defined(VULKAN_ENABLED)
+ VulkanContextAndroid *context_vulkan;
+ RenderingDeviceVulkan *rendering_device_vulkan;
+#endif
+
+ ObjectID window_attached_instance_id;
+
+ Callable window_event_callback;
+ Callable input_event_callback;
+ Callable input_text_callback;
+
+ void _window_callback(const Callable &p_callable, const Variant &p_arg) const;
+
+ static void _dispatch_input_events(const Ref<InputEvent> &p_event);
+
+public:
+ static DisplayServerAndroid *get_singleton();
+
+ virtual bool has_feature(Feature p_feature) const;
+ virtual String get_name() const;
+
+ virtual void clipboard_set(const String &p_text);
+ virtual String clipboard_get() const;
+
+ virtual void screen_set_keep_on(bool p_enable);
+ virtual bool screen_is_kept_on() const;
+
+ virtual void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW);
+ virtual ScreenOrientation screen_get_orientation(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+
+ 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;
+ 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_hide();
+ virtual int virtual_keyboard_get_height() const;
+
+ 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_rect_changed_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);
+
+ void send_window_event(WindowEvent p_event) const;
+ void send_input_event(const Ref<InputEvent> &p_event) const;
+ void send_input_text(const String &p_text) const;
+
+ virtual Vector<WindowID> get_window_list() const;
+ virtual WindowID get_window_at_screen_position(const Point2i &p_position) const;
+ 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_set_title(const String &p_title, 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);
+ 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 void window_set_transient(WindowID p_window, WindowID p_parent);
+ 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_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_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_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID);
+ virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const;
+ 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_request_attention(WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID);
+ virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual bool can_any_window_draw() const;
+
+ virtual void alert(const String &p_alert, const String &p_title);
+
+ virtual void process_events();
+
+ void process_accelerometer(const Vector3 &p_accelerometer);
+ 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_hover(int p_type, Point2 p_pos);
+ void process_double_tap(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);
+
+ 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();
+
+ DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ ~DisplayServerAndroid();
+};
+
+#endif // DISPLAY_SERVER_ANDROID_H
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index e7d4bc6c51..dfaaf68b69 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -44,6 +44,7 @@
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "platform/android/logo.gen.h"
+#include "platform/android/plugin/godot_plugin_config.h"
#include "platform/android/run_icon.gen.h"
#include <string.h>
@@ -194,7 +195,7 @@ static const char *android_perms[] = {
"WRITE_SOCIAL_STREAM",
"WRITE_SYNC_SETTINGS",
"WRITE_USER_DICTIONARY",
- NULL
+ nullptr
};
struct LauncherIcon {
@@ -235,14 +236,12 @@ static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_coun
};
class EditorExportPlatformAndroid : public EditorExportPlatform {
-
GDCLASS(EditorExportPlatformAndroid, EditorExportPlatform);
Ref<ImageTexture> logo;
Ref<ImageTexture> run_icon;
struct Device {
-
String id;
String name;
String description;
@@ -250,40 +249,65 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
};
struct APKExportData {
-
zipFile apk;
EditorProgress *ep;
};
+ Vector<PluginConfig> plugins;
+ volatile bool plugins_changed;
+ Mutex plugins_lock;
Vector<Device> devices;
volatile bool devices_changed;
Mutex device_lock;
- Thread *device_thread;
+ Thread *check_for_changes_thread;
volatile bool quit_request;
- static void _device_poll_thread(void *ud) {
-
+ static void _check_for_changes_poll_thread(void *ud) {
EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud;
while (!ea->quit_request) {
+ // 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();
+
+ MutexLock lock(ea->plugins_lock);
+
+ if (ea->plugins.size() != loaded_plugins.size()) {
+ ea->plugins_changed = true;
+ } else {
+ for (int i = 0; i < ea->plugins.size(); i++) {
+ if (ea->plugins[i].name != loaded_plugins[i].name) {
+ ea->plugins_changed = true;
+ break;
+ }
+ }
+ }
+ if (ea->plugins_changed) {
+ ea->plugins = loaded_plugins;
+ }
+ }
+ }
+
+ // Check for devices updates
String adb = EditorSettings::get_singleton()->get("export/android/adb");
if (FileAccess::exists(adb)) {
-
String devices;
List<String> args;
args.push_back("devices");
int ec;
- OS::get_singleton()->execute(adb, args, true, NULL, &devices, &ec);
+ OS::get_singleton()->execute(adb, args, true, nullptr, &devices, &ec);
Vector<String> ds = devices.split("\n");
Vector<String> ldevices;
for (int i = 1; i < ds.size(); i++) {
-
String d = ds[i];
int dpos = d.find("device");
- if (dpos == -1)
+ if (dpos == -1) {
continue;
+ }
d = d.substr(0, dpos).strip_edges();
ldevices.push_back(d);
}
@@ -293,12 +317,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
bool different = false;
if (ea->devices.size() != ldevices.size()) {
-
different = true;
} else {
-
for (int i = 0; i < ea->devices.size(); i++) {
-
if (ea->devices[i].id != ldevices[i]) {
different = true;
break;
@@ -307,11 +328,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
if (different) {
-
Vector<Device> ndevices;
for (int i = 0; i < ldevices.size(); i++) {
-
Device d;
d.id = ldevices[i];
for (int j = 0; j < ea->devices.size(); j++) {
@@ -332,7 +351,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
int ec2;
String dp;
- OS::get_singleton()->execute(adb, args, true, NULL, &dp, &ec2);
+ OS::get_singleton()->execute(adb, args, true, nullptr, &dp, &ec2);
Vector<String> props = dp.split("\n");
String vendor;
@@ -340,7 +359,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
d.description = "Device ID: " + d.id + "\n";
d.api_level = 0;
for (int j = 0; j < props.size(); j++) {
-
// got information by `shell cat /system/build.prop` before and its format is "property=value"
// it's now changed to use `shell getporp` because of permission issue with Android 8.0 and above
// its format is "[property]: [value]" so changed it as like build.prop
@@ -372,7 +390,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
d.name = vendor + " " + device;
- if (device == String()) continue;
+ if (device == String()) {
+ continue;
+ }
}
ndevices.push_back(d);
@@ -388,8 +408,9 @@ 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) {
break;
+ }
}
}
@@ -406,7 +427,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
String get_project_name(const String &p_name) const {
-
String aname;
if (p_name != "") {
aname = p_name;
@@ -422,7 +442,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
String get_package_name(const String &p_package) const {
-
String pname = p_package;
String basename = ProjectSettings::get_singleton()->get("application/config/name");
basename = basename.to_lower();
@@ -439,16 +458,16 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
first = false;
}
}
- if (name == "")
+ if (name == "") {
name = "noname";
+ }
pname = pname.replace("$genname", name);
return pname;
}
- bool is_package_name_valid(const String &p_package, String *r_error = NULL) const {
-
+ bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const {
String pname = p_package;
if (pname.length() == 0) {
@@ -512,7 +531,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) {
-
/*
* By not compressing files with little or not benefit in doing so,
* a performance gain is expected attime. Moreover, if the APK is
@@ -537,7 +555,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
".scn", // Binary scenes are usually already compressed
".stex", // Streamable textures are usually already compressed
// Trailer for easier processing
- NULL
+ nullptr
};
for (const char **ext = unconditional_compress_ext; *ext; ++ext) {
@@ -559,7 +577,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
static zip_fileinfo get_zip_fileinfo() {
-
OS::Time time = OS::get_singleton()->get_time();
OS::Date date = OS::get_singleton()->get_date();
@@ -586,16 +603,83 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
return abis;
}
+ /// List the gdap files in the directory specified by the p_path parameter.
+ static Vector<String> list_gdap_files(const String &p_path) {
+ 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 == "") {
+ break;
+ }
+
+ if (da->current_is_dir() || da->current_is_hidden()) {
+ continue;
+ }
+
+ if (file.ends_with(PLUGIN_CONFIG_EXT)) {
+ dir_files.push_back(file);
+ }
+ }
+ da->list_dir_end();
+ }
+
+ return dir_files;
+ }
+
+ static Vector<PluginConfig> get_plugins() {
+ Vector<PluginConfig> loaded_plugins;
+
+ String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins");
+
+ // Add the prebuilt plugins
+ loaded_plugins.append_array(get_prebuilt_plugins(plugins_dir));
+
+ if (DirAccess::exists(plugins_dir)) {
+ Vector<String> plugins_filenames = list_gdap_files(plugins_dir);
+
+ if (!plugins_filenames.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]));
+ if (config.valid_config) {
+ loaded_plugins.push_back(config);
+ } else {
+ print_error("Invalid plugin config file " + plugins_filenames[i]);
+ }
+ }
+ }
+ }
+
+ return loaded_plugins;
+ }
+
+ static Vector<PluginConfig> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) {
+ Vector<PluginConfig> enabled_plugins;
+ Vector<PluginConfig> all_plugins = get_plugins();
+ for (int i = 0; i < all_plugins.size(); i++) {
+ PluginConfig plugin = all_plugins[i];
+ bool enabled = p_presets->get("plugins/" + plugin.name);
+ if (enabled) {
+ enabled_plugins.push_back(plugin);
+ }
+ }
+
+ return enabled_plugins;
+ }
+
static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED) {
zip_fileinfo zipfi = get_zip_fileinfo();
zipOpenNewFileInZip(ed->apk,
p_path.utf8().get_data(),
&zipfi,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
compression_method,
Z_DEFAULT_COMPRESSION);
@@ -648,7 +732,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
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
// const int CHUNK_AXML_FILE = 0x00080003;
@@ -688,16 +771,16 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
int xr_mode_index = p_preset->get("xr_features/xr_mode");
- String plugins = p_preset->get("custom_template/plugins");
+ 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)
+ if (enabled) {
perms.push_back("android.permission." + String(*aperms));
+ }
aperms++;
}
@@ -711,19 +794,17 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
if (p_give_internet) {
- if (perms.find("android.permission.INTERNET") == -1)
+ if (perms.find("android.permission.INTERNET") == -1) {
perms.push_back("android.permission.INTERNET");
+ }
}
while (ofs < (uint32_t)p_manifest.size()) {
-
uint32_t chunk = decode_uint32(&p_manifest[ofs]);
uint32_t size = decode_uint32(&p_manifest[ofs + 4]);
switch (chunk) {
-
case CHUNK_STRINGS: {
-
int iofs = ofs + 8;
string_count = decode_uint32(&p_manifest[iofs]);
@@ -744,14 +825,12 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
string_table_begins = st_offset;
for (uint32_t i = 0; i < string_count; i++) {
-
uint32_t string_at = decode_uint32(&p_manifest[st_offset + i * 4]);
string_at += st_offset + string_count * 4;
ERR_FAIL_COND_MSG(string_flags & UTF8_FLAG, "Unimplemented, can't read UTF-8 string table.");
if (string_flags & UTF8_FLAG) {
-
} else {
uint32_t len = decode_uint16(&p_manifest[string_at]);
Vector<CharType> ucstring;
@@ -774,7 +853,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
} break;
case CHUNK_XML_START_TAG: {
-
int iofs = ofs + 8;
uint32_t name = decode_uint32(&p_manifest[iofs + 12]);
@@ -804,8 +882,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
if (tname == "manifest" && attrname == "versionName") {
if (attr_value == 0xFFFFFFFF) {
WARN_PRINT("Version name in a resource, should be plain text");
- } else
+ } else {
string_table.write[attr_value] = version_name;
+ }
}
if (tname == "instrumentation" && attrname == "targetPackage") {
@@ -813,26 +892,20 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
if (tname == "activity" && attrname == "screenOrientation") {
-
encode_uint32(orientation == 0 ? 0 : 1, &p_manifest.write[iofs + 16]);
}
if (tname == "supports-screens") {
-
if (attrname == "smallScreens") {
-
encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
} else if (attrname == "normalScreens") {
-
encode_uint32(screen_support_normal ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
} else if (attrname == "largeScreens") {
-
encode_uint32(screen_support_large ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
} else if (attrname == "xlargeScreens") {
-
encode_uint32(screen_support_xlarge ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
}
}
@@ -853,9 +926,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
}
- if (tname == "meta-data" && attrname == "value" && value == "custom_template_plugins_value") {
+ if (tname == "meta-data" && attrname == "value" && value == "plugins_value" && !plugins_names.empty()) {
// Update the meta-data 'android:value' attribute with the list of enabled plugins.
- string_table.write[attr_value] = plugins;
+ string_table.write[attr_value] = plugins_names;
}
iofs += 20;
@@ -889,8 +962,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
feature_required_list.push_back(hand_tracking_index == 2);
feature_versions.push_back(-1); // no version attribute should be added.
- if (perms.find("oculus.permission.handtracking") == -1) {
- perms.push_back("oculus.permission.handtracking");
+ if (perms.find("com.oculus.permission.HAND_TRACKING") == -1) {
+ perms.push_back("com.oculus.permission.HAND_TRACKING");
}
}
}
@@ -1037,7 +1110,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
}
if (tname == "manifest") {
-
// save manifest ending so we can restore it
Vector<uint8_t> manifest_end;
uint32_t manifest_cur_size = p_manifest.size();
@@ -1123,13 +1195,11 @@ 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];
}
ofs = 0;
for (int i = 0; i < string_table.size(); i++) {
-
encode_uint32(ofs, &ret.write[string_table_begins + i * 4]);
ofs += string_table[i].length() * 2 + 2 + 2;
}
@@ -1138,7 +1208,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
string_data_offset = ret.size() - ofs;
uint8_t *chars = &ret.write[string_data_offset];
for (int i = 0; i < string_table.size(); i++) {
-
String s = string_table[i];
encode_uint16(s.length(), chars);
chars += 2;
@@ -1155,18 +1224,21 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
//pad
- while (ret.size() % 4)
+ while (ret.size() % 4) {
ret.push_back(0);
+ }
uint32_t new_stable_end = ret.size();
uint32_t extra = (p_manifest.size() - string_table_ends);
ret.resize(new_stable_end + extra);
- for (uint32_t i = 0; i < extra; i++)
+ for (uint32_t i = 0; i < extra; i++) {
ret.write[new_stable_end + i] = p_manifest[string_table_ends + i];
+ }
- while (ret.size() % 4)
+ while (ret.size() % 4) {
ret.push_back(0);
+ }
encode_uint32(ret.size(), &ret.write[4]); //update new file size
encode_uint32(new_stable_end - 8, &ret.write[12]); //update new string table size
@@ -1177,19 +1249,36 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
static String _parse_string(const uint8_t *p_bytes, bool p_utf8) {
-
uint32_t offset = 0;
- uint32_t len = decode_uint16(&p_bytes[offset]);
+ uint32_t len = 0;
if (p_utf8) {
- //don't know how to read extended utf8, this will have to be for now
- len >>= 8;
+ uint8_t byte = p_bytes[offset];
+ if (byte & 0x80) {
+ offset += 2;
+ } else {
+ offset += 1;
+ }
+ byte = p_bytes[offset];
+ offset++;
+ if (byte & 0x80) {
+ len = byte & 0x7F;
+ len = (len << 8) + p_bytes[offset];
+ offset++;
+ } else {
+ len = byte;
+ }
+ } else {
+ len = decode_uint16(&p_bytes[offset]);
+ offset += 2;
+ if (len & 0x8000) {
+ len &= 0x7FFF;
+ len = (len << 16) + decode_uint16(&p_bytes[offset]);
+ offset += 2;
+ }
}
- offset += 2;
- //printf("len %i, unicode: %i\n",len,int(p_utf8));
if (p_utf8) {
-
Vector<uint8_t> str8;
str8.resize(len + 1);
for (uint32_t i = 0; i < len; i++) {
@@ -1200,19 +1289,18 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
str.parse_utf8((const char *)str8.ptr());
return str;
} else {
-
String str;
for (uint32_t i = 0; i < len; i++) {
CharType c = decode_uint16(&p_bytes[offset + i * 2]);
- if (c == 0)
+ if (c == 0) {
break;
+ }
str += String::chr(c);
}
return str;
}
}
void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest) {
-
const int UTF8_FLAG = 0x00000100;
uint32_t string_block_len = decode_uint32(&p_manifest[16]);
@@ -1225,20 +1313,17 @@ 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]);
offset += string_table_begins + string_count * 4;
String str = _parse_string(&p_manifest[offset], string_flags & UTF8_FLAG);
if (str.begins_with("godot-project-name")) {
-
if (str == "godot-project-name") {
//project name
str = get_project_name(package_name);
} else {
-
String lang = str.substr(str.find_last("-") + 1, str.length()).replace("-", "_");
String prop = "application/config/name_" + lang;
if (ProjectSettings::get_singleton()->has_setting(prop)) {
@@ -1257,13 +1342,11 @@ 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];
}
int ofs = 0;
for (int i = 0; i < string_table.size(); i++) {
-
encode_uint32(ofs, &ret.write[string_table_begins + i * 4]);
ofs += string_table[i].length() * 2 + 2 + 2;
}
@@ -1271,7 +1354,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
ret.resize(ret.size() + ofs);
uint8_t *chars = &ret.write[ret.size() - ofs];
for (int i = 0; i < string_table.size(); i++) {
-
String s = string_table[i];
encode_uint16(s.length(), chars);
chars += 2;
@@ -1284,8 +1366,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
//pad
- while (ret.size() % 4)
+ while (ret.size() % 4) {
ret.push_back(0);
+ }
//change flags to not use utf8
encode_uint32(string_flags & ~0x100, &ret.write[28]);
@@ -1344,7 +1427,6 @@ public:
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");
if (driver == "GLES2") {
r_features->push_back("etc");
@@ -1361,7 +1443,6 @@ 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));
@@ -1370,7 +1451,14 @@ public:
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::STRING, "custom_template/plugins", PROPERTY_HINT_PLACEHOLDER_TEXT, "Plugin1,Plugin2,..."), ""));
+
+ Vector<PluginConfig> 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;
+
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"));
@@ -1408,7 +1496,6 @@ public:
const char **perms = android_perms;
while (*perms) {
-
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "permissions/" + String(*perms).to_lower()), false));
perms++;
}
@@ -1426,8 +1513,16 @@ public:
return logo;
}
- virtual bool poll_export() {
+ virtual bool should_update_export_options() {
+ bool export_options_changed = plugins_changed;
+ if (export_options_changed) {
+ // don't clear unless we're reporting true, to avoid race
+ plugins_changed = false;
+ }
+ return export_options_changed;
+ }
+ virtual bool poll_export() {
bool dc = devices_changed;
if (dc) {
// don't clear unless we're reporting true, to avoid race
@@ -1437,25 +1532,21 @@ public:
}
virtual int get_options_count() const {
-
MutexLock lock(device_lock);
return devices.size();
}
virtual String get_options_tooltip() const {
-
return TTR("Select device from the list");
}
virtual String get_option_label(int p_index) const {
-
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 {
-
ERR_FAIL_INDEX_V(p_index, devices.size(), "");
MutexLock lock(device_lock);
String s = devices[p_index].description;
@@ -1469,7 +1560,6 @@ public:
}
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
-
ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER);
String can_export_error;
@@ -1493,8 +1583,9 @@ public:
const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
const bool use_reverse = devices[p_device].api_level >= 21;
- if (use_reverse)
+ if (use_reverse) {
p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST;
+ }
String tmp_export_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport.apk");
@@ -1530,7 +1621,7 @@ public:
args.push_back("uninstall");
args.push_back(get_package_name(package_name));
- err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
+ err = OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
}
print_line("Installing to device (please wait...): " + devices[p_device].name);
@@ -1545,7 +1636,7 @@ public:
args.push_back("-r");
args.push_back(tmp_export_path);
- err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
+ err = OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
if (err || rv != 0) {
EditorNode::add_io_error("Could not install to device.");
CLEANUP_AND_RETURN(ERR_CANT_CREATE);
@@ -1553,7 +1644,6 @@ public:
if (use_remote) {
if (use_reverse) {
-
static const char *const msg = "--- Device API >= 21; debugging over USB ---";
EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR);
print_line(String(msg).to_upper());
@@ -1563,10 +1653,9 @@ public:
args.push_back(devices[p_device].id);
args.push_back("reverse");
args.push_back("--remove-all");
- OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
+ OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) {
-
int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
args.clear();
args.push_back("-s");
@@ -1575,12 +1664,11 @@ public:
args.push_back("tcp:" + itos(dbg_port));
args.push_back("tcp:" + itos(dbg_port));
- OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
+ OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
print_line("Reverse result: " + itos(rv));
}
if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) {
-
int fs_port = EditorSettings::get_singleton()->get("filesystem/file_server/port");
args.clear();
@@ -1590,11 +1678,10 @@ public:
args.push_back("tcp:" + itos(fs_port));
args.push_back("tcp:" + itos(fs_port));
- err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
+ err = OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
print_line("Reverse result2: " + itos(rv));
}
} else {
-
static const char *const msg = "--- Device API < 21; debugging over Wi-Fi ---";
EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR);
print_line(String(msg).to_upper());
@@ -1619,7 +1706,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, NULL, NULL, &rv);
+ err = OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
if (err || rv != 0) {
EditorNode::add_io_error("Could not execute on device.");
CLEANUP_AND_RETURN(ERR_CANT_CREATE);
@@ -1634,7 +1721,6 @@ public:
}
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
-
String err;
bool valid = false;
@@ -1668,7 +1754,6 @@ public:
String adb = EditorSettings::get_singleton()->get("export/android/adb");
if (!FileAccess::exists(adb)) {
-
valid = false;
err += TTR("ADB executable not configured in the Editor Settings.") + "\n";
}
@@ -1676,7 +1761,6 @@ public:
String js = EditorSettings::get_singleton()->get("export/android/jarsigner");
if (!FileAccess::exists(js)) {
-
valid = false;
err += TTR("OpenJDK jarsigner not configured in the Editor Settings.") + "\n";
}
@@ -1684,7 +1768,6 @@ public:
String dk = p_preset->get("keystore/debug");
if (!FileAccess::exists(dk)) {
-
dk = EditorSettings::get_singleton()->get("export/android/debug_keystore");
if (!FileAccess::exists(dk)) {
valid = false;
@@ -1699,7 +1782,7 @@ public:
valid = false;
} else {
Error errn;
- DirAccessRef da = DirAccess::open(sdk_path.plus_file("tools"), &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;
@@ -1707,7 +1790,6 @@ public:
}
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;
}
@@ -1716,7 +1798,6 @@ public:
bool apk_expansion = p_preset->get("apk_expansion/enable");
if (apk_expansion) {
-
String apk_expansion_pkey = p_preset->get("apk_expansion/public_key");
if (apk_expansion_pkey == "") {
@@ -1730,7 +1811,6 @@ public:
String pn_err;
if (!is_package_name_valid(get_package_name(pn), &pn_err)) {
-
valid = false;
err += TTR("Invalid package name:") + " " + pn_err + "\n";
}
@@ -1752,7 +1832,6 @@ public:
}
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) {
-
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
String src_apk;
@@ -1789,24 +1868,28 @@ public:
#endif
String build_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/build");
- String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins");
build_command = build_path.plus_file(build_command);
String package_name = get_package_name(p_preset->get("package/unique_name"));
- String plugins = p_preset->get("custom_template/plugins");
+
+ 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 custom_maven_repos = get_plugins_custom_maven_repos(enabled_plugins);
List<String> cmdline;
cmdline.push_back("build");
cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name.
- cmdline.push_back("-Pcustom_template_plugins_dir=" + plugins_dir); // argument to specify the plugins directory.
- cmdline.push_back("-Pcustom_template_plugins=" + plugins); // argument to specify the list of plugins to enable.
+ 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, NULL, NULL, &ec);
+ OS::get_singleton()->execute(build_command, cmdline, true, nullptr, nullptr, &ec);
print_line("exit code: " + itos(ec));
}
*/
@@ -1827,11 +1910,11 @@ public:
}
} else {
-
- if (p_debug)
+ if (p_debug) {
src_apk = p_preset->get("custom_template/debug");
- else
+ } else {
src_apk = p_preset->get("custom_template/release");
+ }
src_apk = src_apk.strip_edges();
if (src_apk == "") {
@@ -1851,7 +1934,7 @@ public:
return ERR_FILE_BAD_PATH;
}
- FileAccess *src_f = NULL;
+ FileAccess *src_f = nullptr;
zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
if (ep.step("Creating APK...", 0)) {
@@ -1860,7 +1943,6 @@ public:
unzFile pkg = unzOpen2(src_apk.utf8().get_data(), &io);
if (!pkg) {
-
EditorNode::add_io_error("Could not find template APK to export:\n" + src_apk);
return ERR_FILE_NOT_FOUND;
}
@@ -1868,7 +1950,7 @@ public:
int ret = unzGoToFirstFile(pkg);
zlib_filefunc_def io2 = io;
- FileAccess *dst_f = NULL;
+ FileAccess *dst_f = nullptr;
io2.opaque = &dst_f;
String tmp_unaligned_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned.apk");
@@ -1879,7 +1961,7 @@ public:
return m_err; \
}
- zipFile unaligned_apk = zipOpen2(tmp_unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
+ 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");
@@ -1932,12 +2014,12 @@ public:
ImageLoader::load_image(path, launcher_adaptive_icon_background_image);
}
+ Vector<String> invalid_abis(enabled_abis);
while (ret == UNZ_OK) {
-
//get filename
unz_file_info info;
char fname[16384];
- ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0);
+ ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
bool skip = false;
@@ -1977,6 +2059,7 @@ public:
bool enabled = false;
for (int i = 0; i < enabled_abis.size(); ++i) {
if (file.begins_with("lib/" + enabled_abis[i] + "/")) {
+ invalid_abis.erase(enabled_abis[i]);
enabled = true;
break;
}
@@ -2001,11 +2084,11 @@ public:
zipOpenNewFileInZip(unaligned_apk,
file.utf8().get_data(),
&zipfi,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
uncompressed ? 0 : Z_DEFLATED,
Z_DEFAULT_COMPRESSION);
@@ -2016,6 +2099,13 @@ public:
ret = unzGoToNextFile(pkg);
}
+ if (!invalid_abis.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.");
+ CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND);
+ }
+
if (ep.step("Adding files...", 1)) {
CLEANUP_AND_RETURN(ERR_SKIP);
}
@@ -2031,7 +2121,6 @@ public:
gen_export_flags(cl, p_flags);
if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
-
APKExportData ed;
ed.ep = &ep;
ed.apk = unaligned_apk;
@@ -2040,7 +2129,6 @@ public:
//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);
@@ -2059,7 +2147,6 @@ public:
cl.push_back(apk_expansion_pkey.strip_edges());
} else {
-
APKExportData ed;
ed.ep = &ep;
ed.apk = unaligned_apk;
@@ -2076,14 +2163,17 @@ public:
cl.push_back("--xr_mode_regular");
}
- if (use_32_fb)
+ if (use_32_fb) {
cl.push_back("--use_depth_32");
+ }
- if (immersive)
+ if (immersive) {
cl.push_back("--use_immersive");
+ }
- if (debug_opengl)
+ if (debug_opengl) {
cl.push_back("--debug_opengl");
+ }
if (cl.size()) {
//add comandline
@@ -2091,13 +2181,13 @@ public:
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)
+ if (!length) {
continue;
+ }
clf.resize(base + 4 + length);
encode_uint32(length, &clf.write[base]);
copymem(&clf.write[base + 4], txt.ptr(), length);
@@ -2108,11 +2198,11 @@ public:
zipOpenNewFileInZip(unaligned_apk,
"assets/_cl_",
&zipfi,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
0, // No compress (little size gain and potentially slower startup)
Z_DEFAULT_COMPRESSION);
@@ -2120,7 +2210,7 @@ public:
zipCloseFileInZip(unaligned_apk);
}
- zipClose(unaligned_apk, NULL);
+ zipClose(unaligned_apk, nullptr);
unzClose(pkg);
if (err != OK) {
@@ -2128,7 +2218,6 @@ public:
}
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.");
@@ -2139,13 +2228,11 @@ public:
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");
@@ -2188,7 +2275,7 @@ public:
args.push_back(tmp_unaligned_path);
args.push_back(user);
int retval;
- OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &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);
@@ -2205,7 +2292,7 @@ public:
args.push_back(tmp_unaligned_path);
args.push_back("-verbose");
- OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &retval);
+ 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);
@@ -2222,7 +2309,6 @@ public:
unzFile tmp_unaligned = unzOpen2(tmp_unaligned_path.utf8().get_data(), &io);
if (!tmp_unaligned) {
-
EditorNode::add_io_error("Could not unzip temporary unaligned APK.");
CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND);
}
@@ -2230,22 +2316,21 @@ public:
ret = unzGoToFirstFile(tmp_unaligned);
io2 = io;
- dst_f = NULL;
+ dst_f = nullptr;
io2.opaque = &dst_f;
- zipFile final_apk = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
+ zipFile final_apk = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2);
// Take files from the unaligned APK and write them out to the aligned one
// in raw mode, i.e. not uncompressing and recompressing, aligning them as needed,
// following what is done in https://github.com/android/platform_build/blob/master/tools/zipalign/ZipAlign.cpp
int bias = 0;
while (ret == UNZ_OK) {
-
unz_file_info info;
memset(&info, 0, sizeof(info));
char fname[16384];
char extra[16384];
- ret = unzGetCurrentFileInfo(tmp_unaligned, &info, fname, 16384, extra, 16384 - ZIP_ALIGNMENT, NULL, 0);
+ ret = unzGetCurrentFileInfo(tmp_unaligned, &info, fname, 16384, extra, 16384 - ZIP_ALIGNMENT, nullptr, 0);
String file = fname;
@@ -2277,9 +2362,9 @@ public:
&zipfi,
extra,
info.size_file_extra + padding,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
method,
level,
1); // raw write
@@ -2291,14 +2376,13 @@ public:
ret = unzGoToNextFile(tmp_unaligned);
}
- zipClose(final_apk, NULL);
+ zipClose(final_apk, nullptr);
unzClose(tmp_unaligned);
CLEANUP_AND_RETURN(OK);
}
virtual void get_platform_features(List<String> *r_features) {
-
r_features->push_back("mobile");
r_features->push_back("Android");
}
@@ -2307,7 +2391,6 @@ public:
}
EditorExportPlatformAndroid() {
-
Ref<Image> img = memnew(Image(_android_logo));
logo.instance();
logo->create_from_image(img);
@@ -2317,19 +2400,19 @@ public:
run_icon->create_from_image(img);
devices_changed = true;
+ plugins_changed = true;
quit_request = false;
- device_thread = Thread::create(_device_poll_thread, this);
+ check_for_changes_thread = Thread::create(_check_for_changes_poll_thread, this);
}
~EditorExportPlatformAndroid() {
quit_request = true;
- Thread::wait_to_finish(device_thread);
- memdelete(device_thread);
+ Thread::wait_to_finish(check_for_changes_thread);
+ memdelete(check_for_changes_thread);
}
};
void register_android_exporter() {
-
String exe_ext;
if (OS::get_singleton()->get_name() == "Windows") {
exe_ext = "*.exe";
diff --git a/platform/android/export/export.h b/platform/android/export/export.h
index ce786cc8b6..d11ab9f49e 100644
--- a/platform/android/export/export.h
+++ b/platform/android/export/export.h
@@ -28,4 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef ANDROID_EXPORT_H
+#define ANDROID_EXPORT_H
+
void register_android_exporter();
+
+#endif // ANDROID_EXPORT_H
diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp
index 965342b364..05d5fb576d 100644
--- a/platform/android/file_access_android.cpp
+++ b/platform/android/file_access_android.cpp
@@ -31,7 +31,7 @@
#include "file_access_android.h"
#include "core/print_string.h"
-AAssetManager *FileAccessAndroid::asset_manager = NULL;
+AAssetManager *FileAccessAndroid::asset_manager = nullptr;
/*void FileAccessAndroid::make_default() {
@@ -39,12 +39,10 @@ AAssetManager *FileAccessAndroid::asset_manager = NULL;
}*/
FileAccess *FileAccessAndroid::create_android() {
-
return memnew(FileAccessAndroid);
}
Error FileAccessAndroid::_open(const String &p_path, int p_mode_flags) {
-
String path = fix_path(p_path).simplify_path();
if (path.begins_with("/"))
path = path.substr(1, path.length());
@@ -64,20 +62,17 @@ Error FileAccessAndroid::_open(const String &p_path, int p_mode_flags) {
}
void FileAccessAndroid::close() {
-
if (!a)
return;
AAsset_close(a);
- a = NULL;
+ a = nullptr;
}
bool FileAccessAndroid::is_open() const {
-
- return a != NULL;
+ return a != nullptr;
}
void FileAccessAndroid::seek(size_t p_position) {
-
ERR_FAIL_COND(!a);
AAsset_seek(a, p_position, SEEK_SET);
pos = p_position;
@@ -90,29 +85,24 @@ void FileAccessAndroid::seek(size_t p_position) {
}
void FileAccessAndroid::seek_end(int64_t p_position) {
-
ERR_FAIL_COND(!a);
AAsset_seek(a, p_position, SEEK_END);
pos = len + p_position;
}
size_t FileAccessAndroid::get_position() const {
-
return pos;
}
size_t FileAccessAndroid::get_len() const {
-
return len;
}
bool FileAccessAndroid::eof_reached() const {
-
return eof;
}
uint8_t FileAccessAndroid::get_8() const {
-
if (pos >= len) {
eof = true;
return 0;
@@ -125,7 +115,6 @@ uint8_t FileAccessAndroid::get_8() const {
}
int FileAccessAndroid::get_buffer(uint8_t *p_dst, int p_length) const {
-
off_t r = AAsset_read(a, p_dst, p_length);
if (pos + p_length > len) {
@@ -133,7 +122,6 @@ int FileAccessAndroid::get_buffer(uint8_t *p_dst, int p_length) const {
}
if (r >= 0) {
-
pos += r;
if (pos > len) {
pos = len;
@@ -143,22 +131,18 @@ int FileAccessAndroid::get_buffer(uint8_t *p_dst, int p_length) const {
}
Error FileAccessAndroid::get_error() const {
-
return eof ? ERR_FILE_EOF : OK; //not sure what else it may happen
}
void FileAccessAndroid::flush() {
-
ERR_FAIL();
}
void FileAccessAndroid::store_8(uint8_t p_dest) {
-
ERR_FAIL();
}
bool FileAccessAndroid::file_exists(const String &p_path) {
-
String path = fix_path(p_path).simplify_path();
if (path.begins_with("/"))
path = path.substr(1, path.length());
@@ -175,7 +159,7 @@ bool FileAccessAndroid::file_exists(const String &p_path) {
}
FileAccessAndroid::FileAccessAndroid() {
- a = NULL;
+ a = nullptr;
eof = false;
}
diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h
index 6b5ec541fd..a347c63ffb 100644
--- a/platform/android/file_access_android.h
+++ b/platform/android/file_access_android.h
@@ -38,7 +38,6 @@
//#include <android_native_app_glue.h>
class FileAccessAndroid : public FileAccess {
-
static FileAccess *create_android();
mutable AAsset *a;
mutable size_t len;
diff --git a/platform/android/file_access_jandroid.cpp b/platform/android/file_access_jandroid.cpp
index db3aa4255e..df8b57fd3a 100644
--- a/platform/android/file_access_jandroid.cpp
+++ b/platform/android/file_access_jandroid.cpp
@@ -33,7 +33,7 @@
#include "thread_jandroid.h"
#include <unistd.h>
-jobject FileAccessJAndroid::io = NULL;
+jobject FileAccessJAndroid::io = nullptr;
jclass FileAccessJAndroid::cls;
jmethodID FileAccessJAndroid::_file_open = 0;
jmethodID FileAccessJAndroid::_file_get_size = 0;
@@ -44,12 +44,10 @@ 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();
@@ -75,7 +73,6 @@ Error FileAccessJAndroid::_open(const String &p_path, int p_mode_flags) {
}
void FileAccessJAndroid::close() {
-
if (!is_open())
return;
@@ -86,12 +83,10 @@ void FileAccessJAndroid::close() {
}
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.");
@@ -99,42 +94,37 @@ void FileAccessJAndroid::seek(size_t 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 {
+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;
@@ -150,7 +140,6 @@ int FileAccessJAndroid::get_buffer(uint8_t *p_dst, int p_length) const {
}
Error FileAccessJAndroid::get_error() const {
-
if (eof_reached())
return ERR_FILE_EOF;
return OK;
@@ -163,7 +152,6 @@ 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();
@@ -184,7 +172,6 @@ bool FileAccessJAndroid::file_exists(const String &p_path) {
}
void FileAccessJAndroid::setup(jobject p_io) {
-
io = p_io;
JNIEnv *env = ThreadAndroid::get_env();
@@ -201,12 +188,10 @@ void FileAccessJAndroid::setup(jobject p_io) {
}
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
index b361c64922..e252a4d3ac 100644
--- a/platform/android/file_access_jandroid.h
+++ b/platform/android/file_access_jandroid.h
@@ -34,7 +34,6 @@
#include "core/os/file_access.h"
#include "java_godot_lib_jni.h"
class FileAccessJAndroid : public FileAccess {
-
static jobject io;
static jclass cls;
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index cc480d1c84..f5b1d29f22 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -32,8 +32,8 @@
<!-- Metadata populated at export time and used by Godot to figure out which plugins must be enabled. -->
<meta-data
- android:name="custom_template_plugins"
- android:value="custom_template_plugins_value"/>
+ android:name="plugins"
+ android:value="plugins_value"/>
<activity
android:name=".GodotApp"
diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle
index 1a3bb77670..ea341b37b1 100644
--- a/platform/android/java/app/build.gradle
+++ b/platform/android/java/app/build.gradle
@@ -21,6 +21,16 @@ allprojects {
mavenCentral()
google()
jcenter()
+
+ // Godot user plugins custom maven repos
+ String[] mavenRepos = getGodotPluginsMavenRepos()
+ if (mavenRepos != null && mavenRepos.size() > 0) {
+ for (String repoUrl : mavenRepos) {
+ maven {
+ url repoUrl
+ }
+ }
+ }
}
}
@@ -40,15 +50,18 @@ dependencies {
releaseImplementation fileTree(dir: 'libs/release', include: ['*.jar', '*.aar'])
}
- // Godot prebuilt plugins
- implementation fileTree(dir: 'libs/plugins', include: ["GodotPayment*.aar"])
+ // Godot user plugins remote dependencies
+ String[] remoteDeps = getGodotPluginsRemoteBinaries()
+ if (remoteDeps != null && remoteDeps.size() > 0) {
+ for (String dep : remoteDeps) {
+ implementation dep
+ }
+ }
- // Godot user plugins dependencies
- String pluginsDir = getGodotPluginsDirectory()
- String[] pluginsBinaries = getGodotPluginsBinaries()
- if (pluginsDir != null && !pluginsDir.isEmpty() &&
- pluginsBinaries != null && pluginsBinaries.size() > 0) {
- implementation fileTree(dir: pluginsDir, include: pluginsBinaries)
+ // Godot user plugins local dependencies
+ String[] pluginsBinaries = getGodotPluginsLocalBinaries()
+ if (pluginsBinaries != null && pluginsBinaries.size() > 0) {
+ implementation files(pluginsBinaries)
}
}
@@ -56,6 +69,11 @@ android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+
defaultConfig {
// Feel free to modify the application id to your own.
applicationId getExportPackageName()
@@ -71,7 +89,9 @@ android {
packagingOptions {
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
- doNotStrip '**/*.so'
+
+ // Should be uncommented for development purpose within Android Studio
+ // doNotStrip '**/*.so'
}
// Both signing and zip-aligning will be done at export time
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index eaaefcbccb..5251bc3066 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -1,21 +1,21 @@
ext.versions = [
- androidGradlePlugin: '3.6.0',
+ androidGradlePlugin: '3.5.3',
compileSdk : 29,
minSdk : 18,
targetSdk : 29,
- buildTools : '29.0.1',
- supportCoreUtils : '28.0.0',
+ buildTools : '29.0.3',
+ supportCoreUtils : '1.0.0',
kotlinVersion : '1.3.61',
- v4Support : '28.0.0'
+ v4Support : '1.0.0'
]
ext.libraries = [
androidGradlePlugin: "com.android.tools.build:gradle:$versions.androidGradlePlugin",
- supportCoreUtils : "com.android.support:support-core-utils:$versions.supportCoreUtils",
+ supportCoreUtils : "androidx.legacy:legacy-support-core-utils:$versions.supportCoreUtils",
kotlinGradlePlugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlinVersion",
kotlinStdLib : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlinVersion",
- v4Support : "com.android.support:support-v4:$versions.v4Support"
+ v4Support : "androidx.legacy:legacy-support-v4:$versions.v4Support"
]
ext.getExportPackageName = { ->
@@ -28,39 +28,68 @@ ext.getExportPackageName = { ->
return appId
}
+final String PLUGIN_VALUE_SEPARATOR_REGEX = "\\|"
+
/**
- * Parse the project properties for the 'custom_template_plugins' property and return
- * their binaries for inclusion in the build dependencies.
- *
- * The listed plugins must have their binaries in the project plugins directory.
+ * Parse the project properties for the 'plugins_maven_repos' property and return the list
+ * of maven repos.
*/
-ext.getGodotPluginsBinaries = { ->
- String[] binDeps = []
+ext.getGodotPluginsMavenRepos = { ->
+ Set<String> mavenRepos = []
- // Retrieve the list of enabled plugins.
- if (project.hasProperty("custom_template_plugins")) {
- String pluginsList = project.property("custom_template_plugins")
- if (pluginsList != null && !pluginsList.trim().isEmpty()) {
- for (String plugin : pluginsList.split(",")) {
- binDeps += plugin.trim() + "*.aar"
+ // Retrieve the list of maven repos.
+ if (project.hasProperty("plugins_maven_repos")) {
+ String mavenReposProperty = project.property("plugins_maven_repos")
+ if (mavenReposProperty != null && !mavenReposProperty.trim().isEmpty()) {
+ for (String mavenRepoUrl : mavenReposProperty.split(PLUGIN_VALUE_SEPARATOR_REGEX)) {
+ mavenRepos += mavenRepoUrl.trim()
}
}
}
- return binDeps
+ return mavenRepos
+}
+
+/**
+ * Parse the project properties for the 'plugins_remote_binaries' property and return
+ * it for inclusion in the build dependencies.
+ */
+ext.getGodotPluginsRemoteBinaries = { ->
+ Set<String> remoteDeps = []
+
+ // Retrieve the list of remote plugins binaries.
+ if (project.hasProperty("plugins_remote_binaries")) {
+ String remoteDepsList = project.property("plugins_remote_binaries")
+ if (remoteDepsList != null && !remoteDepsList.trim().isEmpty()) {
+ for (String dep: remoteDepsList.split(PLUGIN_VALUE_SEPARATOR_REGEX)) {
+ remoteDeps += dep.trim()
+ }
+ }
+ }
+ return remoteDeps
}
/**
- * Parse the project properties for the 'custom_template_plugins_dir' property and return
- * its value.
+ * Parse the project properties for the 'plugins_local_binaries' property and return
+ * their binaries for inclusion in the build dependencies.
*
- * The returned value is the directory containing user plugins.
+ * Returns the prebuilt plugins if the 'plugins_local_binaries' property is unavailable.
*/
-ext.getGodotPluginsDirectory = { ->
- // The plugins directory is provided by the 'custom_template_plugins_dir' property.
- String pluginsDir = project.hasProperty("custom_template_plugins_dir")
- ? project.property("custom_template_plugins_dir")
- : ""
+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"]
+
+ // 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)) {
+ binDeps += plugin.trim()
+ }
+ }
+ }
- return pluginsDir
+ return binDeps
}
diff --git a/platform/android/java/app/settings.gradle b/platform/android/java/app/settings.gradle
new file mode 100644
index 0000000000..33b863c7bf
--- /dev/null
+++ b/platform/android/java/app/settings.gradle
@@ -0,0 +1,2 @@
+// Empty settings.gradle file to denote this directory as being the root project
+// of the Godot custom build.
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index 865b61956c..01a3607b20 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -140,7 +140,7 @@ task generateGodotTemplates(type: GradleBuild) {
startParameter.excludedTaskNames += ":lib:" + getSconsTaskName(buildType)
}
- tasks = ["copyGodotPaymentPluginToAppModule"]
+ tasks = []
// Only build the apks and aar files for which we have native shared libraries.
for (String target : supportedTargets) {
@@ -161,6 +161,7 @@ task generateGodotTemplates(type: GradleBuild) {
}
}
+ dependsOn 'copyGodotPaymentPluginToAppModule'
finalizedBy 'zipCustomBuild'
}
diff --git a/platform/android/java/gradle.properties b/platform/android/java/gradle.properties
index aac7c9b461..e14cd5ba5c 100644
--- a/platform/android/java/gradle.properties
+++ b/platform/android/java/gradle.properties
@@ -7,6 +7,9 @@
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
+android.enableJetifier=true
+android.useAndroidX=true
+
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
diff --git a/platform/android/java/lib/AndroidManifest.xml b/platform/android/java/lib/AndroidManifest.xml
index b133585f99..fa39bc0f1d 100644
--- a/platform/android/java/lib/AndroidManifest.xml
+++ b/platform/android/java/lib/AndroidManifest.xml
@@ -13,7 +13,7 @@
<instrumentation
android:icon="@mipmap/icon"
android:label="@string/godot_project_name_string"
- android:name=".GodotInstrumentation"
+ android:name="org.godotengine.godot.GodotInstrumentation"
android:targetPackage="org.godotengine.godot" />
</manifest>
diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle
index 062f91e08e..19eee5a315 100644
--- a/platform/android/java/lib/build.gradle
+++ b/platform/android/java/lib/build.gradle
@@ -1,4 +1,5 @@
apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
dependencies {
implementation libraries.supportCoreUtils
@@ -11,7 +12,6 @@ def pathToRootDir = "../../../../"
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
- useLibrary 'org.apache.http.legacy'
defaultConfig {
minSdkVersion versions.minSdk
@@ -26,7 +26,9 @@ android {
packagingOptions {
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
- doNotStrip '**/*.so'
+
+ // Should be uncommented for development purpose within Android Studio
+ // doNotStrip '**/*.so'
}
sourceSets {
diff --git a/platform/android/java/lib/res/drawable-hdpi/notify_panel_notification_icon_bg.png b/platform/android/java/lib/res/drawable-hdpi/notify_panel_notification_icon_bg.png
deleted file mode 100644
index f849d8e90d..0000000000
--- a/platform/android/java/lib/res/drawable-hdpi/notify_panel_notification_icon_bg.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/java/lib/res/drawable-mdpi/notify_panel_notification_icon_bg.png b/platform/android/java/lib/res/drawable-mdpi/notify_panel_notification_icon_bg.png
deleted file mode 100644
index 1dfb28b33a..0000000000
--- a/platform/android/java/lib/res/drawable-mdpi/notify_panel_notification_icon_bg.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/java/lib/res/drawable-xhdpi/notify_panel_notification_icon_bg.png b/platform/android/java/lib/res/drawable-xhdpi/notify_panel_notification_icon_bg.png
deleted file mode 100644
index 372b763ec5..0000000000
--- a/platform/android/java/lib/res/drawable-xhdpi/notify_panel_notification_icon_bg.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/java/lib/res/drawable-xxhdpi/notify_panel_notification_icon_bg.png b/platform/android/java/lib/res/drawable-xxhdpi/notify_panel_notification_icon_bg.png
deleted file mode 100644
index 302a972049..0000000000
--- a/platform/android/java/lib/res/drawable-xxhdpi/notify_panel_notification_icon_bg.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java b/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java
index 0abaf2e052..d481c22204 100644
--- a/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java
+++ b/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java
@@ -29,9 +29,9 @@ import com.google.android.vending.expansion.downloader.IDownloaderClient;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
-import android.os.Build;
import android.os.Messenger;
-import android.support.v4.app.NotificationCompat;
+
+import androidx.core.app.NotificationCompat;
/**
* This class handles displaying the notification associated with the download
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 594cae774b..8b7a9c6c74 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java
@@ -34,16 +34,13 @@ import java.util.HashMap;
import java.util.Set;
public class Dictionary extends HashMap<String, Object> {
-
protected String[] keys_cache;
public String[] get_keys() {
-
String[] ret = new String[size()];
int i = 0;
Set<String> keys = keySet();
for (String key : keys) {
-
ret[i] = key;
i++;
};
@@ -52,12 +49,10 @@ public class Dictionary extends HashMap<String, Object> {
};
public Object[] get_values() {
-
Object[] ret = new Object[size()];
int i = 0;
Set<String> keys = keySet();
for (String key : keys) {
-
ret[i] = get(key);
i++;
};
@@ -70,7 +65,6 @@ public class Dictionary extends HashMap<String, Object> {
};
public void set_values(Object[] vals) {
-
int i = 0;
for (String key : keys_cache) {
put(key, vals[i]);
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 1798a1df3a..f27d8620ec 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -30,6 +30,13 @@
package org.godotengine.godot;
+import org.godotengine.godot.input.GodotEditText;
+import org.godotengine.godot.plugin.GodotPlugin;
+import org.godotengine.godot.plugin.GodotPluginRegistry;
+import org.godotengine.godot.utils.GodotNetUtils;
+import org.godotengine.godot.utils.PermissionsUtil;
+import org.godotengine.godot.xr.XRMode;
+
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityManager;
@@ -59,10 +66,6 @@ import android.os.Messenger;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings.Secure;
-import android.support.annotation.CallSuper;
-import android.support.annotation.Keep;
-import android.support.annotation.NonNull;
-import android.support.v4.app.FragmentActivity;
import android.view.Display;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -77,6 +80,12 @@ import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
+
+import androidx.annotation.CallSuper;
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.FragmentActivity;
+
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller;
@@ -84,6 +93,7 @@ import com.google.android.vending.expansion.downloader.Helpers;
import com.google.android.vending.expansion.downloader.IDownloaderClient;
import com.google.android.vending.expansion.downloader.IDownloaderService;
import com.google.android.vending.expansion.downloader.IStub;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
@@ -91,15 +101,8 @@ import java.security.MessageDigest;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
-import org.godotengine.godot.input.GodotEditText;
-import org.godotengine.godot.plugin.GodotPlugin;
-import org.godotengine.godot.plugin.GodotPluginRegistry;
-import org.godotengine.godot.utils.GodotNetUtils;
-import org.godotengine.godot.utils.PermissionsUtil;
-import org.godotengine.godot.xr.XRMode;
public abstract class Godot extends FragmentActivity implements SensorEventListener, IDownloaderClient {
-
private IStub mDownloaderClientStub;
private TextView mStatusText;
private TextView mProgressFraction;
@@ -153,7 +156,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
private String[] command_line;
private boolean use_apk_expansion;
- public GodotView mView;
+ public GodotRenderView mRenderView;
private boolean godot_initialized = false;
private SensorManager mSensorManager;
@@ -213,35 +216,41 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
setContentView(layout);
// GodotEditText layout
- GodotEditText edittext = new GodotEditText(this);
- edittext.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ GodotEditText editText = new GodotEditText(this);
+ editText.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
// ...add to FrameLayout
- layout.addView(edittext);
+ layout.addView(editText);
- mView = new GodotView(this, xrMode, use_32_bits, use_debug_opengl);
- layout.addView(mView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- edittext.setView(mView);
- io.setEdit(edittext);
+ GodotLib.setup(command_line);
+
+ final String videoDriver = GodotLib.getGlobal("rendering/quality/driver/driver_name");
+ if (videoDriver.equals("Vulkan")) {
+ mRenderView = new GodotVulkanRenderView(this);
+ } else {
+ mRenderView = new GodotGLRenderView(this, xrMode, use_32_bits, use_debug_opengl);
+ }
- mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ View view = mRenderView.getView();
+ layout.addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ editText.setView(mRenderView);
+ io.setEdit(editText);
+
+ view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Point fullSize = new Point();
getWindowManager().getDefaultDisplay().getSize(fullSize);
Rect gameSize = new Rect();
- mView.getWindowVisibleDisplayFrame(gameSize);
+ mRenderView.getView().getWindowVisibleDisplayFrame(gameSize);
final int keyboardHeight = fullSize.y - gameSize.bottom;
GodotLib.setVirtualKeyboardHeight(keyboardHeight);
}
});
- final String[] current_command_line = command_line;
- mView.queueEvent(new Runnable() {
+ mRenderView.queueOnRenderThread(new Runnable() {
@Override
public void run() {
- GodotLib.setup(current_command_line);
-
// Must occur after GodotLib.setup has completed.
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
plugin.onRegisterPluginWithGodotNative();
@@ -351,7 +360,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
for (int i = 0; i < argc; i++) {
r = is.read(len);
if (r < 4) {
-
return new String[0];
}
int strlen = ((int)(len[3] & 0xFF) << 24) | ((int)(len[2] & 0xFF) << 16) | ((int)(len[1] & 0xFF) << 8) | ((int)(len[0] & 0xFF));
@@ -384,7 +392,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
*/
@Keep
private Surface getSurface() {
- return mView.getHolder().getSurface();
+ return mRenderView.getView().getHolder().getSurface();
}
/**
@@ -399,9 +407,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
String expansion_pack_path;
private void initializeGodot() {
-
if (expansion_pack_path != null) {
-
String[] new_cmdline;
int cll = 0;
if (command_line != null) {
@@ -448,7 +454,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
@Override
protected void onCreate(Bundle icicle) {
-
super.onCreate(icicle);
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
@@ -465,7 +470,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
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;
@@ -509,7 +513,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
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) {
@@ -531,7 +534,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
boolean pack_valid = true;
if (!f.exists()) {
-
pack_valid = false;
} else if (obbIsCorrupted(expansion_pack_path, main_pack_md5)) {
@@ -543,7 +545,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
if (!pack_valid) {
-
Intent notifierIntent = new Intent(this, this.getClass());
notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -592,7 +593,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
@Override
protected void onDestroy() {
-
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
plugin.onMainDestroy();
}
@@ -617,7 +617,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
return;
}
- mView.onPause();
+ mRenderView.onActivityPaused();
mSensorManager.unregisterListener(this);
@@ -627,7 +627,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
public String getClipboard() {
-
String copiedText = "";
if (mClipboard.getPrimaryClip() != null) {
@@ -639,7 +638,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
public void setClipboard(String p_text) {
-
ClipData clip = ClipData.newPlainText("myLabel", p_text);
mClipboard.setPrimaryClip(clip);
}
@@ -655,7 +653,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
return;
}
- mView.onResume();
+ mRenderView.onActivityResumed();
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME);
@@ -721,8 +719,8 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
final float z = adjustedValues[2];
final int typeOfSensor = event.sensor.getType();
- if (mView != null) {
- mView.queueEvent(new Runnable() {
+ if (mRenderView != null) {
+ mRenderView.queueOnRenderThread(new Runnable() {
@Override
public void run() {
if (typeOfSensor == Sensor.TYPE_ACCELEROMETER) {
@@ -773,8 +771,8 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
}
- if (shouldQuit && mView != null) {
- mView.queueEvent(new Runnable() {
+ if (shouldQuit && mRenderView != null) {
+ mRenderView.queueOnRenderThread(new Runnable() {
@Override
public void run() {
GodotLib.back();
@@ -789,8 +787,8 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
* This must be called after the render thread has started.
*/
public final void runOnRenderThread(@NonNull Runnable action) {
- if (mView != null) {
- mView.queueEvent(action);
+ if (mRenderView != null) {
+ mRenderView.queueOnRenderThread(action);
}
}
@@ -799,9 +797,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
private boolean obbIsCorrupted(String f, String main_pack_md5) {
-
try {
-
InputStream fis = new FileInputStream(f);
// Create MD5 Hash
@@ -842,16 +838,14 @@ 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 (mView != null) {
+ 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);
@@ -860,7 +854,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
//System.out.printf("gaction: %d\n",event.getAction());
final int action = event.getAction() & MotionEvent.ACTION_MASK;
- mView.queueEvent(new Runnable() {
+ mRenderView.queueOnRenderThread(new Runnable() {
@Override
public void run() {
switch (action) {
@@ -910,8 +904,9 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
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);
- mView.queueEvent(new Runnable() {
+ if (cnt == 0)
+ return super.onKeyMultiple(inKeyCode, repeatCount, event);
+ mRenderView.queueOnRenderThread(new Runnable() {
// This method will be called on the rendering thread:
public void run() {
for (int i = 0, n = cc.length; i < n; i++) {
@@ -1033,6 +1028,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
progress.mOverallTotal));
}
public void initInputDevices() {
- mView.initInputDevices();
+ mRenderView.initInputDevices();
}
}
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 1fb242d0bc..a3dae15980 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java
@@ -35,6 +35,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
+
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
/**
@@ -45,7 +46,6 @@ import com.google.android.vending.expansion.downloader.DownloaderClientMarshalle
* <receiver android:name=".GodotDownloaderAlarmReceiver"/>
*/
public class GodotDownloaderAlarmReceiver extends BroadcastReceiver {
-
@Override
public void onReceive(Context context, Intent intent) {
Log.d("GODOT", "Alarma recivida");
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 7e74e8a80d..434da95bc0 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java
@@ -33,6 +33,7 @@ package org.godotengine.godot;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
+
import com.google.android.vending.expansion.downloader.impl.DownloaderService;
/**
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
index 8d3c2ae319..14dd893faa 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* GodotView.java */
+/* GodotGLRenderView.java */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -29,12 +29,7 @@
/*************************************************************************/
package org.godotengine.godot;
-import android.annotation.SuppressLint;
-import android.graphics.PixelFormat;
-import android.opengl.GLSurfaceView;
-import android.view.GestureDetector;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
+
import org.godotengine.godot.input.GodotGestureHandler;
import org.godotengine.godot.input.GodotInputHandler;
import org.godotengine.godot.utils.GLUtils;
@@ -46,6 +41,14 @@ import org.godotengine.godot.xr.regular.RegularConfigChooser;
import org.godotengine.godot.xr.regular.RegularContextFactory;
import org.godotengine.godot.xr.regular.RegularFallbackConfigChooser;
+import android.annotation.SuppressLint;
+import android.graphics.PixelFormat;
+import android.opengl.GLSurfaceView;
+import android.view.GestureDetector;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.SurfaceView;
+
/**
* A simple GLSurfaceView sub-class that demonstrate how to perform
* OpenGL ES 2.0 rendering into a GL Surface. Note the following important
@@ -64,16 +67,13 @@ import org.godotengine.godot.xr.regular.RegularFallbackConfigChooser;
* that matches it exactly (with regards to red/green/blue/alpha channels
* bit depths). Failure to do so would result in an EGL_BAD_MATCH error.
*/
-public class GodotView extends GLSurfaceView {
-
- private static String TAG = GodotView.class.getSimpleName();
-
+public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
private final Godot activity;
private final GodotInputHandler inputHandler;
private final GestureDetector detector;
private final GodotRenderer godotRenderer;
- public GodotView(Godot activity, XRMode xrMode, boolean p_use_32_bits, boolean p_use_debug_opengl) {
+ public GodotGLRenderView(Godot activity, XRMode xrMode, boolean p_use_32_bits, boolean p_use_debug_opengl) {
super(activity);
GLUtils.use_32 = p_use_32_bits;
GLUtils.use_debug_opengl = p_use_debug_opengl;
@@ -85,10 +85,36 @@ public class GodotView extends GLSurfaceView {
init(xrMode, false, 16, 0);
}
+ @Override
+ public SurfaceView getView() {
+ return this;
+ }
+
+ @Override
public void initInputDevices() {
this.inputHandler.initInputDevices();
}
+ @Override
+ public void queueOnRenderThread(Runnable event) {
+ queueEvent(event);
+ }
+
+ @Override
+ public void onActivityPaused() {
+ onPause();
+ }
+
+ @Override
+ public void onActivityResumed() {
+ onResume();
+ }
+
+ @Override
+ public void onBackPressed() {
+ activity.onBackPressed();
+ }
+
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
@@ -113,11 +139,9 @@ public class GodotView extends GLSurfaceView {
}
private void init(XRMode xrMode, boolean translucent, int depth, int stencil) {
-
setPreserveEGLContextOnPause(true);
setFocusableInTouchMode(true);
switch (xrMode) {
-
case OVR:
// Replace the default egl config chooser.
setEGLConfigChooser(new OvrConfigChooser());
@@ -170,10 +194,6 @@ public class GodotView extends GLSurfaceView {
setRenderer(godotRenderer);
}
- public void onBackPressed() {
- activity.onBackPressed();
- }
-
@Override
public void onResume() {
super.onResume();
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 68ce40ba10..93f4786e83 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
@@ -29,6 +29,9 @@
/*************************************************************************/
package org.godotengine.godot;
+
+import org.godotengine.godot.input.*;
+
import android.content.*;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -39,22 +42,18 @@ import android.os.*;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
+
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
-import org.godotengine.godot.input.*;
-//android.os.Build
// Wrapper for native library
public class GodotIO {
-
AssetManager am;
Godot activity;
GodotEditText edit;
- MediaPlayer mediaPlayer;
-
final int SCREEN_LANDSCAPE = 0;
final int SCREEN_PORTRAIT = 1;
final int SCREEN_REVERSE_LANDSCAPE = 2;
@@ -70,7 +69,6 @@ public class GodotIO {
public int last_file_id = 1;
class AssetData {
-
public boolean eof = false;
public String path;
public InputStream is;
@@ -81,7 +79,6 @@ public class GodotIO {
SparseArray<AssetData> streams;
public int file_open(String path, boolean write) {
-
//System.out.printf("file_open: Attempt to Open %s\n",path);
//Log.v("MyApp", "TRYING TO OPEN FILE: " + path);
@@ -94,7 +91,6 @@ public class GodotIO {
ad.is = am.open(path);
} catch (Exception e) {
-
//System.out.printf("Exception on file_open: %s\n",path);
return -1;
}
@@ -102,7 +98,6 @@ public class GodotIO {
try {
ad.len = ad.is.available();
} catch (Exception e) {
-
System.out.printf("Exception availabling on file_open: %s\n", path);
return -1;
}
@@ -115,7 +110,6 @@ public class GodotIO {
return last_file_id;
}
public int file_get_size(int id) {
-
if (streams.get(id) == null) {
System.out.printf("file_get_size: Invalid file id: %d\n", id);
return -1;
@@ -124,7 +118,6 @@ public class GodotIO {
return streams.get(id).len;
}
public void file_seek(int id, int bytes) {
-
if (streams.get(id) == null) {
System.out.printf("file_get_size: Invalid file id: %d\n", id);
return;
@@ -137,7 +130,6 @@ public class GodotIO {
bytes = 0;
try {
-
if (bytes > (int)ad.pos) {
int todo = bytes - (int)ad.pos;
while (todo > 0) {
@@ -145,7 +137,6 @@ public class GodotIO {
}
ad.pos = bytes;
} else if (bytes < (int)ad.pos) {
-
ad.is = am.open(ad.path);
ad.pos = bytes;
@@ -157,14 +148,12 @@ public class GodotIO {
ad.eof = false;
} catch (IOException e) {
-
System.out.printf("Exception on file_seek: %s\n", e);
return;
}
}
public int file_tell(int id) {
-
if (streams.get(id) == null) {
System.out.printf("file_read: Can't tell eof for invalid file id: %d\n", id);
return 0;
@@ -174,7 +163,6 @@ public class GodotIO {
return ad.pos;
}
public boolean file_eof(int id) {
-
if (streams.get(id) == null) {
System.out.printf("file_read: Can't check eof for invalid file id: %d\n", id);
return false;
@@ -185,7 +173,6 @@ public class GodotIO {
}
public byte[] file_read(int id, int bytes) {
-
if (streams.get(id) == null) {
System.out.printf("file_read: Can't read invalid file id: %d\n", id);
return new byte[0];
@@ -194,13 +181,11 @@ public class GodotIO {
AssetData ad = streams.get(id);
if (ad.pos + bytes > ad.len) {
-
bytes = ad.len - ad.pos;
ad.eof = true;
}
if (bytes == 0) {
-
return new byte[0];
}
@@ -209,7 +194,6 @@ public class GodotIO {
try {
r = ad.is.read(buf1);
} catch (IOException e) {
-
System.out.printf("Exception on file_read: %s\n", e);
return new byte[bytes];
}
@@ -221,19 +205,16 @@ public class GodotIO {
ad.pos += r;
if (r < bytes) {
-
byte[] buf2 = new byte[r];
for (int i = 0; i < r; i++)
buf2[i] = buf1[i];
return buf2;
} else {
-
return buf1;
}
}
public void file_close(int id) {
-
if (streams.get(id) == null) {
System.out.printf("file_close: Can't close invalid file id: %d\n", id);
return;
@@ -247,7 +228,6 @@ public class GodotIO {
/////////////////////////
class AssetDir {
-
public String[] files;
public int current;
public String path;
@@ -258,7 +238,6 @@ public class GodotIO {
SparseArray<AssetDir> dirs;
public int dir_open(String path) {
-
AssetDir ad = new AssetDir();
ad.current = 0;
ad.path = path;
@@ -271,7 +250,6 @@ public class GodotIO {
return -1;
}
} catch (IOException e) {
-
System.out.printf("Exception on dir_open: %s\n", e);
return -1;
}
@@ -310,7 +288,6 @@ public class GodotIO {
}
public String dir_next(int id) {
-
if (dirs.get(id) == null) {
System.out.printf("dir_next: invalid dir id: %d\n", id);
return "";
@@ -329,7 +306,6 @@ public class GodotIO {
}
public void dir_close(int id) {
-
if (dirs.get(id) == null) {
System.out.printf("dir_close: invalid dir id: %d\n", id);
return;
@@ -339,7 +315,6 @@ public class GodotIO {
}
GodotIO(Godot p_activity) {
-
am = p_activity.getAssets();
activity = p_activity;
//streams = new HashMap<Integer, AssetData>();
@@ -430,7 +405,6 @@ public class GodotIO {
}
public void audioPause(boolean p_pause) {
-
if (p_pause)
mAudioTrack.pause();
else
@@ -442,7 +416,6 @@ public class GodotIO {
/////////////////////////
public int openURI(String p_uri) {
-
try {
Log.v("MyApp", "TRYING TO OPEN URI: " + p_uri);
String path = p_uri;
@@ -451,7 +424,6 @@ public class GodotIO {
//absolute path to filesystem, prepend file://
path = "file://" + path;
if (p_uri.endsWith(".png") || p_uri.endsWith(".jpg") || p_uri.endsWith(".gif") || p_uri.endsWith(".webp")) {
-
type = "image/*";
}
}
@@ -467,18 +439,15 @@ public class GodotIO {
activity.startActivity(intent);
return 0;
} catch (ActivityNotFoundException e) {
-
return 1;
}
}
public String getDataDir() {
-
return activity.getFilesDir().getAbsolutePath();
}
public String getLocale() {
-
return Locale.getDefault().toString();
}
@@ -491,9 +460,9 @@ public class GodotIO {
return (int)(metrics.density * 160f);
}
- public void showKeyboard(String p_existing_text, int p_max_input_length) {
+ public void showKeyboard(String p_existing_text, 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);
+ edit.showKeyboard(p_existing_text, 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);
@@ -505,9 +474,7 @@ public class GodotIO {
};
public void setScreenOrientation(int p_orientation) {
-
switch (p_orientation) {
-
case SCREEN_LANDSCAPE: {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} break;
@@ -530,44 +497,14 @@ public class GodotIO {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
} break;
}
- };
-
- public void setEdit(GodotEditText _edit) {
- edit = _edit;
}
- public void playVideo(String p_path) {
- Uri filePath = Uri.parse(p_path);
- mediaPlayer = new MediaPlayer();
-
- try {
- mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
- mediaPlayer.setDataSource(activity.getApplicationContext(), filePath);
- mediaPlayer.prepare();
- mediaPlayer.start();
- } catch (IOException e) {
- System.out.println("IOError while playing video");
- }
- }
-
- public boolean isVideoPlaying() {
- if (mediaPlayer != null) {
- return mediaPlayer.isPlaying();
- }
- return false;
- }
-
- public void pauseVideo() {
- if (mediaPlayer != null) {
- mediaPlayer.pause();
- }
+ public int getScreenOrientation() {
+ return activity.getRequestedOrientation();
}
- public void stopVideo() {
- if (mediaPlayer != null) {
- mediaPlayer.release();
- mediaPlayer = null;
- }
+ public void setEdit(GodotEditText _edit) {
+ edit = _edit;
}
public static final int SYSTEM_DIR_DESKTOP = 0;
@@ -580,7 +517,6 @@ public class GodotIO {
public static final int SYSTEM_DIR_RINGTONES = 7;
public String getSystemDir(int idx) {
-
String what = "";
switch (idx) {
case SYSTEM_DIR_DESKTOP: {
@@ -625,7 +561,6 @@ public class GodotIO {
public static String unique_id = "";
public String getUniqueID() {
-
return unique_id;
}
}
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 89a65aea24..3693f36557 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -32,6 +32,8 @@ package org.godotengine.godot;
import android.app.Activity;
import android.hardware.SensorEvent;
+import android.view.Surface;
+
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
@@ -39,7 +41,6 @@ import javax.microedition.khronos.opengles.GL10;
* Wrapper for native library
*/
public class GodotLib {
-
public static GodotIO io;
static {
@@ -65,18 +66,19 @@ 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 GL thread when the underlying Android surface is created or recreated.
+ * Invoked on the render thread when the underlying Android surface is created or recreated.
+ * @param p_surface
* @param p_32_bits
- * @see android.opengl.GLSurfaceView.Renderer#onSurfaceCreated(GL10, EGLConfig)
*/
- public static native void newcontext(boolean p_32_bits);
+ public static native void newcontext(Surface p_surface, boolean p_32_bits);
/**
* Forward {@link Activity#onBackPressed()} event from the main thread to the GL thread.
@@ -188,7 +190,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.
@@ -196,7 +198,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/iphone/platform_refcount.h b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
index 9029418462..27e63f3a66 100644
--- a/platform/iphone/platform_refcount.h
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* platform_refcount.h */
+/* GodotRenderView.java */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,21 +28,19 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/safe_refcount.h"
+package org.godotengine.godot;
-#ifdef IPHONE_ENABLED
+import android.view.SurfaceView;
-#define REFCOUNT_T int
-#define REFCOUNT_GET_T int const volatile &
+public interface GodotRenderView {
+ abstract public SurfaceView getView();
-#include <libkern/OSAtomic.h>
+ abstract public void initInputDevices();
-inline int atomic_conditional_increment(volatile int *v) {
- return (*v == 0) ? 0 : OSAtomicIncrement32(v);
-}
+ abstract public void queueOnRenderThread(Runnable event);
-inline int atomic_decrement(volatile int *v) {
- return OSAtomicDecrement32(v);
-}
+ abstract public void onActivityPaused();
+ abstract public void onActivityResumed();
-#endif
+ abstract public void onBackPressed();
+}
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 ee9a2aee4f..64395f7d1e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
@@ -30,19 +30,20 @@
package org.godotengine.godot;
+import org.godotengine.godot.plugin.GodotPlugin;
+import org.godotengine.godot.plugin.GodotPluginRegistry;
+import org.godotengine.godot.utils.GLUtils;
+
import android.content.Context;
import android.opengl.GLSurfaceView;
+
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
-import org.godotengine.godot.plugin.GodotPlugin;
-import org.godotengine.godot.plugin.GodotPluginRegistry;
-import org.godotengine.godot.utils.GLUtils;
/**
* Godot's renderer implementation.
*/
class GodotRenderer implements GLSurfaceView.Renderer {
-
private final GodotPluginRegistry pluginRegistry;
private boolean activityJustResumed = false;
@@ -63,14 +64,14 @@ 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);
}
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
- GodotLib.newcontext(GLUtils.use_32);
+ GodotLib.newcontext(null, GLUtils.use_32);
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
plugin.onGLSurfaceCreated(gl, config);
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
new file mode 100644
index 0000000000..e9872b58ff
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
@@ -0,0 +1,142 @@
+/*************************************************************************/
+/* GodotVulkanRenderView.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;
+
+import org.godotengine.godot.input.GodotGestureHandler;
+import org.godotengine.godot.input.GodotInputHandler;
+import org.godotengine.godot.vulkan.VkRenderer;
+import org.godotengine.godot.vulkan.VkSurfaceView;
+
+import android.annotation.SuppressLint;
+import android.view.GestureDetector;
+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 GodotInputHandler mInputHandler;
+ private final GestureDetector mGestureDetector;
+ private final VkRenderer mRenderer;
+
+ public GodotVulkanRenderView(Godot activity) {
+ super(activity);
+
+ mActivity = activity;
+ mInputHandler = new GodotInputHandler(this);
+ mGestureDetector = new GestureDetector(mActivity, new GodotGestureHandler(this));
+ mRenderer = new VkRenderer();
+
+ setFocusableInTouchMode(true);
+ startRenderer(mRenderer);
+ }
+
+ @Override
+ public SurfaceView getView() {
+ return this;
+ }
+
+ @Override
+ public void initInputDevices() {
+ mInputHandler.initInputDevices();
+ }
+
+ @Override
+ public void queueOnRenderThread(Runnable event) {
+ queueOnVkThread(event);
+ }
+
+ @Override
+ public void onActivityPaused() {
+ onPause();
+ }
+
+ @Override
+ public void onActivityResumed() {
+ onResume();
+ }
+
+ @Override
+ public void onBackPressed() {
+ mActivity.onBackPressed();
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+ mGestureDetector.onTouchEvent(event);
+ return mActivity.gotTouchEvent(event);
+ }
+
+ @Override
+ public boolean onKeyUp(final int keyCode, KeyEvent event) {
+ return mInputHandler.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyDown(final int keyCode, KeyEvent event) {
+ return mInputHandler.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ return mInputHandler.onGenericMotionEvent(event) || super.onGenericMotionEvent(event);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ queueOnVkThread(new Runnable() {
+ @Override
+ public void run() {
+ // Resume the renderer
+ mRenderer.onVkResume();
+ GodotLib.focusin();
+ }
+ });
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ queueOnVkThread(new Runnable() {
+ @Override
+ public void run() {
+ GodotLib.focusout();
+ // Pause the renderer
+ mRenderer.onVkPause();
+ }
+ });
+ }
+}
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 e901b4b36d..7f596575a8 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
@@ -29,6 +29,9 @@
/*************************************************************************/
package org.godotengine.godot.input;
+
+import org.godotengine.godot.*;
+
import android.content.Context;
import android.os.Handler;
import android.os.Message;
@@ -38,8 +41,8 @@ import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
+
import java.lang.ref.WeakReference;
-import org.godotengine.godot.*;
public class GodotEditText extends EditText {
// ===========================================================
@@ -51,10 +54,11 @@ public class GodotEditText extends EditText {
// ===========================================================
// Fields
// ===========================================================
- private GodotView mView;
+ private GodotRenderView mRenderView;
private GodotTextInputWrapper mInputWrapper;
private EditHandler sHandler = new EditHandler(this);
private String mOriginText;
+ private int mMaxInputLength;
private static class EditHandler extends Handler {
private final WeakReference<GodotEditText> mEdit;
@@ -76,22 +80,22 @@ public class GodotEditText extends EditText {
// ===========================================================
public GodotEditText(final Context context) {
super(context);
- this.initView();
+ initView();
}
public GodotEditText(final Context context, final AttributeSet attrs) {
super(context, attrs);
- this.initView();
+ initView();
}
public GodotEditText(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
- this.initView();
+ initView();
}
protected void initView() {
- this.setPadding(0, 0, 0, 0);
- this.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
+ setPadding(0, 0, 0, 0);
+ setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
}
private void handleMessage(final Message msg) {
@@ -101,12 +105,19 @@ public class GodotEditText extends EditText {
String text = edit.mOriginText;
if (edit.requestFocus()) {
edit.removeTextChangedListener(edit.mInputWrapper);
+ setMaxInputLength(edit);
edit.setText("");
edit.append(text);
+ if (msg.arg2 != -1) {
+ edit.setSelection(msg.arg1, msg.arg2);
+ edit.mInputWrapper.setSelection(true);
+ } else {
+ edit.mInputWrapper.setSelection(false);
+ }
+
edit.mInputWrapper.setOriginText(text);
edit.addTextChangedListener(edit.mInputWrapper);
- setMaxInputLength(edit, msg.arg1);
- final InputMethodManager imm = (InputMethodManager)mView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ final InputMethodManager imm = (InputMethodManager)mRenderView.getView().getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(edit, 0);
}
} break;
@@ -115,32 +126,28 @@ public class GodotEditText extends EditText {
GodotEditText edit = (GodotEditText)msg.obj;
edit.removeTextChangedListener(mInputWrapper);
- final InputMethodManager imm = (InputMethodManager)mView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ final InputMethodManager imm = (InputMethodManager)mRenderView.getView().getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(edit.getWindowToken(), 0);
- edit.mView.requestFocus();
+ edit.mRenderView.getView().requestFocus();
} break;
}
}
- private void setMaxInputLength(EditText p_edit_text, int p_max_input_length) {
- if (p_max_input_length > 0) {
- InputFilter[] filters = new InputFilter[1];
- filters[0] = new InputFilter.LengthFilter(p_max_input_length);
- p_edit_text.setFilters(filters);
- } else {
- p_edit_text.setFilters(new InputFilter[] {});
- }
+ private void setMaxInputLength(EditText p_edit_text) {
+ InputFilter[] filters = new InputFilter[1];
+ filters[0] = new InputFilter.LengthFilter(this.mMaxInputLength);
+ p_edit_text.setFilters(filters);
}
// ===========================================================
// Getter & Setter
// ===========================================================
- public void setView(final GodotView view) {
- this.mView = view;
+ public void setView(final GodotRenderView view) {
+ mRenderView = view;
if (mInputWrapper == null)
- mInputWrapper = new GodotTextInputWrapper(mView, this);
- this.setOnEditorActionListener(mInputWrapper);
- view.requestFocus();
+ mInputWrapper = new GodotTextInputWrapper(mRenderView, this);
+ setOnEditorActionListener(mInputWrapper);
+ view.getView().requestFocus();
}
// ===========================================================
@@ -152,7 +159,7 @@ public class GodotEditText extends EditText {
/* Let GlSurfaceView get focus if back key is input. */
if (keyCode == KeyEvent.KEYCODE_BACK) {
- this.mView.requestFocus();
+ mRenderView.getView().requestFocus();
}
return true;
@@ -161,13 +168,24 @@ public class GodotEditText extends EditText {
// ===========================================================
// Methods
// ===========================================================
- public void showKeyboard(String p_existing_text, int p_max_input_length) {
- this.mOriginText = p_existing_text;
+ public void showKeyboard(String p_existing_text, 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;
+ this.mMaxInputLength = maxInputLength;
+ } else if (p_cursor_end == -1) { // not text selection
+ this.mOriginText = p_existing_text.substring(0, p_cursor_start);
+ this.mMaxInputLength = maxInputLength - (p_existing_text.length() - p_cursor_start);
+ } else {
+ this.mOriginText = p_existing_text.substring(0, p_cursor_end);
+ this.mMaxInputLength = maxInputLength - (p_existing_text.length() - p_cursor_end);
+ }
final Message msg = new Message();
msg.what = HANDLER_OPEN_IME_KEYBOARD;
msg.obj = this;
- msg.arg1 = p_max_input_length;
+ msg.arg1 = p_cursor_start;
+ msg.arg2 = p_cursor_end;
sHandler.sendMessage(msg);
}
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 1a38a9c3d2..1c9a683bbd 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
@@ -30,26 +30,26 @@
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;
-import org.godotengine.godot.GodotLib;
-import org.godotengine.godot.GodotView;
/**
- * Handles gesture input related events for the {@link GodotView} view.
+ * Handles gesture input related events for the {@link GodotRenderView} view.
* https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener
*/
public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener {
+ private final GodotRenderView mRenderView;
- private final GodotView godotView;
-
- public GodotGestureHandler(GodotView godotView) {
- this.godotView = godotView;
+ public GodotGestureHandler(GodotRenderView godotView) {
+ mRenderView = godotView;
}
private void queueEvent(Runnable task) {
- godotView.queueEvent(task);
+ mRenderView.queueOnRenderThread(task);
}
@Override
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 e00ca86c41..9abd65cc67 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
@@ -32,37 +32,38 @@ package org.godotengine.godot.input;
import static org.godotengine.godot.utils.GLUtils.DEBUG;
+import org.godotengine.godot.GodotLib;
+import org.godotengine.godot.GodotRenderView;
+import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener;
+
import android.util.Log;
import android.view.InputDevice;
import android.view.InputDevice.MotionRange;
import android.view.KeyEvent;
import android.view.MotionEvent;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-import org.godotengine.godot.GodotLib;
-import org.godotengine.godot.GodotView;
-import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener;
/**
- * Handles input related events for the {@link GodotView} view.
+ * Handles input related events for the {@link GodotRenderView} view.
*/
public class GodotInputHandler implements InputDeviceListener {
+ private final ArrayList<Joystick> mJoysticksDevices = new ArrayList<Joystick>();
- private final ArrayList<Joystick> joysticksDevices = new ArrayList<Joystick>();
-
- private final GodotView godotView;
- private final InputManagerCompat inputManager;
+ private final GodotRenderView mRenderView;
+ private final InputManagerCompat mInputManager;
- public GodotInputHandler(GodotView godotView) {
- this.godotView = godotView;
- this.inputManager = InputManagerCompat.Factory.getInputManager(godotView.getContext());
- this.inputManager.registerInputDeviceListener(this, null);
+ public GodotInputHandler(GodotRenderView godotView) {
+ mRenderView = godotView;
+ mInputManager = InputManagerCompat.Factory.getInputManager(mRenderView.getView().getContext());
+ mInputManager.registerInputDeviceListener(this, null);
}
private void queueEvent(Runnable task) {
- godotView.queueEvent(task);
+ mRenderView.queueOnRenderThread(task);
}
private boolean isKeyEvent_GameDevice(int source) {
@@ -84,7 +85,6 @@ public class GodotInputHandler implements InputDeviceListener {
int source = event.getSource();
if (isKeyEvent_GameDevice(source)) {
-
final int button = getGodotButton(keyCode);
final int device_id = findJoystickDevice(event.getDeviceId());
@@ -113,7 +113,7 @@ public class GodotInputHandler implements InputDeviceListener {
public boolean onKeyDown(final int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
- godotView.onBackPressed();
+ mRenderView.onBackPressed();
// press 'back' button should not terminate program
//normal handle 'back' event in game logic
return true;
@@ -127,7 +127,6 @@ public class GodotInputHandler implements InputDeviceListener {
//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)));
if (isKeyEvent_GameDevice(source)) {
-
if (event.getRepeatCount() > 0) // ignore key echo
return true;
@@ -159,12 +158,11 @@ public class GodotInputHandler implements InputDeviceListener {
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());
// Check if the device exists
if (device_id > -1) {
- Joystick joy = joysticksDevices.get(device_id);
+ Joystick joy = mJoysticksDevices.get(device_id);
for (int i = 0; i < joy.axes.size(); i++) {
InputDevice.MotionRange range = joy.axes.get(i);
@@ -208,11 +206,11 @@ public class GodotInputHandler implements InputDeviceListener {
public void initInputDevices() {
/* initially add input devices*/
- int[] deviceIds = inputManager.getInputDeviceIds();
+ int[] deviceIds = mInputManager.getInputDeviceIds();
for (int deviceId : deviceIds) {
- InputDevice device = inputManager.getInputDevice(deviceId);
+ InputDevice device = mInputManager.getInputDevice(deviceId);
if (DEBUG) {
- Log.v("GodotView", String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName()));
+ Log.v("GodotInputHandler", String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName()));
}
onInputDeviceAdded(deviceId);
}
@@ -224,13 +222,13 @@ public class GodotInputHandler implements InputDeviceListener {
// Check if the device has not been already added
if (id < 0) {
- InputDevice device = inputManager.getInputDevice(deviceId);
+ 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 = joysticksDevices.size();
+ id = mJoysticksDevices.size();
Joystick joy = new Joystick();
joy.device_id = deviceId;
@@ -249,7 +247,7 @@ public class GodotInputHandler implements InputDeviceListener {
}
}
- joysticksDevices.add(joy);
+ mJoysticksDevices.add(joy);
final int device_id = id;
final String name = joy.name;
@@ -270,7 +268,7 @@ public class GodotInputHandler implements InputDeviceListener {
// Check if the evice has not been already removed
if (device_id > -1) {
- joysticksDevices.remove(device_id);
+ mJoysticksDevices.remove(device_id);
queueEvent(new Runnable() {
@Override
@@ -360,8 +358,8 @@ public class GodotInputHandler implements InputDeviceListener {
}
private int findJoystickDevice(int device_id) {
- for (int i = 0; i < joysticksDevices.size(); i++) {
- if (joysticksDevices.get(i).device_id == device_id) {
+ for (int i = 0; i < mJoysticksDevices.size(); i++) {
+ if (mJoysticksDevices.get(i).device_id == device_id) {
return i;
}
}
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 18f2d57661..9c7cf9f341 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
@@ -29,6 +29,9 @@
/*************************************************************************/
package org.godotengine.godot.input;
+
+import org.godotengine.godot.*;
+
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
@@ -37,7 +40,6 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
-import org.godotengine.godot.*;
public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListener {
// ===========================================================
@@ -48,17 +50,18 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
// ===========================================================
// Fields
// ===========================================================
- private final GodotView mView;
+ private final GodotRenderView mRenderView;
private final GodotEditText mEdit;
private String mOriginText;
+ private boolean mHasSelection;
// ===========================================================
// Constructors
// ===========================================================
- public GodotTextInputWrapper(final GodotView view, final GodotEditText edit) {
- this.mView = view;
- this.mEdit = edit;
+ public GodotTextInputWrapper(final GodotRenderView view, final GodotEditText edit) {
+ mRenderView = view;
+ mEdit = edit;
}
// ===========================================================
@@ -66,13 +69,17 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
// ===========================================================
private boolean isFullScreenEdit() {
- final TextView textField = this.mEdit;
+ final TextView textField = mEdit;
final InputMethodManager imm = (InputMethodManager)textField.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
return imm.isFullscreenMode();
}
public void setOriginText(final String originText) {
- this.mOriginText = originText;
+ mOriginText = originText;
+ }
+
+ public void setSelection(boolean selection) {
+ mHasSelection = selection;
}
// ===========================================================
@@ -87,12 +94,17 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
public void beforeTextChanged(final CharSequence pCharSequence, final int start, final int count, final int after) {
//Log.d(TAG, "beforeTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",after: " + after);
- mView.queueEvent(new Runnable() {
+ mRenderView.queueOnRenderThread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < count; ++i) {
GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, true);
GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, false);
+
+ if (mHasSelection) {
+ mHasSelection = false;
+ break;
+ }
}
}
});
@@ -106,7 +118,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
for (int i = start; i < start + count; ++i) {
newChars[i - start] = pCharSequence.charAt(i);
}
- mView.queueEvent(new Runnable() {
+ mRenderView.queueOnRenderThread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < count; ++i) {
@@ -124,10 +136,10 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
@Override
public boolean onEditorAction(final TextView pTextView, final int pActionID, final KeyEvent pKeyEvent) {
- if (this.mEdit == pTextView && this.isFullScreenEdit()) {
+ if (mEdit == pTextView && isFullScreenEdit()) {
final String characters = pKeyEvent.getCharacters();
- mView.queueEvent(new Runnable() {
+ mRenderView.queueOnRenderThread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < characters.length(); i++) {
@@ -144,7 +156,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, true);
GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, false);
- this.mView.requestFocus();
+ mRenderView.getView().requestFocus();
return true;
}
return false;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java
index 4042c42e9d..62810ad3a4 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java
@@ -120,7 +120,6 @@ public interface InputManagerCompat {
* Use this to construct a compatible InputManager.
*/
public static class Factory {
-
/**
* Constructs and returns a compatible InputManger
*
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java
index e4bafa7ff9..61828dccae 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java
@@ -23,12 +23,12 @@ import android.os.Build;
import android.os.Handler;
import android.view.InputDevice;
import android.view.MotionEvent;
+
import java.util.HashMap;
import java.util.Map;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class InputManagerV16 implements InputManagerCompat {
-
private final InputManager mInputManager;
private final Map<InputManagerCompat.InputDeviceListener, V16InputDeviceListener> mListeners;
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 0c1bdb32aa..1f3fe1e527 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
@@ -31,6 +31,7 @@
package org.godotengine.godot.input;
import android.view.InputDevice.MotionRange;
+
import java.util.ArrayList;
/**
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 e745cdd0a5..431bd4f5f9 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-2019 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
@@ -30,20 +30,27 @@
package org.godotengine.godot.plugin;
+import org.godotengine.godot.BuildConfig;
+import org.godotengine.godot.Godot;
+
import android.app.Activity;
import android.content.Intent;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
+import android.util.Log;
import android.view.Surface;
import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
-import org.godotengine.godot.Godot;
/**
* Base class for the Godot Android plugins.
@@ -69,8 +76,10 @@ import org.godotengine.godot.Godot;
* 'godot/plugin/v1/[PluginName]/'
*/
public abstract class GodotPlugin {
+ private static final String TAG = GodotPlugin.class.getSimpleName();
private final Godot godot;
+ private final ConcurrentHashMap<String, SignalInfo> registeredSignals = new ConcurrentHashMap<>();
public GodotPlugin(Godot godot) {
this.godot = godot;
@@ -118,6 +127,13 @@ public abstract class GodotPlugin {
nativeRegisterMethod(getPluginName(), method.getName(), method.getReturnType().getName(), pt);
}
+ // Register the signals for this plugin.
+ for (SignalInfo signalInfo : getPluginSignals()) {
+ String signalName = signalInfo.getName();
+ nativeRegisterSignal(getPluginName(), signalName, signalInfo.getParamTypesNames());
+ registeredSignals.put(signalName, signalInfo);
+ }
+
// Get the list of gdnative libraries to register.
Set<String> gdnativeLibrariesPaths = getPluginGDNativeLibrariesPaths();
if (!gdnativeLibrariesPaths.isEmpty()) {
@@ -220,7 +236,17 @@ public abstract class GodotPlugin {
* Returns the list of methods to be exposed to Godot.
*/
@NonNull
- public abstract List<String> getPluginMethods();
+ public List<String> getPluginMethods() {
+ return Collections.emptyList();
+ }
+
+ /**
+ * Returns the list of signals to be exposed to Godot.
+ */
+ @NonNull
+ public Set<SignalInfo> getPluginSignals() {
+ return Collections.emptySet();
+ }
/**
* Returns the paths for the plugin's gdnative libraries.
@@ -253,6 +279,49 @@ public abstract class GodotPlugin {
}
/**
+ * Emit a registered Godot signal.
+ * @param signalName
+ * @param signalArgs
+ */
+ protected void emitSignal(final String signalName, final Object... signalArgs) {
+ try {
+ // Check that the given signal is among the registered set.
+ SignalInfo signalInfo = registeredSignals.get(signalName);
+ if (signalInfo == null) {
+ throw new IllegalArgumentException(
+ "Signal " + signalName + " is not registered for this plugin.");
+ }
+
+ // Validate the arguments count.
+ Class<?>[] signalParamTypes = signalInfo.getParamTypes();
+ if (signalArgs.length != signalParamTypes.length) {
+ throw new IllegalArgumentException(
+ "Invalid arguments count. Should be " + signalParamTypes.length + " but is " + signalArgs.length);
+ }
+
+ // Validate the argument's types.
+ for (int i = 0; i < signalParamTypes.length; i++) {
+ if (!signalParamTypes[i].isInstance(signalArgs[i])) {
+ throw new IllegalArgumentException(
+ "Invalid type for argument #" + i + ". Should be of type " + signalParamTypes[i].getName());
+ }
+ }
+
+ runOnRenderThread(new Runnable() {
+ @Override
+ public void run() {
+ nativeEmitSignal(getPluginName(), signalName, signalArgs);
+ }
+ });
+ } catch (IllegalArgumentException exception) {
+ Log.w(TAG, exception.getMessage());
+ if (BuildConfig.DEBUG) {
+ throw exception;
+ }
+ }
+ }
+
+ /**
* Used to setup a {@link GodotPlugin} instance.
* @param p_name Name of the instance.
*/
@@ -272,4 +341,20 @@ public abstract class GodotPlugin {
* @param gdnlibPaths Paths to the libraries relative to the 'assets' directory.
*/
private native void nativeRegisterGDNativeLibraries(String[] gdnlibPaths);
+
+ /**
+ * Used to complete registration of the {@link GodotPlugin} instance's methods.
+ * @param pluginName Name of the plugin
+ * @param signalName Name of the signal to register
+ * @param signalParamTypes Signal parameters types
+ */
+ private native void nativeRegisterSignal(String pluginName, String signalName, String[] signalParamTypes);
+
+ /**
+ * Used to emit signal by {@link GodotPlugin} instance.
+ * @param pluginName Name of the plugin
+ * @param signalName Name of the signal to emit
+ * @param signalParams Signal parameters
+ */
+ private native void nativeEmitSignal(String pluginName, String signalName, Object[] signalParams);
}
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 b6d949b7bf..12d2ed09fb 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-2019 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* 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 */
@@ -30,26 +30,27 @@
package org.godotengine.godot.plugin;
+import org.godotengine.godot.Godot;
+
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
-import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
+
+import androidx.annotation.Nullable;
+
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
-import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import org.godotengine.godot.Godot;
/**
* Registry used to load and access the registered Godot Android plugins.
*/
public final class GodotPluginRegistry {
-
private static final String TAG = GodotPluginRegistry.class.getSimpleName();
private static final String GODOT_PLUGIN_V1_NAME_PREFIX = "org.godotengine.plugin.v1.";
@@ -57,7 +58,9 @@ public final class GodotPluginRegistry {
/**
* Name for the metadata containing the list of Godot plugins to enable.
*/
- private static final String GODOT_ENABLED_PLUGINS_LABEL = "custom_template_plugins";
+ private static final String GODOT_ENABLED_PLUGINS_LABEL = "plugins";
+
+ private static final String PLUGIN_VALUE_SEPARATOR_REGEX = "\\|";
private static GodotPluginRegistry instance;
private final ConcurrentHashMap<String, GodotPlugin> registry;
@@ -127,13 +130,13 @@ public final class GodotPluginRegistry {
}
// When using the Godot editor for building and exporting the apk, this is used to check
- // which plugins to enable since the custom build template may contain prebuilt plugins.
+ // which plugins to enable.
// When using a custom process to generate the apk, the metadata is not needed since
// it's assumed that the developer is aware of the dependencies included in the apk.
final Set<String> enabledPluginsSet;
if (metaData.containsKey(GODOT_ENABLED_PLUGINS_LABEL)) {
String enabledPlugins = metaData.getString(GODOT_ENABLED_PLUGINS_LABEL, "");
- String[] enabledPluginsList = enabledPlugins.split(",");
+ String[] enabledPluginsList = enabledPlugins.split(PLUGIN_VALUE_SEPARATOR_REGEX);
if (enabledPluginsList.length == 0) {
// No plugins to enable. Aborting early.
return;
@@ -157,6 +160,8 @@ public final class GodotPluginRegistry {
continue;
}
+ Log.i(TAG, "Initializing Godot plugin " + pluginName);
+
// Retrieve the plugin class full name.
String pluginHandleClassFullName = metaData.getString(metaDataName);
if (!TextUtils.isEmpty(pluginHandleClassFullName)) {
@@ -176,6 +181,7 @@ public final class GodotPluginRegistry {
"Meta-data plugin name does not match the value returned by the plugin handle: " + pluginName + " =/= " + pluginHandle.getPluginName());
}
registry.put(pluginName, pluginHandle);
+ Log.i(TAG, "Completed initialization for Godot plugin " + pluginHandle.getPluginName());
} catch (ClassNotFoundException e) {
Log.w(TAG, "Unable to load Godot plugin " + pluginName, e);
} catch (IllegalAccessException e) {
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
new file mode 100644
index 0000000000..f82c4d3fa0
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java
@@ -0,0 +1,99 @@
+/*************************************************************************/
+/* SignalInfo.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;
+
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+
+import java.util.Arrays;
+
+/**
+ * Store information about a {@link GodotPlugin}'s signal.
+ */
+public final class SignalInfo {
+ private final String name;
+ private final Class<?>[] paramTypes;
+ private final String[] paramTypesNames;
+
+ public SignalInfo(@NonNull String signalName, Class<?>... paramTypes) {
+ if (TextUtils.isEmpty(signalName)) {
+ throw new IllegalArgumentException("Invalid signal name: " + signalName);
+ }
+
+ this.name = signalName;
+ this.paramTypes = paramTypes == null ? new Class<?>[ 0 ] : paramTypes;
+ this.paramTypesNames = new String[this.paramTypes.length];
+ for (int i = 0; i < this.paramTypes.length; i++) {
+ this.paramTypesNames[i] = this.paramTypes[i].getName();
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ Class<?>[] getParamTypes() {
+ return paramTypes;
+ }
+
+ String[] getParamTypesNames() {
+ return paramTypesNames;
+ }
+
+ @Override
+ public String toString() {
+ return "SignalInfo{"
+ +
+ "name='" + name + '\'' +
+ ", paramsTypes=" + Arrays.toString(paramTypes) +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof SignalInfo)) {
+ return false;
+ }
+
+ SignalInfo that = (SignalInfo)o;
+
+ return name.equals(that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+}
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 bc0e565774..acc9c4981b 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
@@ -34,7 +34,6 @@ import java.security.MessageDigest;
import java.util.Random;
public class Crypt {
-
public static String md5(String input) {
try {
// Create MD5 Hash
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 9d29551f89..82420eda79 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
@@ -31,6 +31,7 @@
package org.godotengine.godot.utils;
import android.util.Log;
+
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
@@ -39,7 +40,6 @@ import javax.microedition.khronos.egl.EGLDisplay;
* Contains GL utilities methods.
*/
public class GLUtils {
-
private static final String TAG = GLUtils.class.getSimpleName();
public static final boolean DEBUG = false;
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 011d426c7e..0832a9b965 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
@@ -30,10 +30,11 @@
package org.godotengine.godot.utils;
+import org.godotengine.godot.Godot;
+
import android.content.Context;
import android.net.wifi.WifiManager;
import android.util.Log;
-import org.godotengine.godot.Godot;
/**
* This class handles Android-specific networking functions.
@@ -41,7 +42,6 @@ import org.godotengine.godot.Godot;
* to receive broadcast and multicast packets.
*/
public class GodotNetUtils {
-
/* A single, reference counted, multicast lock, or null if permission CHANGE_WIFI_MULTICAST_STATE is missing */
private WifiManager.MulticastLock multicastLock;
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 7cf32b00fe..6837e4f147 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
@@ -30,21 +30,26 @@
package org.godotengine.godot.utils;
+import org.godotengine.godot.Godot;
+
import android.Manifest;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.os.Build;
-import android.support.v4.content.ContextCompat;
+import android.util.Log;
+
+import androidx.core.content.ContextCompat;
+
import java.util.ArrayList;
import java.util.List;
-import org.godotengine.godot.Godot;
/**
* This class includes utility functions for Android permissions related operations.
* @author Cagdas Caglak <cagdascaglak@gmail.com>
*/
public final class PermissionsUtil {
+ private static final String TAG = PermissionsUtil.class.getSimpleName();
static final int REQUEST_RECORD_AUDIO_PERMISSION = 1;
static final int REQUEST_CAMERA_PERMISSION = 2;
@@ -113,8 +118,8 @@ public final class PermissionsUtil {
dangerousPermissions.add(manifestPermission);
}
} catch (PackageManager.NameNotFoundException e) {
- e.printStackTrace();
- return false;
+ // Skip this permission and continue.
+ Log.w(TAG, "Unable to identify permission " + manifestPermission, e);
}
}
@@ -153,8 +158,8 @@ public final class PermissionsUtil {
dangerousPermissions.add(manifestPermission);
}
} catch (PackageManager.NameNotFoundException e) {
- e.printStackTrace();
- return new String[0];
+ // Skip this permission and continue.
+ Log.w(TAG, "Unable to identify permission " + manifestPermission, e);
}
}
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 67faad8ddd..aeb4628d5d 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
@@ -33,6 +33,11 @@ package org.godotengine.godot.vulkan
import android.view.Surface
+import org.godotengine.godot.Godot
+import org.godotengine.godot.GodotLib
+import org.godotengine.godot.plugin.GodotPlugin
+import org.godotengine.godot.plugin.GodotPluginRegistry
+
/**
* Responsible to setting up and driving the Vulkan rendering logic.
*
@@ -48,52 +53,57 @@ import android.view.Surface
*/
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) {
- nativeOnVkSurfaceCreated(surface)
+ GodotLib.newcontext(surface, false)
+
+ for (plugin in pluginRegistry.getAllPlugins()) {
+ plugin.onVkSurfaceCreated(surface)
+ }
}
/**
* Called after the surface is created and whenever its size changes.
*/
fun onVkSurfaceChanged(surface: Surface, width: Int, height: Int) {
- nativeOnVkSurfaceChanged(surface, width, height)
+ GodotLib.resize(surface, width, height)
+
+ for (plugin in pluginRegistry.getAllPlugins()) {
+ plugin.onVkSurfaceChanged(surface, width, height)
+ }
}
/**
* Called to draw the current frame.
*/
fun onVkDrawFrame() {
- nativeOnVkDrawFrame()
+ GodotLib.step()
+ for (plugin in pluginRegistry.getAllPlugins()) {
+ plugin.onVkDrawFrame()
+ }
}
/**
* Called when the rendering thread is resumed.
*/
fun onVkResume() {
- nativeOnVkResume()
+ GodotLib.onRendererResumed()
}
/**
* Called when the rendering thread is paused.
*/
fun onVkPause() {
- nativeOnVkPause()
+ GodotLib.onRendererPaused()
}
/**
* Called when the rendering thread is destroyed and used as signal to tear down the Vulkan logic.
*/
fun onVkDestroy() {
- nativeOnVkDestroy()
}
-
- private external fun nativeOnVkSurfaceCreated(surface: Surface)
- private external fun nativeOnVkSurfaceChanged(surface: Surface, width: Int, height: Int)
- private external fun nativeOnVkResume()
- private external fun nativeOnVkDrawFrame()
- private external fun nativeOnVkPause()
- private external fun nativeOnVkDestroy()
}
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 1c594f3201..6b0e12b21a 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
@@ -49,7 +49,7 @@ import android.view.SurfaceView
* UI thread.
* </ul>
*/
-internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {
+open internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {
companion object {
fun checkState(expression: Boolean, errorMessage: Any) {
@@ -100,7 +100,7 @@ internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHo
*
* Must not be called before a [VkRenderer] has been set.
*/
- fun onResume() {
+ open fun onResume() {
vkThread.onResume()
}
@@ -109,7 +109,7 @@ internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHo
*
* Must not be called before a [VkRenderer] has been set.
*/
- fun onPause() {
+ open fun onPause() {
vkThread.onPause()
}
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 2e332840bf..7557c8aa22 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
@@ -219,9 +219,9 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk
vkRenderer.onVkDrawFrame()
}
} catch (ex: InterruptedException) {
- Log.i(TAG, ex.message)
+ Log.i(TAG, "InterruptedException", ex)
} catch (ex: IllegalStateException) {
- Log.i(TAG, ex.message)
+ Log.i(TAG, "IllegalStateException", ex)
} finally {
threadExiting()
}
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 9209d6ccf2..819bcccdf1 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
@@ -32,6 +32,7 @@ package org.godotengine.godot.xr.ovr;
import android.opengl.EGLExt;
import android.opengl.GLSurfaceView;
+
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
@@ -40,7 +41,6 @@ import javax.microedition.khronos.egl.EGLDisplay;
* EGL config chooser for the Oculus Mobile VR SDK.
*/
public class OvrConfigChooser implements GLSurfaceView.EGLConfigChooser {
-
private static final int[] CONFIG_ATTRIBS = {
EGL10.EGL_RED_SIZE, 8,
EGL10.EGL_GREEN_SIZE, 8,
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 36f4416df2..2d9b921466 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
@@ -32,6 +32,7 @@ package org.godotengine.godot.xr.ovr;
import android.opengl.EGL14;
import android.opengl.GLSurfaceView;
+
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
@@ -41,7 +42,6 @@ import javax.microedition.khronos.egl.EGLDisplay;
* EGL Context factory for the Oculus mobile VR SDK.
*/
public class OvrContextFactory implements GLSurfaceView.EGLContextFactory {
-
private static final int[] CONTEXT_ATTRIBS = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE
};
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 b2aa130f37..43c7f0f966 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
@@ -31,6 +31,7 @@
package org.godotengine.godot.xr.ovr;
import android.opengl.GLSurfaceView;
+
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
@@ -40,7 +41,6 @@ import javax.microedition.khronos.egl.EGLSurface;
* EGL window surface factory for the Oculus mobile VR SDK.
*/
public class OvrWindowSurfaceFactory implements GLSurfaceView.EGLWindowSurfaceFactory {
-
private final static int[] SURFACE_ATTRIBS = {
EGL10.EGL_WIDTH, 16,
EGL10.EGL_HEIGHT, 16,
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 8409e37f8f..54672db282 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
@@ -30,17 +30,18 @@
package org.godotengine.godot.xr.regular;
+import org.godotengine.godot.utils.GLUtils;
+
import android.opengl.GLSurfaceView;
+
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
-import org.godotengine.godot.utils.GLUtils;
/**
* Used to select the egl config for pancake games.
*/
public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser {
-
private static final String TAG = RegularConfigChooser.class.getSimpleName();
private int[] mValue = new int[1];
@@ -72,7 +73,6 @@ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser {
}
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
-
/* Get the number of minimally matching EGL configurations
*/
int[] num_config = new int[1];
@@ -127,7 +127,6 @@ public class RegularConfigChooser implements GLSurfaceView.EGLConfigChooser {
private int findConfigAttrib(EGL10 egl, EGLDisplay display,
EGLConfig config, int attribute, int defaultValue) {
-
if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
return mValue[0];
}
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 f2b4c95a2c..126f3ad5f5 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
@@ -30,14 +30,16 @@
package org.godotengine.godot.xr.regular;
+import org.godotengine.godot.GodotLib;
+import org.godotengine.godot.utils.GLUtils;
+
import android.opengl.GLSurfaceView;
import android.util.Log;
+
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
-import org.godotengine.godot.GodotLib;
-import org.godotengine.godot.utils.GLUtils;
/**
* Factory used to setup the opengl context for pancake games.
@@ -51,7 +53,6 @@ public class RegularContextFactory implements GLSurfaceView.EGLContextFactory {
private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
- String driver_name = GodotLib.getGlobal("rendering/quality/driver/driver_name");
// FIXME: Add support for Vulkan.
Log.w(TAG, "creating OpenGL ES 2.0 context :");
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 71fcf06020..c83c47bed7 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
@@ -30,15 +30,16 @@
package org.godotengine.godot.xr.regular;
+import org.godotengine.godot.utils.GLUtils;
+
import android.util.Log;
+
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
-import org.godotengine.godot.utils.GLUtils;
/* Fallback if 32bit View is not supported*/
public class RegularFallbackConfigChooser extends RegularConfigChooser {
-
private static final String TAG = RegularFallbackConfigChooser.class.getSimpleName();
private RegularConfigChooser fallback;
diff --git a/platform/android/java/plugins/godotpayment/build.gradle b/platform/android/java/plugins/godotpayment/build.gradle
index 4f376c4587..ffab86e26e 100644
--- a/platform/android/java/plugins/godotpayment/build.gradle
+++ b/platform/android/java/plugins/godotpayment/build.gradle
@@ -3,6 +3,7 @@ apply plugin: 'com.android.library'
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
+ useLibrary 'org.apache.http.legacy'
defaultConfig {
minSdkVersion versions.minSdk
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
index c15bc232ce..de009f6d16 100644
--- 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
@@ -33,11 +33,12 @@ 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;
@@ -45,7 +46,6 @@ abstract public class ConsumeTask {
private String mToken;
private static class ConsumeAsyncTask extends AsyncTask<String, String, String> {
-
private WeakReference<ConsumeTask> mTask;
ConsumeAsyncTask(ConsumeTask consume) {
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
index c7d0a5de65..e2b12c94a4 100644
--- 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
@@ -30,22 +30,25 @@
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.support.annotation.NonNull;
import android.util.Log;
+
+import androidx.annotation.NonNull;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import org.godotengine.godot.Dictionary;
-import org.godotengine.godot.Godot;
-import org.godotengine.godot.GodotLib;
-import org.godotengine.godot.plugin.GodotPlugin;
+
import org.json.JSONException;
import org.json.JSONObject;
public class GodotPayment extends GodotPlugin {
-
- private Integer purchaseCallbackId = 0;
+ private Long purchaseCallbackId = 0L;
private String accessToken;
private String purchaseValidationUrlPrefix;
private String transactionId;
@@ -126,11 +129,11 @@ public class GodotPayment extends GodotPlugin {
GodotLib.calldeferred(purchaseCallbackId, "purchase_owned", new Object[] { sku });
}
- public int getPurchaseCallbackId() {
+ public long getPurchaseCallbackId() {
return purchaseCallbackId;
}
- public void setPurchaseCallbackId(int purchaseCallbackId) {
+ public void setPurchaseCallbackId(long purchaseCallbackId) {
this.purchaseCallbackId = purchaseCallbackId;
}
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
index fe5685288b..00e216e8c0 100644
--- 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
@@ -32,11 +32,11 @@ 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) {
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
index d5919e3d9d..435f43c49d 100644
--- 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
@@ -34,7 +34,6 @@ import android.content.Context;
import android.content.SharedPreferences;
public class PaymentsCache {
-
public Context context;
public PaymentsCache(Context context) {
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
index bded1f452f..9b3a338866 100644
--- 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
@@ -40,14 +40,16 @@ 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;
@@ -155,7 +157,6 @@ public class PaymentsManager {
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");
@@ -165,7 +166,6 @@ public class PaymentsManager {
}
for (int i = 0; i < myPurchases.size(); i++) {
-
try {
String receipt = myPurchases.get(i);
JSONObject inappPurchaseData = new JSONObject(receipt);
@@ -226,11 +226,9 @@ public class PaymentsManager {
}
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) {
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
index eecd1d2151..f894bd5132 100644
--- 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
@@ -37,10 +37,10 @@ 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;
@@ -56,7 +56,7 @@ abstract public class PurchaseTask {
PaymentsCache pc = new PaymentsCache(context);
Boolean isBlocked = pc.getConsumableFlag("block", sku);
/*
- if(isBlocked){
+ if(isBlocked) {
Log.d("XXX", "Is awaiting payment confirmation");
error("Awaiting payment confirmation");
return;
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
index b7bd638feb..1d52cf0fa5 100644
--- 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
@@ -34,19 +34,20 @@ 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;
@@ -91,7 +92,6 @@ abstract public class ReleaseAllConsumablesTask {
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");
@@ -103,7 +103,6 @@ abstract public class ReleaseAllConsumablesTask {
//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);
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
index 179cc08ed1..a7156152ce 100644
--- 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
@@ -30,17 +30,19 @@
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.godotengine.godot.utils.HttpRequester;
-import org.godotengine.godot.utils.RequestParams;
+
import org.json.JSONException;
import org.json.JSONObject;
abstract public class ValidateTask {
-
private Activity context;
private GodotPayment godotPayments;
private ProgressDialog dialog;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java
index c78e8c1c66..55b87b49e5 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java
@@ -28,7 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.utils;
+package org.godotengine.godot.plugin.payment.utils;
+
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
@@ -37,8 +38,10 @@ import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
+
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
+
import org.apache.http.conn.ssl.SSLSocketFactory;
/**
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/HttpRequester.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java
index 68f9a83597..0afcf60f38 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/HttpRequester.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java
@@ -28,11 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.utils;
+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;
@@ -40,6 +43,7 @@ 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;
@@ -67,7 +71,6 @@ 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;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/RequestParams.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/RequestParams.java
index 25fa10647d..6b66c7e474 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/RequestParams.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/RequestParams.java
@@ -28,12 +28,12 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.utils;
+package org.godotengine.godot.plugin.payment.utils;
import java.util.ArrayList;
-import java.util.Date;
import java.util.HashMap;
import java.util.List;
+
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
@@ -42,7 +42,6 @@ import org.apache.http.message.BasicNameValuePair;
* @author Luis Linietsky <luis.linietsky@gmail.com>
*/
public class RequestParams {
-
private HashMap<String, String> params;
private String url;
diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp
index 13550c4b11..39de3cb642 100644
--- a/platform/android/java_class_wrapper.cpp
+++ b/platform/android/java_class_wrapper.cpp
@@ -33,16 +33,14 @@
#include "thread_jandroid.h"
bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error, Variant &ret) {
-
Map<StringName, List<MethodInfo>>::Element *M = methods.find(p_method);
if (!M)
return false;
JNIEnv *env = ThreadAndroid::get_env();
- MethodInfo *method = NULL;
+ MethodInfo *method = nullptr;
for (List<MethodInfo>::Element *E = M->get().front(); E; E = E->next()) {
-
if (!p_instance && !E->get()._static) {
r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
continue;
@@ -50,13 +48,11 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
int pc = E->get().param_types.size();
if (pc > p_argcount) {
-
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = pc;
continue;
}
if (pc < p_argcount) {
-
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
r_error.argument = pc;
continue;
@@ -65,10 +61,8 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
bool valid = true;
for (int i = 0; i < pc; i++) {
-
Variant::Type arg_expected = Variant::NIL;
switch (ptypes[i]) {
-
case ARG_TYPE_VOID: {
//bug?
} break;
@@ -86,7 +80,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
case ARG_TYPE_SHORT:
case ARG_TYPE_INT:
case ARG_TYPE_LONG: {
-
if (!p_args[i]->is_num())
arg_expected = Variant::INT;
@@ -95,32 +88,26 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_DOUBLE:
case ARG_TYPE_FLOAT:
case ARG_TYPE_DOUBLE: {
-
if (!p_args[i]->is_num())
arg_expected = Variant::FLOAT;
} break;
case ARG_TYPE_STRING: {
-
if (p_args[i]->get_type() != Variant::STRING)
arg_expected = Variant::STRING;
} break;
case ARG_TYPE_CLASS: {
-
if (p_args[i]->get_type() != Variant::OBJECT)
arg_expected = Variant::OBJECT;
else {
-
Ref<Reference> ref = *p_args[i];
if (!ref.is_null()) {
if (Object::cast_to<JavaObject>(ref.ptr())) {
-
Ref<JavaObject> jo = ref;
//could be faster
jclass c = env->FindClass(E->get().param_sigs[i].operator String().utf8().get_data());
if (!c || !env->IsInstanceOf(jo->instance, c)) {
-
arg_expected = Variant::OBJECT;
} else {
//ok
@@ -133,7 +120,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
} break;
default: {
-
if (p_args[i]->get_type() != Variant::ARRAY)
arg_expected = Variant::ARRAY;
@@ -160,20 +146,18 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
r_error.error = Callable::CallError::CALL_OK;
- jvalue *argv = NULL;
+ jvalue *argv = nullptr;
if (method->param_types.size()) {
-
argv = (jvalue *)alloca(sizeof(jvalue) * method->param_types.size());
}
List<jobject> to_free;
for (int i = 0; i < method->param_types.size(); i++) {
-
switch (method->param_types[i]) {
case ARG_TYPE_VOID: {
//can't happen
- argv[i].l = NULL; //I hope this works
+ argv[i].l = nullptr; //I hope this works
} break;
case ARG_TYPE_BOOLEAN: {
@@ -279,18 +263,15 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
to_free.push_back(jStr);
} break;
case ARG_TYPE_CLASS: {
-
Ref<JavaObject> jo = *p_args[i];
if (jo.is_valid()) {
-
argv[i].l = jo->instance;
} else {
- argv[i].l = NULL; //I hope this works
+ argv[i].l = nullptr; //I hope this works
}
} break;
case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: {
-
Array arr = *p_args[i];
jbooleanArray a = env->NewBooleanArray(arr.size());
for (int j = 0; j < arr.size(); j++) {
@@ -302,7 +283,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
} break;
case ARG_ARRAY_BIT | ARG_TYPE_BYTE: {
-
Array arr = *p_args[i];
jbyteArray a = env->NewByteArray(arr.size());
for (int j = 0; j < arr.size(); j++) {
@@ -314,7 +294,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
} break;
case ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
-
Array arr = *p_args[i];
jcharArray a = env->NewCharArray(arr.size());
for (int j = 0; j < arr.size(); j++) {
@@ -326,7 +305,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
} break;
case ARG_ARRAY_BIT | ARG_TYPE_SHORT: {
-
Array arr = *p_args[i];
jshortArray a = env->NewShortArray(arr.size());
for (int j = 0; j < arr.size(); j++) {
@@ -338,7 +316,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
} break;
case ARG_ARRAY_BIT | ARG_TYPE_INT: {
-
Array arr = *p_args[i];
jintArray a = env->NewIntArray(arr.size());
for (int j = 0; j < arr.size(); j++) {
@@ -360,7 +337,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
} break;
case ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
-
Array arr = *p_args[i];
jfloatArray a = env->NewFloatArray(arr.size());
for (int j = 0; j < arr.size(); j++) {
@@ -372,7 +348,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
} break;
case ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: {
-
Array arr = *p_args[i];
jdoubleArray a = env->NewDoubleArray(arr.size());
for (int j = 0; j < arr.size(); j++) {
@@ -384,11 +359,9 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
} break;
case ARG_ARRAY_BIT | ARG_TYPE_STRING: {
-
Array arr = *p_args[i];
- jobjectArray a = env->NewObjectArray(arr.size(), env->FindClass("java/lang/String"), NULL);
+ jobjectArray a = env->NewObjectArray(arr.size(), env->FindClass("java/lang/String"), nullptr);
for (int j = 0; j < arr.size(); j++) {
-
String s = arr[j];
jstring jStr = env->NewStringUTF(s.utf8().get_data());
env->SetObjectArrayElement(a, j, jStr);
@@ -399,8 +372,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
to_free.push_back(a);
} break;
case ARG_ARRAY_BIT | ARG_TYPE_CLASS: {
-
- argv[i].l = NULL;
+ argv[i].l = nullptr;
} break;
}
}
@@ -409,7 +381,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
bool success = true;
switch (method->return_type) {
-
case ARG_TYPE_VOID: {
if (method->_static) {
env->CallStaticVoidMethodA(_class, method->method, argv);
@@ -434,7 +405,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
}
} break;
case ARG_TYPE_CHAR: {
-
if (method->_static) {
ret = env->CallStaticCharMethodA(_class, method->method, argv);
} else {
@@ -442,7 +412,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
}
} break;
case ARG_TYPE_SHORT: {
-
if (method->_static) {
ret = env->CallStaticShortMethodA(_class, method->method, argv);
} else {
@@ -451,7 +420,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
} break;
case ARG_TYPE_INT: {
-
if (method->_static) {
ret = env->CallStaticIntMethodA(_class, method->method, argv);
} else {
@@ -460,7 +428,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
} break;
case ARG_TYPE_LONG: {
-
if (method->_static) {
ret = (int64_t)env->CallStaticLongMethodA(_class, method->method, argv);
} else {
@@ -469,7 +436,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
} break;
case ARG_TYPE_FLOAT: {
-
if (method->_static) {
ret = env->CallStaticFloatMethodA(_class, method->method, argv);
} else {
@@ -478,7 +444,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
} break;
case ARG_TYPE_DOUBLE: {
-
if (method->_static) {
ret = env->CallStaticDoubleMethodA(_class, method->method, argv);
} else {
@@ -487,7 +452,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
} break;
default: {
-
jobject obj;
if (method->_static) {
obj = env->CallStaticObjectMethodA(_class, method->method, argv);
@@ -498,7 +462,6 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
if (!obj) {
ret = Variant();
} else {
-
if (!_convert_object_to_variant(env, obj, ret, method->return_type)) {
ret = Variant();
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
@@ -518,9 +481,8 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
}
Variant JavaClass::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
-
Variant ret;
- bool found = _call_method(NULL, p_method, p_args, p_argcount, r_error, ret);
+ bool found = _call_method(nullptr, p_method, p_args, p_argcount, r_error, ret);
if (found) {
return ret;
}
@@ -534,7 +496,6 @@ JavaClass::JavaClass() {
/////////////////////
Variant JavaObject::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
-
return Variant();
}
@@ -547,14 +508,12 @@ JavaObject::~JavaObject() {
////////////////////
bool JavaClassWrapper::_get_type_sig(JNIEnv *env, jobject obj, uint32_t &sig, String &strsig) {
-
jstring name2 = (jstring)env->CallObjectMethod(obj, Class_getName);
String str_type = jstring_to_string(name2, env);
env->DeleteLocalRef(name2);
uint32_t t = 0;
if (str_type.begins_with("[")) {
-
t = JavaClass::ARG_ARRAY_BIT;
strsig = "[";
str_type = str_type.substr(1, str_type.length() - 1);
@@ -633,87 +592,71 @@ bool JavaClassWrapper::_get_type_sig(JNIEnv *env, jobject obj, uint32_t &sig, St
}
bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &var, uint32_t p_sig) {
-
if (!obj) {
var = Variant(); //seems null is just null...
return true;
}
switch (p_sig) {
-
case ARG_TYPE_VOID: {
-
return Variant();
} break;
case ARG_TYPE_BOOLEAN | ARG_NUMBER_CLASS_BIT: {
-
var = env->CallBooleanMethod(obj, JavaClassWrapper::singleton->Boolean_booleanValue);
return true;
} break;
case ARG_TYPE_BYTE | ARG_NUMBER_CLASS_BIT: {
-
var = env->CallByteMethod(obj, JavaClassWrapper::singleton->Byte_byteValue);
return true;
} break;
case ARG_TYPE_CHAR | ARG_NUMBER_CLASS_BIT: {
-
var = env->CallCharMethod(obj, JavaClassWrapper::singleton->Character_characterValue);
return true;
} break;
case ARG_TYPE_SHORT | ARG_NUMBER_CLASS_BIT: {
-
var = env->CallShortMethod(obj, JavaClassWrapper::singleton->Short_shortValue);
return true;
} break;
case ARG_TYPE_INT | ARG_NUMBER_CLASS_BIT: {
-
var = env->CallIntMethod(obj, JavaClassWrapper::singleton->Integer_integerValue);
return true;
} break;
case ARG_TYPE_LONG | ARG_NUMBER_CLASS_BIT: {
-
var = (int64_t)env->CallLongMethod(obj, JavaClassWrapper::singleton->Long_longValue);
return true;
} break;
case ARG_TYPE_FLOAT | ARG_NUMBER_CLASS_BIT: {
-
var = env->CallFloatMethod(obj, JavaClassWrapper::singleton->Float_floatValue);
return true;
} break;
case ARG_TYPE_DOUBLE | ARG_NUMBER_CLASS_BIT: {
-
var = env->CallDoubleMethod(obj, JavaClassWrapper::singleton->Double_doubleValue);
return true;
} break;
case ARG_TYPE_STRING: {
-
var = jstring_to_string((jstring)obj, env);
return true;
} break;
case ARG_TYPE_CLASS: {
-
return false;
} break;
case ARG_ARRAY_BIT | ARG_TYPE_VOID: {
-
var = Array(); // ?
return true;
} break;
case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: {
-
Array ret;
jobjectArray arr = (jobjectArray)obj;
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jboolean val;
env->GetBooleanArrayRegion((jbooleanArray)arr, 0, 1, &val);
ret.push_back(val);
@@ -724,14 +667,12 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
} break;
case ARG_ARRAY_BIT | ARG_TYPE_BYTE: {
-
Array ret;
jobjectArray arr = (jobjectArray)obj;
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jbyte val;
env->GetByteArrayRegion((jbyteArray)arr, 0, 1, &val);
ret.push_back(val);
@@ -747,7 +688,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jchar val;
env->GetCharArrayRegion((jcharArray)arr, 0, 1, &val);
ret.push_back(val);
@@ -763,7 +703,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jshort val;
env->GetShortArrayRegion((jshortArray)arr, 0, 1, &val);
ret.push_back(val);
@@ -779,7 +718,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jint val;
env->GetIntArrayRegion((jintArray)arr, 0, 1, &val);
ret.push_back(val);
@@ -795,7 +733,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jlong val;
env->GetLongArrayRegion((jlongArray)arr, 0, 1, &val);
ret.push_back((int64_t)val);
@@ -811,7 +748,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jfloat val;
env->GetFloatArrayRegion((jfloatArray)arr, 0, 1, &val);
ret.push_back(val);
@@ -827,7 +763,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jdouble val;
env->GetDoubleArrayRegion((jdoubleArray)arr, 0, 1, &val);
ret.push_back(val);
@@ -837,14 +772,12 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
return true;
} break;
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: {
-
Array ret;
jobjectArray arr = (jobjectArray)obj;
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jobject o = env->GetObjectArrayElement(arr, i);
if (!o)
ret.push_back(Variant());
@@ -860,14 +793,12 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
} break;
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_BYTE: {
-
Array ret;
jobjectArray arr = (jobjectArray)obj;
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jobject o = env->GetObjectArrayElement(arr, i);
if (!o)
ret.push_back(Variant());
@@ -882,14 +813,12 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
return true;
} break;
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
-
Array ret;
jobjectArray arr = (jobjectArray)obj;
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jobject o = env->GetObjectArrayElement(arr, i);
if (!o)
ret.push_back(Variant());
@@ -904,14 +833,12 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
return true;
} break;
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_SHORT: {
-
Array ret;
jobjectArray arr = (jobjectArray)obj;
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jobject o = env->GetObjectArrayElement(arr, i);
if (!o)
ret.push_back(Variant());
@@ -926,14 +853,12 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
return true;
} break;
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_INT: {
-
Array ret;
jobjectArray arr = (jobjectArray)obj;
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jobject o = env->GetObjectArrayElement(arr, i);
if (!o)
ret.push_back(Variant());
@@ -948,14 +873,12 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
return true;
} break;
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_LONG: {
-
Array ret;
jobjectArray arr = (jobjectArray)obj;
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jobject o = env->GetObjectArrayElement(arr, i);
if (!o)
ret.push_back(Variant());
@@ -970,14 +893,12 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
return true;
} break;
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
-
Array ret;
jobjectArray arr = (jobjectArray)obj;
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jobject o = env->GetObjectArrayElement(arr, i);
if (!o)
ret.push_back(Variant());
@@ -998,7 +919,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jobject o = env->GetObjectArrayElement(arr, i);
if (!o)
ret.push_back(Variant());
@@ -1014,14 +934,12 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
} break;
case ARG_ARRAY_BIT | ARG_TYPE_STRING: {
-
Array ret;
jobjectArray arr = (jobjectArray)obj;
int count = env->GetArrayLength(arr);
for (int i = 0; i < count; i++) {
-
jobject o = env->GetObjectArrayElement(arr, i);
if (!o)
ret.push_back(Variant());
@@ -1036,7 +954,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
return true;
} break;
case ARG_ARRAY_BIT | ARG_TYPE_CLASS: {
-
} break;
}
@@ -1044,7 +961,6 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
}
Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
-
if (class_cache.has(p_class))
return class_cache[p_class];
@@ -1066,7 +982,6 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
int count = env->GetArrayLength(methods);
for (int i = 0; i < count; i++) {
-
jobject obj = env->GetObjectArrayElement(methods, i);
ERR_CONTINUE(!obj);
@@ -1096,7 +1011,6 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
String signature = "(";
for (int j = 0; j < count2; j++) {
-
jobject obj2 = env->GetObjectArrayElement(param_types, j);
String strsig;
uint32_t sig = 0;
@@ -1138,7 +1052,6 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
bool discard = false;
for (List<JavaClass::MethodInfo>::Element *E = java_class->methods[str_method].front(); E; E = E->next()) {
-
float new_likeliness = 0;
float existing_likeliness = 0;
@@ -1146,7 +1059,6 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
continue;
bool valid = true;
for (int j = 0; j < E->get().param_types.size(); j++) {
-
Variant::Type _new;
float new_l;
Variant::Type existing;
@@ -1195,7 +1107,6 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
count = env->GetArrayLength(fields);
for (int i = 0; i < count; i++) {
-
jobject obj = env->GetObjectArrayElement(fields, i);
ERR_CONTINUE(!obj);
@@ -1205,19 +1116,15 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
int mods = env->CallIntMethod(obj, Field_getModifiers);
if ((mods & 0x8) && (mods & 0x10) && (mods & 0x1)) { //static final public!
- jobject objc = env->CallObjectMethod(obj, Field_get, NULL);
+ jobject objc = env->CallObjectMethod(obj, Field_get, nullptr);
if (objc) {
-
uint32_t sig;
String strsig;
jclass cl = env->GetObjectClass(objc);
if (JavaClassWrapper::_get_type_sig(env, cl, sig, strsig)) {
-
if ((sig & JavaClass::ARG_TYPE_MASK) <= JavaClass::ARG_TYPE_STRING) {
-
Variant value;
if (JavaClass::_convert_object_to_variant(env, objc, value, sig)) {
-
java_class->constant_map[str_field] = value;
}
}
@@ -1236,10 +1143,9 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
return Ref<JavaClass>();
}
-JavaClassWrapper *JavaClassWrapper::singleton = NULL;
+JavaClassWrapper *JavaClassWrapper::singleton = nullptr;
JavaClassWrapper::JavaClassWrapper(jobject p_activity) {
-
singleton = this;
JNIEnv *env = ThreadAndroid::get_env();
diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp
index 8d075f8e97..0a42adeaf2 100644
--- a/platform/android/java_godot_io_wrapper.cpp
+++ b/platform/android/java_godot_io_wrapper.cpp
@@ -53,14 +53,11 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc
_get_model = p_env->GetMethodID(cls, "getModel", "()Ljava/lang/String;");
_get_screen_DPI = p_env->GetMethodID(cls, "getScreenDPI", "()I");
_get_unique_id = p_env->GetMethodID(cls, "getUniqueID", "()Ljava/lang/String;");
- _show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;I)V");
+ _show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;III)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");
_get_system_dir = p_env->GetMethodID(cls, "getSystemDir", "(I)Ljava/lang/String;");
- _play_video = p_env->GetMethodID(cls, "playVideo", "(Ljava/lang/String;)V");
- _is_video_playing = p_env->GetMethodID(cls, "isVideoPlaying", "()Z");
- _pause_video = p_env->GetMethodID(cls, "pauseVideo", "()V");
- _stop_video = p_env->GetMethodID(cls, "stopVideo", "()V");
}
}
@@ -135,11 +132,11 @@ bool GodotIOJavaWrapper::has_vk() {
return (_show_keyboard != 0) && (_hide_keyboard != 0);
}
-void GodotIOJavaWrapper::show_vk(const String &p_existing, int p_max_input_length) {
+void GodotIOJavaWrapper::show_vk(const String &p_existing, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
if (_show_keyboard) {
JNIEnv *env = ThreadAndroid::get_env();
jstring jStr = env->NewStringUTF(p_existing.utf8().get_data());
- env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_max_input_length);
+ env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_max_input_length, p_cursor_start, p_cursor_end);
}
}
@@ -157,40 +154,22 @@ void GodotIOJavaWrapper::set_screen_orientation(int p_orient) {
}
}
-String GodotIOJavaWrapper::get_system_dir(int p_dir) {
- if (_get_system_dir) {
+int GodotIOJavaWrapper::get_screen_orientation() {
+ if (_get_screen_orientation) {
JNIEnv *env = ThreadAndroid::get_env();
- jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir);
- return jstring_to_string(s, env);
+ return env->CallIntMethod(godot_io_instance, _get_screen_orientation);
} else {
- return String(".");
+ return 0;
}
}
-void GodotIOJavaWrapper::play_video(const String &p_path) {
- // Why is this not here?!?!
-}
-
-bool GodotIOJavaWrapper::is_video_playing() {
- if (_is_video_playing) {
+String GodotIOJavaWrapper::get_system_dir(int p_dir) {
+ if (_get_system_dir) {
JNIEnv *env = ThreadAndroid::get_env();
- return env->CallBooleanMethod(godot_io_instance, _is_video_playing);
+ jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir);
+ return jstring_to_string(s, env);
} else {
- return false;
- }
-}
-
-void GodotIOJavaWrapper::pause_video() {
- if (_pause_video) {
- JNIEnv *env = ThreadAndroid::get_env();
- env->CallVoidMethod(godot_io_instance, _pause_video);
- }
-}
-
-void GodotIOJavaWrapper::stop_video() {
- if (_stop_video) {
- JNIEnv *env = ThreadAndroid::get_env();
- env->CallVoidMethod(godot_io_instance, _stop_video);
+ return String(".");
}
}
diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h
index 7dfed52187..1742021379 100644
--- a/platform/android/java_godot_io_wrapper.h
+++ b/platform/android/java_godot_io_wrapper.h
@@ -54,11 +54,8 @@ private:
jmethodID _show_keyboard = 0;
jmethodID _hide_keyboard = 0;
jmethodID _set_screen_orientation = 0;
+ jmethodID _get_screen_orientation = 0;
jmethodID _get_system_dir = 0;
- jmethodID _play_video = 0;
- jmethodID _is_video_playing = 0;
- jmethodID _pause_video = 0;
- jmethodID _stop_video = 0;
public:
GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instance);
@@ -73,16 +70,13 @@ public:
int get_screen_dpi();
String get_unique_id();
bool has_vk();
- void show_vk(const String &p_existing, int p_max_input_length);
+ void show_vk(const String &p_existing, 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);
void set_screen_orientation(int p_orient);
+ int get_screen_orientation();
String get_system_dir(int p_dir);
- void play_video(const String &p_path);
- bool is_video_playing();
- void pause_video();
- void stop_video();
};
#endif /* !JAVA_GODOT_IO_WRAPPER_H */
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 8bbf41d82d..1f61c4a805 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -29,31 +29,36 @@
/*************************************************************************/
#include "java_godot_lib_jni.h"
+
#include "java_godot_io_wrapper.h"
#include "java_godot_wrapper.h"
#include "android/asset_manager_jni.h"
-#include "android_keys_utils.h"
#include "api/java_class_wrapper.h"
+#include "api/jni_singleton.h"
#include "audio_driver_jandroid.h"
#include "core/engine.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/input_default.h"
#include "main/main.h"
#include "net_socket_android.h"
#include "os_android.h"
#include "string_android.h"
#include "thread_jandroid.h"
+
#include <unistd.h>
-static JavaClassWrapper *java_class_wrapper = NULL;
-static OS_Android *os_android = NULL;
-static GodotJavaWrapper *godot_java = NULL;
-static GodotIOJavaWrapper *godot_io_java = NULL;
+#include <android/native_window_jni.h>
+
+static JavaClassWrapper *java_class_wrapper = nullptr;
+static OS_Android *os_android = nullptr;
+static GodotJavaWrapper *godot_java = nullptr;
+static GodotIOJavaWrapper *godot_io_java = nullptr;
static bool initialized = false;
static int step = 0;
@@ -73,7 +78,6 @@ 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) {
-
initialized = true;
JavaVM *jvm;
@@ -121,18 +125,17 @@ 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();
- const char **cmdline = NULL;
- jstring *j_cmdline = NULL;
+ const char **cmdline = nullptr;
+ jstring *j_cmdline = nullptr;
int cmdlen = 0;
if (p_cmdline) {
cmdlen = env->GetArrayLength(p_cmdline);
if (cmdlen) {
cmdline = (const char **)malloc((cmdlen + 1) * sizeof(const char *));
- cmdline[cmdlen] = NULL;
+ cmdline[cmdlen] = nullptr;
j_cmdline = (jstring *)malloc(cmdlen * sizeof(jstring));
for (int i = 0; i < cmdlen; i++) {
-
jstring string = (jstring)env->GetObjectArrayElement(p_cmdline, i);
const char *rawString = env->GetStringUTFChars(string, 0);
@@ -158,22 +161,36 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc
}
java_class_wrapper = memnew(JavaClassWrapper(godot_java->get_activity()));
+ ClassDB::register_class<JNISingleton>();
}
-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) {
+ if (os_android) {
+ os_android->set_display_size(Size2i(p_width, p_height));
- if (os_android)
- os_android->set_display_size(Size2(width, 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);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jboolean p_32_bits) {
+ 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) {
if (os_android) {
if (step == 0) {
// During startup
os_android->set_context_is_16_bits(!p_32_bits);
+ if (p_surface) {
+ ANativeWindow *native_window = ANativeWindow_fromSurface(env, p_surface);
+ os_android->set_native_window(native_window);
+ }
} else {
- // GL context recreated because it was lost; restart app to let it reload everything
+ // Rendering context recreated because it was lost; restart app to let it reload everything
os_android->main_loop_end();
godot_java->restart(env);
step = -1; // Ensure no further steps are attempted
@@ -193,7 +210,6 @@ 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,
// but for Godot purposes, the main thread is the one running the game loop
Main::setup2(Thread::get_caller_id());
@@ -211,34 +227,31 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl
++step;
}
- os_android->process_accelerometer(accelerometer);
- os_android->process_gravity(gravity);
- os_android->process_magnetometer(magnetometer);
- os_android->process_gyroscope(gyroscope);
+ DisplayServerAndroid::get_singleton()->process_accelerometer(accelerometer);
+ DisplayServerAndroid::get_singleton()->process_gravity(gravity);
+ DisplayServerAndroid::get_singleton()->process_magnetometer(magnetometer);
+ DisplayServerAndroid::get_singleton()->process_gyroscope(gyroscope);
if (os_android->main_loop_iterate()) {
-
godot_java->force_quit(env);
}
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint count, jintArray positions) {
-
if (step == 0)
return;
- Vector<OS_Android::TouchPos> points;
+ Vector<DisplayServerAndroid::TouchPos> points;
for (int i = 0; i < count; i++) {
-
jint p[3];
env->GetIntArrayRegion(positions, i * 3, 3, p);
- OS_Android::TouchPos tp;
+ DisplayServerAndroid::TouchPos tp;
tp.pos = Point2(p[1], p[2]);
tp.id = p[0];
points.push_back(tp);
}
- os_android->process_touch(ev, pointer, points);
+ DisplayServerAndroid::get_singleton()->process_touch(ev, pointer, points);
/*
if (os_android)
@@ -250,78 +263,78 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jc
if (step == 0)
return;
- os_android->process_hover(p_type, Point2(p_x, p_y));
+ 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) {
if (step == 0)
return;
- os_android->process_double_tap(Point2(p_x, p_y));
+ DisplayServerAndroid::get_singleton()->process_double_tap(Point2(p_x, p_y));
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jclass clazz, jint p_x, jint p_y) {
if (step == 0)
return;
- os_android->process_scroll(Point2(p_x, p_y));
+ DisplayServerAndroid::get_singleton()->process_scroll(Point2(p_x, p_y));
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed) {
if (step == 0)
return;
- OS_Android::JoypadEvent jevent;
+ DisplayServerAndroid::JoypadEvent jevent;
jevent.device = p_device;
- jevent.type = OS_Android::JOY_EVENT_BUTTON;
+ jevent.type = DisplayServerAndroid::JOY_EVENT_BUTTON;
jevent.index = p_button;
jevent.pressed = p_pressed;
- os_android->process_joy_event(jevent);
+ DisplayServerAndroid::get_singleton()->process_joy_event(jevent);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value) {
if (step == 0)
return;
- OS_Android::JoypadEvent jevent;
+ DisplayServerAndroid::JoypadEvent jevent;
jevent.device = p_device;
- jevent.type = OS_Android::JOY_EVENT_AXIS;
+ jevent.type = DisplayServerAndroid::JOY_EVENT_AXIS;
jevent.index = p_axis;
jevent.value = p_value;
- os_android->process_joy_event(jevent);
+ DisplayServerAndroid::get_singleton()->process_joy_event(jevent);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y) {
if (step == 0)
return;
- OS_Android::JoypadEvent jevent;
+ DisplayServerAndroid::JoypadEvent jevent;
jevent.device = p_device;
- jevent.type = OS_Android::JOY_EVENT_HAT;
+ jevent.type = DisplayServerAndroid::JOY_EVENT_HAT;
int hat = 0;
if (p_hat_x != 0) {
if (p_hat_x < 0)
- hat |= InputDefault::HAT_MASK_LEFT;
+ hat |= Input::HAT_MASK_LEFT;
else
- hat |= InputDefault::HAT_MASK_RIGHT;
+ hat |= Input::HAT_MASK_RIGHT;
}
if (p_hat_y != 0) {
if (p_hat_y < 0)
- hat |= InputDefault::HAT_MASK_UP;
+ hat |= Input::HAT_MASK_UP;
else
- hat |= InputDefault::HAT_MASK_DOWN;
+ hat |= Input::HAT_MASK_DOWN;
}
jevent.hat = hat;
- os_android->process_joy_event(jevent);
+ DisplayServerAndroid::get_singleton()->process_joy_event(jevent);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jclass clazz, jint p_device, jboolean p_connected, jstring p_name) {
if (os_android) {
String name = jstring_to_string(p_name, env);
- os_android->joy_connection_changed(p_device, p_connected, name);
+ Input::get_singleton()->joy_connection_changed(p_device, p_connected, name);
}
}
@@ -329,29 +342,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jcla
if (step == 0)
return;
- Ref<InputEventKey> ievent;
- ievent.instance();
- int val = p_unicode_char;
- int keycode = android_get_keysym(p_keycode);
- int phy_keycode = android_get_keysym(p_scancode);
- ievent->set_keycode(keycode);
- ievent->set_physical_keycode(phy_keycode);
- ievent->set_unicode(val);
- ievent->set_pressed(p_pressed);
-
- if (val == '\n') {
- ievent->set_keycode(KEY_ENTER);
- } else if (val == 61448) {
- ievent->set_keycode(KEY_BACKSPACE);
- ievent->set_unicode(KEY_BACKSPACE);
- } else if (val == 61453) {
- ievent->set_keycode(KEY_ENTER);
- ievent->set_unicode(KEY_ENTER);
- } else if (p_keycode == 4) {
- os_android->main_loop_request_go_back();
- }
-
- os_android->process_event(ievent);
+ DisplayServerAndroid::get_singleton()->process_key_event(p_keycode, p_scancode, p_unicode_char, p_pressed);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z) {
@@ -371,7 +362,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jclass clazz) {
-
if (step == 0)
return;
@@ -379,7 +369,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env,
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jclass clazz) {
-
if (step == 0)
return;
@@ -387,21 +376,18 @@ 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();
AudioDriverAndroid::thread_func(env);
}
JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jclass clazz, jstring path) {
-
String js = jstring_to_string(path, env);
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);
@@ -413,7 +399,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *en
Variant *vlist = (Variant *)alloca(sizeof(Variant) * count);
Variant **vptr = (Variant **)alloca(sizeof(Variant *) * count);
for (int i = 0; i < count; i++) {
-
jobject obj = env->GetObjectArrayElement(params, i);
Variant v;
if (obj)
@@ -428,12 +413,11 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *en
obj->call(str_method, (const Variant **)vptr, count, err);
// something
- env->PopLocalFrame(NULL);
+ 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);
@@ -445,7 +429,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *
Variant args[VARIANT_ARG_MAX];
for (int i = 0; i < MIN(count, VARIANT_ARG_MAX); i++) {
-
jobject obj = env->GetObjectArrayElement(params, i);
if (obj)
args[i] = _jobject_to_variant(env, obj);
@@ -454,7 +437,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *
obj->call_deferred(str_method, args[0], args[1], args[2], args[3], args[4]);
// something
- env->PopLocalFrame(NULL);
+ env->PopLocalFrame(nullptr);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result) {
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index a7a5970440..e8be7be0d0 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -40,8 +40,8 @@ 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_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_newcontext(JNIEnv *env, jclass clazz, jboolean p_32_bits);
+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);
@@ -61,8 +61,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/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 2a540bb4a9..8ef99dfab0 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -80,13 +80,13 @@ jobject GodotJavaWrapper::get_activity() {
jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env) {
if (cls) {
- if (p_env == NULL)
+ if (p_env == nullptr)
p_env = ThreadAndroid::get_env();
jfieldID fid = p_env->GetStaticFieldID(cls, p_name, p_class);
return p_env->GetStaticObjectField(cls, fid);
} else {
- return NULL;
+ return nullptr;
}
}
@@ -96,13 +96,13 @@ jobject GodotJavaWrapper::get_class_loader() {
jmethodID getClassLoader = env->GetMethodID(cls, "getClassLoader", "()Ljava/lang/ClassLoader;");
return env->CallObjectMethod(godot_instance, getClassLoader);
} else {
- return NULL;
+ return nullptr;
}
}
void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
if (_on_video_init)
- if (p_env == NULL)
+ if (p_env == nullptr)
p_env = ThreadAndroid::get_env();
p_env->CallVoidMethod(godot_instance, _on_video_init);
@@ -110,7 +110,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 == NULL) {
+ if (p_env == nullptr) {
p_env = ThreadAndroid::get_env();
}
}
@@ -119,7 +119,7 @@ void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) {
void GodotJavaWrapper::restart(JNIEnv *p_env) {
if (_restart)
- if (p_env == NULL)
+ if (p_env == nullptr)
p_env = ThreadAndroid::get_env();
p_env->CallVoidMethod(godot_instance, _restart);
@@ -127,7 +127,7 @@ void GodotJavaWrapper::restart(JNIEnv *p_env) {
void GodotJavaWrapper::force_quit(JNIEnv *p_env) {
if (_finish)
- if (p_env == NULL)
+ if (p_env == nullptr)
p_env = ThreadAndroid::get_env();
p_env->CallVoidMethod(godot_instance, _finish);
@@ -244,7 +244,7 @@ jobject GodotJavaWrapper::get_surface() {
JNIEnv *env = ThreadAndroid::get_env();
return env->CallObjectMethod(godot_instance, _get_surface);
} else {
- return NULL;
+ return nullptr;
}
}
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index fb77c8ba6a..89d6b6db46 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -68,14 +68,14 @@ public:
~GodotJavaWrapper();
jobject get_activity();
- jobject get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env = NULL);
+ jobject get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env = nullptr);
jobject get_class_loader();
- void on_video_init(JNIEnv *p_env = NULL);
- void on_godot_main_loop_started(JNIEnv *p_env = NULL);
- void restart(JNIEnv *p_env = NULL);
- void force_quit(JNIEnv *p_env = NULL);
+ void on_video_init(JNIEnv *p_env = nullptr);
+ void on_godot_main_loop_started(JNIEnv *p_env = nullptr);
+ void restart(JNIEnv *p_env = nullptr);
+ void force_quit(JNIEnv *p_env = nullptr);
void set_keep_screen_on(bool p_enabled);
void alert(const String &p_message, const String &p_title);
int get_gles_version_code();
diff --git a/platform/android/jni_utils.cpp b/platform/android/jni_utils.cpp
index 3fa4e80884..8e1ae53b78 100644
--- a/platform/android/jni_utils.cpp
+++ b/platform/android/jni_utils.cpp
@@ -31,13 +31,10 @@
#include "jni_utils.h"
jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject) {
-
jvalret v;
switch (p_type) {
-
case Variant::BOOL: {
-
if (force_jobject) {
jclass bclass = env->FindClass("java/lang/Boolean");
jmethodID ctor = env->GetMethodID(bclass, "<init>", "(Z)V");
@@ -52,9 +49,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
};
} break;
case Variant::INT: {
-
if (force_jobject) {
-
jclass bclass = env->FindClass("java/lang/Integer");
jmethodID ctor = env->GetMethodID(bclass, "<init>", "(I)V");
jvalue val;
@@ -69,9 +64,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
};
} break;
case Variant::FLOAT: {
-
if (force_jobject) {
-
jclass bclass = env->FindClass("java/lang/Double");
jmethodID ctor = env->GetMethodID(bclass, "<init>", "(D)V");
jvalue val;
@@ -86,19 +79,16 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
};
} break;
case Variant::STRING: {
-
String s = *p_arg;
jstring jStr = env->NewStringUTF(s.utf8().get_data());
v.val.l = jStr;
v.obj = jStr;
} break;
case Variant::PACKED_STRING_ARRAY: {
-
Vector<String> sarray = *p_arg;
jobjectArray arr = env->NewObjectArray(sarray.size(), env->FindClass("java/lang/String"), env->NewStringUTF(""));
for (int j = 0; j < sarray.size(); j++) {
-
jstring str = env->NewStringUTF(sarray[j].utf8().get_data());
env->SetObjectArrayElement(arr, j, str);
env->DeleteLocalRef(str);
@@ -109,7 +99,6 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
} break;
case Variant::DICTIONARY: {
-
Dictionary dict = *p_arg;
jclass dclass = env->FindClass("org/godotengine/godot/Dictionary");
jmethodID ctor = env->GetMethodID(dclass, "<init>", "()V");
@@ -130,7 +119,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
env->CallVoidMethodA(jdict, set_keys, &val);
env->DeleteLocalRef(jkeys);
- jobjectArray jvalues = env->NewObjectArray(keys.size(), env->FindClass("java/lang/Object"), NULL);
+ jobjectArray jvalues = env->NewObjectArray(keys.size(), env->FindClass("java/lang/Object"), nullptr);
for (int j = 0; j < keys.size(); j++) {
Variant var = dict[keys[j]];
@@ -152,7 +141,6 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
} break;
case Variant::PACKED_INT32_ARRAY: {
-
Vector<int> array = *p_arg;
jintArray arr = env->NewIntArray(array.size());
const int *r = array.ptr();
@@ -171,7 +159,6 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
} break;
case Variant::PACKED_FLOAT32_ARRAY: {
-
Vector<float> array = *p_arg;
jfloatArray arr = env->NewFloatArray(array.size());
const float *r = array.ptr();
@@ -185,7 +172,6 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
#endif
default: {
-
v.val.i = 0;
} break;
}
@@ -193,7 +179,6 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a
}
String _get_class_name(JNIEnv *env, jclass cls, bool *array) {
-
jclass cclass = env->FindClass("java/lang/Class");
jmethodID getName = env->GetMethodID(cclass, "getName", "()Ljava/lang/String;");
jstring clsName = (jstring)env->CallObjectMethod(cls, getName);
@@ -210,8 +195,7 @@ String _get_class_name(JNIEnv *env, jclass cls, bool *array) {
}
Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
-
- if (obj == NULL) {
+ if (obj == nullptr) {
return Variant();
}
@@ -220,12 +204,10 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
String name = _get_class_name(env, c, &array);
if (name == "java.lang.String") {
-
return jstring_to_string((jstring)obj, env);
};
if (name == "[Ljava.lang.String;") {
-
jobjectArray arr = (jobjectArray)obj;
int stringCount = env->GetArrayLength(arr);
Vector<String> sarr;
@@ -240,14 +222,12 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
};
if (name == "java.lang.Boolean") {
-
jmethodID boolValue = env->GetMethodID(c, "booleanValue", "()Z");
bool ret = env->CallBooleanMethod(obj, boolValue);
return ret;
};
if (name == "java.lang.Integer" || name == "java.lang.Long") {
-
jclass nclass = env->FindClass("java/lang/Number");
jmethodID longValue = env->GetMethodID(nclass, "longValue", "()J");
jlong ret = env->CallLongMethod(obj, longValue);
@@ -255,7 +235,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
};
if (name == "[I") {
-
jintArray arr = (jintArray)obj;
int fCount = env->GetArrayLength(arr);
Vector<int> sarr;
@@ -267,7 +246,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
};
if (name == "[B") {
-
jbyteArray arr = (jbyteArray)obj;
int fCount = env->GetArrayLength(arr);
Vector<uint8_t> sarr;
@@ -279,7 +257,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
};
if (name == "java.lang.Float" || name == "java.lang.Double") {
-
jclass nclass = env->FindClass("java/lang/Number");
jmethodID doubleValue = env->GetMethodID(nclass, "doubleValue", "()D");
double ret = env->CallDoubleMethod(obj, doubleValue);
@@ -287,7 +264,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
};
if (name == "[D") {
-
jdoubleArray arr = (jdoubleArray)obj;
int fCount = env->GetArrayLength(arr);
PackedFloat32Array sarr;
@@ -296,7 +272,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
real_t *w = sarr.ptrw();
for (int i = 0; i < fCount; i++) {
-
double n;
env->GetDoubleArrayRegion(arr, i, 1, &n);
w[i] = n;
@@ -305,7 +280,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
};
if (name == "[F") {
-
jfloatArray arr = (jfloatArray)obj;
int fCount = env->GetArrayLength(arr);
PackedFloat32Array sarr;
@@ -314,7 +288,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
real_t *w = sarr.ptrw();
for (int i = 0; i < fCount; i++) {
-
float n;
env->GetFloatArrayRegion(arr, i, 1, &n);
w[i] = n;
@@ -323,7 +296,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
};
if (name == "[Ljava.lang.Object;") {
-
jobjectArray arr = (jobjectArray)obj;
int objCount = env->GetArrayLength(arr);
Array varr;
@@ -339,7 +311,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
};
if (name == "java.util.HashMap" || name == "org.godotengine.godot.Dictionary") {
-
Dictionary ret;
jclass oclass = c;
jmethodID get_keys = env->GetMethodID(oclass, "get_keys", "()[Ljava/lang/String;");
@@ -355,7 +326,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
env->DeleteLocalRef(arr);
for (int i = 0; i < keys.size(); i++) {
-
ret[keys[i]] = vals[i];
};
@@ -368,7 +338,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
}
Variant::Type get_jni_type(const String &p_type) {
-
static struct {
const char *name;
Variant::Type type;
@@ -384,13 +353,12 @@ Variant::Type get_jni_type(const String &p_type) {
{ "[F", Variant::PACKED_FLOAT32_ARRAY },
{ "[Ljava.lang.String;", Variant::PACKED_STRING_ARRAY },
{ "org.godotengine.godot.Dictionary", Variant::DICTIONARY },
- { NULL, Variant::NIL }
+ { nullptr, Variant::NIL }
};
int idx = 0;
while (_type_to_vtype[idx].name) {
-
if (p_type == _type_to_vtype[idx].name)
return _type_to_vtype[idx].type;
@@ -401,7 +369,6 @@ Variant::Type get_jni_type(const String &p_type) {
}
const char *get_jni_sig(const String &p_type) {
-
static struct {
const char *name;
const char *sig;
@@ -417,13 +384,12 @@ const char *get_jni_sig(const String &p_type) {
{ "[B", "[B" },
{ "[F", "[F" },
{ "[Ljava.lang.String;", "[Ljava/lang/String;" },
- { NULL, "V" }
+ { nullptr, "V" }
};
int idx = 0;
while (_type_to_vtype[idx].name) {
-
if (p_type == _type_to_vtype[idx].name)
return _type_to_vtype[idx].sig;
diff --git a/platform/android/jni_utils.h b/platform/android/jni_utils.h
index 925340a680..5320715853 100644
--- a/platform/android/jni_utils.h
+++ b/platform/android/jni_utils.h
@@ -37,10 +37,9 @@
#include <jni.h>
struct jvalret {
-
jobject obj;
jvalue val;
- jvalret() { obj = NULL; }
+ jvalret() { obj = nullptr; }
};
jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_arg, bool force_jobject = false);
@@ -53,190 +52,4 @@ Variant::Type get_jni_type(const String &p_type);
const char *get_jni_sig(const String &p_type);
-class JNISingleton : public Object {
-
- GDCLASS(JNISingleton, Object);
-
- struct MethodData {
-
- jmethodID method;
- Variant::Type ret_type;
- Vector<Variant::Type> argtypes;
- };
-
- jobject instance;
- Map<StringName, MethodData> method_map;
-
-public:
- virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
-
- ERR_FAIL_COND_V(!instance, Variant());
-
- r_error.error = Callable::CallError::CALL_OK;
-
- Map<StringName, MethodData>::Element *E = method_map.find(p_method);
- if (!E) {
-
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
-
- int ac = E->get().argtypes.size();
- if (ac < p_argcount) {
-
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = ac;
- return Variant();
- }
-
- if (ac > p_argcount) {
-
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = ac;
- return Variant();
- }
-
- for (int i = 0; i < p_argcount; i++) {
-
- if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) {
-
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = i;
- r_error.expected = E->get().argtypes[i];
- }
- }
-
- jvalue *v = NULL;
-
- if (p_argcount) {
-
- v = (jvalue *)alloca(sizeof(jvalue) * p_argcount);
- }
-
- JNIEnv *env = ThreadAndroid::get_env();
-
- int res = env->PushLocalFrame(16);
-
- ERR_FAIL_COND_V(res != 0, Variant());
-
- List<jobject> to_erase;
- for (int i = 0; i < p_argcount; i++) {
-
- jvalret vr = _variant_to_jvalue(env, E->get().argtypes[i], p_args[i]);
- v[i] = vr.val;
- if (vr.obj)
- to_erase.push_back(vr.obj);
- }
-
- Variant ret;
-
- switch (E->get().ret_type) {
-
- case Variant::NIL: {
-
- env->CallVoidMethodA(instance, E->get().method, v);
- } break;
- case Variant::BOOL: {
-
- ret = env->CallBooleanMethodA(instance, E->get().method, v) == JNI_TRUE;
- } break;
- case Variant::INT: {
-
- ret = env->CallIntMethodA(instance, E->get().method, v);
- } break;
- case Variant::FLOAT: {
-
- ret = env->CallFloatMethodA(instance, E->get().method, v);
- } break;
- case Variant::STRING: {
-
- jobject o = env->CallObjectMethodA(instance, E->get().method, v);
- ret = jstring_to_string((jstring)o, env);
- env->DeleteLocalRef(o);
- } break;
- case Variant::PACKED_STRING_ARRAY: {
-
- jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v);
-
- ret = _jobject_to_variant(env, arr);
-
- env->DeleteLocalRef(arr);
- } break;
- case Variant::PACKED_INT32_ARRAY: {
-
- jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v);
-
- int fCount = env->GetArrayLength(arr);
- Vector<int> sarr;
- sarr.resize(fCount);
-
- int *w = sarr.ptrw();
- env->GetIntArrayRegion(arr, 0, fCount, w);
- ret = sarr;
- env->DeleteLocalRef(arr);
- } break;
- case Variant::PACKED_FLOAT32_ARRAY: {
-
- jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v);
-
- int fCount = env->GetArrayLength(arr);
- Vector<float> sarr;
- sarr.resize(fCount);
-
- float *w = sarr.ptrw();
- env->GetFloatArrayRegion(arr, 0, fCount, w);
- ret = sarr;
- env->DeleteLocalRef(arr);
- } break;
-
-#ifndef _MSC_VER
-#warning This is missing 64 bits arrays, I have no idea how to do it in JNI
-#endif
- case Variant::DICTIONARY: {
-
- jobject obj = env->CallObjectMethodA(instance, E->get().method, v);
- ret = _jobject_to_variant(env, obj);
- env->DeleteLocalRef(obj);
-
- } break;
- default: {
-
- env->PopLocalFrame(NULL);
- ERR_FAIL_V(Variant());
- } break;
- }
-
- while (to_erase.size()) {
- env->DeleteLocalRef(to_erase.front()->get());
- to_erase.pop_front();
- }
-
- env->PopLocalFrame(NULL);
-
- return ret;
- }
-
- jobject get_instance() const {
-
- return instance;
- }
- void set_instance(jobject p_instance) {
-
- instance = p_instance;
- }
-
- void add_method(const StringName &p_name, jmethodID p_method, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) {
-
- MethodData md;
- md.method = p_method;
- md.argtypes = p_args;
- md.ret_type = p_ret_type;
- method_map[p_name] = md;
- }
-
- JNISingleton() {
- instance = NULL;
- }
-};
-
#endif // JNI_UTILS_H
diff --git a/platform/android/net_socket_android.cpp b/platform/android/net_socket_android.cpp
index 320bdd3817..0341ef3ec6 100644
--- a/platform/android/net_socket_android.cpp
+++ b/platform/android/net_socket_android.cpp
@@ -38,7 +38,6 @@ jmethodID NetSocketAndroid::_multicast_lock_acquire = 0;
jmethodID NetSocketAndroid::_multicast_lock_release = 0;
void NetSocketAndroid::setup(jobject p_net_utils) {
-
JNIEnv *env = ThreadAndroid::get_env();
net_utils = env->NewGlobalRef(p_net_utils);
@@ -72,11 +71,6 @@ void NetSocketAndroid::make_default() {
_create = _create_func;
}
-NetSocketAndroid::NetSocketAndroid() :
- wants_broadcast(false),
- multicast_groups(0) {
-}
-
NetSocketAndroid::~NetSocketAndroid() {
close();
}
diff --git a/platform/android/net_socket_android.h b/platform/android/net_socket_android.h
index 4fc80d2de1..955d906535 100644
--- a/platform/android/net_socket_android.h
+++ b/platform/android/net_socket_android.h
@@ -45,15 +45,14 @@
* joins/leaves a multicast group.
*/
class NetSocketAndroid : public NetSocketPosix {
-
private:
static jobject net_utils;
static jclass cls;
static jmethodID _multicast_lock_acquire;
static jmethodID _multicast_lock_release;
- bool wants_broadcast;
- int multicast_groups;
+ bool wants_broadcast = false;
+ int multicast_groups = 0;
static void multicast_lock_acquire();
static void multicast_lock_release();
@@ -71,7 +70,7 @@ public:
virtual Error join_multicast_group(const IP_Address &p_multi_address, String p_if_name);
virtual Error leave_multicast_group(const IP_Address &p_multi_address, String p_if_name);
- NetSocketAndroid();
+ NetSocketAndroid() {}
~NetSocketAndroid();
};
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 8021d6dd07..baf6ee952a 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -32,15 +32,11 @@
#include "core/io/file_access_buffered_fa.h"
#include "core/project_settings.h"
-#if defined(OPENGL_ENABLED)
-#include "drivers/gles2/rasterizer_gles2.h"
-#endif
#include "drivers/unix/dir_access_unix.h"
#include "drivers/unix/file_access_unix.h"
#include "file_access_android.h"
#include "main/main.h"
-#include "servers/visual/visual_server_raster.h"
-#include "servers/visual/visual_server_wrap_mt.h"
+#include "platform/android/display_server_android.h"
#include "dir_access_jandroid.h"
#include "file_access_jandroid.h"
@@ -60,31 +56,7 @@ public:
virtual ~AndroidLogger() {}
};
-int OS_Android::get_video_driver_count() const {
-
- return 2;
-}
-
-const char *OS_Android::get_video_driver_name(int p_driver) const {
-
- switch (p_driver) {
- case VIDEO_DRIVER_GLES2:
- return "GLES2";
- }
- ERR_FAIL_V_MSG(NULL, "Invalid video driver index: " + itos(p_driver) + ".");
-}
-int OS_Android::get_audio_driver_count() const {
-
- return 1;
-}
-
-const char *OS_Android::get_audio_driver_name(int p_driver) const {
-
- return "Android";
-}
-
void OS_Android::initialize_core() {
-
OS_Unix::initialize_core();
if (use_apk_expansion)
@@ -110,71 +82,33 @@ void OS_Android::initialize_core() {
NetSocketAndroid::make_default();
}
-void OS_Android::set_opengl_extensions(const char *p_gl_extensions) {
-
- ERR_FAIL_COND(!p_gl_extensions);
- gl_extensions = p_gl_extensions;
-}
-
-int OS_Android::get_current_video_driver() const {
- return video_driver_index;
+void OS_Android::initialize() {
+ initialize_core();
}
-Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
+void OS_Android::initialize_joypads() {
+ Input::get_singleton()->set_fallback_mapping(godot_java->get_input_fallback_mapping());
- // FIXME: Add Vulkan support. Readd fallback code from Vulkan to GLES2?
-
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- bool gl_initialization_error = false;
-
- 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.\n"
- "Please try updating your Android version.",
- "Unable to initialize video driver");
- return ERR_UNAVAILABLE;
- }
- }
-#endif
-
- video_driver_index = p_video_driver;
-
- visual_server = memnew(VisualServerRaster);
- if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
- visual_server = memnew(VisualServerWrapMT(visual_server, false));
- }
-
- visual_server->init();
-
- AudioDriverManager::initialize(p_audio_driver);
-
- input = memnew(InputDefault);
- input->set_fallback_mapping(godot_java->get_input_fallback_mapping());
-
- return OK;
+ // This queries/updates the currently connected devices/joypads.
+ godot_java->init_input_devices();
}
void OS_Android::set_main_loop(MainLoop *p_main_loop) {
-
main_loop = p_main_loop;
- input->set_main_loop(p_main_loop);
}
void OS_Android::delete_main_loop() {
-
- memdelete(main_loop);
+ if (main_loop) {
+ memdelete(main_loop);
+ main_loop = nullptr;
+ }
}
void OS_Android::finalize() {
+}
- memdelete(input);
+OS_Android *OS_Android::get_singleton() {
+ return (OS_Android *)OS::get_singleton();
}
GodotJavaWrapper *OS_Android::get_godot_java() {
@@ -185,24 +119,15 @@ GodotIOJavaWrapper *OS_Android::get_godot_io_java() {
return godot_io_java;
}
-void OS_Android::alert(const String &p_alert, const String &p_title) {
-
- //print("ALERT: %s\n", p_alert.utf8().get_data());
- godot_java->alert(p_alert, p_title);
-}
-
bool OS_Android::request_permission(const String &p_name) {
-
return godot_java->request_permission(p_name);
}
bool OS_Android::request_permissions() {
-
return godot_java->request_permissions();
}
Vector<String> OS_Android::get_granted_permissions() const {
-
return godot_java->get_granted_permissions();
}
@@ -212,383 +137,53 @@ Error OS_Android::open_dynamic_library(const String p_path, void *&p_library_han
return OK;
}
-void OS_Android::set_mouse_show(bool p_show) {
-
- //android has no mouse...
-}
-
-void OS_Android::set_mouse_grab(bool p_grab) {
-
- //it really has no mouse...!
-}
-
-bool OS_Android::is_mouse_grab_enabled() const {
-
- //*sigh* technology has evolved so much since i was a kid..
- return false;
-}
-
-Point2 OS_Android::get_mouse_position() const {
-
- return Point2();
-}
-
-int OS_Android::get_mouse_button_state() const {
-
- return 0;
-}
-
-void OS_Android::set_window_title(const String &p_title) {
- //This queries/updates the currently connected devices/joypads
- //Set_window_title is called when initializing the main loop (main.cpp)
- //therefore this place is found to be suitable (I found no better).
- godot_java->init_input_devices();
-}
-
-void OS_Android::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
-}
-
-OS::VideoMode OS_Android::get_video_mode(int p_screen) const {
-
- return default_videomode;
-}
-
-void OS_Android::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
-
- p_list->push_back(default_videomode);
-}
-
-void OS_Android::set_keep_screen_on(bool p_enabled) {
- OS::set_keep_screen_on(p_enabled);
-
- godot_java->set_keep_screen_on(p_enabled);
-}
-
-Size2 OS_Android::get_window_size() const {
-
- return Vector2(default_videomode.width, default_videomode.height);
-}
-
String OS_Android::get_name() const {
-
return "Android";
}
MainLoop *OS_Android::get_main_loop() const {
-
return main_loop;
}
-bool OS_Android::can_draw() const {
-
- return true; //always?
-}
-
void OS_Android::main_loop_begin() {
-
if (main_loop)
main_loop->init();
}
bool OS_Android::main_loop_iterate() {
-
if (!main_loop)
return false;
return Main::iteration();
}
void OS_Android::main_loop_end() {
-
if (main_loop)
main_loop->finish();
}
void OS_Android::main_loop_focusout() {
-
- if (main_loop)
- main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
+ DisplayServerAndroid::get_singleton()->send_window_event(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
audio_driver_android.set_pause(true);
}
void OS_Android::main_loop_focusin() {
-
- if (main_loop)
- main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
+ DisplayServerAndroid::get_singleton()->send_window_event(DisplayServer::WINDOW_EVENT_FOCUS_IN);
audio_driver_android.set_pause(false);
}
-void OS_Android::process_joy_event(OS_Android::JoypadEvent p_event) {
-
- switch (p_event.type) {
- case JOY_EVENT_BUTTON:
- input->joy_button(p_event.device, p_event.index, p_event.pressed);
- break;
- case JOY_EVENT_AXIS:
- InputDefault::JoyAxis value;
- value.min = -1;
- value.value = p_event.value;
- input->joy_axis(p_event.device, p_event.index, value);
- break;
- case JOY_EVENT_HAT:
- input->joy_hat(p_event.device, p_event.hat);
- break;
- default:
- return;
- }
-}
-
-void OS_Android::process_event(Ref<InputEvent> p_event) {
-
- input->parse_input_event(p_event);
-}
-
-void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos> &p_points) {
-
- switch (p_what) {
- case 0: { //gesture begin
-
- if (touch.size()) {
- //end all if exist
- for (int i = 0; i < touch.size(); i++) {
-
- Ref<InputEventScreenTouch> ev;
- ev.instance();
- ev->set_index(touch[i].id);
- ev->set_pressed(false);
- ev->set_position(touch[i].pos);
- input->parse_input_event(ev);
- }
- }
-
- touch.resize(p_points.size());
- for (int i = 0; i < p_points.size(); i++) {
- touch.write[i].id = p_points[i].id;
- touch.write[i].pos = p_points[i].pos;
- }
-
- //send touch
- for (int i = 0; i < touch.size(); i++) {
-
- Ref<InputEventScreenTouch> ev;
- ev.instance();
- ev->set_index(touch[i].id);
- ev->set_pressed(true);
- ev->set_position(touch[i].pos);
- input->parse_input_event(ev);
- }
-
- } break;
- case 1: { //motion
-
- ERR_FAIL_COND(touch.size() != p_points.size());
-
- for (int i = 0; i < touch.size(); i++) {
-
- int idx = -1;
- for (int j = 0; j < p_points.size(); j++) {
-
- if (touch[i].id == p_points[j].id) {
- idx = j;
- break;
- }
- }
-
- ERR_CONTINUE(idx == -1);
-
- if (touch[i].pos == p_points[idx].pos)
- continue; //no move unncesearily
-
- Ref<InputEventScreenDrag> ev;
- ev.instance();
- ev->set_index(touch[i].id);
- ev->set_position(p_points[idx].pos);
- ev->set_relative(p_points[idx].pos - touch[i].pos);
- input->parse_input_event(ev);
- touch.write[i].pos = p_points[idx].pos;
- }
-
- } break;
- case 2: { //release
-
- if (touch.size()) {
- //end all if exist
- for (int i = 0; i < touch.size(); i++) {
-
- Ref<InputEventScreenTouch> ev;
- ev.instance();
- ev->set_index(touch[i].id);
- ev->set_pressed(false);
- ev->set_position(touch[i].pos);
- input->parse_input_event(ev);
- }
- touch.clear();
- }
- } break;
- case 3: { // add touch
-
- for (int i = 0; i < p_points.size(); i++) {
- if (p_points[i].id == p_pointer) {
- TouchPos tp = p_points[i];
- touch.push_back(tp);
-
- Ref<InputEventScreenTouch> ev;
- ev.instance();
-
- ev->set_index(tp.id);
- ev->set_pressed(true);
- ev->set_position(tp.pos);
- input->parse_input_event(ev);
-
- break;
- }
- }
- } break;
- case 4: { // remove touch
-
- for (int i = 0; i < touch.size(); i++) {
- if (touch[i].id == p_pointer) {
-
- Ref<InputEventScreenTouch> ev;
- ev.instance();
- ev->set_index(touch[i].id);
- ev->set_pressed(false);
- ev->set_position(touch[i].pos);
- input->parse_input_event(ev);
- touch.remove(i);
-
- break;
- }
- }
- } break;
- }
-}
-
-void OS_Android::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
- Ref<InputEventMouseMotion> ev;
- ev.instance();
- ev->set_position(p_pos);
- ev->set_global_position(p_pos);
- ev->set_relative(p_pos - hover_prev_pos);
- input->parse_input_event(ev);
- hover_prev_pos = p_pos;
- } break;
- }
-}
-
-void OS_Android::process_double_tap(Point2 p_pos) {
- Ref<InputEventMouseButton> ev;
- ev.instance();
- ev->set_position(p_pos);
- ev->set_global_position(p_pos);
- ev->set_pressed(false);
- ev->set_doubleclick(true);
- input->parse_input_event(ev);
-}
-
-void OS_Android::process_scroll(Point2 p_pos) {
- Ref<InputEventPanGesture> ev;
- ev.instance();
- ev->set_position(p_pos);
- ev->set_delta(p_pos - scroll_prev_pos);
- input->parse_input_event(ev);
- scroll_prev_pos = p_pos;
-}
-
-void OS_Android::process_accelerometer(const Vector3 &p_accelerometer) {
-
- input->set_accelerometer(p_accelerometer);
-}
-
-void OS_Android::process_gravity(const Vector3 &p_gravity) {
-
- input->set_gravity(p_gravity);
-}
-
-void OS_Android::process_magnetometer(const Vector3 &p_magnetometer) {
-
- input->set_magnetometer(p_magnetometer);
-}
-
-void OS_Android::process_gyroscope(const Vector3 &p_gyroscope) {
-
- input->set_gyroscope(p_gyroscope);
-}
-
-bool OS_Android::has_touchscreen_ui_hint() const {
-
- return true;
-}
-
-bool OS_Android::has_virtual_keyboard() const {
-
- return true;
-}
-
-int OS_Android::get_virtual_keyboard_height() const {
- return godot_io_java->get_vk_height();
-
- // ERR_PRINT("Cannot obtain virtual keyboard height.");
- // return 0;
-}
-
-void OS_Android::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length) {
-
- if (godot_io_java->has_vk()) {
- godot_io_java->show_vk(p_existing_text, p_max_input_length);
- } else {
-
- ERR_PRINT("Virtual keyboard not available");
- };
-}
-
-void OS_Android::hide_virtual_keyboard() {
-
- if (godot_io_java->has_vk()) {
-
- godot_io_java->hide_vk();
- } else {
-
- ERR_PRINT("Virtual keyboard not available");
- };
-}
-
-void OS_Android::init_video_mode(int p_video_width, int p_video_height) {
-
- default_videomode.width = p_video_width;
- default_videomode.height = p_video_height;
- default_videomode.fullscreen = true;
- default_videomode.resizable = false;
-}
-
void OS_Android::main_loop_request_go_back() {
-
- if (main_loop)
- main_loop->notification(MainLoop::NOTIFICATION_WM_GO_BACK_REQUEST);
-}
-
-void OS_Android::set_display_size(Size2 p_size) {
-
- default_videomode.width = p_size.x;
- default_videomode.height = p_size.y;
+ DisplayServerAndroid::get_singleton()->send_window_event(DisplayServer::WINDOW_EVENT_GO_BACK_REQUEST);
}
Error OS_Android::shell_open(String p_uri) {
-
return godot_io_java->open_uri(p_uri);
}
String OS_Android::get_resource_dir() const {
-
return "/"; //android has its own filesystem for resources inside the APK
}
String OS_Android::get_locale() const {
-
String locale = godot_io_java->get_locale();
if (locale != "") {
return locale;
@@ -597,28 +192,7 @@ String OS_Android::get_locale() const {
return OS_Unix::get_locale();
}
-void OS_Android::set_clipboard(const String &p_text) {
-
- // DO we really need the fallback to OS_Unix here?!
- if (godot_java->has_set_clipboard()) {
- godot_java->set_clipboard(p_text);
- } else {
- OS_Unix::set_clipboard(p_text);
- }
-}
-
-String OS_Android::get_clipboard() const {
-
- // DO we really need the fallback to OS_Unix here?!
- if (godot_java->has_get_clipboard()) {
- return godot_java->get_clipboard();
- }
-
- return OS_Unix::get_clipboard();
-}
-
String OS_Android::get_model_name() const {
-
String model = godot_io_java->get_model();
if (model != "")
return model;
@@ -626,19 +200,12 @@ String OS_Android::get_model_name() const {
return OS_Unix::get_model_name();
}
-int OS_Android::get_screen_dpi(int p_screen) const {
-
- return godot_io_java->get_screen_dpi();
-}
-
String OS_Android::get_user_data_dir() const {
-
if (data_dir_cache != String())
return data_dir_cache;
String data_dir = godot_io_java->get_user_data_dir();
if (data_dir != "") {
-
//store current dir
char real_current_dir_name[2048];
getcwd(real_current_dir_name, 2048);
@@ -662,13 +229,7 @@ String OS_Android::get_user_data_dir() const {
return ".";
}
-void OS_Android::set_screen_orientation(ScreenOrientation p_orientation) {
-
- godot_io_java->set_screen_orientation(p_orientation);
-}
-
String OS_Android::get_unique_id() const {
-
String unique_id = godot_io_java->get_unique_id();
if (unique_id != "")
return unique_id;
@@ -676,50 +237,45 @@ String OS_Android::get_unique_id() const {
return OS::get_unique_id();
}
-Error OS_Android::native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) {
- // FIXME: Add support for volume, audio and subtitle tracks
-
- godot_io_java->play_video(p_path);
- return OK;
-}
-
-bool OS_Android::native_video_is_playing() const {
-
- return godot_io_java->is_video_playing();
-}
-
-void OS_Android::native_video_pause() {
-
- godot_io_java->pause_video();
-}
-
String OS_Android::get_system_dir(SystemDir p_dir) const {
-
return godot_io_java->get_system_dir(p_dir);
}
-void OS_Android::native_video_stop() {
+void OS_Android::set_display_size(const Size2i &p_size) {
+ display_size = p_size;
+}
- godot_io_java->stop_video();
+Size2i OS_Android::get_display_size() const {
+ return display_size;
}
void OS_Android::set_context_is_16_bits(bool p_is_16) {
-
+#if defined(OPENGL_ENABLED)
//use_16bits_fbo = p_is_16;
//if (rasterizer)
// rasterizer->set_force_16_bits_fbo(p_is_16);
+#endif
}
-void OS_Android::joy_connection_changed(int p_device, bool p_connected, String p_name) {
- return input->joy_connection_changed(p_device, p_connected, p_name, "");
+void OS_Android::set_opengl_extensions(const char *p_gl_extensions) {
+#if defined(OPENGL_ENABLED)
+ ERR_FAIL_COND(!p_gl_extensions);
+ gl_extensions = p_gl_extensions;
+#endif
}
-bool OS_Android::is_joy_known(int p_device) {
- return input->is_joy_mapped(p_device);
+void OS_Android::set_native_window(ANativeWindow *p_native_window) {
+#if defined(VULKAN_ENABLED)
+ native_window = p_native_window;
+#endif
}
-String OS_Android::get_joy_guid(int p_device) const {
- return input->get_joy_guid_remapped(p_device);
+ANativeWindow *OS_Android::get_native_window() const {
+#if defined(VULKAN_ENABLED)
+ return native_window;
+#else
+ return nullptr;
+#endif
}
void OS_Android::vibrate_handheld(int p_duration_ms) {
@@ -747,19 +303,21 @@ bool OS_Android::_check_internal_feature_support(const String &p_feature) {
}
OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion) {
+ display_size.width = 800;
+ display_size.height = 600;
use_apk_expansion = p_use_apk_expansion;
- default_videomode.width = 800;
- default_videomode.height = 600;
- default_videomode.fullscreen = true;
- default_videomode.resizable = false;
-
- main_loop = NULL;
- gl_extensions = NULL;
- //rasterizer = NULL;
+
+ main_loop = nullptr;
+
+#if defined(OPENGL_ENABLED)
+ gl_extensions = nullptr;
use_gl2 = false;
+#endif
- visual_server = NULL;
+#if defined(VULKAN_ENABLED)
+ native_window = nullptr;
+#endif
godot_java = p_godot_java;
godot_io_java = p_godot_io_java;
@@ -769,6 +327,8 @@ OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_god
_set_logger(memnew(CompositeLogger(loggers)));
AudioDriverManager::add_driver(&audio_driver_android);
+
+ DisplayServerAndroid::register_android_driver();
}
OS_Android::~OS_Android() {
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index ec6ffe5438..cac7efaa88 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -33,79 +33,45 @@
#include "audio_driver_jandroid.h"
#include "audio_driver_opensl.h"
-#include "core/os/input.h"
#include "core/os/main_loop.h"
#include "drivers/unix/os_unix.h"
-#include "main/input_default.h"
#include "servers/audio_server.h"
-#include "servers/visual/rasterizer.h"
class GodotJavaWrapper;
class GodotIOJavaWrapper;
-class OS_Android : public OS_Unix {
-public:
- struct TouchPos {
- int id;
- Point2 pos;
- };
-
- enum {
- JOY_EVENT_BUTTON = 0,
- JOY_EVENT_AXIS = 1,
- JOY_EVENT_HAT = 2
- };
-
- struct JoypadEvent {
-
- int device;
- int type;
- int index;
- bool pressed;
- float value;
- int hat;
- };
+struct ANativeWindow;
+class OS_Android : public OS_Unix {
private:
- Vector<TouchPos> touch;
- Point2 hover_prev_pos; // needed to calculate the relative position on hover events
- Point2 scroll_prev_pos; // needed to calculate the relative position on scroll events
+ Size2i display_size;
- bool use_gl2;
bool use_apk_expansion;
+#if defined(OPENGL_ENABLED)
bool use_16bits_fbo;
+ const char *gl_extensions;
+#endif
- VisualServer *visual_server;
+#if defined(VULKAN_ENABLED)
+ ANativeWindow *native_window;
+#endif
mutable String data_dir_cache;
//AudioDriverAndroid audio_driver_android;
AudioDriverOpenSL audio_driver_android;
- const char *gl_extensions;
-
- InputDefault *input;
- VideoMode default_videomode;
MainLoop *main_loop;
GodotJavaWrapper *godot_java;
GodotIOJavaWrapper *godot_io_java;
- int video_driver_index;
-
public:
- // functions used by main to initialize/deinitialize the OS
- virtual int get_video_driver_count() const;
- virtual const char *get_video_driver_name(int p_driver) const;
-
- virtual int get_audio_driver_count() const;
- virtual const char *get_audio_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 initialize();
+
+ virtual void initialize_joypads();
virtual void set_main_loop(MainLoop *p_main_loop);
virtual void delete_main_loop();
@@ -114,37 +80,19 @@ public:
typedef int64_t ProcessID;
- static OS *get_singleton();
+ static OS_Android *get_singleton();
GodotJavaWrapper *get_godot_java();
GodotIOJavaWrapper *get_godot_io_java();
- virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
virtual bool request_permission(const String &p_name);
virtual bool request_permissions();
virtual Vector<String> get_granted_permissions() const;
virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);
- 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 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 void set_keep_screen_on(bool p_enabled);
-
- virtual Size2 get_window_size() const;
-
virtual String get_name() const;
virtual MainLoop *get_main_loop() const;
- virtual bool can_draw() const;
-
void main_loop_begin();
bool main_loop_iterate();
void main_loop_request_go_back();
@@ -152,53 +100,25 @@ public:
void main_loop_focusout();
void main_loop_focusin();
- 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);
- virtual void hide_virtual_keyboard();
- virtual int get_virtual_keyboard_height() const;
-
- void set_opengl_extensions(const char *p_gl_extensions);
- void set_display_size(Size2 p_size);
+ void set_display_size(const Size2i &p_size);
+ Size2i get_display_size() const;
void set_context_is_16_bits(bool p_is_16);
+ void set_opengl_extensions(const char *p_gl_extensions);
- virtual void set_screen_orientation(ScreenOrientation p_orientation);
+ 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 void set_clipboard(const String &p_text);
- virtual String get_clipboard() const;
virtual String get_model_name() const;
- virtual int get_screen_dpi(int p_screen = 0) const;
virtual String get_unique_id() const;
virtual String get_system_dir(SystemDir p_dir) const;
- void process_accelerometer(const Vector3 &p_accelerometer);
- 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_hover(int p_type, Point2 p_pos);
- void process_double_tap(Point2 p_pos);
- void process_scroll(Point2 p_pos);
- void process_joy_event(JoypadEvent p_event);
- void process_event(Ref<InputEvent> p_event);
- void init_video_mode(int p_video_width, int p_video_height);
-
- 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_stop();
-
- virtual bool is_joy_known(int p_device);
- virtual String get_joy_guid(int p_device) const;
- void joy_connection_changed(int p_device, bool p_connected, String p_name);
void vibrate_handheld(int p_duration_ms);
virtual bool _check_internal_feature_support(const String &p_feature);
diff --git a/platform/android/plugin/godot_plugin_config.h b/platform/android/plugin/godot_plugin_config.h
new file mode 100644
index 0000000000..9ad7de1202
--- /dev/null
+++ b/platform/android/plugin/godot_plugin_config.h
@@ -0,0 +1,251 @@
+/*************************************************************************/
+/* godot_plugin_config.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 GODOT_PLUGIN_CONFIG_H
+#define GODOT_PLUGIN_CONFIG_H
+
+#include "core/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 = "|";
+
+/*
+ The `config` section and fields are required and defined as follow:
+- **name**: name of the plugin
+- **binary_type**: can be either `local` or `remote`. The type affects the **binary** field
+- **binary**:
+ - if **binary_type** is `local`, then this should be the filename of the plugin `aar` file in the `res://android/plugins` directory (e.g: `MyPlugin.aar`).
+ - if **binary_type** is `remote`, then this should be a declaration for a remote gradle binary (e.g: "org.godot.example:my-plugin:0.0.0").
+
+The `dependencies` section and fields are optional and defined as follow:
+- **local**: contains a list of local `.aar` binary files the plugin depends on. The local binary dependencies must also be located in the `res://android/plugins` directory.
+- **remote**: contains a list of remote binary gradle dependencies for the plugin.
+- **custom_maven_repos**: contains a list of urls specifying custom maven repos required for the plugin's dependencies.
+
+ See https://github.com/godotengine/godot/issues/38157#issuecomment-618773871
+ */
+struct PluginConfig {
+ // Set to true when the config file is properly loaded.
+ bool valid_config = false;
+
+ // Required config section
+ String name;
+ String binary_type;
+ String binary;
+
+ // Optional dependencies section
+ Vector<String> local_dependencies;
+ Vector<String> remote_dependencies;
+ Vector<String> custom_maven_repos;
+};
+
+/*
+ * Set of prebuilt plugins.
+ */
+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 inline String resolve_local_dependency_path(String plugin_config_dir, String dependency_path) {
+ String absolute_path;
+ if (!dependency_path.empty()) {
+ if (dependency_path.is_abs_path()) {
+ absolute_path = ProjectSettings::get_singleton()->globalize_path(dependency_path);
+ } else {
+ absolute_path = plugin_config_dir.plus_file(dependency_path);
+ }
+ }
+
+ 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()) {
+ 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]));
+ }
+ }
+ 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));
+ 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;
+
+ bool valid_binary = false;
+ if (valid_binary_type) {
+ valid_binary = !plugin_config.binary.empty() &&
+ (plugin_config.binary_type == BINARY_TYPE_REMOTE ||
+ FileAccess::exists(plugin_config.binary));
+ }
+
+ bool valid_local_dependencies = true;
+ if (!plugin_config.local_dependencies.empty()) {
+ for (int i = 0; i < plugin_config.local_dependencies.size(); i++) {
+ if (!FileAccess::exists(plugin_config.local_dependencies[i])) {
+ valid_local_dependencies = false;
+ break;
+ }
+ }
+ }
+ 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 = {};
+
+ 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());
+
+ 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;
+
+ 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()) {
+ 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.valid_config = is_plugin_config_valid(plugin_config);
+ }
+ }
+
+ return plugin_config;
+}
+
+static inline String get_plugins_binaries(String binary_type, Vector<PluginConfig> plugins_configs) {
+ String plugins_binaries;
+ if (!plugins_configs.empty()) {
+ Vector<String> binaries;
+ for (int i = 0; i < plugins_configs.size(); i++) {
+ PluginConfig config = plugins_configs[i];
+ if (!config.valid_config) {
+ continue;
+ }
+
+ if (config.binary_type == binary_type) {
+ binaries.push_back(config.binary);
+ }
+
+ if (binary_type == BINARY_TYPE_LOCAL) {
+ binaries.append_array(config.local_dependencies);
+ }
+
+ if (binary_type == BINARY_TYPE_REMOTE) {
+ binaries.append_array(config.remote_dependencies);
+ }
+ }
+
+ plugins_binaries = String(PLUGIN_VALUE_SEPARATOR).join(binaries);
+ }
+
+ return plugins_binaries;
+}
+
+static inline String get_plugins_custom_maven_repos(Vector<PluginConfig> plugins_configs) {
+ String custom_maven_repos;
+ if (!plugins_configs.empty()) {
+ Vector<String> repos_urls;
+ for (int i = 0; i < plugins_configs.size(); i++) {
+ PluginConfig config = plugins_configs[i];
+ if (!config.valid_config) {
+ continue;
+ }
+
+ repos_urls.append_array(config.custom_maven_repos);
+ }
+
+ custom_maven_repos = String(PLUGIN_VALUE_SEPARATOR).join(repos_urls);
+ }
+ return custom_maven_repos;
+}
+
+static inline String get_plugins_names(Vector<PluginConfig> plugins_configs) {
+ String plugins_names;
+ if (!plugins_configs.empty()) {
+ Vector<String> names;
+ for (int i = 0; i < plugins_configs.size(); i++) {
+ PluginConfig config = plugins_configs[i];
+ if (!config.valid_config) {
+ continue;
+ }
+
+ names.push_back(config.name);
+ }
+ plugins_names = String(PLUGIN_VALUE_SEPARATOR).join(names);
+ }
+
+ return plugins_names;
+}
+
+#endif // GODOT_PLUGIN_CONFIG_H
diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp
index 7413236e5d..557743fa73 100644
--- a/platform/android/plugin/godot_plugin_jni.cpp
+++ b/platform/android/plugin/godot_plugin_jni.cpp
@@ -33,6 +33,7 @@
#include <core/engine.h>
#include <core/error_macros.h>
#include <core/project_settings.h>
+#include <platform/android/api/jni_singleton.h>
#include <platform/android/jni_utils.h>
#include <platform/android/string_android.h>
@@ -41,9 +42,8 @@ static HashMap<String, JNISingleton *> jni_singletons;
extern "C" {
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jobject obj, jstring name) {
-
String singname = jstring_to_string(name, env);
- JNISingleton *s = memnew(JNISingleton);
+ JNISingleton *s = (JNISingleton *)ClassDB::instance("JNISingleton");
s->set_instance(env->NewGlobalRef(obj));
jni_singletons[singname] = s;
@@ -52,7 +52,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args) {
-
String singname = jstring_to_string(sname, env);
ERR_FAIL_COND(!jni_singletons.has(singname));
@@ -67,7 +66,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis
int stringCount = env->GetArrayLength(args);
for (int i = 0; i < stringCount; i++) {
-
jstring string = (jstring)env->GetObjectArrayElement(args, i);
const String rawString = jstring_to_string(string, env);
types.push_back(get_jni_type(rawString));
@@ -79,13 +77,55 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis
jclass cls = env->GetObjectClass(s->get_instance());
jmethodID mid = env->GetMethodID(cls, mname.ascii().get_data(), cs.ascii().get_data());
if (!mid) {
-
print_line("Failed getting method ID " + mname);
}
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) {
+ String singleton_name = jstring_to_string(j_plugin_name, env);
+
+ ERR_FAIL_COND(!jni_singletons.has(singleton_name));
+
+ JNISingleton *singleton = jni_singletons.get(singleton_name);
+
+ String signal_name = jstring_to_string(j_signal_name, env);
+ Vector<Variant::Type> types;
+
+ int stringCount = env->GetArrayLength(j_signal_param_types);
+
+ for (int i = 0; i < stringCount; i++) {
+ jstring j_signal_param_type = (jstring)env->GetObjectArrayElement(j_signal_param_types, i);
+ const String signal_param_type = jstring_to_string(j_signal_param_type, env);
+ types.push_back(get_jni_type(signal_param_type));
+ }
+
+ 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) {
+ String singleton_name = jstring_to_string(j_plugin_name, env);
+
+ ERR_FAIL_COND(!jni_singletons.has(singleton_name));
+
+ JNISingleton *singleton = jni_singletons.get(singleton_name);
+
+ String signal_name = jstring_to_string(j_signal_name, env);
+
+ int count = env->GetArrayLength(j_signal_params);
+ const Variant *args[count];
+
+ 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;
+ 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) {
int gdnlib_count = env->GetArrayLength(gdnlib_paths);
if (gdnlib_count == 0) {
diff --git a/platform/android/plugin/godot_plugin_jni.h b/platform/android/plugin/godot_plugin_jni.h
index 0d613d3bfe..80ce332e7c 100644
--- a/platform/android/plugin/godot_plugin_jni.h
+++ b/platform/android/plugin/godot_plugin_jni.h
@@ -37,6 +37,8 @@
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);
}
diff --git a/platform/android/string_android.h b/platform/android/string_android.h
index 51c488c8cc..88ccd3b652 100644
--- a/platform/android/string_android.h
+++ b/platform/android/string_android.h
@@ -40,13 +40,13 @@
* @param env JNI environment instance. If null obtained by ThreadAndroid::get_env().
* @return Godot string instance.
*/
-static inline String jstring_to_string(jstring source, JNIEnv *env = NULL) {
+static inline String jstring_to_string(jstring source, JNIEnv *env = nullptr) {
String result;
if (source) {
if (!env) {
env = ThreadAndroid::get_env();
}
- const char *const source_utf8 = env->GetStringUTFChars(source, NULL);
+ const char *const source_utf8 = env->GetStringUTFChars(source, nullptr);
if (source_utf8) {
result.parse_utf8(source_utf8);
env->ReleaseStringUTFChars(source, source_utf8);
diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp
index 98b61ad755..13aa313ebf 100644
--- a/platform/android/thread_jandroid.cpp
+++ b/platform/android/thread_jandroid.cpp
@@ -48,17 +48,14 @@ 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
@@ -66,11 +63,10 @@ void *ThreadAndroid::thread_callback(void *userdata) {
pthread_setspecific(thread_id_key, (void *)memnew(ID(t->id)));
t->callback(t->user);
ScriptServer::thread_exit();
- return NULL;
+ 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;
@@ -83,7 +79,6 @@ Thread *ThreadAndroid::create_func_jandroid(ThreadCreateCallback p_callback, voi
}
Thread::ID ThreadAndroid::get_thread_id_func_jandroid() {
-
void *value = pthread_getspecific(thread_id_key);
if (value)
@@ -95,39 +90,35 @@ Thread::ID ThreadAndroid::get_thread_id_func_jandroid() {
}
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, NULL);
+ 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 != NULL) {
+ if (env != nullptr) {
java_vm->DetachCurrentThread();
- pthread_setspecific(jvm_key, NULL);
+ pthread_setspecific(jvm_key, nullptr);
}
}
pthread_key_t ThreadAndroid::jvm_key;
-JavaVM *ThreadAndroid::java_vm = NULL;
+JavaVM *ThreadAndroid::java_vm = nullptr;
void ThreadAndroid::setup_thread() {
-
if (pthread_getspecific(jvm_key))
return; //already setup
JNIEnv *env;
- java_vm->AttachCurrentThread(&env, NULL);
+ 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;
@@ -137,18 +128,16 @@ void ThreadAndroid::make_default(JavaVM *p_java_vm) {
}
JNIEnv *ThreadAndroid::get_env() {
-
if (!pthread_getspecific(jvm_key)) {
setup_thread();
}
- JNIEnv *env = NULL;
- java_vm->AttachCurrentThread(&env, NULL);
+ JNIEnv *env = nullptr;
+ java_vm->AttachCurrentThread(&env, nullptr);
return env;
}
ThreadAndroid::ThreadAndroid() {
-
pthread = 0;
}
diff --git a/platform/android/thread_jandroid.h b/platform/android/thread_jandroid.h
index eb4725ae68..9cfcc64813 100644
--- a/platform/android/thread_jandroid.h
+++ b/platform/android/thread_jandroid.h
@@ -37,7 +37,6 @@
#include <sys/types.h>
class ThreadAndroid : public Thread {
-
static pthread_key_t thread_id_key;
static ID next_thread_id;
diff --git a/platform/android/vulkan/vk_renderer_jni.h b/platform/android/vulkan/vk_renderer_jni.h
deleted file mode 100644
index 017766fea2..0000000000
--- a/platform/android/vulkan/vk_renderer_jni.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*************************************************************************/
-/* vk_renderer_jni.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 VK_RENDERER_JNI_H
-#define VK_RENDERER_JNI_H
-
-#include <android/log.h>
-#include <jni.h>
-
-extern "C" {
-JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceCreated(JNIEnv *env, jobject obj, jobject j_surface);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceChanged(JNIEnv *env, jobject object, jobject j_surface, jint width, jint height);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkResume(JNIEnv *env, jobject obj);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDrawFrame(JNIEnv *env, jobject obj);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkPause(JNIEnv *env, jobject obj);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDestroy(JNIEnv *env, jobject obj);
-}
-
-#endif // VK_RENDERER_JNI_H
diff --git a/platform/android/vulkan/vk_renderer_jni.cpp b/platform/android/vulkan/vulkan_context_android.cpp
index 3026e7daad..5fb7a83da4 100644
--- a/platform/android/vulkan/vk_renderer_jni.cpp
+++ b/platform/android/vulkan/vulkan_context_android.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* vk_renderer_jni.cpp */
+/* vulkan_context_android.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,31 +28,33 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "vk_renderer_jni.h"
+#include "vulkan_context_android.h"
+#include <vulkan/vulkan_android.h>
-extern "C" {
-
-JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceCreated(JNIEnv *env, jobject obj, jobject j_surface) {
- // TODO: complete
+const char *VulkanContextAndroid::_get_platform_surface_extension() const {
+ return VK_KHR_ANDROID_SURFACE_EXTENSION_NAME;
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkSurfaceChanged(JNIEnv *env, jobject object, jobject j_surface, jint width, jint height) {
- // TODO: complete
-}
+int VulkanContextAndroid::window_create(ANativeWindow *p_window, int p_width, int p_height) {
+ VkAndroidSurfaceCreateInfoKHR createInfo;
+ createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
+ createInfo.pNext = nullptr;
+ createInfo.flags = 0;
+ createInfo.window = p_window;
-JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkResume(JNIEnv *env, jobject obj) {
- // TODO: complete
-}
+ VkSurfaceKHR surface;
+ VkResult err = vkCreateAndroidSurfaceKHR(_get_instance(), &createInfo, nullptr, &surface);
+ if (err != VK_SUCCESS) {
+ ERR_FAIL_V_MSG(-1, "vkCreateAndroidSurfaceKHR failed with error " + itos(err));
+ }
-JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDrawFrame(JNIEnv *env, jobject obj) {
- // TODO: complete
+ return _window_create(DisplayServer::MAIN_WINDOW_ID, surface, p_width, p_height);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkPause(JNIEnv *env, jobject obj) {
- // TODO: complete
+VulkanContextAndroid::VulkanContextAndroid() {
+ // TODO: fix validation layers
+ use_validation_layers = false;
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_vulkan_VkRenderer_nativeOnVkDestroy(JNIEnv *env, jobject obj) {
- // TODO: complete
-}
+VulkanContextAndroid::~VulkanContextAndroid() {
}
diff --git a/platform/android/vulkan/vulkan_context_android.h b/platform/android/vulkan/vulkan_context_android.h
new file mode 100644
index 0000000000..6bd3cbee36
--- /dev/null
+++ b/platform/android/vulkan/vulkan_context_android.h
@@ -0,0 +1,48 @@
+/*************************************************************************/
+/* vulkan_context_android.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 VULKAN_CONTEXT_ANDROID_H
+#define VULKAN_CONTEXT_ANDROID_H
+
+#include "drivers/vulkan/vulkan_context.h"
+
+struct ANativeWindow;
+
+class VulkanContextAndroid : public VulkanContext {
+ virtual const char *_get_platform_surface_extension() const;
+
+public:
+ int window_create(ANativeWindow *p_window, int p_width, int p_height);
+
+ VulkanContextAndroid();
+ ~VulkanContextAndroid();
+};
+
+#endif // VULKAN_CONTEXT_ANDROID_H
diff --git a/platform/haiku/SCsub b/platform/haiku/SCsub
index 592f56bbbf..dbff6c5ae9 100644
--- a/platform/haiku/SCsub
+++ b/platform/haiku/SCsub
@@ -1,28 +1,25 @@
#!/usr/bin/env python
-Import('env')
+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'
+ "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
-)
+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'])
+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')
+ 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
index 0a5df14743..2fbbeeb176 100644
--- a/platform/haiku/audio_driver_media_kit.cpp
+++ b/platform/haiku/audio_driver_media_kit.cpp
@@ -34,16 +34,16 @@
#include "core/project_settings.h"
-int32_t *AudioDriverMediaKit::samples_in = NULL;
+int32_t *AudioDriverMediaKit::samples_in = nullptr;
Error AudioDriverMediaKit::init() {
active = false;
- mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE);
+ mix_rate = GLOBAL_GET("audio/mix_rate");
speaker_mode = SPEAKER_MODE_STEREO;
channels = 2;
- int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
+ 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);
@@ -59,12 +59,12 @@ Error AudioDriverMediaKit::init() {
&format,
"godot_sound_server",
AudioDriverMediaKit::PlayBuffer,
- NULL,
+ nullptr,
this);
if (player->InitCheck() != B_OK) {
fprintf(stderr, "MediaKit ERR: can not create a BSoundPlayer instance\n");
- ERR_FAIL_COND_V(player == NULL, ERR_CANT_OPEN);
+ ERR_FAIL_COND_V(player == nullptr, ERR_CANT_OPEN);
}
player->Start();
@@ -126,7 +126,7 @@ void AudioDriverMediaKit::finish() {
}
AudioDriverMediaKit::AudioDriverMediaKit() {
- player = NULL;
+ player = nullptr;
}
AudioDriverMediaKit::~AudioDriverMediaKit() {
diff --git a/platform/haiku/detect.py b/platform/haiku/detect.py
index dd72294816..0b84df8f9b 100644
--- a/platform/haiku/detect.py
+++ b/platform/haiku/detect.py
@@ -12,7 +12,7 @@ def get_name():
def can_build():
- if (os.name != "posix" or sys.platform == "darwin"):
+ if os.name != "posix" or sys.platform == "darwin":
return False
return True
@@ -22,41 +22,40 @@ def get_opts():
from SCons.Variables import EnumVariable
return [
- EnumVariable('debug_symbols', 'Add debugging symbols to release builds', 'yes', ('yes', 'no', 'full')),
+ EnumVariable("debug_symbols", "Add debugging symbols to release builds", "yes", ("yes", "no", "full")),
]
def get_flags():
- return [
- ]
+ 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'])
+ 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"] == "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'])
+ elif env["target"] == "debug":
+ env.Prepend(CCFLAGS=["-g3", "-DDEBUG_ENABLED", "-DDEBUG_MEMORY_ENABLED"])
## Architecture
- is64 = sys.maxsize > 2**32
- if (env["bits"] == "default"):
+ is64 = sys.maxsize > 2 ** 32
+ if env["bits"] == "default":
env["bits"] = "64" if is64 else "32"
## Compiler configuration
@@ -66,89 +65,94 @@ def configure(env):
## Dependencies
- if not env['builtin_libwebp']:
- env.ParseConfig('pkg-config libwebp --cflags --libs')
+ 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 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_freetype"]:
+ env.ParseConfig("pkg-config freetype2 --cflags --libs")
- if not env['builtin_libpng']:
- env.ParseConfig('pkg-config libpng16 --cflags --libs')
+ if not env["builtin_libpng"]:
+ env.ParseConfig("pkg-config libpng16 --cflags --libs")
- if not env['builtin_bullet']:
+ if not env["builtin_bullet"]:
# We need at least version 2.88
import subprocess
- bullet_version = subprocess.check_output(['pkg-config', 'bullet', '--modversion']).strip()
+
+ 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"))
+ 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')
+ env.ParseConfig("pkg-config bullet --cflags --libs")
- if not env['builtin_enet']:
- env.ParseConfig('pkg-config libenet --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_squish"]:
+ env.ParseConfig("pkg-config libsquish --cflags --libs")
- if not env['builtin_zstd']:
- env.ParseConfig('pkg-config libzstd --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_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_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_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_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 not env["builtin_libogg"]:
+ env.ParseConfig("pkg-config ogg --cflags --libs")
- if env['builtin_libtheora']:
- list_of_x86 = ['x86_64', 'x86', 'i386', 'i586']
+ 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_wslay"]:
+ env.ParseConfig("pkg-config libwslay --cflags --libs")
- if not env['builtin_mbedtls']:
+ 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'])
+ env.Append(LIBS=["mbedtls", "mbedcrypto", "mbedx509"])
- if not env['builtin_miniupnpc']:
+ 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')
+ 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'])
+ 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/haiku_direct_window.cpp b/platform/haiku/haiku_direct_window.cpp
index 2d7efe6b61..0a40f847f4 100644
--- a/platform/haiku/haiku_direct_window.cpp
+++ b/platform/haiku/haiku_direct_window.cpp
@@ -42,10 +42,10 @@ HaikuDirectWindow::HaikuDirectWindow(BRect p_frame) :
last_button_mask = 0;
last_key_modifier_state = 0;
- view = NULL;
- update_runner = NULL;
- input = NULL;
- main_loop = NULL;
+ view = nullptr;
+ update_runner = nullptr;
+ input = nullptr;
+ main_loop = nullptr;
}
HaikuDirectWindow::~HaikuDirectWindow() {
@@ -74,7 +74,7 @@ void HaikuDirectWindow::SetMainLoop(MainLoop *p_main_loop) {
bool HaikuDirectWindow::QuitRequested() {
StopMessageRunner();
- main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
+ main_loop->notification(NOTIFICATION_WM_CLOSE_REQUEST);
return false;
}
@@ -278,7 +278,7 @@ void HaikuDirectWindow::HandleKeyboardEvent(BMessage *message) {
event->set_echo(message->HasInt32("be:key_repeat"));
event->set_unicode(0);
- const char *bytes = NULL;
+ const char *bytes = nullptr;
if (message->FindString("bytes", &bytes) == B_OK) {
event->set_unicode(BUnicodeChar::FromUTF8(&bytes));
}
diff --git a/platform/haiku/haiku_direct_window.h b/platform/haiku/haiku_direct_window.h
index ccc9542f0e..4817abbb7a 100644
--- a/platform/haiku/haiku_direct_window.h
+++ b/platform/haiku/haiku_direct_window.h
@@ -35,8 +35,8 @@
#include <DirectWindow.h>
+#include "core/input/input.h"
#include "core/os/os.h"
-#include "main/input_default.h"
#include "haiku_gl_view.h"
diff --git a/platform/haiku/key_mapping_haiku.h b/platform/haiku/key_mapping_haiku.h
index a0e85e3390..e735108e44 100644
--- a/platform/haiku/key_mapping_haiku.h
+++ b/platform/haiku/key_mapping_haiku.h
@@ -32,7 +32,7 @@
#define KEY_MAPPING_HAIKU_H
class KeyMappingHaiku {
- KeyMappingHaiku(){};
+ KeyMappingHaiku() {}
public:
static unsigned int get_keysym(int32 raw_char, int32 key);
diff --git a/platform/haiku/os_haiku.cpp b/platform/haiku/os_haiku.cpp
index a082ba53f9..7a2591784f 100644
--- a/platform/haiku/os_haiku.cpp
+++ b/platform/haiku/os_haiku.cpp
@@ -32,9 +32,9 @@
#include "drivers/gles2/rasterizer_gles2.h"
#include "main/main.h"
-#include "servers/physics/physics_server_sw.h"
-#include "servers/visual/visual_server_raster.h"
-#include "servers/visual/visual_server_wrap_mt.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>
@@ -85,7 +85,7 @@ int OS_Haiku::get_current_video_driver() const {
}
Error OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
- main_loop = NULL;
+ main_loop = nullptr;
current_video_mode = p_desired;
app = new HaikuApplication();
@@ -116,13 +116,13 @@ Error OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p
RasterizerGLES2::make_current();
#endif
- visual_server = memnew(VisualServerRaster);
+ rendering_server = memnew(RenderingServerRaster);
// FIXME: Reimplement threaded rendering
if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
- visual_server = memnew(VisualServerWrapMT(visual_server, false));
+ rendering_server = memnew(RenderingServerWrapMT(rendering_server, false));
}
- ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V(!rendering_server, ERR_UNAVAILABLE);
video_driver_index = p_video_driver;
@@ -130,7 +130,7 @@ Error OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p
window->SetInput(input);
window->Show();
- visual_server->init();
+ rendering_server->init();
AudioDriverManager::initialize(p_audio_driver);
@@ -142,10 +142,10 @@ void OS_Haiku::finalize() {
memdelete(main_loop);
}
- main_loop = NULL;
+ main_loop = nullptr;
- visual_server->finish();
- memdelete(visual_server);
+ rendering_server->finish();
+ memdelete(rendering_server);
memdelete(input);
@@ -169,8 +169,8 @@ void OS_Haiku::delete_main_loop() {
memdelete(main_loop);
}
- main_loop = NULL;
- window->SetMainLoop(NULL);
+ main_loop = nullptr;
+ window->SetMainLoop(nullptr);
}
void OS_Haiku::release_rendering_thread() {
@@ -267,7 +267,7 @@ void OS_Haiku::set_window_position(const Point2 &p_position) {
void OS_Haiku::set_window_fullscreen(bool p_enabled) {
window->SetFullScreen(p_enabled);
current_video_mode.fullscreen = p_enabled;
- visual_server->init();
+ rendering_server->init();
}
bool OS_Haiku::is_window_fullscreen() const {
@@ -324,12 +324,10 @@ String OS_Haiku::get_executable_path() const {
}
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")) {
@@ -340,7 +338,6 @@ String OS_Haiku::get_config_path() const {
}
String OS_Haiku::get_data_path() const {
-
if (has_environment("XDG_DATA_HOME")) {
return get_environment("XDG_DATA_HOME");
} else if (has_environment("HOME")) {
@@ -351,7 +348,6 @@ String OS_Haiku::get_data_path() const {
}
String OS_Haiku::get_cache_path() const {
-
if (has_environment("XDG_CACHE_HOME")) {
return get_environment("XDG_CACHE_HOME");
} else if (has_environment("HOME")) {
diff --git a/platform/haiku/os_haiku.h b/platform/haiku/os_haiku.h
index fc8cb77a91..d3ef9400d4 100644
--- a/platform/haiku/os_haiku.h
+++ b/platform/haiku/os_haiku.h
@@ -33,12 +33,12 @@
#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 "main/input_default.h"
#include "servers/audio_server.h"
-#include "servers/visual_server.h"
+#include "servers/rendering_server.h"
class OS_Haiku : public OS_Unix {
private:
@@ -46,7 +46,7 @@ private:
HaikuDirectWindow *window;
MainLoop *main_loop;
InputDefault *input;
- VisualServer *visual_server;
+ RenderingServer *rendering_server;
VideoMode current_video_mode;
int video_driver_index;
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub
index 1f82f51888..a48629f720 100644
--- a/platform/iphone/SCsub
+++ b/platform/iphone/SCsub
@@ -1,31 +1,35 @@
#!/usr/bin/env python
-Import('env')
+Import("env")
iphone_lib = [
- 'godot_iphone.cpp',
- 'os_iphone.cpp',
- 'semaphore_iphone.cpp',
- 'gl_view.mm',
- 'main.m',
- 'app_delegate.mm',
- 'view_controller.mm',
- 'game_center.mm',
- 'in_app_store.mm',
- 'icloud.mm',
- 'ios.mm',
- 'vulkan_context_iphone.mm',
+ "godot_iphone.cpp",
+ "os_iphone.cpp",
+ "semaphore_iphone.cpp",
+ "gl_view.mm",
+ "main.m",
+ "app_delegate.mm",
+ "view_controller.mm",
+ "game_center.mm",
+ "in_app_store.mm",
+ "icloud.mm",
+ "ios.mm",
+ "vulkan_context_iphone.mm",
]
env_ios = env.Clone()
-ios_lib = env_ios.add_library('iphone', iphone_lib)
+ios_lib = env_ios.add_library("iphone", iphone_lib)
+
def combine_libs(target=None, source=None, env=None):
lib_path = target[0].srcnode().abspath
if "osxcross" in env:
- libtool = '$IPHONEPATH/usr/bin/${ios_triple}libtool'
+ libtool = "$IPHONEPATH/usr/bin/${ios_triple}libtool"
else:
libtool = "$IPHONEPATH/usr/bin/libtool"
- env.Execute(libtool + ' -static -o "' + lib_path + '" ' + ' '.join([('"' + lib.srcnode().abspath + '"') for lib in source]))
+ env.Execute(
+ libtool + ' -static -o "' + lib_path + '" ' + " ".join([('"' + lib.srcnode().abspath + '"') for lib in source])
+ )
+
-combine_command = env_ios.Command('#bin/libgodot' + env_ios['LIBSUFFIX'], [ios_lib] + env_ios['LIBS'], combine_libs)
+combine_command = env_ios.Command("#bin/libgodot" + env_ios["LIBSUFFIX"], [ios_lib] + env_ios["LIBS"], combine_libs)
diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm
index 0ac8bb7a56..c4ef185bf1 100644
--- a/platform/iphone/app_delegate.mm
+++ b/platform/iphone/app_delegate.mm
@@ -247,37 +247,31 @@ static void on_focus_in(ViewController *view_controller, bool *is_focus_out) {
int joy_id = [self getJoyIdForController:controller];
if (element == gamepad.buttonA) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_0,
+ 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_1,
+ 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_2,
+ 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_3,
+ 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_L,
+ 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_R,
+ OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_RIGHT_SHOULDER,
gamepad.rightShoulder.isPressed);
- } else if (element == gamepad.leftTrigger) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_L2,
- gamepad.leftTrigger.isPressed);
- } else if (element == gamepad.rightTrigger) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_R2,
- gamepad.rightTrigger.isPressed);
} else if (element == gamepad.dpad) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_UP,
+ OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
gamepad.dpad.up.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_DOWN,
+ OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
gamepad.dpad.down.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_LEFT,
+ OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
gamepad.dpad.left.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_RIGHT,
+ OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
gamepad.dpad.right.isPressed);
};
@@ -285,20 +279,20 @@ static void on_focus_in(ViewController *view_controller, bool *is_focus_out) {
jx.min = -1;
if (element == gamepad.leftThumbstick) {
jx.value = gamepad.leftThumbstick.xAxis.value;
- OSIPhone::get_singleton()->joy_axis(joy_id, JOY_ANALOG_LX, jx);
+ 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_ANALOG_LY, jx);
+ 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_ANALOG_RX, jx);
+ 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_ANALOG_RY, jx);
+ 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_ANALOG_L2, jx);
+ 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_ANALOG_R2, jx);
+ OSIPhone::get_singleton()->joy_axis(joy_id, JOY_AXIS_TRIGGER_RIGHT, jx);
};
};
} else if (controller.gamepad != nil) {
@@ -309,31 +303,31 @@ static void on_focus_in(ViewController *view_controller, bool *is_focus_out) {
int joy_id = [self getJoyIdForController:controller];
if (element == gamepad.buttonA) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_0,
+ 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_1,
+ 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_2,
+ 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_3,
+ 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_L,
+ 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_R,
+ 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_DPAD_UP,
+ OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
gamepad.dpad.up.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_DOWN,
+ OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
gamepad.dpad.down.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_LEFT,
+ OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
gamepad.dpad.left.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_RIGHT,
+ OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
gamepad.dpad.right.isPressed);
};
};
@@ -347,19 +341,19 @@ static void on_focus_in(ViewController *view_controller, bool *is_focus_out) {
int joy_id = [self getJoyIdForController:controller];
if (element == gamepad.buttonA) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_0,
+ 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_2,
+ 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_DPAD_UP,
+ OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
gamepad.dpad.up.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_DOWN,
+ OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
gamepad.dpad.down.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_LEFT,
+ OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
gamepad.dpad.left.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_RIGHT,
+ OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
gamepad.dpad.right.isPressed);
};
};
@@ -432,7 +426,6 @@ OS::VideoMode _get_video_mode() {
static int frame_count = 0;
- (void)drawView:(UIView *)view;
{
-
switch (frame_count) {
case 0: {
OS::get_singleton()->set_video_mode(_get_video_mode());
@@ -469,7 +462,6 @@ static int frame_count = 0;
}; break;
case 1: {
-
Main::setup2();
++frame_count;
@@ -496,7 +488,6 @@ static int frame_count = 0;
ProjectSettings::get_singleton()->set("Info.plist/" + ukey, uval);
} else if ([value isKindOfClass:[NSNumber class]]) {
-
NSNumber *n = (NSNumber *)value;
double dval = [n doubleValue];
@@ -508,7 +499,6 @@ static int frame_count = 0;
}; break;
case 2: {
-
Main::start();
++frame_count;
diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py
index e01950c1db..3e6c2f0ecf 100644
--- a/platform/iphone/detect.py
+++ b/platform/iphone/detect.py
@@ -2,6 +2,7 @@ import os
import sys
from methods import detect_darwin_sdk_path
+
def is_active():
return True
@@ -12,7 +13,7 @@ def get_name():
def can_build():
- if sys.platform == 'darwin' or ("OSXCROSS_IOS" in os.environ):
+ if sys.platform == "darwin" or ("OSXCROSS_IOS" in os.environ):
return True
return False
@@ -20,22 +21,31 @@ def can_build():
def get_opts():
from SCons.Variables import BoolVariable
+
return [
- ('IPHONEPATH', 'Path to iPhone toolchain', '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain'),
- ('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)', 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_exceptions', 'Enable exceptions', False),
- ('ios_triple', 'Triple for ios toolchain', ''),
+ (
+ "IPHONEPATH",
+ "Path to iPhone toolchain",
+ "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain",
+ ),
+ ("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)",
+ 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_exceptions", "Enable exceptions", False),
+ ("ios_triple", "Triple for ios toolchain", ""),
]
def get_flags():
return [
- ('tools', False),
+ ("tools", False),
]
@@ -43,32 +53,32 @@ def configure(env):
## Build type
- if (env["target"].startswith("release")):
- env.Append(CPPDEFINES=['NDEBUG', ('NS_BLOCK_ASSERTIONS', 1)])
- if (env["optimize"] == "speed"): #optimize for speed (default)
- env.Append(CCFLAGS=['-O2', '-ftree-vectorize', '-fomit-frame-pointer'])
- env.Append(LINKFLAGS=['-O2'])
- else: #optimize for size
- env.Append(CCFLAGS=['-Os', '-ftree-vectorize'])
- env.Append(LINKFLAGS=['-Os'])
+ if env["target"].startswith("release"):
+ env.Append(CPPDEFINES=["NDEBUG", ("NS_BLOCK_ASSERTIONS", 1)])
+ if env["optimize"] == "speed": # optimize for speed (default)
+ env.Append(CCFLAGS=["-O2", "-ftree-vectorize", "-fomit-frame-pointer"])
+ env.Append(LINKFLAGS=["-O2"])
+ else: # optimize for size
+ env.Append(CCFLAGS=["-Os", "-ftree-vectorize"])
+ env.Append(LINKFLAGS=["-Os"])
if env["target"] == "release_debug":
- env.Append(CPPDEFINES=['DEBUG_ENABLED'])
+ env.Append(CPPDEFINES=["DEBUG_ENABLED"])
- elif (env["target"] == "debug"):
- env.Append(CCFLAGS=['-gdwarf-2', '-O0'])
- env.Append(CPPDEFINES=['_DEBUG', ('DEBUG', 1), 'DEBUG_ENABLED', 'DEBUG_MEMORY_ENABLED'])
+ elif env["target"] == "debug":
+ env.Append(CCFLAGS=["-gdwarf-2", "-O0"])
+ env.Append(CPPDEFINES=["_DEBUG", ("DEBUG", 1), "DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED"])
- if (env["use_lto"]):
- env.Append(CCFLAGS=['-flto'])
- env.Append(LINKFLAGS=['-flto'])
+ if env["use_lto"]:
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto"])
## Architecture
if env["arch"] == "x86": # i386
env["bits"] = "32"
elif env["arch"] == "x86_64":
env["bits"] = "64"
- elif (env["arch"] == "arm" or env["arch"] == "arm32" or env["arch"] == "armv7" or env["bits"] == "32"): # arm
+ elif env["arch"] == "arm" or env["arch"] == "arm32" or env["arch"] == "armv7" or env["bits"] == "32": # arm
env["arch"] = "arm"
env["bits"] = "32"
else: # armv64
@@ -81,108 +91,145 @@ def configure(env):
if "OSXCROSS_IOS" in os.environ:
env["osxcross"] = True
- env['ENV']['PATH'] = env['IPHONEPATH'] + "/Developer/usr/bin/:" + env['ENV']['PATH']
+ env["ENV"]["PATH"] = env["IPHONEPATH"] + "/Developer/usr/bin/:" + env["ENV"]["PATH"]
- compiler_path = '$IPHONEPATH/usr/bin/${ios_triple}'
- s_compiler_path = '$IPHONEPATH/Developer/usr/bin/'
+ compiler_path = "$IPHONEPATH/usr/bin/${ios_triple}"
+ s_compiler_path = "$IPHONEPATH/Developer/usr/bin/"
ccache_path = os.environ.get("CCACHE")
if ccache_path is None:
- env['CC'] = compiler_path + 'clang'
- env['CXX'] = compiler_path + 'clang++'
- env['S_compiler'] = s_compiler_path + 'gcc'
+ env["CC"] = compiler_path + "clang"
+ env["CXX"] = compiler_path + "clang++"
+ env["S_compiler"] = s_compiler_path + "gcc"
else:
# there aren't any ccache wrappers available for iOS,
# to enable caching we need to prepend the path to the ccache binary
- env['CC'] = ccache_path + ' ' + compiler_path + 'clang'
- env['CXX'] = ccache_path + ' ' + compiler_path + 'clang++'
- env['S_compiler'] = ccache_path + ' ' + s_compiler_path + 'gcc'
- env['AR'] = compiler_path + 'ar'
- env['RANLIB'] = compiler_path + 'ranlib'
+ env["CC"] = ccache_path + " " + compiler_path + "clang"
+ env["CXX"] = ccache_path + " " + compiler_path + "clang++"
+ env["S_compiler"] = ccache_path + " " + s_compiler_path + "gcc"
+ env["AR"] = compiler_path + "ar"
+ env["RANLIB"] = compiler_path + "ranlib"
## Compile flags
- if (env["arch"] == "x86" or env["arch"] == "x86_64"):
- detect_darwin_sdk_path('iphonesimulator', env)
- env['ENV']['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
+ if env["arch"] == "x86" or env["arch"] == "x86_64":
+ detect_darwin_sdk_path("iphonesimulator", env)
+ env["ENV"]["MACOSX_DEPLOYMENT_TARGET"] = "10.9"
arch_flag = "i386" if env["arch"] == "x86" else env["arch"]
- env.Append(CCFLAGS=('-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').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())
- 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())
- env.Append(CPPDEFINES=['NEED_LONG_INT'])
- env.Append(CPPDEFINES=['LIBYUV_DISABLE_NEON'])
+ env.Append(
+ CCFLAGS=(
+ "-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"
+ ).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()
+ )
+ 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()
+ )
+ env.Append(CPPDEFINES=["NEED_LONG_INT"])
+ env.Append(CPPDEFINES=["LIBYUV_DISABLE_NEON"])
# Disable exceptions on non-tools (template) builds
- if not env['tools']:
- if env['ios_exceptions']:
- env.Append(CCFLAGS=['-fexceptions'])
+ if not env["tools"]:
+ if env["ios_exceptions"]:
+ env.Append(CCFLAGS=["-fexceptions"])
else:
- env.Append(CCFLAGS=['-fno-exceptions'])
+ env.Append(CCFLAGS=["-fno-exceptions"])
## Link flags
- if (env["arch"] == "x86" or env["arch"] == "x86_64"):
+ if env["arch"] == "x86" or env["arch"] == "x86_64":
arch_flag = "i386" if env["arch"] == "x86" else env["arch"]
- env.Append(LINKFLAGS=['-arch', arch_flag, '-mios-simulator-version-min=10.0',
- '-isysroot', '$IPHONESDK',
- '-Xlinker',
- '-objc_abi_version',
- '-Xlinker', '2',
- '-F$IPHONESDK',
- ])
- elif (env["arch"] == "arm"):
- env.Append(LINKFLAGS=['-arch', 'armv7', '-Wl,-dead_strip', '-miphoneos-version-min=10.0'])
- if (env["arch"] == "arm64"):
- env.Append(LINKFLAGS=['-arch', 'arm64', '-Wl,-dead_strip', '-miphoneos-version-min=10.0'])
-
- 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',
- ])
+ env.Append(
+ LINKFLAGS=[
+ "-arch",
+ arch_flag,
+ "-mios-simulator-version-min=10.0",
+ "-isysroot",
+ "$IPHONESDK",
+ "-Xlinker",
+ "-objc_abi_version",
+ "-Xlinker",
+ "2",
+ "-F$IPHONESDK",
+ ]
+ )
+ elif env["arch"] == "arm":
+ env.Append(LINKFLAGS=["-arch", "armv7", "-Wl,-dead_strip", "-miphoneos-version-min=10.0"])
+ if env["arch"] == "arm64":
+ env.Append(LINKFLAGS=["-arch", "arm64", "-Wl,-dead_strip", "-miphoneos-version-min=10.0"])
+
+ 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["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["store_kit"]:
+ env.Append(CPPDEFINES=["STOREKIT_ENABLED"])
+ env.Append(LINKFLAGS=["-framework", "StoreKit"])
- if env['icloud']:
- env.Append(CPPDEFINES=['ICLOUD_ENABLED'])
+ if env["icloud"]:
+ env.Append(CPPDEFINES=["ICLOUD_ENABLED"])
- env.Prepend(CPPPATH=['$IPHONESDK/usr/include',
- '$IPHONESDK/System/Library/Frameworks/AudioUnit.framework/Headers',
- ])
+ env.Prepend(
+ CPPPATH=["$IPHONESDK/usr/include", "$IPHONESDK/System/Library/Frameworks/AudioUnit.framework/Headers",]
+ )
- env['ENV']['CODESIGN_ALLOCATE'] = '/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate'
+ env["ENV"]["CODESIGN_ALLOCATE"] = "/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate"
- env.Prepend(CPPPATH=['#platform/iphone'])
- env.Append(CPPDEFINES=['IPHONE_ENABLED', 'UNIX_ENABLED', 'COREAUDIO_ENABLED'])
+ env.Prepend(CPPPATH=["#platform/iphone"])
+ env.Append(CPPDEFINES=["IPHONE_ENABLED", "UNIX_ENABLED", "COREAUDIO_ENABLED"])
- 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'])
+ 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"])
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index 08f3c3f91f..63c3cb8c23 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "export.h"
+
#include "core/io/image_loader.h"
#include "core/io/marshalls.h"
#include "core/io/resource_saver.h"
@@ -47,7 +48,6 @@
#include <sys/stat.h>
class EditorExportPlatformIOS : public EditorExportPlatform {
-
GDCLASS(EditorExportPlatformIOS, EditorExportPlatform);
int version_code;
@@ -71,15 +71,11 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
String modules_buildphase;
String modules_buildgrp;
};
-
struct ExportArchitecture {
String name;
- bool is_default;
+ bool is_default = false;
- ExportArchitecture() :
- name(""),
- is_default(false) {
- }
+ ExportArchitecture() {}
ExportArchitecture(String p_name, bool p_is_default) {
name = p_name;
@@ -106,8 +102,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
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<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets);
- bool is_package_name_valid(const String &p_package, String *r_error = NULL) const {
-
+ bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const {
String pname = p_package;
if (pname.length() == 0) {
@@ -150,7 +145,6 @@ public:
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
virtual void get_platform_features(List<String> *r_features) {
-
r_features->push_back("mobile");
r_features->push_back("iOS");
}
@@ -163,7 +157,6 @@ public:
};
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");
@@ -209,7 +202,6 @@ static const LoadingScreenInfo loading_screen_infos[] = {
};
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"), ""));
@@ -423,7 +415,9 @@ String EditorExportPlatformIOS::_get_linker_flags() {
String result;
for (int i = 0; i < export_plugins.size(); ++i) {
String flags = export_plugins[i]->get_ios_linker_flags();
- if (flags.length() == 0) continue;
+ if (flags.length() == 0) {
+ continue;
+ }
if (result.length() > 0) {
result += ' ';
}
@@ -443,7 +437,6 @@ String EditorExportPlatformIOS::_get_cpp_code() {
}
void EditorExportPlatformIOS::_blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot) {
-
ERR_FAIL_COND(p_dst.is_null());
ERR_FAIL_COND(p_src.is_null());
@@ -456,8 +449,12 @@ void EditorExportPlatformIOS::_blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p
int xs = (x_pos >= 0) ? 0 : -x_pos;
int ys = (y_pos >= 0) ? 0 : -y_pos;
- if (sw + x_pos > p_dst->get_width()) sw = p_dst->get_width() - x_pos;
- if (sh + y_pos > p_dst->get_height()) sh = p_dst->get_height() - y_pos;
+ if (sw + x_pos > p_dst->get_width()) {
+ sw = p_dst->get_width() - x_pos;
+ }
+ if (sh + y_pos > p_dst->get_height()) {
+ sh = p_dst->get_height() - y_pos;
+ }
for (int y = ys; y < sh; y++) {
for (int x = xs; x < sw; x++) {
@@ -795,7 +792,7 @@ void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPrese
String pbx_resources_refs;
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";
+ "$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 build_id = (++current_id).str();
String ref_id = (++current_id).str();
@@ -925,6 +922,14 @@ Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir
Vector<String> frameworks = export_plugins[i]->get_ios_frameworks();
Error err = _export_additional_assets(p_out_dir, frameworks, 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_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_FAIL_COND_V(err, err);
@@ -980,10 +985,11 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
String team_id = p_preset->get("application/app_store_team_id");
ERR_FAIL_COND_V_MSG(team_id.length() == 0, ERR_CANT_OPEN, "App Store Team ID not specified - cannot configure the project.");
- if (p_debug)
+ if (p_debug) {
src_pkg_name = p_preset->get("custom_template/debug");
- else
+ } else {
src_pkg_name = p_preset->get("custom_template/release");
+ }
if (src_pkg_name == "") {
String err;
@@ -1029,8 +1035,9 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
String pack_path = dest_dir + binary_name + ".pck";
Vector<SharedObject> libraries;
Error err = save_pack(p_preset, pack_path, &libraries);
- if (err)
+ if (err) {
return err;
+ }
if (ep.step("Extracting and configuring Xcode project", 1)) {
return ERR_SKIP;
@@ -1040,12 +1047,13 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
print_line("Static library: " + library_to_use);
String pkg_name;
- if (p_preset->get("application/name") != "")
+ if (p_preset->get("application/name") != "") {
pkg_name = p_preset->get("application/name"); // app_name
- else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "")
+ } else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") {
pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name"));
- else
+ } else {
pkg_name = "Unnamed";
+ }
bool found_library = false;
int total_size = 0;
@@ -1076,7 +1084,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE);
print_line("Unzipping...");
- FileAccess *src_f = NULL;
+ FileAccess *src_f = nullptr;
zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io);
if (!src_pkg_zip) {
@@ -1098,7 +1106,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
//get filename
unz_file_info info;
char fname[16384];
- ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, NULL, 0, NULL, 0);
+ ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0);
String file = fname;
@@ -1202,22 +1210,41 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
return ERR_FILE_NOT_FOUND;
}
+ // Copy project static libs to the project
+ Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ for (int i = 0; i < export_plugins.size(); i++) {
+ Vector<String> project_static_libs = export_plugins[i]->get_ios_project_static_libs();
+ for (int j = 0; j < project_static_libs.size(); j++) {
+ const String &static_lib_path = project_static_libs[j];
+ String dest_lib_file_path = dest_dir + static_lib_path.get_file();
+ Error lib_copy_err = tmp_app_path->copy(static_lib_path, dest_lib_file_path);
+ if (lib_copy_err != OK) {
+ ERR_PRINT("Can't copy '" + static_lib_path + "'.");
+ memdelete(tmp_app_path);
+ return lib_copy_err;
+ }
+ }
+ }
+
String iconset_dir = dest_dir + binary_name + "/Images.xcassets/AppIcon.appiconset/";
err = OK;
if (!tmp_app_path->dir_exists(iconset_dir)) {
err = tmp_app_path->make_dir_recursive(iconset_dir);
}
memdelete(tmp_app_path);
- if (err)
+ if (err) {
return err;
+ }
err = _export_icons(p_preset, iconset_dir);
- if (err)
+ if (err) {
return err;
+ }
err = _export_loading_screens(p_preset, dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/");
- if (err)
+ if (err) {
return err;
+ }
print_line("Exporting additional assets");
Vector<IOSExportAsset> assets;
@@ -1287,7 +1314,6 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
}
bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
-
String err;
bool valid = false;
@@ -1345,14 +1371,14 @@ bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset
err += etc_error;
}
- if (!err.empty())
+ if (!err.empty()) {
r_error = err;
+ }
return valid;
}
EditorExportPlatformIOS::EditorExportPlatformIOS() {
-
Ref<Image> img = memnew(Image(_iphone_logo));
logo.instance();
logo->create_from_image(img);
@@ -1362,7 +1388,6 @@ EditorExportPlatformIOS::~EditorExportPlatformIOS() {
}
void register_iphone_exporter() {
-
Ref<EditorExportPlatformIOS> platform;
platform.instance();
diff --git a/platform/iphone/export/export.h b/platform/iphone/export/export.h
index 77b2a07bd1..043d21f533 100644
--- a/platform/iphone/export/export.h
+++ b/platform/iphone/export/export.h
@@ -28,4 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef IPHONE_EXPORT_H
+#define IPHONE_EXPORT_H
+
void register_iphone_exporter();
+
+#endif // IPHONE_EXPORT_H
diff --git a/platform/iphone/game_center.h b/platform/iphone/game_center.h
index d35cc4b87c..0d3ef5b696 100644
--- a/platform/iphone/game_center.h
+++ b/platform/iphone/game_center.h
@@ -36,7 +36,6 @@
#include "core/object.h"
class GameCenter : public Object {
-
GDCLASS(GameCenter, Object);
static GameCenter *instance;
diff --git a/platform/iphone/game_center.mm b/platform/iphone/game_center.mm
index 99d539d4ff..8d470da1a8 100644
--- a/platform/iphone/game_center.mm
+++ b/platform/iphone/game_center.mm
@@ -75,7 +75,6 @@ void GameCenter::return_connect_error(const char *p_error_description) {
}
void GameCenter::connect() {
-
//if this class isn't available, game center isn't implemented
if ((NSClassFromString(@"GKLocalPlayer")) == nil) {
return_connect_error("GameCenter not available");
@@ -125,7 +124,6 @@ bool GameCenter::is_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"];
@@ -156,7 +154,6 @@ Error GameCenter::post_score(Variant p_score) {
};
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"];
@@ -192,7 +189,6 @@ Error GameCenter::award_achievement(Variant p_params) {
};
void GameCenter::request_achievement_descriptions() {
-
[GKAchievementDescription loadAchievementDescriptionsWithCompletionHandler:^(NSArray *descriptions, NSError *error) {
Dictionary ret;
ret["type"] = "achievement_descriptions";
@@ -207,7 +203,6 @@ void GameCenter::request_achievement_descriptions() {
Array replayable;
for (int i = 0; i < [descriptions count]; i++) {
-
GKAchievementDescription *description = [descriptions objectAtIndex:i];
const char *str = [description.identifier UTF8String];
@@ -247,7 +242,6 @@ void GameCenter::request_achievement_descriptions() {
};
void GameCenter::request_achievements() {
-
[GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error) {
Dictionary ret;
ret["type"] = "achievements";
@@ -257,7 +251,6 @@ void GameCenter::request_achievements() {
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 : ""));
@@ -278,7 +271,6 @@ void GameCenter::request_achievements() {
};
void GameCenter::reset_achievements() {
-
[GKAchievement resetAchievementsWithCompletionHandler:^(NSError *error) {
Dictionary ret;
ret["type"] = "reset_achievements";
@@ -294,7 +286,6 @@ void GameCenter::reset_achievements() {
};
Error GameCenter::show_game_center(Variant p_params) {
-
ERR_FAIL_COND_V(!NSProtocolFromString(@"GKGameCenterControllerDelegate"), FAILED);
Dictionary params = p_params;
@@ -338,7 +329,6 @@ Error GameCenter::show_game_center(Variant p_params) {
};
Error GameCenter::request_identity_verification_signature() {
-
ERR_FAIL_COND_V(!is_authenticated(), ERR_UNAUTHORIZED);
GKLocalPlayer *player = [GKLocalPlayer localPlayer];
@@ -365,7 +355,6 @@ Error GameCenter::request_identity_verification_signature() {
};
void GameCenter::game_center_closed() {
-
Dictionary ret;
ret["type"] = "show_game_center";
ret["result"] = "ok";
@@ -373,12 +362,10 @@ void GameCenter::game_center_closed() {
}
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();
@@ -395,6 +382,6 @@ GameCenter::GameCenter() {
authenticated = false;
};
-GameCenter::~GameCenter(){};
+GameCenter::~GameCenter() {}
#endif
diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm
index ede60a502d..1169ebc6b4 100644
--- a/platform/iphone/gl_view.mm
+++ b/platform/iphone/gl_view.mm
@@ -174,7 +174,6 @@ void _focus_out_video() {
};
void _unpause_video() {
-
[_instance.avPlayer play];
video_playing = true;
};
@@ -207,14 +206,12 @@ 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) {
@@ -234,10 +231,8 @@ static int get_touch_id(UITouch *p_touch) {
};
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)
@@ -249,9 +244,7 @@ static int remove_touch(UITouch *p_touch) {
};
static void clear_touches() {
-
for (int i = 0; i < max_touches; i++) {
-
touches[i] = NULL;
};
};
@@ -396,7 +389,6 @@ static void clear_touches() {
active = TRUE;
printf("start animation!\n");
if (useCADisplayLink) {
-
// Approximate frame rate
// assumes device refreshes at 60 fps
int frameInterval = (int)floor(animationInterval * 60.0f);
@@ -446,7 +438,6 @@ static void clear_touches() {
// Updates the OpenGL view when the timer fires
- (void)drawView {
-
if (!active) {
printf("draw view not active!\n");
return;
@@ -489,9 +480,7 @@ static void clear_touches() {
- (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;
@@ -504,12 +493,9 @@ static void clear_touches() {
}
- (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;
@@ -525,9 +511,7 @@ static void clear_touches() {
- (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;
@@ -541,7 +525,6 @@ static void clear_touches() {
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
-
OSIPhone::get_singleton()->touches_cancelled();
clear_touches();
};
@@ -599,7 +582,6 @@ static void clear_touches() {
NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
switch (routeChangeReason) {
-
case AVAudioSessionRouteChangeReasonNewDeviceAvailable: {
NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
NSLog(@"Headphone/Line plugged in");
@@ -609,7 +591,6 @@ static void clear_touches() {
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");
@@ -685,7 +666,6 @@ static void clear_touches() {
}
- (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();
@@ -695,7 +675,6 @@ static void clear_touches() {
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];
diff --git a/platform/iphone/godot_iphone.cpp b/platform/iphone/godot_iphone.cpp
index 8c3eddc5f7..b9d217c9d2 100644
--- a/platform/iphone/godot_iphone.cpp
+++ b/platform/iphone/godot_iphone.cpp
@@ -36,7 +36,7 @@
#include <string.h>
#include <unistd.h>
-static OSIPhone *os = NULL;
+static OSIPhone *os = nullptr;
extern "C" {
int add_path(int p_argc, char **p_args);
@@ -46,11 +46,11 @@ int add_cmdline(int p_argc, char **p_args);
int iphone_main(int, int, int, char **, String);
int iphone_main(int width, int height, int argc, char **argv, String data_dir) {
-
size_t len = strlen(argv[0]);
while (len--) {
- if (argv[0][len] == '/') break;
+ if (argv[0][len] == '/')
+ break;
}
if (len >= 0) {
@@ -71,7 +71,7 @@ int iphone_main(int width, int height, int argc, char **argv, String data_dir) {
for (int i = 0; i < argc; i++) {
fargv[i] = argv[i];
};
- fargv[argc] = NULL;
+ fargv[argc] = nullptr;
argc = add_path(argc, fargv);
argc = add_cmdline(argc, fargv);
@@ -85,7 +85,6 @@ int iphone_main(int width, int height, int argc, char **argv, String data_dir) {
};
void iphone_finish() {
-
printf("iphone_finish\n");
Main::cleanup();
delete os;
diff --git a/platform/iphone/icloud.h b/platform/iphone/icloud.h
index 401a6cbeb8..b11e22fec6 100644
--- a/platform/iphone/icloud.h
+++ b/platform/iphone/icloud.h
@@ -36,7 +36,6 @@
#include "core/object.h"
class ICloud : public Object {
-
GDCLASS(ICloud, Object);
static ICloud *instance;
diff --git a/platform/iphone/icloud.mm b/platform/iphone/icloud.mm
index 251f78f2da..c768274b1f 100644
--- a/platform/iphone/icloud.mm
+++ b/platform/iphone/icloud.mm
@@ -58,12 +58,10 @@ void ICloud::_bind_methods() {
};
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();
@@ -284,6 +282,7 @@ Error ICloud::synchronize_key_values() {
return FAILED;
}
}
+
/*
Error ICloud::initial_sync() {
//you sometimes have to write something to the store to get it to download new data. go apple!
@@ -298,6 +297,7 @@ Error ICloud::initial_sync() {
}
return synchronize();
}
+
*/
ICloud::ICloud() {
ERR_FAIL_COND(instance != NULL);
@@ -354,6 +354,6 @@ ICloud::ICloud() {
}];
}
-ICloud::~ICloud(){};
+ICloud::~ICloud() {}
#endif
diff --git a/platform/iphone/in_app_store.h b/platform/iphone/in_app_store.h
index 493877a5a7..44e65e77ed 100644
--- a/platform/iphone/in_app_store.h
+++ b/platform/iphone/in_app_store.h
@@ -36,7 +36,6 @@
#include "core/object.h"
class InAppStore : public Object {
-
GDCLASS(InAppStore, Object);
static InAppStore *instance;
diff --git a/platform/iphone/in_app_store.mm b/platform/iphone/in_app_store.mm
index a8a887824f..a2efd6691b 100644
--- a/platform/iphone/in_app_store.mm
+++ b/platform/iphone/in_app_store.mm
@@ -57,6 +57,7 @@ NSMutableDictionary *pending_transactions = [NSMutableDictionary dictionary];
[numberFormatter release];
return formattedString;
}
+
@end
InAppStore *InAppStore::instance = NULL;
@@ -80,7 +81,6 @@ void InAppStore::_bind_methods() {
@implementation ProductsDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
-
NSArray *products = response.products;
Dictionary ret;
ret["type"] = "product_info";
@@ -93,7 +93,6 @@ void InAppStore::_bind_methods() {
PackedStringArray currency_codes;
for (NSUInteger i = 0; i < [products count]; i++) {
-
SKProduct *product = [products objectAtIndex:i];
const char *str = [product.localizedTitle UTF8String];
@@ -116,7 +115,6 @@ void InAppStore::_bind_methods() {
PackedStringArray invalid_ids;
for (NSString *ipid in response.invalidProductIdentifiers) {
-
invalid_ids.push_back(String::utf8([ipid UTF8String]));
};
ret["invalid_ids"] = invalid_ids;
@@ -129,7 +127,6 @@ void InAppStore::_bind_methods() {
@end
Error InAppStore::request_product_info(Variant p_params) {
-
Dictionary params = p_params;
ERR_FAIL_COND_V(!params.has("product_ids"), ERR_INVALID_PARAMETER);
@@ -155,7 +152,6 @@ Error InAppStore::request_product_info(Variant p_params) {
};
Error InAppStore::restore_purchases() {
-
printf("restoring purchases!\n");
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
@@ -169,10 +165,8 @@ Error InAppStore::restore_purchases() {
@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");
@@ -189,11 +183,9 @@ Error InAppStore::restore_purchases() {
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];
@@ -263,7 +255,6 @@ Error InAppStore::restore_purchases() {
@end
Error InAppStore::purchase(Variant p_params) {
-
ERR_FAIL_COND_V(![SKPaymentQueue canMakePayments], ERR_UNAVAILABLE);
if (![SKPaymentQueue canMakePayments])
return ERR_UNAVAILABLE;
@@ -286,7 +277,6 @@ int InAppStore::get_pending_event_count() {
};
Variant InAppStore::pop_pending_event() {
-
Variant front = pending_events.front()->get();
pending_events.pop_front();
@@ -294,12 +284,10 @@ Variant InAppStore::pop_pending_event() {
};
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];
@@ -307,7 +295,6 @@ void InAppStore::_record_purchase(String product_id) {
};
InAppStore *InAppStore::get_singleton() {
-
return instance;
};
@@ -334,6 +321,6 @@ void InAppStore::set_auto_finish_transaction(bool b) {
auto_finish_transactions = b;
}
-InAppStore::~InAppStore(){};
+InAppStore::~InAppStore() {}
#endif
diff --git a/platform/iphone/ios.h b/platform/iphone/ios.h
index 1378fdbbc5..2b29e6f268 100644
--- a/platform/iphone/ios.h
+++ b/platform/iphone/ios.h
@@ -34,7 +34,6 @@
#include "core/object.h"
class iOS : public Object {
-
GDCLASS(iOS, Object);
static void _bind_methods();
diff --git a/platform/iphone/ios.mm b/platform/iphone/ios.mm
index 2656f125b9..5923f558a5 100644
--- a/platform/iphone/ios.mm
+++ b/platform/iphone/ios.mm
@@ -34,7 +34,6 @@
#import <UIKit/UIKit.h>
void iOS::_bind_methods() {
-
ClassDB::bind_method(D_METHOD("get_rate_url", "app_id"), &iOS::get_rate_url);
};
@@ -81,4 +80,4 @@ String iOS::get_rate_url(int p_app_id) const {
return ret;
};
-iOS::iOS(){};
+iOS::iOS() {}
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
index 497f2f747d..41dd623e69 100644
--- a/platform/iphone/os_iphone.cpp
+++ b/platform/iphone/os_iphone.cpp
@@ -37,13 +37,13 @@
#endif
#if defined(VULKAN_ENABLED)
-#include "servers/visual/rasterizer_rd/rasterizer_rd.h"
+#include "servers/rendering/rasterizer_rd/rasterizer_rd.h"
// #import <QuartzCore/CAMetalLayer.h>
#include <vulkan/vulkan_metal.h>
#endif
-#include "servers/visual/visual_server_raster.h"
-#include "servers/visual/visual_server_wrap_mt.h"
+#include "servers/rendering/rendering_server_raster.h"
+#include "servers/rendering/rendering_server_wrap_mt.h"
#include "main/main.h"
@@ -58,28 +58,24 @@
#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(NULL, "Invalid video driver index: " + itos(p_driver) + ".");
+ 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();
@@ -88,17 +84,14 @@ void OSIPhone::set_data_dir(String p_dir) {
};
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);
@@ -134,16 +127,16 @@ Error OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p
RasterizerRD::make_current();
#endif
- visual_server = memnew(VisualServerRaster);
+ rendering_server = memnew(RenderingServerRaster);
// FIXME: Reimplement threaded rendering
if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
- visual_server = memnew(VisualServerWrapMT(visual_server, false));
+ rendering_server = memnew(RenderingServerWrapMT(rendering_server, false));
}
- visual_server->init();
- //visual_server->cursor_set_visible(false, 0);
+ 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 visual_server->init() is called
+ // 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
@@ -174,12 +167,10 @@ Error OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p
};
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) {
@@ -189,13 +180,11 @@ void OSIPhone::set_main_loop(MainLoop *p_main_loop) {
};
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]);
};
};
@@ -205,7 +194,6 @@ bool OSIPhone::iterate() {
};
void OSIPhone::key(uint32_t p_key, bool p_pressed) {
-
Ref<InputEventKey> ev;
ev.instance();
ev->set_echo(false);
@@ -217,7 +205,6 @@ void OSIPhone::key(uint32_t p_key, bool p_pressed) {
};
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();
@@ -232,9 +219,7 @@ void OSIPhone::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_d
};
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);
@@ -245,18 +230,14 @@ void OSIPhone::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_
};
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);
};
@@ -270,7 +251,6 @@ void OSIPhone::update_gravity(float p_x, float p_y, float 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));
@@ -333,17 +313,15 @@ void OSIPhone::joy_axis(int p_device, int p_axis, const InputDefault::JoyAxis &p
};
void OSIPhone::delete_main_loop() {
-
if (main_loop) {
main_loop->finish();
memdelete(main_loop);
};
- main_loop = NULL;
+ main_loop = nullptr;
};
void OSIPhone::finalize() {
-
delete_main_loop();
memdelete(input);
@@ -361,8 +339,8 @@ void OSIPhone::finalize() {
memdelete(icloud);
#endif
- visual_server->finish();
- memdelete(visual_server);
+ rendering_server->finish();
+ memdelete(rendering_server);
// memdelete(rasterizer);
// Free unhandled events before close
@@ -372,28 +350,24 @@ void OSIPhone::finalize() {
event_count = 0;
};
-void OSIPhone::set_mouse_show(bool p_show){};
-void OSIPhone::set_mouse_grab(bool p_grab){};
+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::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());
@@ -431,22 +405,18 @@ Error OSIPhone::get_dynamic_library_symbol_handle(void *p_library_handle, const
}
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;
@@ -471,7 +441,7 @@ 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) {
+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);
};
@@ -497,17 +467,14 @@ void OSIPhone::set_keep_screen_on(bool 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;
@@ -516,7 +483,6 @@ String OSIPhone::get_model_name() const {
}
Size2 OSIPhone::get_window_size() const {
-
return Vector2(video_mode.width, video_mode.height);
}
@@ -527,7 +493,6 @@ Rect2 OSIPhone::get_window_safe_area() const {
}
bool OSIPhone::has_touchscreen_ui_hint() const {
-
return true;
}
@@ -600,7 +565,6 @@ void OSIPhone::vibrate_handheld(int p_duration_ms) {
}
bool OSIPhone::_check_internal_feature_support(const String &p_feature) {
-
return p_feature == "mobile";
}
@@ -608,7 +572,7 @@ bool OSIPhone::_check_internal_feature_support(const String &p_feature) {
// 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 = NULL;
+static init_callback *ios_init_callbacks = nullptr;
static int ios_init_callbacks_count = 0;
static int ios_init_callbacks_capacity = 0;
@@ -631,12 +595,12 @@ OSIPhone::OSIPhone(int width, int height, String p_data_dir) {
ios_init_callbacks[i]();
}
free(ios_init_callbacks);
- ios_init_callbacks = NULL;
+ ios_init_callbacks = nullptr;
ios_init_callbacks_count = 0;
ios_init_callbacks_capacity = 0;
- main_loop = NULL;
- visual_server = NULL;
+ main_loop = nullptr;
+ rendering_server = nullptr;
VideoMode vm;
vm.fullscreen = true;
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index f42679e754..955eb15d57 100644
--- a/platform/iphone/os_iphone.h
+++ b/platform/iphone/os_iphone.h
@@ -33,18 +33,16 @@
#ifndef OS_IPHONE_H
#define OS_IPHONE_H
-#include "core/os/input.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 "main/input_default.h"
#include "servers/audio_server.h"
-#include "servers/visual/rasterizer.h"
-#include "servers/visual_server.h"
+#include "servers/rendering/rasterizer.h"
+#include "servers/rendering_server.h"
#if defined(VULKAN_ENABLED)
#include "drivers/vulkan/rendering_device_vulkan.h"
@@ -52,7 +50,6 @@
#endif
class OSIPhone : public OS_Unix {
-
private:
enum {
MAX_MOUSE_COUNT = 8,
@@ -62,7 +59,7 @@ private:
static HashMap<String, void *> dynamic_symbol_lookup_table;
friend void register_dynamic_symbol(char *name, void *address);
- VisualServer *visual_server;
+ RenderingServer *rendering_server;
AudioDriverCoreAudio audio_driver;
@@ -101,7 +98,6 @@ private:
virtual void finalize();
struct MouseList {
-
bool pressed[MAX_MOUSE_COUNT];
MouseList() {
for (int i = 0; i < MAX_MOUSE_COUNT; i++)
@@ -174,7 +170,7 @@ public:
virtual bool can_draw() 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);
+ 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;
diff --git a/platform/iphone/view_controller.mm b/platform/iphone/view_controller.mm
index 465e38e45e..279bcc1226 100644
--- a/platform/iphone/view_controller.mm
+++ b/platform/iphone/view_controller.mm
@@ -40,7 +40,6 @@ int add_path(int, char **);
int add_cmdline(int, char **);
int add_path(int p_argc, char **p_args) {
-
NSString *str = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_path"];
if (!str)
return p_argc;
@@ -54,13 +53,11 @@ int add_path(int p_argc, char **p_args) {
};
int add_cmdline(int p_argc, char **p_args) {
-
NSArray *arr = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_cmdline"];
if (!arr)
return p_argc;
for (int i = 0; i < [arr count]; i++) {
-
NSString *str = [arr objectAtIndex:i];
if (!str)
continue;
@@ -81,7 +78,6 @@ int add_cmdline(int p_argc, char **p_args) {
@implementation ViewController
- (void)didReceiveMemoryWarning {
-
printf("*********** did receive memory warning!\n");
};
diff --git a/platform/iphone/vulkan_context_iphone.h b/platform/iphone/vulkan_context_iphone.h
index 200057e14d..cadd701636 100644
--- a/platform/iphone/vulkan_context_iphone.h
+++ b/platform/iphone/vulkan_context_iphone.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* vulkan_context_osx.h */
+/* vulkan_context_iphone.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -35,7 +35,6 @@
// #import <UIKit/UIKit.h>
class VulkanContextIPhone : public VulkanContext {
-
virtual const char *_get_platform_surface_extension() const;
public:
diff --git a/platform/iphone/vulkan_context_iphone.mm b/platform/iphone/vulkan_context_iphone.mm
index f49b85c097..44c940dc3a 100644
--- a/platform/iphone/vulkan_context_iphone.mm
+++ b/platform/iphone/vulkan_context_iphone.mm
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* vulkan_context_osx.mm */
+/* vulkan_context_iphone.mm */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -36,7 +36,6 @@ const char *VulkanContextIPhone::_get_platform_surface_extension() const {
}
int VulkanContextIPhone::window_create(void *p_window, int p_width, int p_height) {
-
VkIOSSurfaceCreateInfoMVK createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
createInfo.pNext = NULL;
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub
index d3cd8f76b7..dcf9a46bf9 100644
--- a/platform/javascript/SCsub
+++ b/platform/javascript/SCsub
@@ -1,67 +1,70 @@
#!/usr/bin/env python
-Import('env')
+Import("env")
javascript_files = [
- 'audio_driver_javascript.cpp',
- 'http_client_javascript.cpp',
- 'javascript_eval.cpp',
- 'javascript_main.cpp',
- 'os_javascript.cpp',
+ "audio_driver_javascript.cpp",
+ "display_server_javascript.cpp",
+ "http_client_javascript.cpp",
+ "javascript_eval.cpp",
+ "javascript_main.cpp",
+ "os_javascript.cpp",
]
-build_targets = ['#bin/godot${PROGSUFFIX}.js', '#bin/godot${PROGSUFFIX}.wasm']
-if env['threads_enabled']:
- build_targets.append('#bin/godot${PROGSUFFIX}.worker.js')
+build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm"]
+if env["threads_enabled"]:
+ build_targets.append("#bin/godot${PROGSUFFIX}.worker.js")
build = env.add_program(build_targets, javascript_files)
js_libraries = [
- 'http_request.js',
+ "native/http_request.js",
]
for lib in js_libraries:
- env.Append(LINKFLAGS=['--js-library', env.File(lib).path])
+ env.Append(LINKFLAGS=["--js-library", env.File(lib).path])
env.Depends(build, js_libraries)
-js_modules = [
- 'id_handler.js',
+js_pre = [
+ "native/id_handler.js",
+ "native/utils.js",
]
-for module in js_modules:
- env.Append(LINKFLAGS=['--pre-js', env.File(module).path])
-env.Depends(build, js_modules)
+for js in js_pre:
+ env.Append(LINKFLAGS=["--pre-js", env.File(js).path])
+env.Depends(build, js_pre)
engine = [
- 'engine/preloader.js',
- 'engine/loader.js',
- 'engine/utils.js',
- 'engine/engine.js',
+ "engine/preloader.js",
+ "engine/utils.js",
+ "engine/engine.js",
]
-externs = [
- env.File('#platform/javascript/engine/externs.js')
-]
-js_engine = env.CreateEngineFile('#bin/godot${PROGSUFFIX}.engine.js', engine, externs)
+externs = [env.File("#platform/javascript/engine/externs.js")]
+js_engine = env.CreateEngineFile("#bin/godot${PROGSUFFIX}.engine.js", engine, externs)
env.Depends(js_engine, externs)
wrap_list = [
build[0],
js_engine,
]
-js_wrapped = env.Textfile('#bin/godot', [env.File(f) for f in wrap_list], TEXTFILESUFFIX='${PROGSUFFIX}.wrapped.js')
+js_wrapped = env.Textfile("#bin/godot", [env.File(f) for f in wrap_list], TEXTFILESUFFIX="${PROGSUFFIX}.wrapped.js")
-zip_dir = env.Dir('#bin/.javascript_zip')
+zip_dir = env.Dir("#bin/.javascript_zip")
+binary_name = "godot.tools" if env["tools"] else "godot"
out_files = [
- zip_dir.File('godot.js'),
- zip_dir.File('godot.wasm'),
- zip_dir.File('godot.html')
-]
-in_files = [
- js_wrapped,
- build[1],
- '#misc/dist/html/full-size.html'
+ zip_dir.File(binary_name + ".js"),
+ zip_dir.File(binary_name + ".wasm"),
+ zip_dir.File(binary_name + ".html"),
]
-if env['threads_enabled']:
+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])
- out_files.append(zip_dir.File('godot.worker.js'))
+ out_files.append(zip_dir.File(binary_name + ".worker.js"))
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')
+env.Zip(
+ "#bin/godot",
+ zip_files,
+ ZIPROOT=zip_dir,
+ ZIPSUFFIX="${PROGSUFFIX}${ZIPSUFFIX}",
+ ZIPCOMSTR="Archving $SOURCES as $TARGET",
+)
diff --git a/platform/javascript/api/api.cpp b/platform/javascript/api/api.cpp
index 88de13d771..9c73e5c4c4 100644
--- a/platform/javascript/api/api.cpp
+++ b/platform/javascript/api/api.cpp
@@ -35,40 +35,34 @@
static JavaScript *javascript_eval;
void register_javascript_api() {
-
ClassDB::register_virtual_class<JavaScript>();
javascript_eval = memnew(JavaScript);
Engine::get_singleton()->add_singleton(Engine::Singleton("JavaScript", javascript_eval));
}
void unregister_javascript_api() {
-
memdelete(javascript_eval);
}
-JavaScript *JavaScript::singleton = NULL;
+JavaScript *JavaScript::singleton = nullptr;
JavaScript *JavaScript::get_singleton() {
-
return singleton;
}
JavaScript::JavaScript() {
-
- ERR_FAIL_COND_MSG(singleton != NULL, "JavaScript singleton already exist.");
+ ERR_FAIL_COND_MSG(singleton != nullptr, "JavaScript singleton already exist.");
singleton = this;
}
JavaScript::~JavaScript() {}
void JavaScript::_bind_methods() {
-
ClassDB::bind_method(D_METHOD("eval", "code", "use_global_execution_context"), &JavaScript::eval, DEFVAL(false));
}
#if !defined(JAVASCRIPT_ENABLED) || !defined(JAVASCRIPT_EVAL_ENABLED)
Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
-
return Variant();
}
#endif
diff --git a/platform/javascript/api/api.h b/platform/javascript/api/api.h
index 164d679205..8afe0f33ce 100644
--- a/platform/javascript/api/api.h
+++ b/platform/javascript/api/api.h
@@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef JAVASCRIPT_API_H
+#define JAVASCRIPT_API_H
+
void register_javascript_api();
void unregister_javascript_api();
+
+#endif // JAVASCRIPT_API_H
diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp
index d63c6a40a5..b52bd4ce60 100644
--- a/platform/javascript/audio_driver_javascript.cpp
+++ b/platform/javascript/audio_driver_javascript.cpp
@@ -30,27 +30,25 @@
#include "audio_driver_javascript.h"
+#include "core/project_settings.h"
+
#include <emscripten.h>
-AudioDriverJavaScript *AudioDriverJavaScript::singleton = NULL;
+AudioDriverJavaScript *AudioDriverJavaScript::singleton = nullptr;
const char *AudioDriverJavaScript::get_name() const {
-
return "JavaScript";
}
extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_js_mix() {
-
AudioDriverJavaScript::singleton->mix_to_js();
}
extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_process_capture(float sample) {
-
AudioDriverJavaScript::singleton->process_capture(sample);
}
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);
@@ -61,17 +59,20 @@ void AudioDriverJavaScript::mix_to_js() {
}
void AudioDriverJavaScript::process_capture(float sample) {
-
int32_t sample32 = int32_t(sample * 32768.f) * (1U << 16);
input_buffer_write(sample32);
}
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),
+ 'context': new (window.AudioContext || window.webkitAudioContext)({ sampleRate: MIX_RATE, latencyHint: LATENCY}),
'input': null,
'stream': null,
'script': null
@@ -80,26 +81,19 @@ Error AudioDriverJavaScript::init() {
/* 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);
- var ctx = ref['context'];
- var CHANNEL_COUNT = $1;
-
- var channelCount = ctx.destination.channelCount;
- var script = null;
- try {
- // Try letting the browser recommend a buffer length.
- script = ctx.createScriptProcessor(0, 2, channelCount);
- } catch (e) {
- // ...otherwise, default to 4096.
- script = ctx.createScriptProcessor(4096, 2, channelCount);
- }
+ 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, channel_count);
+ }, _driver_id, buffer_length, channel_count);
/* clang-format on */
if (!buffer_length) {
return FAILED;
@@ -115,7 +109,6 @@ Error AudioDriverJavaScript::init() {
}
void AudioDriverJavaScript::start() {
-
/* clang-format off */
EM_ASM({
const ref = Module.IDHandler.get($0);
@@ -163,8 +156,26 @@ void AudioDriverJavaScript::resume() {
/* clang-format on */
}
-int AudioDriverJavaScript::get_mix_rate() const {
+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 */
+}
+int AudioDriverJavaScript::get_mix_rate() const {
/* clang-format off */
return EM_ASM_INT({
const ref = Module.IDHandler.get($0);
@@ -174,7 +185,6 @@ int AudioDriverJavaScript::get_mix_rate() const {
}
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);
@@ -190,23 +200,45 @@ void AudioDriverJavaScript::lock() {
void AudioDriverJavaScript::unlock() {
}
-void AudioDriverJavaScript::finish() {
+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);
- }, _driver_id);
+ }, id);
/* clang-format on */
+}
+void AudioDriverJavaScript::finish() {
if (internal_buffer) {
memdelete_arr(internal_buffer);
- internal_buffer = NULL;
+ internal_buffer = nullptr;
}
- _driver_id = 0;
}
Error AudioDriverJavaScript::capture_start() {
-
input_buffer_init(buffer_length);
/* clang-format off */
@@ -236,7 +268,6 @@ Error AudioDriverJavaScript::capture_start() {
}
Error AudioDriverJavaScript::capture_stop() {
-
/* clang-format off */
EM_ASM({
var ref = Module.IDHandler.get($0);
@@ -262,9 +293,8 @@ Error AudioDriverJavaScript::capture_stop() {
}
AudioDriverJavaScript::AudioDriverJavaScript() {
-
_driver_id = 0;
- internal_buffer = NULL;
+ internal_buffer = nullptr;
buffer_length = 0;
singleton = this;
diff --git a/platform/javascript/audio_driver_javascript.h b/platform/javascript/audio_driver_javascript.h
index f6f2dacd4e..9b26be001e 100644
--- a/platform/javascript/audio_driver_javascript.h
+++ b/platform/javascript/audio_driver_javascript.h
@@ -34,7 +34,6 @@
#include "servers/audio_server.h"
class AudioDriverJavaScript : public AudioDriver {
-
float *internal_buffer;
int _driver_id;
@@ -51,11 +50,13 @@ public:
virtual Error init();
virtual void start();
void resume();
+ virtual float get_latency();
virtual int get_mix_rate() const;
virtual SpeakerMode get_speaker_mode() const;
virtual void lock();
virtual void unlock();
virtual void finish();
+ void finish_async();
virtual Error capture_start();
virtual Error capture_stop();
diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py
index fb02752aa7..81287cead8 100644
--- a/platform/javascript/detect.py
+++ b/platform/javascript/detect.py
@@ -2,37 +2,39 @@ import os
from emscripten_helpers import parse_config, run_closure_compiler, create_engine_file
+
def is_active():
return True
def get_name():
- return 'JavaScript'
+ return "JavaScript"
def can_build():
- return 'EM_CONFIG' in os.environ or os.path.exists(os.path.expanduser('~/.emscripten'))
+ return "EM_CONFIG" in os.environ or os.path.exists(os.path.expanduser("~/.emscripten"))
def get_opts():
from SCons.Variables import BoolVariable
+
return [
# 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('use_closure_compiler', 'Use closure compiler to minimize Javascript code', False),
+ BoolVariable("javascript_eval", "Enable JavaScript eval interface", True),
+ BoolVariable("threads_enabled", "Enable WebAssembly Threads support (limited browser support)", False),
+ BoolVariable("use_closure_compiler", "Use closure compiler to minimize JavaScript code", False),
]
def get_flags():
return [
- ('tools', False),
- ('builtin_pcre2_with_jit', False),
+ ("tools", False),
+ ("builtin_pcre2_with_jit", False),
# Disabling the mbedtls module reduces file size.
# The module has little use due to the limited networking functionality
# in this platform. For the available networking methods, the browser
# manages TLS.
- ('module_mbedtls_enabled', False),
+ ("module_mbedtls_enabled", False),
]
@@ -40,125 +42,130 @@ def configure(env):
## Build type
- if env['target'] == 'release':
+ if env["target"] == "release":
# Use -Os to prioritize optimizing for reduced file size. This is
# particularly valuable for the web platform because it directly
# decreases download time.
# -Os reduces file size by around 5 MiB over -O3. -Oz only saves about
# 100 KiB over -Os, which does not justify the negative impact on
# run-time performance.
- env.Append(CCFLAGS=['-Os'])
- env.Append(LINKFLAGS=['-Os'])
- elif env['target'] == 'release_debug':
- env.Append(CCFLAGS=['-Os'])
- env.Append(LINKFLAGS=['-Os'])
- env.Append(CPPDEFINES=['DEBUG_ENABLED'])
+ env.Append(CCFLAGS=["-Os"])
+ env.Append(LINKFLAGS=["-Os"])
+ elif env["target"] == "release_debug":
+ env.Append(CCFLAGS=["-Os"])
+ env.Append(LINKFLAGS=["-Os"])
+ env.Append(CPPDEFINES=["DEBUG_ENABLED"])
# Retain function names for backtraces at the cost of file size.
- env.Append(LINKFLAGS=['--profiling-funcs'])
- else: # 'debug'
- env.Append(CPPDEFINES=['DEBUG_ENABLED'])
- env.Append(CCFLAGS=['-O1', '-g'])
- env.Append(LINKFLAGS=['-O1', '-g'])
- 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")
+ env.Append(LINKFLAGS=["--profiling-funcs"])
+ else: # "debug"
+ env.Append(CPPDEFINES=["DEBUG_ENABLED"])
+ env.Append(CCFLAGS=["-O1", "-g"])
+ env.Append(LINKFLAGS=["-O1", "-g"])
+ 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'])
+ env.Append(LINKFLAGS=["-s", "TOTAL_MEMORY=33554432"])
else:
# Disable exceptions and rtti on non-tools (template) builds
# These flags help keep the file size down.
- env.Append(CCFLAGS=['-fno-exceptions', '-fno-rtti'])
+ env.Append(CCFLAGS=["-fno-exceptions", "-fno-rtti"])
# Don't use dynamic_cast, necessary with no-rtti.
- env.Append(CPPDEFINES=['NO_SAFE_CAST'])
+ env.Append(CPPDEFINES=["NO_SAFE_CAST"])
## Copy env variables.
- env['ENV'] = os.environ
+ 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_lto"]:
+ env.Append(CCFLAGS=["-s", "WASM_OBJECT_FILES=0"])
+ env.Append(LINKFLAGS=["-s", "WASM_OBJECT_FILES=0"])
+ env.Append(LINKFLAGS=["--llvm-lto", "1"])
# Closure compiler
- if env['use_closure_compiler']:
+ if env["use_closure_compiler"]:
# For emscripten support code.
- env.Append(LINKFLAGS=['--closure', '1'])
+ env.Append(LINKFLAGS=["--closure", "1"])
# Register builder for our Engine files
- jscc = env.Builder(generator=run_closure_compiler, suffix='.cc.js', src_suffix='.js')
- env.Append(BUILDERS = {'BuildJS' : jscc})
+ jscc = env.Builder(generator=run_closure_compiler, suffix=".cc.js", src_suffix=".js")
+ env.Append(BUILDERS={"BuildJS": jscc})
# 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'
+ env["ENV"]["EMCC_CLOSURE_ARGS"] = "--language_in ECMASCRIPT6"
em_config = parse_config()
- env.PrependENVPath('PATH', em_config['EMCC_ROOT'])
+ env.PrependENVPath("PATH", em_config["EMCC_ROOT"])
- env['CC'] = 'emcc'
- env['CXX'] = 'em++'
- env['LINK'] = 'emcc'
+ env["CC"] = "emcc"
+ env["CXX"] = "em++"
+ env["LINK"] = "emcc"
- env['AR'] = 'emar'
- env['RANLIB'] = 'emranlib'
+ env["AR"] = "emar"
+ env["RANLIB"] = "emranlib"
# Use TempFileMunge since some AR invocations are too long for cmd.exe.
# Use POSIX-style paths, required with TempFileMunge.
- env['ARCOM_POSIX'] = env['ARCOM'].replace(
- '$TARGET', '$TARGET.posix').replace(
- '$SOURCES', '$SOURCES.posix')
- env['ARCOM'] = '${TEMPFILE(ARCOM_POSIX)}'
+ env["ARCOM_POSIX"] = env["ARCOM"].replace("$TARGET", "$TARGET.posix").replace("$SOURCES", "$SOURCES.posix")
+ env["ARCOM"] = "${TEMPFILE(ARCOM_POSIX)}"
# All intermediate files are just LLVM bitcode.
- env['OBJPREFIX'] = ''
- env['OBJSUFFIX'] = '.bc'
- env['PROGPREFIX'] = ''
+ env["OBJPREFIX"] = ""
+ env["OBJSUFFIX"] = ".bc"
+ env["PROGPREFIX"] = ""
# Program() output consists of multiple files, so specify suffixes manually at builder.
- env['PROGSUFFIX'] = ''
- env['LIBPREFIX'] = 'lib'
- env['LIBSUFFIX'] = '.bc'
- env['LIBPREFIXES'] = ['$LIBPREFIX']
- env['LIBSUFFIXES'] = ['$LIBSUFFIX']
+ env["PROGSUFFIX"] = ""
+ env["LIBPREFIX"] = "lib"
+ env["LIBSUFFIX"] = ".bc"
+ env["LIBPREFIXES"] = ["$LIBPREFIX"]
+ env["LIBSUFFIXES"] = ["$LIBSUFFIX"]
- env.Prepend(CPPPATH=['#platform/javascript'])
- env.Append(CPPDEFINES=['JAVASCRIPT_ENABLED', 'UNIX_ENABLED'])
+ env.Prepend(CPPPATH=["#platform/javascript"])
+ env.Append(CPPDEFINES=["JAVASCRIPT_ENABLED", "UNIX_ENABLED"])
- if env['javascript_eval']:
- env.Append(CPPDEFINES=['JAVASCRIPT_EVAL_ENABLED'])
+ if env["javascript_eval"]:
+ env.Append(CPPDEFINES=["JAVASCRIPT_EVAL_ENABLED"])
# 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', 'WASM_MEM_MAX=2048MB'])
+ 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", "WASM_MEM_MAX=2048MB"])
else:
- env.Append(CPPDEFINES=['NO_THREADS'])
+ env.Append(CPPDEFINES=["NO_THREADS"])
# Reduce code size by generating less support code (e.g. skip NodeJS support).
- env.Append(LINKFLAGS=['-s', 'ENVIRONMENT=web,worker'])
+ 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(LIBS=["idbfs.js"])
- env.Append(LINKFLAGS=['-s', 'BINARYEN=1'])
- env.Append(LINKFLAGS=['-s', 'MODULARIZE=1', '-s', 'EXPORT_NAME="Godot"'])
+ env.Append(LINKFLAGS=["-s", "BINARYEN=1"])
+ env.Append(LINKFLAGS=["-s", "MODULARIZE=1", "-s", "EXPORT_NAME='Godot'"])
# Allow increasing memory buffer size during runtime. This is efficient
# when using WebAssembly (in comparison to asm.js) and works well for
# us since we don't know requirements at compile-time.
- env.Append(LINKFLAGS=['-s', 'ALLOW_MEMORY_GROWTH=1'])
+ env.Append(LINKFLAGS=["-s", "ALLOW_MEMORY_GROWTH=1"])
# This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1.
- env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1'])
+ env.Append(LINKFLAGS=["-s", "USE_WEBGL2=1"])
+
+ env.Append(LINKFLAGS=["-s", "INVOKE_RUN=0"])
- 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.
- env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS=["callMain", "FS"]'])
+ # callMain for manual start, FS for preloading, PATH and ERRNO_CODES for BrowserFS.
+ env.Append(LINKFLAGS=["-s", "EXTRA_EXPORTED_RUNTIME_METHODS=['callMain', 'FS', 'PATH']"])
+ # Add code that allow exiting runtime.
+ env.Append(LINKFLAGS=["-s", "EXIT_RUNTIME=1"])
diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp
new file mode 100644
index 0000000000..b95674efc3
--- /dev/null
+++ b/platform/javascript/display_server_javascript.cpp
@@ -0,0 +1,1193 @@
+/*************************************************************************/
+/* display_server_javascript.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 "platform/javascript/display_server_javascript.h"
+
+#include "drivers/dummy/rasterizer_dummy.h"
+#include "platform/javascript/os_javascript.h"
+
+#include <emscripten.h>
+#include <png.h>
+
+#include "dom_keys.inc"
+
+#define DOM_BUTTON_LEFT 0
+#define DOM_BUTTON_MIDDLE 1
+#define DOM_BUTTON_RIGHT 2
+#define DOM_BUTTON_XBUTTON1 3
+#define DOM_BUTTON_XBUTTON2 4
+
+DisplayServerJavaScript *DisplayServerJavaScript::get_singleton() {
+ return static_cast<DisplayServerJavaScript *>(DisplayServer::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;
+}
+
+static void focus_canvas() {
+ /* clang-format off */
+ EM_ASM(
+ Module['canvas'].focus();
+ );
+ /* clang-format on */
+}
+
+static bool is_canvas_focused() {
+ /* clang-format off */
+ return EM_ASM_INT_V(
+ return document.activeElement == Module['canvas'];
+ );
+ /* clang-format on */
+}
+
+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)));
+}
+
+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) {
+ // This event property is the only reliable data on
+ // browser fullscreen state.
+ if (p_event->isFullscreen) {
+ display->window_mode = WINDOW_MODE_FULLSCREEN;
+ } else {
+ display->window_mode = WINDOW_MODE_WINDOWED;
+ }
+ }
+ 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();
+ if (!ds) {
+ ERR_FAIL_MSG("Unable to drop files because the DisplayServer is not active");
+ }
+ 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]));
+ }
+ Variant v = files;
+ Variant *vp = &v;
+ Variant ret;
+ Callable::CallError ce;
+ ds->drop_files_callback.call((const Variant **)&vp, 1, ret, ce);
+}
+
+// Keys
+
+template <typename T>
+static void 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> 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));
+
+ String unicode = String::utf8(emscripten_event->key);
+ // Check if empty or multi-character (e.g. `CapsLock`).
+ if (unicode.length() != 1) {
+ // Might be empty as well, but better than nonsense.
+ unicode = String::utf8(emscripten_event->charValue);
+ }
+ if (unicode.length() == 1) {
+ ev->set_unicode(unicode[0]);
+ }
+
+ return ev;
+}
+
+EM_BOOL DisplayServerJavaScript::keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) {
+ DisplayServerJavaScript *display = get_singleton();
+ Ref<InputEventKey> ev = setup_key_event(p_event);
+ ev->set_pressed(true);
+ if (ev->get_unicode() == 0 && keycode_has_unicode(ev->get_keycode())) {
+ // Defer to keypress event for legacy unicode retrieval.
+ display->deferred_key_event = ev;
+ // Do not suppress keypress event.
+ return false;
+ }
+ Input::get_singleton()->parse_input_event(ev);
+ return true;
+}
+
+EM_BOOL DisplayServerJavaScript::keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) {
+ DisplayServerJavaScript *display = get_singleton();
+ display->deferred_key_event->set_unicode(p_event->charCode);
+ Input::get_singleton()->parse_input_event(display->deferred_key_event);
+ return true;
+}
+
+EM_BOOL DisplayServerJavaScript::keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) {
+ Ref<InputEventKey> ev = setup_key_event(p_event);
+ ev->set_pressed(false);
+ Input::get_singleton()->parse_input_event(ev);
+ return ev->get_keycode() != KEY_UNKNOWN && ev->get_keycode() != 0;
+}
+
+// Mouse
+
+EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) {
+ DisplayServerJavaScript *display = get_singleton();
+
+ Ref<InputEventMouseButton> ev;
+ ev.instance();
+ ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_MOUSEDOWN);
+ ev->set_position(compute_position_in_canvas(p_event->clientX, p_event->clientY));
+ ev->set_global_position(ev->get_position());
+ dom2godot_mod(p_event, ev);
+
+ switch (p_event->button) {
+ case DOM_BUTTON_LEFT:
+ ev->set_button_index(BUTTON_LEFT);
+ break;
+ case DOM_BUTTON_MIDDLE:
+ ev->set_button_index(BUTTON_MIDDLE);
+ break;
+ case DOM_BUTTON_RIGHT:
+ ev->set_button_index(BUTTON_RIGHT);
+ break;
+ case DOM_BUTTON_XBUTTON1:
+ ev->set_button_index(BUTTON_XBUTTON1);
+ break;
+ case DOM_BUTTON_XBUTTON2:
+ ev->set_button_index(BUTTON_XBUTTON2);
+ break;
+ default:
+ return false;
+ }
+
+ if (ev->is_pressed()) {
+ double diff = emscripten_get_now() - display->last_click_ms;
+
+ if (ev->get_button_index() == display->last_click_button_index) {
+ if (diff < 400 && Point2(display->last_click_pos).distance_to(ev->get_position()) < 5) {
+ display->last_click_ms = 0;
+ display->last_click_pos = Point2(-100, -100);
+ display->last_click_button_index = -1;
+ ev->set_doubleclick(true);
+ }
+
+ } else {
+ display->last_click_button_index = ev->get_button_index();
+ }
+
+ if (!ev->is_doubleclick()) {
+ display->last_click_ms += diff;
+ display->last_click_pos = ev->get_position();
+ }
+ }
+
+ Input *input = Input::get_singleton();
+ int mask = input->get_mouse_button_mask();
+ int button_flag = 1 << (ev->get_button_index() - 1);
+ if (ev->is_pressed()) {
+ // Since the event is consumed, focus manually. The containing iframe,
+ // if exists, may not have focus yet, so focus even if already focused.
+ focus_canvas();
+ mask |= button_flag;
+ } else if (mask & button_flag) {
+ mask &= ~button_flag;
+ } else {
+ // Received release event, but press was outside the canvas, so ignore.
+ return false;
+ }
+ ev->set_button_mask(mask);
+
+ input->parse_input_event(ev);
+ // Prevent multi-click text selection and wheel-click scrolling anchor.
+ // Context menu is prevented through contextmenu event.
+ return true;
+}
+
+EM_BOOL DisplayServerJavaScript::mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) {
+ 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)
+ return false;
+
+ Ref<InputEventMouseMotion> ev;
+ ev.instance();
+ dom2godot_mod(p_event, ev);
+ ev->set_button_mask(input_mask);
+
+ ev->set_position(pos);
+ ev->set_global_position(ev->get_position());
+
+ ev->set_relative(Vector2(p_event->movementX, p_event->movementY));
+ input->set_mouse_position(ev->get_position());
+ ev->set_speed(input->get_last_mouse_speed());
+
+ input->parse_input_event(ev);
+ // Don't suppress mouseover/-leave events.
+ return false;
+}
+
+// Cursor
+static const char *godot2dom_cursor(DisplayServer::CursorShape p_shape) {
+ switch (p_shape) {
+ case DisplayServer::CURSOR_ARROW:
+ return "auto";
+ case DisplayServer::CURSOR_IBEAM:
+ return "text";
+ case DisplayServer::CURSOR_POINTING_HAND:
+ return "pointer";
+ case DisplayServer::CURSOR_CROSS:
+ return "crosshair";
+ case DisplayServer::CURSOR_WAIT:
+ return "progress";
+ case DisplayServer::CURSOR_BUSY:
+ return "wait";
+ case DisplayServer::CURSOR_DRAG:
+ return "grab";
+ case DisplayServer::CURSOR_CAN_DROP:
+ return "grabbing";
+ case DisplayServer::CURSOR_FORBIDDEN:
+ return "no-drop";
+ case DisplayServer::CURSOR_VSIZE:
+ return "ns-resize";
+ case DisplayServer::CURSOR_HSIZE:
+ return "ew-resize";
+ case DisplayServer::CURSOR_BDIAGSIZE:
+ return "nesw-resize";
+ case DisplayServer::CURSOR_FDIAGSIZE:
+ return "nwse-resize";
+ case DisplayServer::CURSOR_MOVE:
+ return "move";
+ case DisplayServer::CURSOR_VSPLIT:
+ return "row-resize";
+ case DisplayServer::CURSOR_HSPLIT:
+ return "col-resize";
+ case DisplayServer::CURSOR_HELP:
+ return "help";
+ default:
+ return "auto";
+ }
+}
+
+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));
+ }
+ }
+
+ cursor_shape = p_shape;
+}
+
+DisplayServer::CursorShape DisplayServerJavaScript::cursor_get_shape() const {
+ return cursor_shape;
+}
+
+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;
+ Size2 texture_size;
+ Rect2 atlas_rect;
+
+ if (texture.is_valid()) {
+ image = texture->get_data();
+ }
+
+ if (!image.is_valid() && atlas_texture.is_valid()) {
+ texture = atlas_texture->get_atlas();
+
+ atlas_rect.size.width = texture->get_width();
+ atlas_rect.size.height = texture->get_height();
+ atlas_rect.position.x = atlas_texture->get_region().position.x;
+ atlas_rect.position.y = atlas_texture->get_region().position.y;
+
+ texture_size.width = atlas_texture->get_region().size.x;
+ texture_size.height = atlas_texture->get_region().size.y;
+ } else if (image.is_valid()) {
+ texture_size.width = texture->get_width();
+ texture_size.height = texture->get_height();
+ }
+
+ ERR_FAIL_COND(!texture.is_valid());
+ ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
+ ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
+ ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
+
+ image = texture->get_data();
+
+ ERR_FAIL_COND(!image.is_valid());
+
+ image = image->duplicate();
+
+ if (atlas_texture.is_valid())
+ image->crop_from_point(
+ atlas_rect.position.x,
+ atlas_rect.position.y,
+ texture_size.width,
+ texture_size.height);
+
+ if (image->get_format() != Image::FORMAT_RGBA8) {
+ image->convert(Image::FORMAT_RGBA8);
+ }
+
+ png_image png_meta;
+ memset(&png_meta, 0, sizeof png_meta);
+ png_meta.version = PNG_IMAGE_VERSION;
+ png_meta.width = texture_size.width;
+ png_meta.height = texture_size.height;
+ png_meta.format = PNG_FORMAT_RGBA;
+
+ PackedByteArray png;
+ size_t len;
+ PackedByteArray data = image->get_data();
+ ERR_FAIL_COND(!png_image_write_get_memory_size(png_meta, len, 0, data.ptr(), 0, nullptr));
+
+ 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] = "";
+ }
+
+ 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);
+ }
+
+ cursor_set_shape(cursor_shape);
+}
+
+// Mouse mode
+void DisplayServerJavaScript::mouse_set_mode(MouseMode p_mode) {
+ ERR_FAIL_COND_MSG(p_mode == MOUSE_MODE_CONFINED, "MOUSE_MODE_CONFINED is not supported for the HTML5 platform.");
+ if (p_mode == mouse_get_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);
+ emscripten_exit_pointerlock();
+
+ } else if (p_mode == MOUSE_MODE_HIDDEN) {
+ set_css_cursor("none");
+ emscripten_exit_pointerlock();
+
+ } else if (p_mode == MOUSE_MODE_CAPTURED) {
+ EMSCRIPTEN_RESULT result = emscripten_request_pointerlock("canvas", 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())
+ 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;
+}
+
+// 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);
+ if (!is_canvas_focused()) {
+ if (cursor_inside_canvas) {
+ focus_canvas();
+ } else {
+ return false;
+ }
+ }
+
+ Input *input = Input::get_singleton();
+ Ref<InputEventMouseButton> ev;
+ ev.instance();
+ ev->set_position(input->get_mouse_position());
+ ev->set_global_position(ev->get_position());
+
+ ev->set_shift(input->is_key_pressed(KEY_SHIFT));
+ ev->set_alt(input->is_key_pressed(KEY_ALT));
+ ev->set_control(input->is_key_pressed(KEY_CONTROL));
+ ev->set_metakey(input->is_key_pressed(KEY_META));
+
+ if (p_event->deltaY < 0)
+ ev->set_button_index(BUTTON_WHEEL_UP);
+ else if (p_event->deltaY > 0)
+ ev->set_button_index(BUTTON_WHEEL_DOWN);
+ else if (p_event->deltaX > 0)
+ ev->set_button_index(BUTTON_WHEEL_LEFT);
+ else if (p_event->deltaX < 0)
+ ev->set_button_index(BUTTON_WHEEL_RIGHT);
+ else
+ return false;
+
+ // Different browsers give wildly different delta values, and we can't
+ // interpret deltaMode, so use default value for wheel events' factor.
+
+ int button_flag = 1 << (ev->get_button_index() - 1);
+
+ ev->set_pressed(true);
+ ev->set_button_mask(input->get_mouse_button_mask() | button_flag);
+ input->parse_input_event(ev);
+
+ ev->set_pressed(false);
+ ev->set_button_mask(input->get_mouse_button_mask() & ~button_flag);
+ input->parse_input_event(ev);
+
+ return true;
+}
+
+// Touch
+EM_BOOL DisplayServerJavaScript::touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) {
+ DisplayServerJavaScript *display = get_singleton();
+ Ref<InputEventScreenTouch> ev;
+ ev.instance();
+ int lowest_id_index = -1;
+ for (int i = 0; i < p_event->numTouches; ++i) {
+ const EmscriptenTouchPoint &touch = p_event->touches[i];
+ if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier)
+ lowest_id_index = i;
+ if (!touch.isChanged)
+ continue;
+ ev->set_index(touch.identifier);
+ ev->set_position(compute_position_in_canvas(touch.clientX, touch.clientY));
+ display->touches[i] = ev->get_position();
+ ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_TOUCHSTART);
+
+ Input::get_singleton()->parse_input_event(ev);
+ }
+ // Resume audio context after input in case autoplay was denied.
+ return true;
+}
+
+EM_BOOL DisplayServerJavaScript::touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) {
+ DisplayServerJavaScript *display = get_singleton();
+ Ref<InputEventScreenDrag> ev;
+ ev.instance();
+ int lowest_id_index = -1;
+ for (int i = 0; i < p_event->numTouches; ++i) {
+ const EmscriptenTouchPoint &touch = p_event->touches[i];
+ if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier)
+ lowest_id_index = i;
+ if (!touch.isChanged)
+ continue;
+ ev->set_index(touch.identifier);
+ ev->set_position(compute_position_in_canvas(touch.clientX, touch.clientY));
+ Point2 &prev = display->touches[i];
+ ev->set_relative(ev->get_position() - prev);
+ prev = ev->get_position();
+
+ Input::get_singleton()->parse_input_event(ev);
+ }
+ return true;
+}
+
+bool DisplayServerJavaScript::screen_is_touchscreen(int p_screen) const {
+ return EM_ASM_INT({ return 'ontouchstart' in window; });
+}
+
+// Gamepad
+
+EM_BOOL DisplayServerJavaScript::gamepad_change_callback(int p_event_type, const EmscriptenGamepadEvent *p_event, void *p_user_data) {
+ 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);
+ } else {
+ input->joy_connection_changed(p_event->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++) {
+ Input::JoyAxis joy_axis;
+ joy_axis.min = -1;
+ joy_axis.value = state.axis[axis];
+ input->joy_axis(joypad, axis, 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");
+ return drivers;
+}
+
+// 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::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 */
+ 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 */
+ return clipboard;
+}
+
+extern "C" EMSCRIPTEN_KEEPALIVE void send_window_event(int p_notification) {
+ 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;
+ }
+ 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()) {
+ Variant event = int(p_notification);
+ Variant *eventp = &event;
+ Variant ret;
+ Callable::CallError ce;
+ ds->window_event_callback.call((const Variant **)&eventp, 1, ret, ce);
+ }
+}
+
+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 */
+}
+
+void DisplayServerJavaScript::set_icon(const Ref<Image> &p_icon) {
+ ERR_FAIL_COND(p_icon.is_null());
+ Ref<Image> icon = p_icon;
+ if (icon->is_compressed()) {
+ icon = icon->duplicate();
+ ERR_FAIL_COND(icon->decompress() != OK);
+ }
+ if (icon->get_format() != Image::FORMAT_RGBA8) {
+ if (icon == p_icon)
+ icon = icon->duplicate();
+ icon->convert(Image::FORMAT_RGBA8);
+ }
+
+ png_image png_meta;
+ memset(&png_meta, 0, sizeof png_meta);
+ png_meta.version = PNG_IMAGE_VERSION;
+ png_meta.width = icon->get_width();
+ png_meta.height = icon->get_height();
+ png_meta.format = PNG_FORMAT_RGBA;
+
+ PackedByteArray png;
+ size_t len;
+ PackedByteArray data = icon->get_data();
+ ERR_FAIL_COND(!png_image_write_get_memory_size(png_meta, len, 0, data.ptr(), 0, nullptr));
+
+ 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 */
+}
+
+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();
+
+ Callable cb = get_singleton()->input_event_callback;
+ if (!cb.is_null()) {
+ Variant ev = p_event;
+ Variant *evp = &ev;
+ Variant ret;
+ Callable::CallError ce;
+ cb.call((const Variant **)&evp, 1, ret, ce);
+ }
+}
+
+DisplayServer *DisplayServerJavaScript::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ return memnew(DisplayServerJavaScript(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+}
+
+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 */
+
+ RasterizerDummy::make_current(); // TODO GLES2 in Godot 4.0... or webgpu?
+#if 0
+ EmscriptenWebGLContextAttributes attributes;
+ emscripten_webgl_init_context_attributes(&attributes);
+ attributes.alpha = GLOBAL_GET("display/window/per_pixel_transparency/allowed");
+ attributes.antialias = false;
+ ERR_FAIL_INDEX_V(p_video_driver, VIDEO_DRIVER_MAX, ERR_INVALID_PARAMETER);
+
+ if (p_desired.layered) {
+ set_window_per_pixel_transparency_enabled(true);
+ }
+
+ bool gl_initialization_error = false;
+
+ if (RasterizerGLES2::is_viable() == OK) {
+ attributes.majorVersion = 1;
+ RasterizerGLES2::register_config();
+ RasterizerGLES2::make_current();
+ } else {
+ gl_initialization_error = true;
+ }
+
+ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(canvas_id.utf8().get_data(), &attributes);
+ if (emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS) {
+ gl_initialization_error = true;
+ }
+
+ if (gl_initialization_error) {
+ OS::get_singleton()->alert("Your browser does not seem to support WebGL. Please update your browser version.",
+ "Unable to initialize video driver");
+ return ERR_UNAVAILABLE;
+ }
+
+ 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); \
+ 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)
+ 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 */
+
+ 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);
+}
+
+bool DisplayServerJavaScript::has_feature(Feature p_feature) const {
+ switch (p_feature) {
+ //case FEATURE_CONSOLE_WINDOW:
+ //case FEATURE_GLOBAL_MENU:
+ //case FEATURE_HIDPI:
+ //case FEATURE_IME:
+ case FEATURE_ICON:
+ case FEATURE_CLIPBOARD:
+ case FEATURE_CURSOR_SHAPE:
+ case FEATURE_CUSTOM_CURSOR_SHAPE:
+ case FEATURE_MOUSE:
+ case FEATURE_TOUCHSCREEN:
+ return true;
+ //case FEATURE_MOUSE_WARP:
+ //case FEATURE_NATIVE_DIALOG:
+ //case FEATURE_NATIVE_ICON:
+ //case FEATURE_NATIVE_VIDEO:
+ //case FEATURE_WINDOW_TRANSPARENCY:
+ //case FEATURE_KEEP_SCREEN_ON:
+ //case FEATURE_ORIENTATION:
+ //case FEATURE_VIRTUAL_KEYBOARD:
+ default:
+ return false;
+ }
+}
+
+void DisplayServerJavaScript::register_javascript_driver() {
+ register_create_function("javascript", create_func, get_rendering_drivers_func);
+}
+
+String DisplayServerJavaScript::get_name() const {
+ return "javascript";
+}
+
+int DisplayServerJavaScript::get_screen_count() const {
+ return 1;
+}
+
+Point2i DisplayServerJavaScript::screen_get_position(int p_screen) const {
+ return Point2i(); // TODO offsetX/Y?
+}
+
+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);
+}
+
+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 DisplayServerJavaScript::screen_get_dpi(int p_screen) const {
+ return 96; // TODO maybe check pixel ratio via window.devicePixelRatio * 96? Inexact.
+}
+
+Vector<DisplayServer::WindowID> DisplayServerJavaScript::get_window_list() const {
+ Vector<WindowID> ret;
+ ret.push_back(MAIN_WINDOW_ID);
+ return ret;
+}
+
+DisplayServerJavaScript::WindowID DisplayServerJavaScript::get_window_at_screen_position(const Point2i &p_position) const {
+ return MAIN_WINDOW_ID;
+}
+
+void DisplayServerJavaScript::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
+ window_attached_instance_id = p_instance;
+}
+
+ObjectID DisplayServerJavaScript::window_get_attached_instance_id(WindowID p_window) const {
+ return window_attached_instance_id;
+}
+
+void DisplayServerJavaScript::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
+ // Not supported.
+}
+
+void DisplayServerJavaScript::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
+ window_event_callback = p_callable;
+}
+
+void DisplayServerJavaScript::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
+ input_event_callback = p_callable;
+}
+
+void DisplayServerJavaScript::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
+ input_text_callback = p_callable; // TODO unused... do I need this?
+}
+
+void DisplayServerJavaScript::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
+ drop_files_callback = p_callable;
+}
+
+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 */
+}
+
+int DisplayServerJavaScript::window_get_current_screen(WindowID p_window) const {
+ return 1;
+}
+
+void DisplayServerJavaScript::window_set_current_screen(int p_screen, WindowID p_window) {
+ // Not implemented.
+}
+
+Point2i DisplayServerJavaScript::window_get_position(WindowID p_window) const {
+ return Point2i(); // TODO Does this need implementation?
+}
+
+void DisplayServerJavaScript::window_set_position(const Point2i &p_position, WindowID p_window) {
+ // Not supported.
+}
+
+void DisplayServerJavaScript::window_set_transient(WindowID p_window, WindowID p_parent) {
+ // Not supported.
+}
+
+void DisplayServerJavaScript::window_set_max_size(const Size2i p_size, WindowID p_window) {
+ // Not supported.
+}
+
+Size2i DisplayServerJavaScript::window_get_max_size(WindowID p_window) const {
+ return Size2i();
+}
+
+void DisplayServerJavaScript::window_set_min_size(const Size2i p_size, WindowID p_window) {
+ // Not supported.
+}
+
+Size2i DisplayServerJavaScript::window_get_min_size(WindowID p_window) const {
+ return Size2i();
+}
+
+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);
+}
+
+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]);
+}
+
+Size2i DisplayServerJavaScript::window_get_real_size(WindowID p_window) const {
+ return window_get_size(p_window);
+}
+
+void DisplayServerJavaScript::window_set_mode(WindowMode p_mode, WindowID p_window) {
+ if (window_mode == p_mode)
+ return;
+
+ switch (p_mode) {
+ case WINDOW_MODE_WINDOWED: {
+ if (window_mode == WINDOW_MODE_FULLSCREEN) {
+ emscripten_exit_fullscreen();
+ }
+ 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.");
+ } break;
+ case WINDOW_MODE_MAXIMIZED:
+ case WINDOW_MODE_MINIMIZED:
+ WARN_PRINT("WindowMode MAXIMIZED and MINIMIZED are not supported in HTML5 platform.");
+ break;
+ default:
+ break;
+ }
+}
+
+DisplayServerJavaScript::WindowMode DisplayServerJavaScript::window_get_mode(WindowID p_window) const {
+ return window_mode;
+}
+
+bool DisplayServerJavaScript::window_is_maximize_allowed(WindowID p_window) const {
+ return false;
+}
+
+void DisplayServerJavaScript::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
+ // Not supported.
+}
+
+bool DisplayServerJavaScript::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
+ return false;
+}
+
+void DisplayServerJavaScript::window_request_attention(WindowID p_window) {
+ // Not supported.
+}
+
+void DisplayServerJavaScript::window_move_to_foreground(WindowID p_window) {
+ // Not supported.
+}
+
+bool DisplayServerJavaScript::window_can_draw(WindowID p_window) const {
+ return true;
+}
+
+bool DisplayServerJavaScript::can_any_window_draw() const {
+ return true;
+}
+
+void DisplayServerJavaScript::process_events() {
+ if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS)
+ process_joypads();
+}
+
+int DisplayServerJavaScript::get_current_video_driver() const {
+ return 1;
+}
+
+void DisplayServerJavaScript::swap_buffers() {
+ //emscripten_webgl_commit_frame();
+}
diff --git a/platform/javascript/display_server_javascript.h b/platform/javascript/display_server_javascript.h
new file mode 100644
index 0000000000..9860ecdf98
--- /dev/null
+++ b/platform/javascript/display_server_javascript.h
@@ -0,0 +1,187 @@
+/*************************************************************************/
+/* display_server_javascript.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 DISPLAY_SERVER_JAVASCRIPT_H
+#define DISPLAY_SERVER_JAVASCRIPT_H
+
+#include "servers/display_server.h"
+
+#include <emscripten.h>
+#include <emscripten/html5.h>
+
+class DisplayServerJavaScript : public DisplayServer {
+ //int video_driver_index;
+
+ Vector2 windowed_size;
+
+ ObjectID window_attached_instance_id = {};
+
+ Ref<InputEventKey> deferred_key_event;
+ CursorShape cursor_shape = CURSOR_ARROW;
+ String cursors[CURSOR_MAX];
+ Map<CursorShape, Vector<Variant>> cursors_cache;
+ Point2 touches[32];
+
+ Point2i last_click_pos = Point2(-100, -100); // TODO check this again.
+ double last_click_ms = 0;
+ int last_click_button_index = -1;
+
+ 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);
+ static EM_BOOL keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
+ static EM_BOOL keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
+
+ static EM_BOOL mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data);
+ static EM_BOOL mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data);
+
+ static EM_BOOL wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data);
+
+ 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);
+ void process_joypads();
+
+ static Vector<String> get_rendering_drivers_func();
+ static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+
+ static void _dispatch_input_event(const Ref<InputEvent> &p_event);
+
+protected:
+ virtual 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;
+
+ // 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;
+
+ // 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());
+
+ // mouse
+ virtual void mouse_set_mode(MouseMode p_mode);
+ virtual MouseMode mouse_get_mode() const;
+
+ // touch
+ virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+
+ // clipboard
+ virtual void clipboard_set(const String &p_text);
+ virtual String clipboard_get() const;
+
+ // 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;
+
+ // windows
+ virtual Vector<DisplayServer::WindowID> get_window_list() const;
+ virtual WindowID get_window_at_screen_position(const Point2i &p_position) const;
+
+ 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_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_title(const String &p_title, 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);
+
+ 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 void window_set_transient(WindowID p_window, WindowID p_parent);
+
+ 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_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_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.
+
+ 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 bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ 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_request_attention(WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual bool can_any_window_draw() const;
+
+ // events
+ virtual void process_events();
+
+ // icon
+ virtual void set_icon(const Ref<Image> &p_icon);
+
+ // others
+ virtual void swap_buffers();
+
+ 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);
+ ~DisplayServerJavaScript();
+};
+
+#endif // DISPLAY_SERVER_JAVASCRIPT_H
diff --git a/platform/javascript/dom_keys.inc b/platform/javascript/dom_keys.inc
index fd9df765d2..882e943471 100644
--- a/platform/javascript/dom_keys.inc
+++ b/platform/javascript/dom_keys.inc
@@ -219,7 +219,6 @@
#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;
@@ -237,9 +236,12 @@ int dom2godot_keycode(int dom_keycode) {
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_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
@@ -249,14 +251,17 @@ int dom2godot_keycode(int dom_keycode) {
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_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_PAUSE:
+ return KEY_PAUSE;
case DOM_VK_CAPS_LOCK:
return KEY_CAPSLOCK;
@@ -279,14 +284,22 @@ int dom2godot_keycode(int dom_keycode) {
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_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;
@@ -297,24 +310,31 @@ int dom2godot_keycode(int dom_keycode) {
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_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_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_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_SUBTRACT:
+ return KEY_KP_SUBTRACT;
+ case DOM_VK_DECIMAL:
+ return KEY_KP_PERIOD;
case DOM_VK_DIVIDE:
return KEY_KP_DIVIDE;
@@ -329,7 +349,8 @@ int dom2godot_keycode(int dom_keycode) {
case DOM_VK_F24: return KEY_UNKNOWN;
*/
- case DOM_VK_NUM_LOCK: return KEY_NUMLOCK;
+ case DOM_VK_NUM_LOCK:
+ return KEY_NUMLOCK;
case DOM_VK_SCROLL_LOCK:
return KEY_SCROLLLOCK;
@@ -341,40 +362,68 @@ int dom2godot_keycode(int dom_keycode) {
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_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_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_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;
+ default:
+ return KEY_UNKNOWN;
};
}
diff --git a/platform/javascript/emscripten_helpers.py b/platform/javascript/emscripten_helpers.py
index bda5b40a74..a55c9d3f48 100644
--- a/platform/javascript/emscripten_helpers.py
+++ b/platform/javascript/emscripten_helpers.py
@@ -1,7 +1,8 @@
import os
+
def parse_config():
- em_config_file = os.getenv('EM_CONFIG') or os.path.expanduser('~/.emscripten')
+ 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)
@@ -13,25 +14,25 @@ def parse_config():
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')
+ 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
def run_closure_compiler(target, source, env, for_signature):
cfg = parse_config()
- cmd = [cfg['NODE_JS'], cfg['CLOSURE_BIN']]
- cmd.extend(['--compilation_level', 'ADVANCED_OPTIMIZATIONS'])
- for f in env['JSEXTERNS']:
- cmd.extend(['--externs', f.get_abspath()])
+ cmd = [cfg["NODE_JS"], cfg["CLOSURE_BIN"]]
+ cmd.extend(["--compilation_level", "ADVANCED_OPTIMIZATIONS"])
+ for f in env["JSEXTERNS"]:
+ cmd.extend(["--externs", f.get_abspath()])
for f in source:
- cmd.extend(['--js', f.get_abspath()])
- cmd.extend(['--js_output_file', target[0].get_abspath()])
- return ' '.join(cmd)
+ cmd.extend(["--js", f.get_abspath()])
+ cmd.extend(["--js_output_file", target[0].get_abspath()])
+ return " ".join(cmd)
def create_engine_file(env, target, source, externs):
- if env['use_closure_compiler']:
+ if env["use_closure_compiler"]:
return env.BuildJS(target, source, JSEXTERNS=externs)
return env.Textfile(target, [env.File(s) for s in source])
diff --git a/platform/javascript/engine/engine.js b/platform/javascript/engine/engine.js
index 6d7509377f..d709422abb 100644
--- a/platform/javascript/engine/engine.js
+++ b/platform/javascript/engine/engine.js
@@ -1,16 +1,8 @@
Function('return this')()['Engine'] = (function() {
-
- var unloadAfterInit = true;
- var canvas = null;
- var resizeCanvasOnStart = false;
- var customLocale = 'en_US';
- var wasmExt = '.wasm';
-
var preloader = new Preloader();
- var loader = new Loader();
- var rtenv = null;
- var executableName = '';
+ var wasmExt = '.wasm';
+ var unloadAfterInit = true;
var loadPath = '';
var loadPromise = null;
var initPromise = null;
@@ -33,31 +25,43 @@ Function('return this')()['Engine'] = (function() {
};
/** @constructor */
- function Engine() {};
+ 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) {
+ 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 = {}
+ var config = {};
if (typeof stdout === 'function')
config.print = stdout;
if (typeof stderr === 'function')
config.printErr = stderr;
- initPromise = loader.init(loadPromise, loadPath, config).then(function() {
- return new Promise(function(resolve, reject) {
- rtenv = loader.env;
+ 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) {
- loadPromise = null;
+ unload();
}
resolve();
+ config = null;
});
});
return initPromise;
@@ -76,33 +80,72 @@ Function('return this')()['Engine'] = (function() {
args.push(arguments[i]);
}
var me = this;
- return new Promise(function(resolve, reject) {
- return me.init().then(function() {
- if (!(canvas instanceof HTMLCanvasElement)) {
- canvas = Utils.findCanvas();
- }
- rtenv['locale'] = customLocale;
- rtenv['canvas'] = canvas;
- rtenv['thisProgram'] = executableName;
- rtenv['resizeCanvasOnStart'] = resizeCanvasOnStart;
- loader.start(preloader.preloadedFiles, args).then(function() {
- loader = null;
- initPromise = null;
- resolve();
+ 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) {
+ Engine.prototype.startGame = function(execName, mainPack, extraArgs) {
// Start and init with execName as loadPath if not inited.
- executableName = execName;
+ this.executableName = execName;
var me = this;
return Promise.all([
this.init(execName),
this.preloadFile(mainPack, mainPack)
]).then(function() {
- return me.start('--main-pack', mainPack);
+ var args = ['--main-pack', mainPack];
+ if (extraArgs)
+ args = args.concat(extraArgs);
+ return me.start.apply(me, args);
});
};
@@ -118,67 +161,85 @@ Function('return this')()['Engine'] = (function() {
};
Engine.prototype.setCanvas = function(canvasElem) {
- canvas = canvasElem;
+ this.canvas = canvasElem;
};
Engine.prototype.setCanvasResizedOnStart = function(enabled) {
- resizeCanvasOnStart = enabled;
+ this.resizeCanvasOnStart = enabled;
};
Engine.prototype.setLocale = function(locale) {
- customLocale = locale;
+ this.customLocale = locale;
};
Engine.prototype.setExecutableName = function(newName) {
- executableName = 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 (rtenv)
- rtenv.print = print;
+ 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 (rtenv)
- rtenv.printErr = printErr;
+ 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['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/loader.js b/platform/javascript/engine/loader.js
deleted file mode 100644
index d27fbf612e..0000000000
--- a/platform/javascript/engine/loader.js
+++ /dev/null
@@ -1,33 +0,0 @@
-var Loader = /** @constructor */ function() {
-
- this.env = null;
-
- this.init = function(loadPromise, basePath, config) {
- var me = this;
- return new Promise(function(resolve, reject) {
- var cfg = config || {};
- cfg['locateFile'] = Utils.createLocateRewrite(basePath);
- cfg['instantiateWasm'] = Utils.createInstantiatePromise(loadPromise);
- loadPromise = null;
- Godot(cfg).then(function(module) {
- me.env = module;
- resolve();
- });
- });
- }
-
- this.start = function(preloadedFiles, args) {
- var me = this;
- return new Promise(function(resolve, reject) {
- if (!me.env) {
- reject(new Error('The engine must be initialized before it can be started'));
- }
- preloadedFiles.forEach(function(file) {
- Utils.copyToFS(me.env['FS'], file.path, file.buffer);
- });
- preloadedFiles.length = 0; // Clear memory
- me.env['callMain'](args);
- resolve();
- });
- }
-};
diff --git a/platform/javascript/engine/utils.js b/platform/javascript/engine/utils.js
index fdff90a923..0c97b38199 100644
--- a/platform/javascript/engine/utils.js
+++ b/platform/javascript/engine/utils.js
@@ -27,24 +27,6 @@ var Utils = {
return instantiateWasm;
},
- copyToFS: function(fs, 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 !== 44) { // '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+'});
- },
-
findCanvas: function() {
var nodes = document.getElementsByTagName('canvas');
if (nodes.length && nodes[0] instanceof HTMLCanvasElement) {
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index da61425747..3573ddac95 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -28,6 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#include "core/io/json.h"
#include "core/io/tcp_server.h"
#include "core/io/zip_io.h"
#include "editor/editor_export.h"
@@ -40,7 +41,6 @@
#define EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG "webassembly_debug.zip"
class EditorHTTPServer : public Reference {
-
private:
Ref<TCP_Server> server;
Ref<StreamPeerTCP> connection;
@@ -148,11 +148,13 @@ public:
}
void poll() {
- if (!server->is_listening())
+ if (!server->is_listening()) {
return;
+ }
if (connection.is_null()) {
- if (!server->is_connection_available())
+ if (!server->is_connection_available()) {
return;
+ }
connection = server->take_connection();
time = OS::get_singleton()->get_ticks_usec();
}
@@ -160,11 +162,11 @@ public:
_clear_client();
return;
}
- if (connection->get_status() != StreamPeerTCP::STATUS_CONNECTED)
+ if (connection->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
return;
+ }
while (true) {
-
char *r = (char *)req_buf;
int l = req_pos - 1;
if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
@@ -190,7 +192,6 @@ public:
};
class EditorExportPlatformJavaScript : public EditorExportPlatform {
-
GDCLASS(EditorExportPlatformJavaScript, EditorExportPlatform);
Ref<ImageTexture> logo;
@@ -198,7 +199,7 @@ class EditorExportPlatformJavaScript : public EditorExportPlatform {
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);
+ void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags);
private:
Ref<EditorHTTPServer> server;
@@ -230,7 +231,6 @@ public:
virtual Ref<Texture2D> get_run_icon() const;
virtual void get_platform_features(List<String> *r_features) {
-
r_features->push_back("web");
r_features->push_back(get_os_name());
}
@@ -238,22 +238,28 @@ public:
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) {
}
+ String get_debug_protocol() const { 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) {
-
+void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags) {
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");
+ Vector<String> flags;
+ String flags_json;
+ gen_export_flags(flags, p_flags);
+ flags_json = JSON::print(flags);
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_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);
str_export += current_line + "\n";
}
@@ -265,7 +271,6 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re
}
void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
-
if (p_preset->get("vram_texture_compression/for_desktop")) {
r_features->push_back("s3tc");
}
@@ -282,7 +287,6 @@ void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportP
}
void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_options) {
-
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"), ""));
@@ -292,22 +296,18 @@ void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_op
}
String EditorExportPlatformJavaScript::get_name() const {
-
return "HTML5";
}
String EditorExportPlatformJavaScript::get_os_name() const {
-
return "HTML5";
}
Ref<Texture2D> EditorExportPlatformJavaScript::get_logo() const {
-
return logo;
}
bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
-
String err;
bool valid = false;
@@ -342,14 +342,14 @@ bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p
}
}
- if (!err.empty())
+ if (!err.empty()) {
r_error = err;
+ }
return valid;
}
List<String> EditorExportPlatformJavaScript::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
-
List<String> list;
list.push_back("html");
return list;
@@ -367,11 +367,11 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
template_path = template_path.strip_edges();
if (template_path == String()) {
-
- if (p_debug)
+ if (p_debug) {
template_path = find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG);
- else
+ } else {
template_path = find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE);
+ }
}
if (!DirAccess::exists(p_path.get_base_dir())) {
@@ -390,12 +390,11 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
return error;
}
- FileAccess *src_f = NULL;
+ FileAccess *src_f = nullptr;
zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
unzFile pkg = unzOpen2(template_path.utf8().get_data(), &io);
if (!pkg) {
-
EditorNode::get_singleton()->show_warning(TTR("Could not open template for export:") + "\n" + template_path);
return ERR_FILE_NOT_FOUND;
}
@@ -410,7 +409,7 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
//get filename
unz_file_info info;
char fname[16384];
- unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0);
+ unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
String file = fname;
@@ -425,22 +424,18 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
//write
if (file == "godot.html") {
-
if (!custom_html.empty()) {
continue;
}
- _fix_html(data, p_preset, p_path.get_file().get_basename(), p_debug);
+ _fix_html(data, p_preset, p_path.get_file().get_basename(), p_debug, p_flags);
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.wasm") {
-
file = p_path.get_file().get_basename() + ".wasm";
}
@@ -458,7 +453,6 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
unzClose(pkg);
if (!custom_html.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);
@@ -468,7 +462,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);
+ _fix_html(buf, p_preset, p_path.get_file().get_basename(), p_debug, p_flags);
f = FileAccess::open(p_path, FileAccess::WRITE);
if (!f) {
@@ -522,11 +516,9 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
}
bool EditorExportPlatformJavaScript::poll_export() {
-
Ref<EditorExportPreset> preset;
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
-
Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i);
if (ep->is_runnable() && ep->get_platform() == this) {
preset = ep;
@@ -552,12 +544,10 @@ Ref<ImageTexture> EditorExportPlatformJavaScript::get_option_icon(int p_index) c
}
int EditorExportPlatformJavaScript::get_options_count() const {
-
return menu_options;
}
Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) {
-
if (p_option == 1) {
MutexLock lock(server_lock);
server->stop();
@@ -605,7 +595,6 @@ Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_prese
}
Ref<Texture2D> EditorExportPlatformJavaScript::get_run_icon() const {
-
return run_icon;
}
@@ -621,7 +610,6 @@ void EditorExportPlatformJavaScript::_server_thread_poll(void *data) {
}
EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() {
-
server.instance();
server_quit = false;
server_thread = Thread::create(_server_thread_poll, this);
@@ -635,10 +623,11 @@ EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() {
run_icon->create_from_image(img);
Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
- if (theme.is_valid())
+ if (theme.is_valid()) {
stop_icon = theme->get_icon("Stop", "EditorIcons");
- else
+ } else {
stop_icon.instance();
+ }
menu_options = 0;
}
@@ -651,7 +640,6 @@ EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() {
}
void register_javascript_exporter() {
-
EDITOR_DEF("export/web/http_host", "localhost");
EDITOR_DEF("export/web/http_port", 8060);
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "export/web/http_port", PROPERTY_HINT_RANGE, "1,65535,1"));
diff --git a/platform/javascript/http_client.h.inc b/platform/javascript/http_client.h.inc
index ac275aadbc..4d5ff88bdd 100644
--- a/platform/javascript/http_client.h.inc
+++ b/platform/javascript/http_client.h.inc
@@ -33,21 +33,21 @@
Error prepare_request(Method p_method, const String &p_url, const Vector<String> &p_headers);
int xhr_id;
-int read_limit;
-int response_read_offset;
-Status status;
+int read_limit = 4096;
+int response_read_offset = 0;
+Status status = STATUS_DISCONNECTED;
String host;
-int port;
-bool use_tls;
+int port = -1;
+bool use_tls = false;
String username;
String password;
-int polled_response_code;
+int polled_response_code = 0;
String polled_response_header;
PackedByteArray polled_response;
#ifdef DEBUG_ENABLED
-bool has_polled;
-uint64_t last_polling_frame;
+bool has_polled = false;
+uint64_t last_polling_frame = 0;
#endif
diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp
index 472384cf30..cb0e48b8a9 100644
--- a/platform/javascript/http_client_javascript.cpp
+++ b/platform/javascript/http_client_javascript.cpp
@@ -29,10 +29,10 @@
/*************************************************************************/
#include "core/io/http_client.h"
+
#include "http_request.h"
Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) {
-
close();
if (p_ssl && !p_verify_host) {
WARN_PRINT("Disabling HTTPClient's host verification is not supported for the HTML5 platform, host will be verified");
@@ -67,17 +67,14 @@ Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl,
}
void HTTPClient::set_connection(const Ref<StreamPeer> &p_connection) {
-
ERR_FAIL_MSG("Accessing an HTTPClient's StreamPeer is not supported for the HTML5 platform.");
}
Ref<StreamPeer> HTTPClient::get_connection() const {
-
ERR_FAIL_V_MSG(REF(), "Accessing an HTTPClient's StreamPeer is not supported for the HTML5 platform.");
}
Error HTTPClient::prepare_request(Method p_method, const String &p_url, const Vector<String> &p_headers) {
-
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);
@@ -88,8 +85,8 @@ Error HTTPClient::prepare_request(Method p_method, const String &p_url, const Ve
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() ? NULL : username.utf8().get_data(),
- password.empty() ? NULL : password.utf8().get_data());
+ username.empty() ? nullptr : username.utf8().get_data(),
+ password.empty() ? nullptr : password.utf8().get_data());
for (int i = 0; i < p_headers.size(); i++) {
int header_separator = p_headers[i].find(": ");
@@ -104,7 +101,6 @@ Error HTTPClient::prepare_request(Method p_method, const String &p_url, const Ve
}
Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body) {
-
Error err = prepare_request(p_method, p_url, p_headers);
if (err != OK)
return err;
@@ -113,7 +109,6 @@ Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector
}
Error HTTPClient::request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body) {
-
Error err = prepare_request(p_method, p_url, p_headers);
if (err != OK)
return err;
@@ -122,7 +117,6 @@ Error HTTPClient::request(Method p_method, const String &p_url, const Vector<Str
}
void HTTPClient::close() {
-
host = "";
port = -1;
use_tls = false;
@@ -134,28 +128,23 @@ void HTTPClient::close() {
}
HTTPClient::Status HTTPClient::get_status() const {
-
return status;
}
bool HTTPClient::has_response() const {
-
return !polled_response_header.empty();
}
bool HTTPClient::is_response_chunked() const {
-
// TODO evaluate using moz-chunked-arraybuffer, fetch & ReadableStream
return false;
}
int HTTPClient::get_response_code() const {
-
return polled_response_code;
}
Error HTTPClient::get_response_headers(List<String> *r_response) {
-
if (polled_response_header.empty())
return ERR_INVALID_PARAMETER;
@@ -168,12 +157,10 @@ Error HTTPClient::get_response_headers(List<String> *r_response) {
}
int HTTPClient::get_response_body_length() const {
-
return polled_response.size();
}
PackedByteArray HTTPClient::read_response_body_chunk() {
-
ERR_FAIL_COND_V(status != STATUS_BODY, PackedByteArray());
int to_read = MIN(read_limit, polled_response.size() - response_read_offset);
@@ -192,17 +179,14 @@ PackedByteArray HTTPClient::read_response_body_chunk() {
}
void HTTPClient::set_blocking_mode(bool p_enable) {
-
ERR_FAIL_COND_MSG(p_enable, "HTTPClient blocking mode is not supported for the HTML5 platform.");
}
bool HTTPClient::is_blocking_mode_enabled() const {
-
return false;
}
void HTTPClient::set_read_chunk_size(int p_size) {
-
read_limit = p_size;
}
@@ -211,9 +195,7 @@ int HTTPClient::get_read_chunk_size() const {
}
Error HTTPClient::poll() {
-
switch (status) {
-
case STATUS_DISCONNECTED:
return ERR_UNCONFIGURED;
@@ -233,7 +215,6 @@ Error HTTPClient::poll() {
return ERR_CONNECTION_ERROR;
case STATUS_REQUESTING: {
-
#ifdef DEBUG_ENABLED
if (!has_polled) {
has_polled = true;
@@ -279,20 +260,9 @@ Error HTTPClient::poll() {
}
HTTPClient::HTTPClient() {
-
xhr_id = godot_xhr_new();
- read_limit = 4096;
- status = STATUS_DISCONNECTED;
- port = -1;
- use_tls = false;
- polled_response_code = 0;
-#ifdef DEBUG_ENABLED
- has_polled = false;
- last_polling_frame = 0;
-#endif
}
HTTPClient::~HTTPClient() {
-
godot_xhr_free(xhr_id);
}
diff --git a/platform/javascript/http_request.h b/platform/javascript/http_request.h
index 57dc4f0d9f..54e98c1927 100644
--- a/platform/javascript/http_request.h
+++ b/platform/javascript/http_request.h
@@ -49,7 +49,7 @@ extern int godot_xhr_new();
extern void godot_xhr_reset(int p_xhr_id);
extern bool 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 = NULL, const char *p_password = NULL);
+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);
diff --git a/platform/javascript/javascript_eval.cpp b/platform/javascript/javascript_eval.cpp
index db8050b90e..3a72b10dd4 100644
--- a/platform/javascript/javascript_eval.cpp
+++ b/platform/javascript/javascript_eval.cpp
@@ -34,14 +34,12 @@
#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();
}
Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
-
union {
bool b;
double d;
diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp
index 815bc7e456..740a72fafa 100644
--- a/platform/javascript/javascript_main.cpp
+++ b/platform/javascript/javascript_main.cpp
@@ -34,21 +34,59 @@
#include <emscripten/emscripten.h>
-extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
+static OS_JavaScript *os = nullptr;
+
+void exit_callback() {
+ emscripten_cancel_main_loop(); // After this, we can exit!
+ Main::cleanup();
+ int exit_code = OS_JavaScript::get_singleton()->get_exit_code();
+ memdelete(os);
+ os = nullptr;
+ emscripten_force_exit(exit_code); // No matter that we call cancel_main_loop, regular "exit" will not work, forcing.
+}
+
+void main_loop_callback() {
+ 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, []);
+ });
+ });
+ }
+}
+extern "C" EMSCRIPTEN_KEEPALIVE void cleanup_after_sync() {
+ emscripten_set_main_loop(exit_callback, -1, false);
+}
+
+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_JavaScript *os = OS_JavaScript::get_singleton();
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->run_async();
+ 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);
+ 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.
@@ -59,13 +97,10 @@ int main(int argc, char *argv[]) {
FS.syncfs(true, function(err) {
ccall('main_after_fs_sync', null, ['string'], [err ? err.message : ""])
});
+
);
/* clang-format on */
- new OS_JavaScript(argc, argv);
- // TODO: Check error return value.
- Main::setup(argv[0], argc - 1, &argv[1]);
-
return 0;
// Continued async in main_after_fs_sync() from the syncfs() callback.
}
diff --git a/platform/javascript/http_request.js b/platform/javascript/native/http_request.js
index f621689f9d..f621689f9d 100644
--- a/platform/javascript/http_request.js
+++ b/platform/javascript/native/http_request.js
diff --git a/platform/javascript/id_handler.js b/platform/javascript/native/id_handler.js
index 67d29075b8..67d29075b8 100644
--- a/platform/javascript/id_handler.js
+++ b/platform/javascript/native/id_handler.js
diff --git a/platform/javascript/native/utils.js b/platform/javascript/native/utils.js
new file mode 100644
index 0000000000..95585d26ae
--- /dev/null
+++ b/platform/javascript/native/utils.js
@@ -0,0 +1,204 @@
+/*************************************************************************/
+/* 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 db90b01f8f..ad4b5a5afa 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -30,668 +30,23 @@
#include "os_javascript.h"
+#include "core/debugger/engine_debugger.h"
#include "core/io/file_access_buffered_fa.h"
-//#include "drivers/gles2/rasterizer_gles2.h"
-#include "drivers/dummy/rasterizer_dummy.h"
+#include "core/io/json.h"
#include "drivers/unix/dir_access_unix.h"
#include "drivers/unix/file_access_unix.h"
#include "main/main.h"
-#include "servers/visual/visual_server_raster.h"
+#include "modules/modules_enabled.gen.h"
+#include "platform/javascript/display_server_javascript.h"
+
+#ifdef MODULE_WEBSOCKET_ENABLED
+#include "modules/websocket/remote_debugger_peer_websocket.h"
+#endif
#include <emscripten.h>
-#include <png.h>
#include <stdlib.h>
-#include "dom_keys.inc"
-
-#define DOM_BUTTON_LEFT 0
-#define DOM_BUTTON_MIDDLE 1
-#define DOM_BUTTON_RIGHT 2
-#define DOM_BUTTON_XBUTTON1 3
-#define DOM_BUTTON_XBUTTON2 4
-#define GODOT_CANVAS_SELECTOR "#canvas"
-
-// Window (canvas)
-
-static void focus_canvas() {
-
- /* clang-format off */
- EM_ASM(
- Module.canvas.focus();
- );
- /* clang-format on */
-}
-
-static bool is_canvas_focused() {
-
- /* clang-format off */
- return EM_ASM_INT_V(
- return document.activeElement == Module.canvas;
- );
- /* clang-format on */
-}
-
-static Point2 compute_position_in_canvas(int x, int y) {
- int canvas_x = EM_ASM_INT({
- return document.getElementById('canvas').getBoundingClientRect().x;
- });
- int canvas_y = EM_ASM_INT({
- return document.getElementById('canvas').getBoundingClientRect().y;
- });
- int canvas_width;
- int canvas_height;
- emscripten_get_canvas_element_size(GODOT_CANVAS_SELECTOR, &canvas_width, &canvas_height);
-
- double element_width;
- double element_height;
- emscripten_get_element_css_size(GODOT_CANVAS_SELECTOR, &element_width, &element_height);
-
- return Point2((int)(canvas_width / element_width * (x - canvas_x)),
- (int)(canvas_height / element_height * (y - canvas_y)));
-}
-
-static bool cursor_inside_canvas = true;
-
-EM_BOOL OS_JavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) {
-
- OS_JavaScript *os = get_singleton();
- // Empty ID is canvas.
- String target_id = String::utf8(p_event->id);
- if (target_id.empty() || target_id == "canvas") {
- // This event property is the only reliable data on
- // browser fullscreen state.
- os->video_mode.fullscreen = p_event->isFullscreen;
- if (os->video_mode.fullscreen) {
- os->entering_fullscreen = false;
- } else {
- // Restoring maximized window now will cause issues,
- // so delay until main_loop_iterate.
- os->just_exited_fullscreen = true;
- }
- }
- return false;
-}
-
-void OS_JavaScript::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
-
- video_mode = p_video_mode;
-}
-
-OS::VideoMode OS_JavaScript::get_video_mode(int p_screen) const {
-
- return video_mode;
-}
-
-Size2 OS_JavaScript::get_screen_size(int p_screen) const {
-
- EmscriptenFullscreenChangeEvent ev;
- EMSCRIPTEN_RESULT result = emscripten_get_fullscreen_status(&ev);
- ERR_FAIL_COND_V(result != EMSCRIPTEN_RESULT_SUCCESS, Size2());
- return Size2(ev.screenWidth, ev.screenHeight);
-}
-
-void OS_JavaScript::set_window_size(const Size2 p_size) {
-
- windowed_size = p_size;
- if (video_mode.fullscreen) {
- window_maximized = false;
- set_window_fullscreen(false);
- } else {
- if (window_maximized) {
- emscripten_exit_soft_fullscreen();
- window_maximized = false;
- }
- emscripten_set_canvas_element_size(GODOT_CANVAS_SELECTOR, p_size.x, p_size.y);
- }
-}
-
-Size2 OS_JavaScript::get_window_size() const {
-
- int canvas[2];
- emscripten_get_canvas_element_size(GODOT_CANVAS_SELECTOR, canvas, canvas + 1);
- return Size2(canvas[0], canvas[1]);
-}
-
-void OS_JavaScript::set_window_maximized(bool p_enabled) {
-
- if (video_mode.fullscreen) {
- window_maximized = p_enabled;
- set_window_fullscreen(false);
- } else if (!p_enabled) {
- emscripten_exit_soft_fullscreen();
- window_maximized = false;
- } else if (!window_maximized) {
- // Prevent calling emscripten_enter_soft_fullscreen mutltiple times,
- // this would hide page elements permanently.
- EmscriptenFullscreenStrategy strategy;
- strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
- strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
- strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
- strategy.canvasResizedCallback = NULL;
- emscripten_enter_soft_fullscreen(GODOT_CANVAS_SELECTOR, &strategy);
- window_maximized = p_enabled;
- }
-}
-
-bool OS_JavaScript::is_window_maximized() const {
-
- return window_maximized;
-}
-
-void OS_JavaScript::set_window_fullscreen(bool p_enabled) {
-
- if (p_enabled == video_mode.fullscreen) {
- return;
- }
-
- // Just request changes here, if successful, logic continues in
- // fullscreen_change_callback.
- if (p_enabled) {
- if (window_maximized) {
- // Soft fullsreen during real fullscreen can cause issues, so exit.
- // This must be called before requesting full screen.
- emscripten_exit_soft_fullscreen();
- }
- EmscriptenFullscreenStrategy strategy;
- strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
- strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
- strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
- strategy.canvasResizedCallback = NULL;
- EMSCRIPTEN_RESULT result = emscripten_request_fullscreen_strategy(GODOT_CANVAS_SELECTOR, 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.");
- // Not fullscreen yet, so prevent "windowed" canvas dimensions from
- // being overwritten.
- entering_fullscreen = true;
- } else {
- // No logic allowed here, since exiting w/ ESC key won't use this function.
- ERR_FAIL_COND(emscripten_exit_fullscreen() != EMSCRIPTEN_RESULT_SUCCESS);
- }
-}
-
-bool OS_JavaScript::is_window_fullscreen() const {
-
- return video_mode.fullscreen;
-}
-
-void OS_JavaScript::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
-
- Size2 screen = get_screen_size();
- p_list->push_back(OS::VideoMode(screen.width, screen.height, true));
-}
-
-bool OS_JavaScript::get_window_per_pixel_transparency_enabled() const {
- if (!is_layered_allowed()) {
- return false;
- }
- return transparency_enabled;
-}
-
-void OS_JavaScript::set_window_per_pixel_transparency_enabled(bool p_enabled) {
- if (!is_layered_allowed()) {
- return;
- }
- transparency_enabled = p_enabled;
-}
-
-// Keys
-
-template <typename T>
-static void 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> 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));
-
- String unicode = String::utf8(emscripten_event->key);
- // Check if empty or multi-character (e.g. `CapsLock`).
- if (unicode.length() != 1) {
- // Might be empty as well, but better than nonsense.
- unicode = String::utf8(emscripten_event->charValue);
- }
- if (unicode.length() == 1) {
- ev->set_unicode(unicode[0]);
- }
-
- return ev;
-}
-
-EM_BOOL OS_JavaScript::keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) {
-
- OS_JavaScript *os = get_singleton();
- Ref<InputEventKey> ev = setup_key_event(p_event);
- ev->set_pressed(true);
- if (ev->get_unicode() == 0 && keycode_has_unicode(ev->get_keycode())) {
- // Defer to keypress event for legacy unicode retrieval.
- os->deferred_key_event = ev;
- // Do not suppress keypress event.
- return false;
- }
- os->input->parse_input_event(ev);
- // Resume audio context after input in case autoplay was denied.
- os->audio_driver_javascript.resume();
- return true;
-}
-
-EM_BOOL OS_JavaScript::keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) {
-
- OS_JavaScript *os = get_singleton();
- os->deferred_key_event->set_unicode(p_event->charCode);
- os->input->parse_input_event(os->deferred_key_event);
- return true;
-}
-
-EM_BOOL OS_JavaScript::keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) {
-
- Ref<InputEventKey> ev = setup_key_event(p_event);
- ev->set_pressed(false);
- get_singleton()->input->parse_input_event(ev);
- return ev->get_keycode() != KEY_UNKNOWN && ev->get_keycode() != 0;
-}
-
-// Mouse
-
-Point2 OS_JavaScript::get_mouse_position() const {
-
- return input->get_mouse_position();
-}
-
-int OS_JavaScript::get_mouse_button_state() const {
-
- return input->get_mouse_button_mask();
-}
-
-EM_BOOL OS_JavaScript::mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) {
-
- OS_JavaScript *os = get_singleton();
-
- Ref<InputEventMouseButton> ev;
- ev.instance();
- ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_MOUSEDOWN);
- ev->set_position(compute_position_in_canvas(p_event->clientX, p_event->clientY));
- ev->set_global_position(ev->get_position());
- dom2godot_mod(p_event, ev);
-
- switch (p_event->button) {
- case DOM_BUTTON_LEFT: ev->set_button_index(BUTTON_LEFT); break;
- case DOM_BUTTON_MIDDLE: ev->set_button_index(BUTTON_MIDDLE); break;
- case DOM_BUTTON_RIGHT: ev->set_button_index(BUTTON_RIGHT); break;
- case DOM_BUTTON_XBUTTON1: ev->set_button_index(BUTTON_XBUTTON1); break;
- case DOM_BUTTON_XBUTTON2: ev->set_button_index(BUTTON_XBUTTON2); break;
- default: return false;
- }
-
- if (ev->is_pressed()) {
-
- double diff = emscripten_get_now() - os->last_click_ms;
-
- if (ev->get_button_index() == os->last_click_button_index) {
-
- if (diff < 400 && Point2(os->last_click_pos).distance_to(ev->get_position()) < 5) {
-
- os->last_click_ms = 0;
- os->last_click_pos = Point2(-100, -100);
- os->last_click_button_index = -1;
- ev->set_doubleclick(true);
- }
-
- } else {
- os->last_click_button_index = ev->get_button_index();
- }
-
- if (!ev->is_doubleclick()) {
- os->last_click_ms += diff;
- os->last_click_pos = ev->get_position();
- }
- }
-
- int mask = os->input->get_mouse_button_mask();
- int button_flag = 1 << (ev->get_button_index() - 1);
- if (ev->is_pressed()) {
- // Since the event is consumed, focus manually. The containing iframe,
- // if exists, may not have focus yet, so focus even if already focused.
- focus_canvas();
- mask |= button_flag;
- } else if (mask & button_flag) {
- mask &= ~button_flag;
- } else {
- // Received release event, but press was outside the canvas, so ignore.
- return false;
- }
- ev->set_button_mask(mask);
-
- os->input->parse_input_event(ev);
- // Resume audio context after input in case autoplay was denied.
- os->audio_driver_javascript.resume();
- // Prevent multi-click text selection and wheel-click scrolling anchor.
- // Context menu is prevented through contextmenu event.
- return true;
-}
-
-EM_BOOL OS_JavaScript::mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) {
-
- OS_JavaScript *os = get_singleton();
-
- int input_mask = os->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)
- return false;
-
- Ref<InputEventMouseMotion> ev;
- ev.instance();
- dom2godot_mod(p_event, ev);
- ev->set_button_mask(input_mask);
-
- ev->set_position(pos);
- ev->set_global_position(ev->get_position());
-
- ev->set_relative(Vector2(p_event->movementX, p_event->movementY));
- os->input->set_mouse_position(ev->get_position());
- ev->set_speed(os->input->get_last_mouse_speed());
-
- os->input->parse_input_event(ev);
- // Don't suppress mouseover/-leave events.
- return false;
-}
-
-static const char *godot2dom_cursor(OS::CursorShape p_shape) {
-
- switch (p_shape) {
- case OS::CURSOR_ARROW:
- default:
- return "auto";
- case OS::CURSOR_IBEAM: return "text";
- case OS::CURSOR_POINTING_HAND: return "pointer";
- case OS::CURSOR_CROSS: return "crosshair";
- case OS::CURSOR_WAIT: return "progress";
- case OS::CURSOR_BUSY: return "wait";
- case OS::CURSOR_DRAG: return "grab";
- case OS::CURSOR_CAN_DROP: return "grabbing";
- case OS::CURSOR_FORBIDDEN: return "no-drop";
- case OS::CURSOR_VSIZE: return "ns-resize";
- case OS::CURSOR_HSIZE: return "ew-resize";
- case OS::CURSOR_BDIAGSIZE: return "nesw-resize";
- case OS::CURSOR_FDIAGSIZE: return "nwse-resize";
- case OS::CURSOR_MOVE: return "move";
- case OS::CURSOR_VSPLIT: return "row-resize";
- case OS::CURSOR_HSPLIT: return "col-resize";
- case OS::CURSOR_HELP: return "help";
- }
-}
-
-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 OS_JavaScript::set_cursor_shape(CursorShape p_shape) {
-
- ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
-
- if (get_mouse_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));
- }
- }
-
- cursor_shape = p_shape;
-}
-
-void OS_JavaScript::set_custom_mouse_cursor(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) {
- set_cursor_shape(p_shape);
- return;
- }
-
- cursors_cache.erase(p_shape);
- }
-
- Ref<Texture2D> texture = p_cursor;
- Ref<AtlasTexture> atlas_texture = p_cursor;
- Ref<Image> image;
- Size2 texture_size;
- Rect2 atlas_rect;
-
- if (texture.is_valid()) {
- image = texture->get_data();
- if (image.is_valid()) {
- image->duplicate();
- }
- }
-
- if (!image.is_valid() && atlas_texture.is_valid()) {
- texture = atlas_texture->get_atlas();
-
- atlas_rect.size.width = texture->get_width();
- atlas_rect.size.height = texture->get_height();
- atlas_rect.position.x = atlas_texture->get_region().position.x;
- atlas_rect.position.y = atlas_texture->get_region().position.y;
-
- texture_size.width = atlas_texture->get_region().size.x;
- texture_size.height = atlas_texture->get_region().size.y;
- } else if (image.is_valid()) {
- texture_size.width = texture->get_width();
- texture_size.height = texture->get_height();
- }
-
- ERR_FAIL_COND(!texture.is_valid());
- ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
- ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
- ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
-
- image = texture->get_data();
-
- ERR_FAIL_COND(!image.is_valid());
-
- image = image->duplicate();
-
- if (atlas_texture.is_valid())
- image->crop_from_point(
- atlas_rect.position.x,
- atlas_rect.position.y,
- texture_size.width,
- texture_size.height);
-
- if (image->get_format() != Image::FORMAT_RGBA8) {
- image->convert(Image::FORMAT_RGBA8);
- }
-
- png_image png_meta;
- memset(&png_meta, 0, sizeof png_meta);
- png_meta.version = PNG_IMAGE_VERSION;
- png_meta.width = texture_size.width;
- png_meta.height = texture_size.height;
- png_meta.format = PNG_FORMAT_RGBA;
-
- PackedByteArray png;
- size_t len;
- PackedByteArray data = image->get_data();
- ERR_FAIL_COND(!png_image_write_get_memory_size(png_meta, len, 0, data.ptr(), 0, NULL));
-
- png.resize(len);
- ERR_FAIL_COND(!png_image_write_to_memory(&png_meta, png.ptrw(), &len, 0, data.ptr(), 0, NULL));
-
- 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] = "";
- }
-
- 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);
- }
-
- set_cursor_shape(cursor_shape);
-}
-
-void OS_JavaScript::set_mouse_mode(OS::MouseMode p_mode) {
-
- ERR_FAIL_COND_MSG(p_mode == MOUSE_MODE_CONFINED, "MOUSE_MODE_CONFINED is not supported for the HTML5 platform.");
- if (p_mode == get_mouse_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));
- set_cursor_shape(cursor_shape);
- emscripten_exit_pointerlock();
-
- } else if (p_mode == MOUSE_MODE_HIDDEN) {
-
- set_css_cursor("none");
- emscripten_exit_pointerlock();
-
- } else if (p_mode == MOUSE_MODE_CAPTURED) {
-
- EMSCRIPTEN_RESULT result = emscripten_request_pointerlock("canvas", 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 set_cursor_shape to make the cursor visible
- set_css_cursor(godot2dom_cursor(cursor_shape));
- set_cursor_shape(cursor_shape);
- }
-}
-
-OS::MouseMode OS_JavaScript::get_mouse_mode() const {
-
- if (is_css_cursor_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;
-}
-
-// Wheel
-
-EM_BOOL OS_JavaScript::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);
- if (!is_canvas_focused()) {
- if (cursor_inside_canvas) {
- focus_canvas();
- } else {
- return false;
- }
- }
-
- InputDefault *input = get_singleton()->input;
- Ref<InputEventMouseButton> ev;
- ev.instance();
- ev->set_position(input->get_mouse_position());
- ev->set_global_position(ev->get_position());
-
- ev->set_shift(input->is_key_pressed(KEY_SHIFT));
- ev->set_alt(input->is_key_pressed(KEY_ALT));
- ev->set_control(input->is_key_pressed(KEY_CONTROL));
- ev->set_metakey(input->is_key_pressed(KEY_META));
-
- if (p_event->deltaY < 0)
- ev->set_button_index(BUTTON_WHEEL_UP);
- else if (p_event->deltaY > 0)
- ev->set_button_index(BUTTON_WHEEL_DOWN);
- else if (p_event->deltaX > 0)
- ev->set_button_index(BUTTON_WHEEL_LEFT);
- else if (p_event->deltaX < 0)
- ev->set_button_index(BUTTON_WHEEL_RIGHT);
- else
- return false;
-
- // Different browsers give wildly different delta values, and we can't
- // interpret deltaMode, so use default value for wheel events' factor.
-
- int button_flag = 1 << (ev->get_button_index() - 1);
-
- ev->set_pressed(true);
- ev->set_button_mask(input->get_mouse_button_mask() | button_flag);
- input->parse_input_event(ev);
-
- ev->set_pressed(false);
- ev->set_button_mask(input->get_mouse_button_mask() & ~button_flag);
- input->parse_input_event(ev);
-
- return true;
-}
-
-// Touch
-
bool OS_JavaScript::has_touchscreen_ui_hint() const {
-
/* clang-format off */
return EM_ASM_INT_V(
return 'ontouchstart' in window;
@@ -699,352 +54,53 @@ bool OS_JavaScript::has_touchscreen_ui_hint() const {
/* clang-format on */
}
-EM_BOOL OS_JavaScript::touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) {
-
- OS_JavaScript *os = get_singleton();
- Ref<InputEventScreenTouch> ev;
- ev.instance();
- int lowest_id_index = -1;
- for (int i = 0; i < p_event->numTouches; ++i) {
-
- const EmscriptenTouchPoint &touch = p_event->touches[i];
- if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier)
- lowest_id_index = i;
- if (!touch.isChanged)
- continue;
- ev->set_index(touch.identifier);
- ev->set_position(compute_position_in_canvas(touch.clientX, touch.clientY));
- os->touches[i] = ev->get_position();
- ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_TOUCHSTART);
-
- os->input->parse_input_event(ev);
- }
- // Resume audio context after input in case autoplay was denied.
- os->audio_driver_javascript.resume();
- return true;
-}
-
-EM_BOOL OS_JavaScript::touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) {
-
- OS_JavaScript *os = get_singleton();
- Ref<InputEventScreenDrag> ev;
- ev.instance();
- int lowest_id_index = -1;
- for (int i = 0; i < p_event->numTouches; ++i) {
-
- const EmscriptenTouchPoint &touch = p_event->touches[i];
- if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier)
- lowest_id_index = i;
- if (!touch.isChanged)
- continue;
- ev->set_index(touch.identifier);
- ev->set_position(compute_position_in_canvas(touch.clientX, touch.clientY));
- Point2 &prev = os->touches[i];
- ev->set_relative(ev->get_position() - prev);
- prev = ev->get_position();
-
- os->input->parse_input_event(ev);
- }
- return true;
-}
-
-// Gamepad
-
-EM_BOOL OS_JavaScript::gamepad_change_callback(int p_event_type, const EmscriptenGamepadEvent *p_event, void *p_user_data) {
-
- InputDefault *input = get_singleton()->input;
- 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);
- } else {
- input->joy_connection_changed(p_event->index, false, "");
- }
- return true;
-}
-
-void OS_JavaScript::process_joypads() {
-
- int joypad_count = emscripten_get_num_gamepads();
- 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];
- if (String::utf8(state.mapping) == "standard" && (button == JOY_ANALOG_L2 || button == JOY_ANALOG_R2)) {
- InputDefault::JoyAxis joy_axis;
- joy_axis.min = 0;
- joy_axis.value = value;
- input->joy_axis(joypad, button, joy_axis);
- } else {
- input->joy_button(joypad, button, value);
- }
- }
- for (int axis = 0; axis < axis_count; axis++) {
-
- InputDefault::JoyAxis joy_axis;
- joy_axis.min = -1;
- joy_axis.value = state.axis[axis];
- input->joy_axis(joypad, axis, joy_axis);
- }
- }
- }
-}
-
-bool OS_JavaScript::is_joy_known(int p_device) {
-
- return input->is_joy_mapped(p_device);
-}
-
-String OS_JavaScript::get_joy_guid(int p_device) const {
-
- return input->get_joy_guid_remapped(p_device);
-}
-
-// Video
-
-int OS_JavaScript::get_video_driver_count() const {
-
- return VIDEO_DRIVER_MAX;
-}
-
-const char *OS_JavaScript::get_video_driver_name(int p_driver) const {
-
- switch (p_driver) {
- case VIDEO_DRIVER_GLES2:
- return "GLES2";
- }
- ERR_FAIL_V_MSG(NULL, "Invalid video driver index: " + itos(p_driver) + ".");
-}
-
// 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";
}
-// Clipboard
-extern "C" EMSCRIPTEN_KEEPALIVE void update_clipboard(const char *p_text) {
- // Only call set_clipboard from OS (sets local clipboard)
- OS::get_singleton()->OS::set_clipboard(p_text);
-}
-
-void OS_JavaScript::set_clipboard(const String &p_text) {
- OS::set_clipboard(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 */
- ERR_FAIL_COND_MSG(err, "Clipboard API is not supported.");
-}
-
-String OS_JavaScript::get_clipboard() 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 */
- return this->OS::get_clipboard();
-}
-
// Lifecycle
-int OS_JavaScript::get_current_video_driver() const {
- return video_driver_index;
-}
-
-void OS_JavaScript::initialize_core() {
-
+void OS_JavaScript::initialize() {
OS_Unix::initialize_core();
FileAccess::make_default<FileAccessBufferedFA<FileAccessUnix>>(FileAccess::ACCESS_RESOURCES);
-}
-
-Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
-
-#if 0
- EmscriptenWebGLContextAttributes attributes;
- emscripten_webgl_init_context_attributes(&attributes);
- attributes.alpha = GLOBAL_GET("display/window/per_pixel_transparency/allowed");
- attributes.antialias = false;
- ERR_FAIL_INDEX_V(p_video_driver, VIDEO_DRIVER_MAX, ERR_INVALID_PARAMETER);
+ DisplayServerJavaScript::register_javascript_driver();
- if (p_desired.layered) {
- set_window_per_pixel_transparency_enabled(true);
- }
-
- bool gl_initialization_error = false;
-
- if (RasterizerGLES2::is_viable() == OK) {
- attributes.majorVersion = 1;
- RasterizerGLES2::register_config();
- RasterizerGLES2::make_current();
- } else {
- gl_initialization_error = true;
- }
-
- EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(GODOT_CANVAS_SELECTOR, &attributes);
- if (emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS) {
- gl_initialization_error = true;
- }
-
- if (gl_initialization_error) {
- OS::get_singleton()->alert("Your browser does not seem to support WebGL. Please update your browser version.",
- "Unable to initialize video driver");
- return ERR_UNAVAILABLE;
- }
-
- video_driver_index = p_video_driver;
-
- video_mode = p_desired;
- // fullscreen_change_callback will correct this if the request is successful.
- video_mode.fullscreen = false;
- // Emscripten only attempts fullscreen requests if the user input callback
- // was registered through one its own functions, so request manually for
- // start-up fullscreen.
- if (p_desired.fullscreen) {
- /* clang-format off */
- EM_ASM({
- const canvas = Module.canvas;
- (canvas.requestFullscreen || canvas.msRequestFullscreen ||
- canvas.mozRequestFullScreen || canvas.mozRequestFullscreen ||
- canvas.webkitRequestFullscreen
- ).call(canvas);
- });
- /* clang-format on */
- }
- /* clang-format off */
- if (EM_ASM_INT_V({ return Module.resizeCanvasOnStart })) {
- /* clang-format on */
- set_window_size(Size2(video_mode.width, video_mode.height));
- } else {
- set_window_size(get_window_size());
- }
+#ifdef MODULE_WEBSOCKET_ENABLED
+ EngineDebugger::register_uri_handler("ws://", RemoteDebuggerPeerWebSocket::create);
+ EngineDebugger::register_uri_handler("wss://", RemoteDebuggerPeerWebSocket::create);
#endif
- RasterizerDummy::make_current(); // TODO GLES2 in Godot 4.0... or webgpu?
char locale_ptr[16];
/* clang-format off */
- EM_ASM_ARGS({
- stringToUTF8(Module.locale, $0, 16);
+ EM_ASM({
+ stringToUTF8(Module['locale'], $0, 16);
}, locale_ptr);
/* clang-format on */
setenv("LANG", locale_ptr, true);
+}
- AudioDriverManager::initialize(p_audio_driver);
- VisualServer *visual_server = memnew(VisualServerRaster());
- input = memnew(InputDefault);
-
- EMSCRIPTEN_RESULT result;
-#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, NULL, true, &cb); \
- EM_CHECK(ev)
-#define SET_EM_CALLBACK_NOTARGET(ev, cb) \
- result = emscripten_set_##ev##_callback(NULL, true, &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(GODOT_CANVAS_SELECTOR, mousedown, mouse_button_callback)
- SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, mouseup, mouse_button_callback)
- SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, wheel, wheel_callback)
- SET_EM_CALLBACK(GODOT_CANVAS_SELECTOR, touchstart, touch_press_callback)
- SET_EM_CALLBACK(GODOT_CANVAS_SELECTOR, touchmove, touchmove_callback)
- SET_EM_CALLBACK(GODOT_CANVAS_SELECTOR, touchend, touch_press_callback)
- SET_EM_CALLBACK(GODOT_CANVAS_SELECTOR, touchcancel, touch_press_callback)
- SET_EM_CALLBACK(GODOT_CANVAS_SELECTOR, keydown, keydown_callback)
- SET_EM_CALLBACK(GODOT_CANVAS_SELECTOR, keypress, keypress_callback)
- SET_EM_CALLBACK(GODOT_CANVAS_SELECTOR, 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({
- const send_notification = cwrap('send_notification', null, ['number']);
- const notifications = arguments;
- (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, index) {
- Module.canvas.addEventListener(event, send_notification.bind(null, notifications[index]));
- });
- // Clipboard
- const update_clipboard = cwrap('update_clipboard', null, ['string']);
- window.addEventListener('paste', function(evt) {
- update_clipboard(evt.clipboardData.getData('text'));
- }, true);
- },
- MainLoop::NOTIFICATION_WM_MOUSE_ENTER,
- MainLoop::NOTIFICATION_WM_MOUSE_EXIT,
- MainLoop::NOTIFICATION_WM_FOCUS_IN,
- MainLoop::NOTIFICATION_WM_FOCUS_OUT
- );
- /* clang-format on */
-
- visual_server->init();
-
- return OK;
+void OS_JavaScript::resume_audio() {
+ audio_driver_javascript.resume();
}
void OS_JavaScript::set_main_loop(MainLoop *p_main_loop) {
-
main_loop = p_main_loop;
- input->set_main_loop(p_main_loop);
}
MainLoop *OS_JavaScript::get_main_loop() const {
-
return main_loop;
}
-void OS_JavaScript::run_async() {
-
- main_loop->init();
- emscripten_set_main_loop(main_loop_callback, -1, false);
-}
-
void OS_JavaScript::main_loop_callback() {
-
get_singleton()->main_loop_iterate();
}
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;
@@ -1063,72 +119,59 @@ bool OS_JavaScript::main_loop_iterate() {
}
}
- if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS)
- process_joypads();
-
- if (just_exited_fullscreen) {
- if (window_maximized) {
- EmscriptenFullscreenStrategy strategy;
- strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
- strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
- strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
- strategy.canvasResizedCallback = NULL;
- emscripten_enter_soft_fullscreen(GODOT_CANVAS_SELECTOR, &strategy);
- } else {
- emscripten_set_canvas_element_size(GODOT_CANVAS_SELECTOR, windowed_size.width, windowed_size.height);
- }
- just_exited_fullscreen = false;
- }
-
- int canvas[2];
- emscripten_get_canvas_element_size(GODOT_CANVAS_SELECTOR, canvas, canvas + 1);
- video_mode.width = canvas[0];
- video_mode.height = canvas[1];
- if (!window_maximized && !video_mode.fullscreen && !just_exited_fullscreen && !entering_fullscreen) {
- windowed_size.width = canvas[0];
- windowed_size.height = canvas[1];
- }
+ DisplayServer::get_singleton()->process_events();
return Main::iteration();
}
void OS_JavaScript::delete_main_loop() {
+ if (main_loop) {
+ memdelete(main_loop);
+ }
+ main_loop = nullptr;
+}
- memdelete(main_loop);
+void OS_JavaScript::finalize_async() {
+ finalizing = true;
+ audio_driver_javascript.finish_async();
}
void OS_JavaScript::finalize() {
-
- memdelete(input);
+ delete_main_loop();
}
// 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) {
-
- ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "OS::execute() is not available on the HTML5 platform.");
+ 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.");
+ return OK;
}
Error OS_JavaScript::kill(const ProcessID &p_pid) {
-
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "OS::kill() is not available on the HTML5 platform.");
}
int OS_JavaScript::get_process_id() const {
-
ERR_FAIL_V_MSG(0, "OS::get_process_id() is not available on the HTML5 platform.");
}
-extern "C" EMSCRIPTEN_KEEPALIVE void send_notification(int p_notification) {
-
- if (p_notification == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || p_notification == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) {
- cursor_inside_canvas = p_notification == MainLoop::NOTIFICATION_WM_MOUSE_ENTER;
- }
- OS_JavaScript::get_singleton()->get_main_loop()->notification(p_notification);
-}
-
bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) {
-
if (p_feature == "HTML5" || p_feature == "web")
return true;
@@ -1140,79 +183,11 @@ bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) {
return false;
}
-void OS_JavaScript::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 */
-}
-
-void OS_JavaScript::set_window_title(const String &p_title) {
-
- /* clang-format off */
- EM_ASM_({
- document.title = UTF8ToString($0);
- }, p_title.utf8().get_data());
- /* clang-format on */
-}
-
-void OS_JavaScript::set_icon(const Ref<Image> &p_icon) {
-
- ERR_FAIL_COND(p_icon.is_null());
- Ref<Image> icon = p_icon;
- if (icon->is_compressed()) {
- icon = icon->duplicate();
- ERR_FAIL_COND(icon->decompress() != OK);
- }
- if (icon->get_format() != Image::FORMAT_RGBA8) {
- if (icon == p_icon)
- icon = icon->duplicate();
- icon->convert(Image::FORMAT_RGBA8);
- }
-
- png_image png_meta;
- memset(&png_meta, 0, sizeof png_meta);
- png_meta.version = PNG_IMAGE_VERSION;
- png_meta.width = icon->get_width();
- png_meta.height = icon->get_height();
- png_meta.format = PNG_FORMAT_RGBA;
-
- PackedByteArray png;
- size_t len;
- PackedByteArray data = icon->get_data();
- ERR_FAIL_COND(!png_image_write_get_memory_size(png_meta, len, 0, data.ptr(), 0, NULL));
-
- png.resize(len);
- ERR_FAIL_COND(!png_image_write_to_memory(&png_meta, png.ptrw(), &len, 0, data.ptr(), 0, NULL));
-
- /* clang-format off */
- EM_ASM_ARGS({
- 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 */
-}
-
String OS_JavaScript::get_executable_path() const {
-
return OS::get_executable_path();
}
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_({
@@ -1223,27 +198,30 @@ Error OS_JavaScript::shell_open(String p_uri) {
}
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";
};
-String OS_JavaScript::get_resource_dir() const {
+String OS_JavaScript::get_cache_path() const {
+ return "/home/web_user/.cache";
+}
- return "/";
+String OS_JavaScript::get_config_path() const {
+ return "/home/web_user/.config";
}
-void OS_JavaScript::file_access_close_callback(const String &p_file, int p_flags) {
+String OS_JavaScript::get_data_path() const {
+ return "/home/web_user/.local/share";
+}
+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();
@@ -1253,42 +231,21 @@ void OS_JavaScript::file_access_close_callback(const String &p_file, int p_flags
}
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;
}
OS_JavaScript *OS_JavaScript::get_singleton() {
-
return static_cast<OS_JavaScript *>(OS::get_singleton());
}
-OS_JavaScript::OS_JavaScript(int p_argc, char *p_argv[]) {
-
- List<String> arguments;
- for (int i = 1; i < p_argc; i++) {
- arguments.push_back(String::utf8(p_argv[i]));
- }
- set_cmdline(p_argv[0], arguments);
-
- last_click_button_index = -1;
- last_click_ms = 0;
- last_click_pos = Point2(-100, -100);
-
- window_maximized = false;
- entering_fullscreen = false;
- just_exited_fullscreen = false;
- transparency_enabled = false;
-
- main_loop = NULL;
-
- idb_available = false;
- sync_wait_time = -1;
+void OS_JavaScript::initialize_joypads() {
+}
+OS_JavaScript::OS_JavaScript() {
AudioDriverManager::add_driver(&audio_driver_javascript);
Vector<Logger *> loggers;
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index ed59477914..f0f18b44f8 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -32,67 +32,27 @@
#define OS_JAVASCRIPT_H
#include "audio_driver_javascript.h"
+#include "core/input/input.h"
#include "drivers/unix/os_unix.h"
-#include "main/input_default.h"
#include "servers/audio_server.h"
-#include "servers/visual/rasterizer.h"
#include <emscripten/html5.h>
class OS_JavaScript : public OS_Unix {
-
- VideoMode video_mode;
- Vector2 windowed_size;
- bool window_maximized;
- bool entering_fullscreen;
- bool just_exited_fullscreen;
- bool transparency_enabled;
-
- InputDefault *input;
- Ref<InputEventKey> deferred_key_event;
- CursorShape cursor_shape;
- String cursors[CURSOR_MAX];
- Map<CursorShape, Vector<Variant>> cursors_cache;
- Point2 touches[32];
-
- Point2i last_click_pos;
- double last_click_ms;
- int last_click_button_index;
-
- MainLoop *main_loop;
- int video_driver_index;
+ MainLoop *main_loop = nullptr;
AudioDriverJavaScript audio_driver_javascript;
- bool idb_available;
- int64_t sync_wait_time;
- int64_t last_sync_check_time;
-
- 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);
- static EM_BOOL keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
- static EM_BOOL keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
-
- static EM_BOOL mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data);
- static EM_BOOL mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data);
-
- static EM_BOOL wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data);
-
- 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);
- void process_joypads();
+ bool finalizing = false;
+ bool idb_available = false;
+ int64_t sync_wait_time = -1;
+ int64_t last_sync_check_time = -1;
static void main_loop_callback();
static void file_access_close_callback(const String &p_file, int p_flags);
protected:
- 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 initialize();
virtual void set_main_loop(MainLoop *p_main_loop);
virtual void delete_main_loop();
@@ -105,65 +65,38 @@ public:
// Override return type to make writing static callbacks less tedious.
static OS_JavaScript *get_singleton();
- 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 void set_window_size(const Size2);
- virtual Size2 get_window_size() const;
- virtual void set_window_maximized(bool p_enabled);
- virtual bool is_window_maximized() const;
- virtual void set_window_fullscreen(bool p_enabled);
- virtual bool is_window_fullscreen() const;
- virtual Size2 get_screen_size(int p_screen = -1) const;
-
- virtual Point2 get_mouse_position() const;
- virtual int get_mouse_button_state() const;
- virtual void set_cursor_shape(CursorShape p_shape);
- virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
- virtual void set_mouse_mode(MouseMode p_mode);
- virtual MouseMode get_mouse_mode() const;
-
- virtual bool get_window_per_pixel_transparency_enabled() const;
- virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
+ virtual void initialize_joypads();
virtual bool has_touchscreen_ui_hint() const;
- virtual bool is_joy_known(int p_device);
- virtual String get_joy_guid(int p_device) const;
-
- virtual int get_video_driver_count() const;
- virtual const char *get_video_driver_name(int p_driver) const;
-
virtual int get_audio_driver_count() const;
virtual const char *get_audio_driver_name(int p_driver) const;
- virtual void set_clipboard(const String &p_text);
- virtual String get_clipboard() const;
-
virtual MainLoop *get_main_loop() const;
- void run_async();
+ void finalize_async();
bool main_loop_iterate();
- virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking = true, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL);
+ 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 void alert(const String &p_alert, const String &p_title = "ALERT!");
- virtual void set_window_title(const String &p_title);
- virtual void set_icon(const Ref<Image> &p_icon);
String get_executable_path() const;
virtual Error shell_open(String p_uri);
virtual String get_name() const;
virtual bool can_draw() const;
- virtual String get_resource_dir() const;
+ 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;
void set_idb_available(bool p_idb_available);
virtual bool is_userfs_persistent() const;
- OS_JavaScript(int p_argc, char *p_argv[]);
+ void resume_audio();
+ bool is_finalizing() { return finalizing; }
+
+ OS_JavaScript();
};
#endif
diff --git a/platform/linuxbsd/SCsub b/platform/linuxbsd/SCsub
new file mode 100644
index 0000000000..ae75a75830
--- /dev/null
+++ b/platform/linuxbsd/SCsub
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+Import("env")
+
+from platform_methods import run_in_subprocess
+import platform_linuxbsd_builders
+
+common_x11 = [
+ "crash_handler_linuxbsd.cpp",
+ "os_linuxbsd.cpp",
+ "joypad_linux.cpp",
+ "context_gl_x11.cpp",
+ "detect_prime_x11.cpp",
+ "display_server_x11.cpp",
+ "vulkan_context_x11.cpp",
+ "key_mapping_x11.cpp",
+]
+
+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"]:
+ env.AddPostAction(prog, run_in_subprocess(platform_linuxbsd_builders.make_debug_linuxbsd))
diff --git a/platform/x11/context_gl_x11.cpp b/platform/linuxbsd/context_gl_x11.cpp
index 5442af3bef..71dc9602b3 100644
--- a/platform/x11/context_gl_x11.cpp
+++ b/platform/linuxbsd/context_gl_x11.cpp
@@ -46,22 +46,18 @@
typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *);
struct ContextGL_X11_Private {
-
::GLXContext glx_context;
};
void ContextGL_X11::release_current() {
-
- glXMakeCurrent(x11_display, None, NULL);
+ glXMakeCurrent(x11_display, None, nullptr);
}
void ContextGL_X11::make_current() {
-
glXMakeCurrent(x11_display, x11_window, p->glx_context);
}
void ContextGL_X11::swap_buffers() {
-
glXSwapBuffers(x11_display, x11_window);
}
@@ -85,7 +81,6 @@ static void set_class_hint(Display *p_display, Window p_window) {
}
Error ContextGL_X11::initialize() {
-
//const char *extensions = glXQueryExtensionsString(x11_display, DefaultScreen(x11_display));
GLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (GLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((const GLubyte *)"glXCreateContextAttribsARB");
@@ -117,7 +112,7 @@ Error ContextGL_X11::initialize() {
int fbcount;
GLXFBConfig fbconfig = 0;
- XVisualInfo *vi = NULL;
+ XVisualInfo *vi = nullptr;
XSetWindowAttributes swa;
swa.event_mask = StructureNotifyMask;
@@ -136,7 +131,7 @@ Error ContextGL_X11::initialize() {
XRenderPictFormat *pict_format = XRenderFindVisualFormat(x11_display, vi->visual);
if (!pict_format) {
XFree(vi);
- vi = NULL;
+ vi = nullptr;
continue;
}
@@ -167,7 +162,6 @@ Error ContextGL_X11::initialize() {
switch (context_type) {
case GLES_2_0_COMPATIBLE: {
-
p->glx_context = glXCreateNewContext(x11_display, fbconfig, GLX_RGBA_TYPE, 0, true);
ERR_FAIL_COND_V(!p->glx_context, ERR_UNCONFIGURED);
} break;
@@ -192,7 +186,6 @@ Error ContextGL_X11::initialize() {
}
int ContextGL_X11::get_window_width() {
-
XWindowAttributes xwa;
XGetWindowAttributes(x11_display, x11_window, &xwa);
@@ -208,9 +201,9 @@ int ContextGL_X11::get_window_height() {
void ContextGL_X11::set_use_vsync(bool p_use) {
static bool setup = false;
- static PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = NULL;
- static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalMESA = NULL;
- static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = NULL;
+ static PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = nullptr;
+ static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalMESA = nullptr;
+ static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = nullptr;
if (!setup) {
setup = true;
@@ -234,14 +227,13 @@ void ContextGL_X11::set_use_vsync(bool p_use) {
return;
use_vsync = p_use;
}
-bool ContextGL_X11::is_using_vsync() const {
+bool ContextGL_X11::is_using_vsync() const {
return use_vsync;
}
ContextGL_X11::ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, const OS::VideoMode &p_default_video_mode, ContextType p_context_type) :
x11_window(p_x11_window) {
-
default_video_mode = p_default_video_mode;
x11_display = p_x11_display;
diff --git a/platform/x11/context_gl_x11.h b/platform/linuxbsd/context_gl_x11.h
index 2c0643c95a..7aed280c98 100644
--- a/platform/x11/context_gl_x11.h
+++ b/platform/linuxbsd/context_gl_x11.h
@@ -42,7 +42,6 @@
struct ContextGL_X11_Private;
class ContextGL_X11 {
-
public:
enum ContextType {
GLES_2_0_COMPATIBLE,
diff --git a/platform/x11/crash_handler_x11.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp
index 19c8f71d0e..b3553e961a 100644
--- a/platform/x11/crash_handler_x11.cpp
+++ b/platform/linuxbsd/crash_handler_linuxbsd.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* crash_handler_x11.cpp */
+/* crash_handler_linuxbsd.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "crash_handler_x11.h"
+#include "crash_handler_linuxbsd.h"
#include "core/os/os.h"
#include "core/project_settings.h"
@@ -46,7 +46,7 @@
#include <stdlib.h>
static void handle_crash(int sig) {
- if (OS::get_singleton() == NULL) {
+ if (OS::get_singleton() == nullptr) {
abort();
}
@@ -63,8 +63,9 @@ static void handle_crash(int sig) {
// Dump the backtrace to stderr with a message to the user
fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig);
- if (OS::get_singleton()->get_main_loop())
+ 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());
char **strings = backtrace_symbols(bt_buffer, size);
@@ -79,14 +80,15 @@ static void handle_crash(int sig) {
if (dladdr(bt_buffer[i], &info) && info.dli_sname) {
if (info.dli_sname[0] == '_') {
int status;
- char *demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status);
+ char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
if (status == 0 && demangled) {
snprintf(fname, 1024, "%s", demangled);
}
- if (demangled)
+ if (demangled) {
free(demangled);
+ }
}
}
@@ -102,7 +104,7 @@ 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, NULL, &output, &ret);
+ Error err = OS::get_singleton()->execute(String("addr2line"), args, true, nullptr, &output, &ret);
if (err == OK) {
output.erase(output.length() - 1, 1);
}
@@ -128,13 +130,14 @@ CrashHandler::~CrashHandler() {
}
void CrashHandler::disable() {
- if (disabled)
+ if (disabled) {
return;
+ }
#ifdef CRASH_HANDLER_ENABLED
- signal(SIGSEGV, NULL);
- signal(SIGFPE, NULL);
- signal(SIGILL, NULL);
+ signal(SIGSEGV, nullptr);
+ signal(SIGFPE, nullptr);
+ signal(SIGILL, nullptr);
#endif
disabled = true;
diff --git a/platform/x11/crash_handler_x11.h b/platform/linuxbsd/crash_handler_linuxbsd.h
index 98620cc789..9bb03579bc 100644
--- a/platform/x11/crash_handler_x11.h
+++ b/platform/linuxbsd/crash_handler_linuxbsd.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* crash_handler_x11.h */
+/* crash_handler_linuxbsd.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -32,7 +32,6 @@
#define CRASH_HANDLER_X11_H
class CrashHandler {
-
bool disabled;
public:
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
new file mode 100644
index 0000000000..07fa06bc06
--- /dev/null
+++ b/platform/linuxbsd/detect.py
@@ -0,0 +1,379 @@
+import os
+import platform
+import sys
+
+
+def is_active():
+ return True
+
+
+def get_name():
+ return "LinuxBSD"
+
+
+def can_build():
+
+ if os.name != "posix" or sys.platform == "darwin":
+ return False
+
+ # Check the minimal dependencies
+ x11_error = os.system("pkg-config --version > /dev/null")
+ if x11_error:
+ return False
+
+ x11_error = os.system("pkg-config x11 --modversion > /dev/null ")
+ if x11_error:
+ return False
+
+ x11_error = os.system("pkg-config xcursor --modversion > /dev/null ")
+ if x11_error:
+ print("xcursor not found.. x11 disabled.")
+ return False
+
+ x11_error = os.system("pkg-config xinerama --modversion > /dev/null ")
+ if x11_error:
+ print("xinerama 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.")
+ return False
+
+ x11_error = os.system("pkg-config xrender --modversion > /dev/null ")
+ if x11_error:
+ print("xrender not found.. x11 disabled.")
+ return False
+
+ x11_error = os.system("pkg-config xi --modversion > /dev/null ")
+ if x11_error:
+ print("xi not found.. Aborting.")
+ return False
+
+ return True
+
+
+def get_opts():
+ from SCons.Variables import BoolVariable, EnumVariable
+
+ return [
+ 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_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("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),
+ ]
+
+
+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"])
+ else: # optimize for size
+ env.Prepend(CCFLAGS=["-Os"])
+
+ if env["debug_symbols"] == "yes":
+ env.Prepend(CCFLAGS=["-g1"])
+ if env["debug_symbols"] == "full":
+ env.Prepend(CCFLAGS=["-g2"])
+
+ elif env["target"] == "release_debug":
+ if env["optimize"] == "speed": # optimize for speed (default)
+ env.Prepend(CCFLAGS=["-O2"])
+ 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":
+ env.Prepend(CCFLAGS=["-g2"])
+
+ elif env["target"] == "debug":
+ env.Prepend(CCFLAGS=["-g3"])
+ env.Prepend(CPPDEFINES=["DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED"])
+ env.Append(LINKFLAGS=["-rdynamic"])
+
+ ## Architecture
+
+ is64 = sys.maxsize > 2 ** 32
+ if env["bits"] == "default":
+ env["bits"] = "64" if is64 else "32"
+
+ ## Compiler configuration
+
+ if "CXX" in env and "clang" in os.path.basename(env["CXX"]):
+ # Convenience check to enforce the use_llvm overrides when CXX is clang(++)
+ env["use_llvm"] = True
+
+ if env["use_llvm"]:
+ 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"]:
+ if env["use_llvm"]:
+ env.Append(LINKFLAGS=["-fuse-ld=lld"])
+ if env["use_thinlto"]:
+ # A convenience so you don't need to write use_lto too when using SCons
+ env["use_lto"] = True
+ else:
+ print("Using LLD with GCC is not supported yet, try compiling with 'use_llvm=yes'.")
+ sys.exit(255)
+
+ if env["use_coverage"]:
+ env.Append(CCFLAGS=["-ftest-coverage", "-fprofile-arcs"])
+ env.Append(LINKFLAGS=["-ftest-coverage", "-fprofile-arcs"])
+
+ if env["use_ubsan"] or env["use_asan"] or env["use_lsan"] or env["use_tsan"]:
+ env.extra_suffix += "s"
+
+ 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_tsan"]:
+ env.Append(CCFLAGS=["-fsanitize=thread"])
+ env.Append(LINKFLAGS=["-fsanitize=thread"])
+
+ if env["use_lto"]:
+ if not env["use_llvm"] and env.GetOption("num_jobs") > 1:
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto=" + str(env.GetOption("num_jobs"))])
+ else:
+ if env["use_lld"] and env["use_thinlto"]:
+ env.Append(CCFLAGS=["-flto=thin"])
+ env.Append(LINKFLAGS=["-flto=thin"])
+ else:
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto"])
+
+ if not env["use_llvm"]:
+ env["RANLIB"] = "gcc-ranlib"
+ env["AR"] = "gcc-ar"
+
+ env.Append(CCFLAGS=["-pipe"])
+ env.Append(LINKFLAGS=["-pipe"])
+
+ # -fpie and -no-pie is supported on GCC 6+ and Clang 4+, both below our
+ # minimal requirements.
+ env.Append(CCFLAGS=["-fpie"])
+ env.Append(LINKFLAGS=["-no-pie"])
+
+ ## Dependencies
+
+ 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 xrandr --cflags --libs")
+ env.ParseConfig("pkg-config xrender --cflags --libs")
+ env.ParseConfig("pkg-config xi --cflags --libs")
+
+ if env["touch"]:
+ env.Append(CPPDEFINES=["TOUCH_ENABLED"])
+
+ # FIXME: Check for existence of the libs before parsing their flags with pkg-config
+
+ # 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.90
+ min_bullet_version = "2.90"
+
+ import subprocess
+
+ bullet_version = subprocess.check_output(["pkg-config", "bullet", "--modversion"]).strip()
+ if str(bullet_version) < min_bullet_version:
+ # Abort as system bullet was requested but too old
+ print(
+ "Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(
+ bullet_version, min_bullet_version
+ )
+ )
+ sys.exit(255)
+ env.ParseConfig("pkg-config bullet --cflags --libs")
+
+ if False: # not env['builtin_assimp']:
+ # FIXME: Add min version check
+ env.ParseConfig("pkg-config assimp --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")
+ else:
+ 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_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 not env["builtin_libwebp"]:
+ env.ParseConfig("pkg-config libwebp --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_wslay"]:
+ env.ParseConfig("pkg-config libwslay --cflags --libs")
+
+ if not env["builtin_miniupnpc"]:
+ # No pkgconfig file so far, hardcode default paths.
+ env.Prepend(CPPPATH=["/usr/include/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
+
+ if os.system("pkg-config --exists alsa") == 0: # 0 means found
+ print("Enabling ALSA")
+ 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")
+
+ if env["pulseaudio"]:
+ 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")
+ 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")
+
+ # Linkflags below this line should typically stay the last ones
+ if not env["builtin_zlib"]:
+ env.ParseConfig("pkg-config zlib --cflags --libs")
+
+ env.Prepend(CPPPATH=["#platform/linuxbsd"])
+ env.Append(CPPDEFINES=["X11_ENABLED", "UNIX_ENABLED"])
+
+ env.Append(CPPDEFINES=["VULKAN_ENABLED"])
+ if not env["builtin_vulkan"]:
+ env.ParseConfig("pkg-config vulkan --cflags --libs")
+ if not env["builtin_glslang"]:
+ # No pkgconfig file for glslang so far
+ env.Append(LIBS=["glslang", "SPIRV"])
+
+ # env.Append(CPPDEFINES=['OPENGL_ENABLED'])
+ env.Append(LIBS=["GL"])
+
+ env.Append(LIBS=["pthread"])
+
+ if platform.system() == "Linux":
+ env.Append(LIBS=["dl"])
+
+ if platform.system().find("BSD") >= 0:
+ env["execinfo"] = True
+
+ if env["execinfo"]:
+ env.Append(LIBS=["execinfo"])
+
+ if not env["tools"]:
+ import subprocess
+ import re
+
+ linker_version_str = subprocess.check_output([env.subst(env["LINK"]), "-Wl,--version"]).decode("utf-8")
+ gnu_ld_version = re.search("^GNU ld [^$]*(\d+\.\d+)$", linker_version_str, re.MULTILINE)
+ if not gnu_ld_version:
+ print(
+ "Warning: Creating template binaries enabled for PCK embedding is currently only supported with GNU ld"
+ )
+ else:
+ if float(gnu_ld_version.group(1)) >= 2.30:
+ env.Append(LINKFLAGS=["-T", "platform/linuxbsd/pck_embed.ld"])
+ else:
+ env.Append(LINKFLAGS=["-T", "platform/linuxbsd/pck_embed.legacy.ld"])
+
+ ## Cross-compilation
+
+ if is64 and env["bits"] == "32":
+ env.Append(CCFLAGS=["-m32"])
+ env.Append(LINKFLAGS=["-m32", "-L/usr/lib/i386-linux-gnu"])
+ elif not is64 and env["bits"] == "64":
+ env.Append(CCFLAGS=["-m64"])
+ env.Append(LINKFLAGS=["-m64", "-L/usr/lib/i686-linux-gnu"])
+
+ # Link those statically for portability
+ if env["use_static_cpp"]:
+ env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"])
diff --git a/platform/x11/detect_prime.cpp b/platform/linuxbsd/detect_prime_x11.cpp
index a0e5f835c0..1e46d3222d 100644
--- a/platform/x11/detect_prime.cpp
+++ b/platform/linuxbsd/detect_prime_x11.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* detect_prime.cpp */
+/* detect_prime_x11.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -31,6 +31,8 @@
#ifdef X11_ENABLED
#if defined(OPENGL_ENABLED)
+#include "detect_prime.h"
+
#include "core/print_string.h"
#include "core/ustring.h"
@@ -65,12 +67,12 @@ vendor vendormap[] = {
{ "Intel", 20 },
{ "nouveau", 10 },
{ "Mesa Project", 0 },
- { NULL, 0 }
+ { nullptr, 0 }
};
// Runs inside a child. Exiting will not quit the engine.
void create_context() {
- Display *x11_display = XOpenDisplay(NULL);
+ Display *x11_display = XOpenDisplay(nullptr);
Window x11_window;
GLXContext glx_context;
@@ -89,7 +91,7 @@ void create_context() {
int fbcount;
GLXFBConfig fbconfig = 0;
- XVisualInfo *vi = NULL;
+ XVisualInfo *vi = nullptr;
XSetWindowAttributes swa;
swa.event_mask = StructureNotifyMask;
@@ -112,7 +114,7 @@ void create_context() {
None
};
- glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, NULL, true, context_attribs);
+ glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, nullptr, true, context_attribs);
swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone);
x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, 10, 10, 0, vi->depth, InputOutput, vi->visual, valuemask, &swa);
@@ -176,7 +178,8 @@ int detect_prime() {
close(fdset[0]);
- if (i) setenv("DRI_PRIME", "1", 1);
+ if (i)
+ setenv("DRI_PRIME", "1", 1);
create_context();
const char *vendor = (const char *)glGetString(GL_VENDOR);
diff --git a/platform/x11/detect_prime.h b/platform/linuxbsd/detect_prime_x11.h
index df636449f4..039bdee76b 100644
--- a/platform/x11/detect_prime.h
+++ b/platform/linuxbsd/detect_prime_x11.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* detect_prime.h */
+/* detect_prime_x11.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
diff --git a/platform/x11/os_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index e1f7691cf6..ef5ac66b34 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* os_x11.cpp */
+/* display_server_x11.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,8 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "os_x11.h"
-#include "detect_prime.h"
+#include "display_server_x11.h"
+
+#ifdef X11_ENABLED
+
+#include "detect_prime_x11.h"
#include "core/os/dir_access.h"
#include "core/print_string.h"
@@ -41,11 +44,10 @@
#endif
#if defined(VULKAN_ENABLED)
-#include "servers/visual/rasterizer_rd/rasterizer_rd.h"
+#include "servers/rendering/rasterizer_rd/rasterizer_rd.h"
#endif
-#include "servers/visual/visual_server_raster.h"
-#include "servers/visual/visual_server_wrap_mt.h"
+#include "scene/resources/texture.h"
#ifdef HAVE_MNTENT
#include <mntent.h>
@@ -86,6 +88,8 @@
#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
@@ -99,584 +103,118 @@
static const double abs_resolution_mult = 10000.0;
static const double abs_resolution_range_mult = 10.0;
-void OS_X11::initialize_core() {
-
- crash_handler.initialize();
-
- OS_Unix::initialize_core();
-}
-
-int OS_X11::get_current_video_driver() const {
- return video_driver_index;
-}
-
-Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
-
- long im_event_mask = 0;
- last_button_state = 0;
-
- xmbstring = NULL;
- x11_window = 0;
- last_click_ms = 0;
- last_click_button_index = -1;
- last_click_pos = Point2(-100, -100);
- args = OS::get_singleton()->get_cmdline_args();
- current_videomode = p_desired;
- main_loop = NULL;
- last_timestamp = 0;
- last_mouse_pos_valid = false;
- last_keyrelease_time = 0;
- xdnd_version = 0;
-
- if (get_render_thread_mode() == RENDER_SEPARATE_THREAD) {
- XInitThreads();
- }
-
- /** XLIB INITIALIZATION **/
- x11_display = XOpenDisplay(NULL);
-
- if (!x11_display) {
- ERR_PRINT("X11 Display is not available");
- return ERR_UNAVAILABLE;
- }
-
- char *modifiers = NULL;
- Bool xkb_dar = False;
- XAutoRepeatOn(x11_display);
- xkb_dar = XkbSetDetectableAutoRepeat(x11_display, True, NULL);
-
- // Try to support IME if detectable auto-repeat is supported
- if (xkb_dar == True) {
-
-#ifdef X_HAVE_UTF8_STRING
- // Xutf8LookupString will be used later instead of XmbLookupString before
- // the multibyte sequences can be converted to unicode string.
- modifiers = XSetLocaleModifiers("");
+bool DisplayServerX11::has_feature(Feature p_feature) const {
+ switch (p_feature) {
+ case FEATURE_SUBWINDOWS:
+#ifdef TOUCH_ENABLED
+ case FEATURE_TOUCHSCREEN:
#endif
- }
-
- if (modifiers == NULL) {
- if (is_stdout_verbose()) {
- WARN_PRINT("IME is disabled");
- }
- XSetLocaleModifiers("@im=none");
- WARN_PRINT("Error setting locale modifiers");
- }
-
- const char *err;
- xrr_get_monitors = NULL;
- xrr_free_monitors = NULL;
- int xrandr_major = 0;
- int xrandr_minor = 0;
- int event_base, error_base;
- xrandr_ext_ok = XRRQueryExtension(x11_display, &event_base, &error_base);
- 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);
- } else {
- XRRQueryVersion(x11_display, &xrandr_major, &xrandr_minor);
- if (((xrandr_major << 8) | xrandr_minor) >= 0x0105) {
- xrr_get_monitors = (xrr_get_monitors_t)dlsym(xrandr_handle, "XRRGetMonitors");
- if (!xrr_get_monitors) {
- err = dlerror();
- fprintf(stderr, "could not find symbol XRRGetMonitors\nError: %s\n", err);
- } else {
- xrr_free_monitors = (xrr_free_monitors_t)dlsym(xrandr_handle, "XRRFreeMonitors");
- if (!xrr_free_monitors) {
- err = dlerror();
- fprintf(stderr, "could not find XRRFreeMonitors\nError: %s\n", err);
- xrr_get_monitors = NULL;
- }
- }
- }
- }
-
- if (!refresh_device_info()) {
- OS::get_singleton()->alert("Your system does not support XInput 2.\n"
- "Please upgrade your distribution.",
- "Unable to initialize XInput");
- return ERR_UNAVAILABLE;
- }
-
- xim = XOpenIM(x11_display, NULL, NULL, NULL);
-
- if (xim == NULL) {
- WARN_PRINT("XOpenIM failed");
- xim_style = 0L;
- } else {
- ::XIMCallback im_destroy_callback;
- im_destroy_callback.client_data = (::XPointer)(this);
- im_destroy_callback.callback = (::XIMProc)(xim_destroy_callback);
- if (XSetIMValues(xim, XNDestroyCallback, &im_destroy_callback,
- NULL) != NULL) {
- WARN_PRINT("Error setting XIM destroy callback");
- }
-
- ::XIMStyles *xim_styles = NULL;
- xim_style = 0L;
- char *imvalret = XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL);
- if (imvalret != NULL || xim_styles == NULL) {
- fprintf(stderr, "Input method doesn't support any styles\n");
- }
-
- if (xim_styles) {
- xim_style = 0L;
- for (int i = 0; i < xim_styles->count_styles; i++) {
-
- if (xim_styles->supported_styles[i] ==
- (XIMPreeditNothing | XIMStatusNothing)) {
-
- xim_style = xim_styles->supported_styles[i];
- break;
- }
- }
-
- XFree(xim_styles);
+ case FEATURE_MOUSE:
+ case FEATURE_MOUSE_WARP:
+ case FEATURE_CLIPBOARD:
+ case FEATURE_CURSOR_SHAPE:
+ case FEATURE_CUSTOM_CURSOR_SHAPE:
+ case FEATURE_IME:
+ case FEATURE_WINDOW_TRANSPARENCY:
+ //case FEATURE_HIDPI:
+ case FEATURE_ICON:
+ case FEATURE_NATIVE_ICON:
+ case FEATURE_SWAP_BUFFERS:
+ return true;
+ default: {
}
- XFree(imvalret);
}
- //!!!!!!!!!!!!!!!!!!!!!!!!!!
- //TODO - do Vulkan and GLES2 support checks, driver selection and fallback
- video_driver_index = p_video_driver;
-#ifndef _MSC_VER
-#warning Forcing vulkan video driver because OpenGL not implemented yet
-#endif
- video_driver_index = VIDEO_DRIVER_VULKAN;
-
- print_verbose("Driver: " + String(get_video_driver_name(video_driver_index)) + " [" + itos(video_driver_index) + "]");
- //!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- //Create window
-
- long visualMask = VisualScreenMask;
- int numberOfVisuals;
- XVisualInfo vInfoTemplate = {};
- vInfoTemplate.screen = DefaultScreen(x11_display);
- XVisualInfo *visualInfo = XGetVisualInfo(x11_display, visualMask, &vInfoTemplate, &numberOfVisuals);
-
- Colormap colormap = XCreateColormap(x11_display, RootWindow(x11_display, vInfoTemplate.screen), visualInfo->visual, AllocNone);
-
- XSetWindowAttributes windowAttributes = {};
- windowAttributes.colormap = colormap;
- windowAttributes.background_pixel = 0xFFFFFFFF;
- windowAttributes.border_pixel = 0;
- windowAttributes.event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask;
-
- unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask;
- x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, visualInfo->depth, InputOutput, visualInfo->visual, valuemask, &windowAttributes);
-
- //set_class_hint(x11_display, x11_window);
- XMapWindow(x11_display, x11_window);
- XFlush(x11_display);
-
- XSync(x11_display, False);
- //XSetErrorHandler(oldHandler);
-
- XFree(visualInfo);
-
- // Init context and rendering device
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- if (getenv("DRI_PRIME") == NULL) {
- int use_prime = -1;
-
- if (getenv("PRIMUS_DISPLAY") ||
- getenv("PRIMUS_libGLd") ||
- getenv("PRIMUS_libGLa") ||
- getenv("PRIMUS_libGL") ||
- getenv("PRIMUS_LOAD_GLOBAL") ||
- getenv("BUMBLEBEE_SOCKET")) {
-
- print_verbose("Optirun/primusrun detected. Skipping GPU detection");
- use_prime = 0;
- }
+ return false;
+}
- if (getenv("LD_LIBRARY_PATH")) {
- String ld_library_path(getenv("LD_LIBRARY_PATH"));
- Vector<String> libraries = ld_library_path.split(":");
+String DisplayServerX11::get_name() const {
+ return "X11";
+}
- for (int i = 0; i < libraries.size(); ++i) {
- if (FileAccess::exists(libraries[i] + "/libGL.so.1") ||
- FileAccess::exists(libraries[i] + "/libGL.so")) {
+void DisplayServerX11::alert(const String &p_alert, const String &p_title) {
+ const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" };
- print_verbose("Custom libGL override detected. Skipping GPU detection");
- use_prime = 0;
- }
- }
- }
+ String path = OS::get_singleton()->get_environment("PATH");
+ Vector<String> path_elems = path.split(":", false);
+ String program;
- if (use_prime == -1) {
- print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic.");
- use_prime = detect_prime();
- }
+ for (int i = 0; i < path_elems.size(); i++) {
+ for (uint64_t k = 0; k < sizeof(message_programs) / sizeof(char *); k++) {
+ String tested_path = path_elems[i].plus_file(message_programs[k]);
- if (use_prime) {
- print_line("Found discrete GPU, setting DRI_PRIME=1 to use it.");
- print_line("Note: Set DRI_PRIME=0 in the environment to disable Godot from using the discrete GPU.");
- setenv("DRI_PRIME", "1", 1);
+ if (FileAccess::exists(tested_path)) {
+ program = tested_path;
+ break;
}
}
- ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE;
-
- context_gles2 = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, opengl_api_type));
-
- if (context_gles2->initialize() != OK) {
- memdelete(context_gles2);
- context_gles2 = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
-
- context_gles2->set_use_vsync(current_videomode.use_vsync);
-
- if (RasterizerGLES2::is_viable() == OK) {
- RasterizerGLES2::register_config();
- RasterizerGLES2::make_current();
- } else {
- memdelete(context_gles2);
- context_gles2 = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
-
- context_vulkan = memnew(VulkanContextX11);
- if (context_vulkan->initialize() != OK) {
- memdelete(context_vulkan);
- context_vulkan = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
- if (context_vulkan->window_create(x11_window, x11_display, get_video_mode().width, get_video_mode().height) == -1) {
- memdelete(context_vulkan);
- context_vulkan = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
+ if (program.length()) {
+ break;
}
-
- //temporary
- rendering_device_vulkan = memnew(RenderingDeviceVulkan);
- rendering_device_vulkan->initialize(context_vulkan);
-
- RasterizerRD::make_current();
}
-#endif
- 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));
- }
-
- if (current_videomode.maximized) {
- current_videomode.maximized = false;
- set_window_maximized(true);
- // borderless fullscreen window mode
- } else if (current_videomode.fullscreen) {
- current_videomode.fullscreen = false;
- set_window_fullscreen(true);
- } else if (current_videomode.borderless_window) {
- Hints hints;
- Atom property;
- hints.flags = 2;
- hints.decorations = 0;
- property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
- }
+ List<String> args;
- // make PID known to X11
- {
- const long pid = this->get_process_id();
- Atom net_wm_pid = XInternAtom(x11_display, "_NET_WM_PID", False);
- XChangeProperty(x11_display, x11_window, net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
+ if (program.ends_with("zenity")) {
+ args.push_back("--error");
+ args.push_back("--width");
+ args.push_back("500");
+ args.push_back("--title");
+ args.push_back(p_title);
+ args.push_back("--text");
+ args.push_back(p_alert);
}
- // disable resizable window
- if (!current_videomode.resizable && !current_videomode.fullscreen) {
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- xsh->flags = PMinSize | PMaxSize;
- XWindowAttributes xwa;
- if (current_videomode.fullscreen) {
- XGetWindowAttributes(x11_display, DefaultRootWindow(x11_display), &xwa);
- } else {
- XGetWindowAttributes(x11_display, x11_window, &xwa);
- }
- xsh->min_width = xwa.width;
- xsh->max_width = xwa.width;
- xsh->min_height = xwa.height;
- xsh->max_height = xwa.height;
- XSetWMNormalHints(x11_display, x11_window, xsh);
- XFree(xsh);
+ if (program.ends_with("kdialog")) {
+ args.push_back("--error");
+ args.push_back(p_alert);
+ args.push_back("--title");
+ args.push_back(p_title);
}
- if (current_videomode.always_on_top) {
- current_videomode.always_on_top = false;
- set_window_always_on_top(true);
+ if (program.ends_with("Xdialog")) {
+ args.push_back("--title");
+ args.push_back(p_title);
+ args.push_back("--msgbox");
+ args.push_back(p_alert);
+ args.push_back("0");
+ args.push_back("0");
}
- ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE);
- ERR_FAIL_COND_V(x11_window == 0, ERR_UNAVAILABLE);
-
- XSetWindowAttributes new_attr;
-
- new_attr.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask |
- ButtonReleaseMask | EnterWindowMask |
- LeaveWindowMask | PointerMotionMask |
- Button1MotionMask |
- Button2MotionMask | Button3MotionMask |
- Button4MotionMask | Button5MotionMask |
- ButtonMotionMask | KeymapStateMask |
- ExposureMask | VisibilityChangeMask |
- StructureNotifyMask |
- SubstructureNotifyMask | SubstructureRedirectMask |
- FocusChangeMask | PropertyChangeMask |
- ColormapChangeMask | OwnerGrabButtonMask |
- im_event_mask;
-
- XChangeWindowAttributes(x11_display, x11_window, CWEventMask, &new_attr);
-
- static unsigned char all_mask_data[XIMaskLen(XI_LASTEVENT)] = {};
- static unsigned char all_master_mask_data[XIMaskLen(XI_LASTEVENT)] = {};
-
- xi.all_event_mask.deviceid = XIAllDevices;
- xi.all_event_mask.mask_len = sizeof(all_mask_data);
- xi.all_event_mask.mask = all_mask_data;
-
- xi.all_master_event_mask.deviceid = XIAllMasterDevices;
- xi.all_master_event_mask.mask_len = sizeof(all_master_mask_data);
- xi.all_master_event_mask.mask = all_master_mask_data;
-
- XISetMask(xi.all_event_mask.mask, XI_HierarchyChanged);
- XISetMask(xi.all_master_event_mask.mask, XI_DeviceChanged);
- XISetMask(xi.all_master_event_mask.mask, XI_RawMotion);
-
-#ifdef TOUCH_ENABLED
- if (xi.touch_devices.size()) {
- XISetMask(xi.all_event_mask.mask, XI_TouchBegin);
- XISetMask(xi.all_event_mask.mask, XI_TouchUpdate);
- XISetMask(xi.all_event_mask.mask, XI_TouchEnd);
- XISetMask(xi.all_event_mask.mask, XI_TouchOwnership);
+ if (program.ends_with("xmessage")) {
+ args.push_back("-center");
+ args.push_back("-title");
+ args.push_back(p_title);
+ args.push_back(p_alert);
}
-#endif
-
- XISelectEvents(x11_display, x11_window, &xi.all_event_mask, 1);
- XISelectEvents(x11_display, DefaultRootWindow(x11_display), &xi.all_master_event_mask, 1);
-
- // Disabled by now since grabbing also blocks mouse events
- // (they are received as extended events instead of standard events)
- /*XIClearMask(xi.touch_event_mask.mask, XI_TouchOwnership);
-
- // Grab touch devices to avoid OS gesture interference
- for (int i = 0; i < xi.touch_devices.size(); ++i) {
- XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask);
- }*/
-
- /* set the titlebar name */
- XStoreName(x11_display, x11_window, "Godot");
-
- wm_delete = XInternAtom(x11_display, "WM_DELETE_WINDOW", true);
- XSetWMProtocols(x11_display, x11_window, &wm_delete, 1);
- im_active = false;
- im_position = Vector2();
-
- if (xim && xim_style) {
-
- xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, x11_window, XNFocusWindow, x11_window, (char *)NULL);
- if (XGetICValues(xic, XNFilterEvents, &im_event_mask, NULL) != NULL) {
- WARN_PRINT("XGetICValues couldn't obtain XNFilterEvents value");
- XDestroyIC(xic);
- xic = NULL;
- }
- if (xic) {
- XUnsetICFocus(xic);
- } else {
- WARN_PRINT("XCreateIC couldn't create xic");
- }
+ if (program.length()) {
+ OS::get_singleton()->execute(program, args, true);
} else {
-
- xic = NULL;
- WARN_PRINT("XCreateIC couldn't create xic");
- }
-
- cursor_size = XcursorGetDefaultSize(x11_display);
- cursor_theme = XcursorGetTheme(x11_display);
-
- if (!cursor_theme) {
- print_verbose("XcursorGetTheme could not get cursor theme");
- cursor_theme = "default";
- }
-
- for (int i = 0; i < CURSOR_MAX; i++) {
-
- cursors[i] = None;
- img[i] = NULL;
- }
-
- current_cursor = CURSOR_ARROW;
-
- for (int i = 0; i < CURSOR_MAX; i++) {
-
- static const char *cursor_file[] = {
- "left_ptr",
- "xterm",
- "hand2",
- "cross",
- "watch",
- "left_ptr_watch",
- "fleur",
- "dnd-move",
- "crossed_circle",
- "v_double_arrow",
- "h_double_arrow",
- "size_bdiag",
- "size_fdiag",
- "move",
- "row_resize",
- "col_resize",
- "question_arrow"
- };
-
- img[i] = XcursorLibraryLoadImage(cursor_file[i], cursor_theme, cursor_size);
- if (!img[i]) {
- const char *fallback = NULL;
-
- switch (i) {
- case CURSOR_POINTING_HAND:
- fallback = "pointer";
- break;
- case CURSOR_CROSS:
- fallback = "crosshair";
- break;
- case CURSOR_WAIT:
- fallback = "wait";
- break;
- case CURSOR_BUSY:
- fallback = "progress";
- break;
- case CURSOR_DRAG:
- fallback = "grabbing";
- break;
- case CURSOR_CAN_DROP:
- fallback = "hand1";
- break;
- case CURSOR_FORBIDDEN:
- fallback = "forbidden";
- break;
- case CURSOR_VSIZE:
- fallback = "ns-resize";
- break;
- case CURSOR_HSIZE:
- fallback = "ew-resize";
- break;
- case CURSOR_BDIAGSIZE:
- fallback = "fd_double_arrow";
- break;
- case CURSOR_FDIAGSIZE:
- fallback = "bd_double_arrow";
- break;
- case CURSOR_MOVE:
- img[i] = img[CURSOR_DRAG];
- break;
- case CURSOR_VSPLIT:
- fallback = "sb_v_double_arrow";
- break;
- case CURSOR_HSPLIT:
- fallback = "sb_h_double_arrow";
- break;
- case CURSOR_HELP:
- fallback = "help";
- break;
- }
- if (fallback != NULL) {
- img[i] = XcursorLibraryLoadImage(fallback, cursor_theme, cursor_size);
- }
- }
- if (img[i]) {
- cursors[i] = XcursorImageLoadCursor(x11_display, img[i]);
- } else {
- print_verbose("Failed loading custom cursor: " + String(cursor_file[i]));
- }
- }
-
- {
- // Creating an empty/transparent cursor
-
- // Create 1x1 bitmap
- Pixmap cursormask = XCreatePixmap(x11_display,
- RootWindow(x11_display, DefaultScreen(x11_display)), 1, 1, 1);
-
- // Fill with zero
- XGCValues xgc;
- xgc.function = GXclear;
- GC gc = XCreateGC(x11_display, cursormask, GCFunction, &xgc);
- XFillRectangle(x11_display, cursormask, gc, 0, 0, 1, 1);
-
- // Color value doesn't matter. Mask zero means no foreground or background will be drawn
- XColor col = {};
-
- Cursor cursor = XCreatePixmapCursor(x11_display,
- cursormask, // source (using cursor mask as placeholder, since it'll all be ignored)
- cursormask, // mask
- &col, &col, 0, 0);
-
- XFreePixmap(x11_display, cursormask);
- XFreeGC(x11_display, gc);
-
- if (cursor == None) {
- ERR_PRINT("FAILED CREATING CURSOR");
- }
-
- null_cursor = cursor;
+ print_line(p_alert);
}
- set_cursor_shape(CURSOR_BUSY);
-
- //Set Xdnd (drag & drop) support
- Atom XdndAware = XInternAtom(x11_display, "XdndAware", False);
- Atom version = 5;
- XChangeProperty(x11_display, x11_window, XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&version, 1);
-
- xdnd_enter = XInternAtom(x11_display, "XdndEnter", False);
- xdnd_position = XInternAtom(x11_display, "XdndPosition", False);
- xdnd_status = XInternAtom(x11_display, "XdndStatus", False);
- xdnd_action_copy = XInternAtom(x11_display, "XdndActionCopy", False);
- xdnd_drop = XInternAtom(x11_display, "XdndDrop", False);
- xdnd_finished = XInternAtom(x11_display, "XdndFinished", False);
- xdnd_selection = XInternAtom(x11_display, "XdndSelection", False);
- requested = None;
-
- visual_server->init();
-
- AudioDriverManager::initialize(p_audio_driver);
-
- input = memnew(InputDefault);
+}
- window_has_focus = true; // Set focus to true at init
-#ifdef JOYDEV_ENABLED
- joypad = memnew(JoypadLinux(input));
-#endif
- _ensure_user_data_dir();
+void DisplayServerX11::_update_real_mouse_position(const WindowData &wd) {
+ Window root_return, child_return;
+ int root_x, root_y, win_x, win_y;
+ unsigned int mask_return;
- if (p_desired.layered) {
- set_window_per_pixel_transparency_enabled(true);
- }
+ Bool xquerypointer_result = XQueryPointer(x11_display, wd.x11_window, &root_return, &child_return, &root_x, &root_y,
+ &win_x, &win_y, &mask_return);
- XEvent xevent;
- while (XPending(x11_display) > 0) {
- XNextEvent(x11_display, &xevent);
- if (xevent.type == ConfigureNotify) {
- _window_changed(&xevent);
+ if (xquerypointer_result) {
+ if (win_x > 0 && win_y > 0 && win_x <= wd.size.width && win_y <= wd.size.height) {
+ last_mouse_pos.x = win_x;
+ last_mouse_pos.y = win_y;
+ last_mouse_pos_valid = true;
+ Input::get_singleton()->set_mouse_position(last_mouse_pos);
}
}
-
- update_real_mouse_position();
-
- return OK;
}
-bool OS_X11::refresh_device_info() {
+bool DisplayServerX11::_refresh_device_info() {
int event_base, error_base;
print_verbose("XInput: Refreshing devices.");
@@ -708,22 +246,27 @@ bool OS_X11::refresh_device_info() {
for (int i = 0; i < dev_count; i++) {
XIDeviceInfo *dev = &info[i];
- if (!dev->enabled)
+ if (!dev->enabled) {
continue;
- if (!(dev->use == XIMasterPointer || dev->use == XIFloatingSlave))
+ }
+ if (!(dev->use == XIMasterPointer || dev->use == XIFloatingSlave)) {
continue;
+ }
bool direct_touch = false;
bool absolute_mode = false;
int resolution_x = 0;
int resolution_y = 0;
- int range_min_x = 0;
- int range_min_y = 0;
- int range_max_x = 0;
- int range_max_y = 0;
- int pressure_resolution = 0;
- int tilt_resolution_x = 0;
- int tilt_resolution_y = 0;
+ double abs_x_min = 0;
+ double abs_x_max = 0;
+ double abs_y_min = 0;
+ double abs_y_max = 0;
+ double pressure_min = 0;
+ double pressure_max = 0;
+ double tilt_x_min = 0;
+ double tilt_x_max = 0;
+ double tilt_y_min = 0;
+ double tilt_y_max = 0;
for (int j = 0; j < dev->num_classes; j++) {
#ifdef TOUCH_ENABLED
if (dev->classes[j]->type == XITouchClass && ((XITouchClassInfo *)dev->classes[j])->mode == XIDirectTouch) {
@@ -735,23 +278,23 @@ bool OS_X11::refresh_device_info() {
if (class_info->number == VALUATOR_ABSX && class_info->mode == XIModeAbsolute) {
resolution_x = class_info->resolution;
- range_min_x = class_info->min;
- range_max_x = class_info->max;
+ abs_x_min = class_info->min;
+ abs_y_max = class_info->max;
absolute_mode = true;
} else if (class_info->number == VALUATOR_ABSY && class_info->mode == XIModeAbsolute) {
resolution_y = class_info->resolution;
- range_min_y = class_info->min;
- range_max_y = class_info->max;
+ abs_y_min = class_info->min;
+ abs_y_max = class_info->max;
absolute_mode = true;
} else if (class_info->number == VALUATOR_PRESSURE && class_info->mode == XIModeAbsolute) {
- pressure_resolution = (class_info->max - class_info->min);
- if (pressure_resolution == 0) pressure_resolution = 1;
+ pressure_min = class_info->min;
+ pressure_max = class_info->max;
} else if (class_info->number == VALUATOR_TILTX && class_info->mode == XIModeAbsolute) {
- tilt_resolution_x = (class_info->max - class_info->min);
- if (tilt_resolution_x == 0) tilt_resolution_x = 1;
+ tilt_x_min = class_info->min;
+ tilt_x_max = class_info->max;
} else if (class_info->number == VALUATOR_TILTY && class_info->mode == XIModeAbsolute) {
- tilt_resolution_y = (class_info->max - class_info->min);
- if (tilt_resolution_y == 0) tilt_resolution_y = 1;
+ tilt_x_min = class_info->min;
+ tilt_x_max = class_info->max;
}
}
}
@@ -762,18 +305,19 @@ bool OS_X11::refresh_device_info() {
if (absolute_mode) {
// If no resolution was reported, use the min/max ranges.
if (resolution_x <= 0) {
- resolution_x = (range_max_x - range_min_x) * abs_resolution_range_mult;
+ resolution_x = (abs_x_max - abs_x_min) * abs_resolution_range_mult;
}
if (resolution_y <= 0) {
- resolution_y = (range_max_y - range_min_y) * abs_resolution_range_mult;
+ resolution_y = (abs_y_max - abs_y_min) * abs_resolution_range_mult;
}
-
xi.absolute_devices[dev->deviceid] = Vector2(abs_resolution_mult / resolution_x, abs_resolution_mult / resolution_y);
print_verbose("XInput: Absolute pointing device: " + String(dev->name));
}
xi.pressure = 0;
- xi.pen_devices[dev->deviceid] = Vector3(pressure_resolution, tilt_resolution_x, tilt_resolution_y);
+ xi.pen_pressure_range[dev->deviceid] = Vector2(pressure_min, pressure_max);
+ xi.pen_tilt_x_range[dev->deviceid] = Vector2(tilt_x_min, tilt_x_max);
+ xi.pen_tilt_y_range[dev->deviceid] = Vector2(tilt_y_min, tilt_y_max);
}
XIFreeDeviceInfo(info);
@@ -786,174 +330,75 @@ bool OS_X11::refresh_device_info() {
return true;
}
-void OS_X11::xim_destroy_callback(::XIM im, ::XPointer client_data,
- ::XPointer call_data) {
-
- WARN_PRINT("Input method stopped");
- OS_X11 *os = reinterpret_cast<OS_X11 *>(client_data);
- os->xim = NULL;
- os->xic = NULL;
-}
-
-void OS_X11::set_ime_active(const bool p_active) {
-
- im_active = p_active;
-
- if (!xic)
- return;
-
- if (p_active) {
- XSetICFocus(xic);
- set_ime_position(im_position);
- } else {
- XUnsetICFocus(xic);
- }
-}
-
-void OS_X11::set_ime_position(const Point2 &p_pos) {
-
- im_position = p_pos;
-
- if (!xic)
- return;
-
- ::XPoint spot;
- spot.x = short(p_pos.x);
- spot.y = short(p_pos.y);
- XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
- XSetICValues(xic, XNPreeditAttributes, preedit_attr, NULL);
- XFree(preedit_attr);
-}
+void DisplayServerX11::_flush_mouse_motion() {
+ while (true) {
+ if (XPending(x11_display) > 0) {
+ XEvent event;
+ XPeekEvent(x11_display, &event);
-String OS_X11::get_unique_id() const {
+ if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
+ XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
- static String machine_id;
- if (machine_id.empty()) {
- if (FileAccess *f = FileAccess::open("/etc/machine-id", FileAccess::READ)) {
- while (machine_id.empty() && !f->eof_reached()) {
- machine_id = f->get_line().strip_edges();
+ if (event_data->evtype == XI_RawMotion) {
+ XNextEvent(x11_display, &event);
+ } else {
+ break;
+ }
+ } else {
+ break;
}
- f->close();
- memdelete(f);
- }
- }
- return machine_id;
-}
-
-void OS_X11::finalize() {
-
- if (main_loop)
- memdelete(main_loop);
- main_loop = NULL;
-
- /*
- if (debugger_connection_console) {
- memdelete(debugger_connection_console);
- }
- */
-#ifdef ALSAMIDI_ENABLED
- driver_alsamidi.close();
-#endif
-
-#ifdef JOYDEV_ENABLED
- memdelete(joypad);
-#endif
-
- xi.touch_devices.clear();
- xi.state.clear();
-
- memdelete(input);
-
- cursors_cache.clear();
- visual_server->finish();
- memdelete(visual_server);
-
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
-
- if (context_gles2)
- memdelete(context_gles2);
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
-
- if (rendering_device_vulkan) {
- rendering_device_vulkan->finalize();
- memdelete(rendering_device_vulkan);
+ } else {
+ break;
}
-
- if (context_vulkan)
- memdelete(context_vulkan);
}
-#endif
-
- if (xrandr_handle)
- dlclose(xrandr_handle);
-
- XUnmapWindow(x11_display, x11_window);
- XDestroyWindow(x11_display, x11_window);
- for (int i = 0; i < CURSOR_MAX; i++) {
- if (cursors[i] != None)
- XFreeCursor(x11_display, cursors[i]);
- if (img[i] != NULL)
- XcursorImageDestroy(img[i]);
- };
-
- if (xic) {
- XDestroyIC(xic);
- }
- if (xim) {
- XCloseIM(xim);
- }
-
- XCloseDisplay(x11_display);
- if (xmbstring)
- memfree(xmbstring);
-
- args.clear();
+ xi.relative_motion.x = 0;
+ xi.relative_motion.y = 0;
}
-void OS_X11::set_mouse_mode(MouseMode p_mode) {
+void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
+ _THREAD_SAFE_METHOD_
- if (p_mode == mouse_mode)
+ if (p_mode == mouse_mode) {
return;
+ }
- if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED)
+ if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
XUngrabPointer(x11_display, CurrentTime);
+ }
// The only modes that show a cursor are VISIBLE and CONFINED
bool showCursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED);
- if (showCursor) {
- XDefineCursor(x11_display, x11_window, cursors[current_cursor]); // show cursor
- } else {
- XDefineCursor(x11_display, x11_window, null_cursor); // hide cursor
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (showCursor) {
+ XDefineCursor(x11_display, E->get().x11_window, cursors[current_cursor]); // show cursor
+ } else {
+ XDefineCursor(x11_display, E->get().x11_window, null_cursor); // hide cursor
+ }
}
-
mouse_mode = p_mode;
if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
-
//flush pending motion events
- flush_mouse_motion();
+ _flush_mouse_motion();
+ WindowData &main_window = windows[MAIN_WINDOW_ID];
if (XGrabPointer(
- x11_display, x11_window, True,
+ x11_display, main_window.x11_window, True,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
- GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime) != GrabSuccess) {
+ GrabModeAsync, GrabModeAsync, windows[MAIN_WINDOW_ID].x11_window, None, CurrentTime) != GrabSuccess) {
ERR_PRINT("NO GRAB");
}
if (mouse_mode == MOUSE_MODE_CAPTURED) {
- center.x = current_videomode.width / 2;
- center.y = current_videomode.height / 2;
+ center.x = main_window.size.width / 2;
+ center.y = main_window.size.height / 2;
- XWarpPointer(x11_display, None, x11_window,
+ XWarpPointer(x11_display, None, main_window.x11_window,
0, 0, 0, 0, (int)center.x, (int)center.y);
- input->set_mouse_position(center);
+ Input::get_singleton()->set_mouse_position(center);
}
} else {
do_mouse_warp = false;
@@ -962,199 +407,149 @@ void OS_X11::set_mouse_mode(MouseMode p_mode) {
XFlush(x11_display);
}
-void OS_X11::warp_mouse_position(const Point2 &p_to) {
+DisplayServerX11::MouseMode DisplayServerX11::mouse_get_mode() const {
+ return mouse_mode;
+}
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
+void DisplayServerX11::mouse_warp_to_position(const Point2i &p_to) {
+ _THREAD_SAFE_METHOD_
+ 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, x11_window,
+ XWarpPointer(x11_display, None, windows[MAIN_WINDOW_ID].x11_window,
0, 0, 0, 0, (int)p_to.x, (int)p_to.y);
}
}
-void OS_X11::flush_mouse_motion() {
- while (true) {
- if (XPending(x11_display) > 0) {
- XEvent event;
- XPeekEvent(x11_display, &event);
+Point2i DisplayServerX11::mouse_get_position() const {
+ return last_mouse_pos;
+}
- if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
- XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
+Point2i DisplayServerX11::mouse_get_absolute_position() const {
+ int number_of_screens = XScreenCount(x11_display);
+ for (int i = 0; i < number_of_screens; i++) {
+ Window root, child;
+ int root_x, root_y, win_x, win_y;
+ unsigned int mask;
+ if (XQueryPointer(x11_display, XRootWindow(x11_display, i), &root, &child, &root_x, &root_y, &win_x, &win_y, &mask)) {
+ XWindowAttributes root_attrs;
+ XGetWindowAttributes(x11_display, root, &root_attrs);
- if (event_data->evtype == XI_RawMotion) {
- XNextEvent(x11_display, &event);
- } else {
- break;
- }
- } else {
- break;
- }
- } else {
- break;
+ return Vector2i(root_attrs.x + root_x, root_attrs.y + root_y);
}
}
-
- xi.relative_motion.x = 0;
- xi.relative_motion.y = 0;
-}
-
-OS::MouseMode OS_X11::get_mouse_mode() const {
- return mouse_mode;
+ return Vector2i();
}
-int OS_X11::get_mouse_button_state() const {
+int DisplayServerX11::mouse_get_button_state() const {
return last_button_state;
}
-Point2 OS_X11::get_mouse_position() const {
- return last_mouse_pos;
-}
-
-bool OS_X11::get_window_per_pixel_transparency_enabled() const {
+void DisplayServerX11::clipboard_set(const String &p_text) {
+ _THREAD_SAFE_METHOD_
- if (!is_layered_allowed()) return false;
- return layered_window;
+ 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);
}
-void OS_X11::set_window_per_pixel_transparency_enabled(bool p_enabled) {
-
- if (!is_layered_allowed()) return;
- if (layered_window != p_enabled) {
- if (p_enabled) {
- set_borderless_window(true);
- layered_window = true;
- } else {
- layered_window = false;
- }
- }
-}
+static String _clipboard_get_impl(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard, Atom target) {
+ String ret;
-void OS_X11::set_window_title(const String &p_title) {
- XStoreName(x11_display, x11_window, p_title.utf8().get_data());
+ 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);
- Atom _net_wm_name = XInternAtom(x11_display, "_NET_WM_NAME", false);
- Atom utf8_string = XInternAtom(x11_display, "UTF8_STRING", false);
- XChangeProperty(x11_display, x11_window, _net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)p_title.utf8().get_data(), p_title.utf8().length());
-}
+ if (Sown == x11_window) {
+ return p_internal_clipboard;
+ };
-void OS_X11::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
-}
+ if (Sown != None) {
+ 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;
+ };
+ };
-OS::VideoMode OS_X11::get_video_mode(int p_screen) const {
- return current_videomode;
-}
+ //
+ // Do not get any data, see how much data is there
+ //
+ XGetWindowProperty(x11_display, x11_window,
+ selection, // Tricky..
+ 0, 0, // offset - len
+ 0, // Delete 0==FALSE
+ AnyPropertyType, //flag
+ &type, // return type
+ &format, // return format
+ &len, &bytes_left, //that
+ &data);
+ // DATA is There
+ if (bytes_left > 0) {
+ 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");
+ }
+ if (data) {
+ XFree(data);
+ }
+ }
+ }
-void OS_X11::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
+ return ret;
}
-void OS_X11::set_wm_fullscreen(bool p_enabled) {
- if (p_enabled && !get_borderless_window()) {
- // remove decorations if the window is not already borderless
- Hints hints;
- Atom property;
- hints.flags = 2;
- hints.decorations = 0;
- property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+static String _clipboard_get(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard) {
+ 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);
}
-
- if (p_enabled && !is_window_resizable()) {
- // 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, x11_window, xsh);
- XFree(xsh);
+ if (ret == "") {
+ ret = _clipboard_get_impl(p_source, x11_window, x11_display, p_internal_clipboard, XA_STRING);
}
+ return ret;
+}
- // Using EWMH -- Extended Window Manager Hints
- XEvent xev;
- Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
- Atom wm_fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False);
-
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = x11_window;
- xev.xclient.message_type = wm_state;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
- xev.xclient.data.l[1] = wm_fullscreen;
- xev.xclient.data.l[2] = 0;
-
- XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
-
- // 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, x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1);
+String DisplayServerX11::clipboard_get() const {
+ _THREAD_SAFE_METHOD_
- XFlush(x11_display);
+ String ret;
+ ret = _clipboard_get(XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window, x11_display, internal_clipboard);
- if (!p_enabled) {
- // Reset the non-resizable flags if we un-set these before.
- Size2 size = get_window_size();
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- if (!is_window_resizable()) {
- 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 (min_size != Size2()) {
- xsh->flags |= PMinSize;
- xsh->min_width = min_size.x;
- xsh->min_height = min_size.y;
- }
- if (max_size != Size2()) {
- xsh->flags |= PMaxSize;
- xsh->max_width = max_size.x;
- xsh->max_height = max_size.y;
- }
- }
- XSetWMNormalHints(x11_display, x11_window, xsh);
- XFree(xsh);
+ if (ret == "") {
+ ret = _clipboard_get(XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window, x11_display, internal_clipboard);
+ };
- // put back or remove decorations according to the last set borderless state
- Hints hints;
- Atom property;
- hints.flags = 2;
- hints.decorations = current_videomode.borderless_window ? 0 : 1;
- property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
- }
+ return ret;
}
-void OS_X11::set_wm_above(bool p_enabled) {
- Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
- Atom wm_above = XInternAtom(x11_display, "_NET_WM_STATE_ABOVE", False);
-
- XClientMessageEvent xev;
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.window = x11_window;
- xev.message_type = wm_state;
- xev.format = 32;
- xev.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
- xev.data.l[1] = wm_above;
- xev.data.l[3] = 1;
- XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xev);
-}
+int DisplayServerX11::get_screen_count() const {
+ _THREAD_SAFE_METHOD_
-int OS_X11::get_screen_count() const {
// Using Xinerama Extension
int event_base, error_base;
const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base);
- if (!ext_okay) return 0;
+ if (!ext_okay) {
+ return 0;
+ }
int count;
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
@@ -1162,41 +557,11 @@ int OS_X11::get_screen_count() const {
return count;
}
-int OS_X11::get_current_screen() const {
- int x, y;
- Window child;
- XTranslateCoordinates(x11_display, x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child);
-
- int count = get_screen_count();
- for (int i = 0; i < count; i++) {
- Point2i pos = get_screen_position(i);
- Size2i size = get_screen_size(i);
- if ((x >= pos.x && x < pos.x + size.width) && (y >= pos.y && y < pos.y + size.height))
- return i;
- }
- return 0;
-}
-
-void OS_X11::set_current_screen(int p_screen) {
- int count = get_screen_count();
- if (p_screen >= count) return;
-
- if (current_videomode.fullscreen) {
- Point2i position = get_screen_position(p_screen);
- Size2i size = get_screen_size(p_screen);
-
- XMoveResizeWindow(x11_display, x11_window, position.x, position.y, size.x, size.y);
- } else {
- if (p_screen != get_current_screen()) {
- Point2i position = get_screen_position(p_screen);
- XMoveWindow(x11_display, x11_window, position.x, position.y);
- }
- }
-}
+Point2i DisplayServerX11::screen_get_position(int p_screen) const {
+ _THREAD_SAFE_METHOD_
-Point2 OS_X11::get_screen_position(int p_screen) const {
- if (p_screen == -1) {
- p_screen = get_current_screen();
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
}
// Using Xinerama Extension
@@ -1219,39 +584,51 @@ Point2 OS_X11::get_screen_position(int p_screen) const {
return position;
}
-Size2 OS_X11::get_screen_size(int p_screen) const {
- if (p_screen == -1) {
- p_screen = get_current_screen();
+Size2i DisplayServerX11::screen_get_size(int p_screen) const {
+ return screen_get_usable_rect(p_screen).size;
+}
+
+Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
}
// Using Xinerama Extension
int event_base, error_base;
const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base);
- if (!ext_okay) return Size2i(0, 0);
+ if (!ext_okay) {
+ return Rect2i(0, 0, 0, 0);
+ }
int count;
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
- if (p_screen >= count) return Size2i(0, 0);
+ if (p_screen >= count) {
+ return Rect2i(0, 0, 0, 0);
+ }
- Size2i size = Point2i(xsi[p_screen].width, xsi[p_screen].height);
+ Rect2i rect = Rect2i(xsi[p_screen].x_org, xsi[p_screen].y_org, xsi[p_screen].width, xsi[p_screen].height);
XFree(xsi);
- return size;
+ return rect;
}
-int OS_X11::get_screen_dpi(int p_screen) const {
- if (p_screen == -1) {
- p_screen = get_current_screen();
+int DisplayServerX11::screen_get_dpi(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
}
//invalid screen?
ERR_FAIL_INDEX_V(p_screen, get_screen_count(), 0);
//Get physical monitor Dimensions through XRandR and calculate dpi
- Size2 sc = get_screen_size(p_screen);
+ Size2i sc = screen_get_size(p_screen);
if (xrandr_ext_ok) {
int count = 0;
if (xrr_get_monitors) {
- xrr_monitor_info *monitors = xrr_get_monitors(x11_display, x11_window, true, &count);
+ xrr_monitor_info *monitors = xrr_get_monitors(x11_display, windows[MAIN_WINDOW_ID].x11_window, true, &count);
if (p_screen < count) {
double xdpi = sc.width / (double)monitors[p_screen].mwidth * 25.4;
double ydpi = sc.height / (double)monitors[p_screen].mheight * 25.4;
@@ -1273,24 +650,245 @@ int OS_X11::get_screen_dpi(int p_screen) const {
int height_mm = DisplayHeightMM(x11_display, p_screen);
double xdpi = (width_mm ? sc.width / (double)width_mm * 25.4 : 0);
double ydpi = (height_mm ? sc.height / (double)height_mm * 25.4 : 0);
- if (xdpi || ydpi)
+ if (xdpi || ydpi) {
return (xdpi + ydpi) / (xdpi && ydpi ? 2 : 1);
+ }
//could not get dpi
return 96;
}
-Point2 OS_X11::get_window_position() const {
+bool DisplayServerX11::screen_is_touchscreen(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+#ifndef _MSC_VER
+#warning Need to get from proper window
+#endif
+
+ return DisplayServer::screen_is_touchscreen(p_screen);
+}
+
+Vector<DisplayServer::WindowID> DisplayServerX11::get_window_list() const {
+ _THREAD_SAFE_METHOD_
+
+ Vector<int> ret;
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+
+DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+ _THREAD_SAFE_METHOD_
+
+ WindowID id = _create_window(p_mode, p_flags, p_rect);
+ for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
+ if (p_flags & (1 << i)) {
+ window_set_flag(WindowFlags(i), true, id);
+ }
+ }
+
+ return id;
+}
+
+void DisplayServerX11::delete_sub_window(WindowID p_id) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_id));
+ ERR_FAIL_COND_MSG(p_id == MAIN_WINDOW_ID, "Main window can't be deleted"); //ma
+
+ WindowData &wd = windows[p_id];
+
+ while (wd.transient_children.size()) {
+ window_set_transient(wd.transient_children.front()->get(), INVALID_WINDOW_ID);
+ }
+
+ if (wd.transient_parent != INVALID_WINDOW_ID) {
+ window_set_transient(p_id, INVALID_WINDOW_ID);
+ }
+
+#ifdef VULKAN_ENABLED
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_destroy(p_id);
+ }
+#endif
+ XUnmapWindow(x11_display, wd.x11_window);
+ XDestroyWindow(x11_display, wd.x11_window);
+ if (wd.xic) {
+ XDestroyIC(wd.xic);
+ }
+
+ windows.erase(p_id);
+}
+
+void DisplayServerX11::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ wd.instance_id = p_instance;
+}
+
+ObjectID DisplayServerX11::window_get_attached_instance_id(WindowID p_window) const {
+ ERR_FAIL_COND_V(!windows.has(p_window), ObjectID());
+ const WindowData &wd = windows[p_window];
+ return wd.instance_id;
+}
+
+DisplayServerX11::WindowID DisplayServerX11::get_window_at_screen_position(const Point2i &p_position) const {
+ return INVALID_WINDOW_ID;
+}
+
+void DisplayServerX11::window_set_title(const String &p_title, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ XStoreName(x11_display, wd.x11_window, p_title.utf8().get_data());
+
+ 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());
+}
+
+void DisplayServerX11::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.rect_changed_callback = p_callable;
+}
+
+void DisplayServerX11::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.event_callback = p_callable;
+}
+
+void DisplayServerX11::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.input_event_callback = p_callable;
+}
+
+void DisplayServerX11::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.input_text_callback = p_callable;
+}
+
+void DisplayServerX11::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.drop_files_callback = p_callable;
+}
+
+int DisplayServerX11::window_get_current_screen(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), -1);
+ const WindowData &wd = windows[p_window];
+
int x, y;
Window child;
- XTranslateCoordinates(x11_display, x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child);
- return Point2i(x, y);
+ XTranslateCoordinates(x11_display, wd.x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child);
+
+ int count = get_screen_count();
+ for (int i = 0; i < count; i++) {
+ Point2i pos = screen_get_position(i);
+ Size2i size = screen_get_size(i);
+ if ((x >= pos.x && x < pos.x + size.width) && (y >= pos.y && y < pos.y + size.height)) {
+ return i;
+ }
+ }
+ return 0;
}
-void OS_X11::set_window_position(const Point2 &p_position) {
+void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ int count = get_screen_count();
+ if (p_screen >= count) {
+ return;
+ }
+
+ if (window_get_mode(p_window) == WINDOW_MODE_FULLSCREEN) {
+ Point2i position = screen_get_position(p_screen);
+ Size2i size = screen_get_size(p_screen);
+
+ XMoveResizeWindow(x11_display, wd.x11_window, position.x, position.y, size.x, size.y);
+ } else {
+ if (p_screen != window_get_current_screen(p_window)) {
+ Point2i position = screen_get_position(p_screen);
+ XMoveWindow(x11_display, wd.x11_window, position.x, position.y);
+ }
+ }
+}
+
+void DisplayServerX11::window_set_transient(WindowID p_window, WindowID p_parent) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(p_window == p_parent);
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd_window = windows[p_window];
+
+ ERR_FAIL_COND(wd_window.transient_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));
+
+ WindowData &wd_parent = windows[wd_window.transient_parent];
+
+ wd_window.transient_parent = INVALID_WINDOW_ID;
+ wd_parent.transient_children.erase(p_window);
+
+ XSetTransientForHint(x11_display, wd_window.x11_window, None);
+ } 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");
+ WindowData &wd_parent = windows[p_parent];
+
+ wd_window.transient_parent = p_parent;
+ wd_parent.transient_children.insert(p_window);
+
+ XSetTransientForHint(x11_display, wd_window.x11_window, wd_parent.x11_window);
+ }
+}
+
+Point2i DisplayServerX11::window_get_position(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Point2i());
+ const WindowData &wd = windows[p_window];
+
+ return wd.position;
+}
+
+void DisplayServerX11::window_set_position(const Point2i &p_position, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
int x = 0;
int y = 0;
- if (!get_borderless_window()) {
+ if (!window_get_flag(WINDOW_FLAG_BORDERLESS, p_window)) {
//exclude window decorations
XSync(x11_display, False);
Atom prop = XInternAtom(x11_display, "_NET_FRAME_EXTENTS", True);
@@ -1299,9 +897,9 @@ void OS_X11::set_window_position(const Point2 &p_position) {
int format;
unsigned long len;
unsigned long remaining;
- unsigned char *data = NULL;
- if (XGetWindowProperty(x11_display, x11_window, prop, 0, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
- if (format == 32 && len == 4) {
+ unsigned char *data = nullptr;
+ if (XGetWindowProperty(x11_display, wd.x11_window, prop, 0, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
+ if (format == 32 && len == 4 && data) {
long *extents = (long *)data;
x = extents[0];
y = extents[2];
@@ -1310,290 +908,251 @@ void OS_X11::set_window_position(const Point2 &p_position) {
}
}
}
- XMoveWindow(x11_display, x11_window, p_position.x - x, p_position.y - y);
- update_real_mouse_position();
+ XMoveWindow(x11_display, wd.x11_window, p_position.x - x, p_position.y - y);
+ _update_real_mouse_position(wd);
}
-Size2 OS_X11::get_window_size() const {
- // Use current_videomode width and height instead of XGetWindowAttributes
- // since right after a XResizeWindow the attributes may not be updated yet
- return Size2i(current_videomode.width, current_videomode.height);
-}
+void DisplayServerX11::window_set_max_size(const Size2i p_size, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
-Size2 OS_X11::get_real_window_size() const {
- XWindowAttributes xwa;
- XSync(x11_display, False);
- XGetWindowAttributes(x11_display, x11_window, &xwa);
- int w = xwa.width;
- int h = xwa.height;
- Atom prop = XInternAtom(x11_display, "_NET_FRAME_EXTENTS", True);
- if (prop != None) {
- Atom type;
- int format;
- unsigned long len;
- unsigned long remaining;
- unsigned char *data = NULL;
- if (XGetWindowProperty(x11_display, x11_window, prop, 0, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
- if (format == 32 && len == 4) {
- long *extents = (long *)data;
- w += extents[0] + extents[1]; // left, right
- h += extents[2] + extents[3]; // top, bottom
- }
- XFree(data);
- }
- }
- return Size2(w, h);
-}
-
-Size2 OS_X11::get_max_window_size() const {
- return max_size;
-}
-
-Size2 OS_X11::get_min_window_size() const {
- return min_size;
-}
-
-void OS_X11::set_min_window_size(const Size2 p_size) {
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
- if ((p_size != Size2()) && (max_size != Size2()) && ((p_size.x > max_size.x) || (p_size.y > max_size.y))) {
- ERR_PRINT("Minimum window size can't be larger than maximum window size!");
+ if ((p_size != Size2i()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {
+ ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
return;
}
- min_size = p_size;
+ wd.max_size = p_size;
- if (is_window_resizable()) {
+ if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
XSizeHints *xsh;
xsh = XAllocSizeHints();
xsh->flags = 0L;
- if (min_size != Size2()) {
+ if (wd.min_size != Size2i()) {
xsh->flags |= PMinSize;
- xsh->min_width = min_size.x;
- xsh->min_height = min_size.y;
+ xsh->min_width = wd.min_size.x;
+ xsh->min_height = wd.min_size.y;
}
- if (max_size != Size2()) {
+ if (wd.max_size != Size2i()) {
xsh->flags |= PMaxSize;
- xsh->max_width = max_size.x;
- xsh->max_height = max_size.y;
+ xsh->max_width = wd.max_size.x;
+ xsh->max_height = wd.max_size.y;
}
- XSetWMNormalHints(x11_display, x11_window, xsh);
+ XSetWMNormalHints(x11_display, wd.x11_window, xsh);
XFree(xsh);
XFlush(x11_display);
}
}
-void OS_X11::set_max_window_size(const Size2 p_size) {
+Size2i DisplayServerX11::window_get_max_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
- if ((p_size != Size2()) && ((p_size.x < min_size.x) || (p_size.y < min_size.y))) {
- ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+
+ return wd.max_size;
+}
+
+void DisplayServerX11::window_set_min_size(const Size2i p_size, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if ((p_size != Size2i()) && (wd.max_size != Size2i()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {
+ ERR_PRINT("Minimum window size can't be larger than maximum window size!");
return;
}
- max_size = p_size;
+ wd.min_size = p_size;
- if (is_window_resizable()) {
+ if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
XSizeHints *xsh;
xsh = XAllocSizeHints();
xsh->flags = 0L;
- if (min_size != Size2()) {
+ if (wd.min_size != Size2i()) {
xsh->flags |= PMinSize;
- xsh->min_width = min_size.x;
- xsh->min_height = min_size.y;
+ xsh->min_width = wd.min_size.x;
+ xsh->min_height = wd.min_size.y;
}
- if (max_size != Size2()) {
+ if (wd.max_size != Size2i()) {
xsh->flags |= PMaxSize;
- xsh->max_width = max_size.x;
- xsh->max_height = max_size.y;
+ xsh->max_width = wd.max_size.x;
+ xsh->max_height = wd.max_size.y;
}
- XSetWMNormalHints(x11_display, x11_window, xsh);
+ XSetWMNormalHints(x11_display, wd.x11_window, xsh);
XFree(xsh);
XFlush(x11_display);
}
}
-void OS_X11::set_window_size(const Size2 p_size) {
+Size2i DisplayServerX11::window_get_min_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
- if (current_videomode.width == p_size.width && current_videomode.height == p_size.height)
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+
+ return wd.min_size;
+}
+
+void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+
+ Size2i size = p_size;
+ size.x = MAX(1, size.x);
+ size.y = MAX(1, size.y);
+
+ WindowData &wd = windows[p_window];
+
+ if (wd.size.width == size.width && wd.size.height == size.height) {
return;
+ }
XWindowAttributes xwa;
XSync(x11_display, False);
- XGetWindowAttributes(x11_display, x11_window, &xwa);
+ XGetWindowAttributes(x11_display, wd.x11_window, &xwa);
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 (!is_window_resizable()) {
+ if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
xsh->flags = PMinSize | PMaxSize;
- xsh->min_width = p_size.x;
- xsh->max_width = p_size.x;
- xsh->min_height = p_size.y;
- xsh->max_height = p_size.y;
+ 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 (min_size != Size2()) {
+ if (wd.min_size != Size2i()) {
xsh->flags |= PMinSize;
- xsh->min_width = min_size.x;
- xsh->min_height = min_size.y;
+ xsh->min_width = wd.min_size.x;
+ xsh->min_height = wd.min_size.y;
}
- if (max_size != Size2()) {
+ if (wd.max_size != Size2i()) {
xsh->flags |= PMaxSize;
- xsh->max_width = max_size.x;
- xsh->max_height = max_size.y;
+ xsh->max_width = wd.max_size.x;
+ xsh->max_height = wd.max_size.y;
}
}
- XSetWMNormalHints(x11_display, x11_window, xsh);
+ XSetWMNormalHints(x11_display, wd.x11_window, xsh);
XFree(xsh);
// Resize the window
- XResizeWindow(x11_display, x11_window, p_size.x, p_size.y);
+ XResizeWindow(x11_display, wd.x11_window, size.x, size.y);
// Update our videomode width and height
- current_videomode.width = p_size.x;
- current_videomode.height = p_size.y;
+ wd.size = size;
for (int timeout = 0; timeout < 50; ++timeout) {
XSync(x11_display, False);
- XGetWindowAttributes(x11_display, x11_window, &xwa);
+ XGetWindowAttributes(x11_display, wd.x11_window, &xwa);
- if (old_w != xwa.width || old_h != xwa.height)
+ if (old_w != xwa.width || old_h != xwa.height) {
break;
+ }
usleep(10000);
}
}
-void OS_X11::set_window_fullscreen(bool p_enabled) {
+Size2i DisplayServerX11::window_get_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
- if (current_videomode.fullscreen == p_enabled)
- return;
-
- if (layered_window)
- set_window_per_pixel_transparency_enabled(false);
-
- if (p_enabled && current_videomode.always_on_top) {
- // Fullscreen + Always-on-top requires a maximized window on some window managers (Metacity)
- set_window_maximized(true);
- }
- set_wm_fullscreen(p_enabled);
- if (!p_enabled && current_videomode.always_on_top) {
- // Restore
- set_window_maximized(false);
- }
- if (!p_enabled) {
- set_window_position(last_position_before_fs);
- } else {
- last_position_before_fs = get_window_position();
- }
- current_videomode.fullscreen = p_enabled;
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+ return wd.size;
}
-bool OS_X11::is_window_fullscreen() const {
- return current_videomode.fullscreen;
-}
+Size2i DisplayServerX11::window_get_real_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
-void OS_X11::set_window_resizable(bool p_enabled) {
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- if (!p_enabled) {
- Size2 size = get_window_size();
-
- 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 (min_size != Size2()) {
- xsh->flags |= PMinSize;
- xsh->min_width = min_size.x;
- xsh->min_height = min_size.y;
- }
- if (max_size != Size2()) {
- xsh->flags |= PMaxSize;
- xsh->max_width = max_size.x;
- xsh->max_height = max_size.y;
+ XWindowAttributes xwa;
+ XSync(x11_display, False);
+ XGetWindowAttributes(x11_display, wd.x11_window, &xwa);
+ int w = xwa.width;
+ int h = xwa.height;
+ Atom prop = XInternAtom(x11_display, "_NET_FRAME_EXTENTS", True);
+ if (prop != None) {
+ Atom type;
+ int format;
+ unsigned long len;
+ unsigned long remaining;
+ unsigned char *data = nullptr;
+ if (XGetWindowProperty(x11_display, wd.x11_window, prop, 0, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
+ if (format == 32 && len == 4 && data) {
+ long *extents = (long *)data;
+ w += extents[0] + extents[1]; // left, right
+ h += extents[2] + extents[3]; // top, bottom
+ }
+ XFree(data);
}
}
-
- XSetWMNormalHints(x11_display, x11_window, xsh);
- XFree(xsh);
-
- current_videomode.resizable = p_enabled;
-
- XFlush(x11_display);
+ return Size2i(w, h);
}
-bool OS_X11::is_window_resizable() const {
- return current_videomode.resizable;
-}
+bool DisplayServerX11::window_is_maximize_allowed(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
-void OS_X11::set_window_minimized(bool p_enabled) {
- // Using ICCCM -- Inter-Client Communication Conventions Manual
- XEvent xev;
- Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False);
-
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = x11_window;
- xev.xclient.message_type = wm_change;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = p_enabled ? WM_IconicState : WM_NormalState;
-
- XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+ ERR_FAIL_COND_V(!windows.has(p_window), false);
+ const WindowData &wd = windows[p_window];
- Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
- Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False);
-
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = x11_window;
- xev.xclient.message_type = wm_state;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
- xev.xclient.data.l[1] = wm_hidden;
-
- XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
-}
-
-bool OS_X11::is_window_minimized() const {
- // Using ICCCM -- Inter-Client Communication Conventions Manual
- Atom property = XInternAtom(x11_display, "WM_STATE", True);
+ Atom property = XInternAtom(x11_display, "_NET_WM_ALLOWED_ACTIONS", False);
Atom type;
int format;
unsigned long len;
unsigned long remaining;
- unsigned char *data = NULL;
+ unsigned char *data = nullptr;
int result = XGetWindowProperty(
x11_display,
- x11_window,
+ wd.x11_window,
property,
0,
- 32,
+ 1024,
False,
- AnyPropertyType,
+ XA_ATOM,
&type,
&format,
&len,
&remaining,
&data);
- if (result == Success) {
- long *state = (long *)data;
- if (state[0] == WM_IconicState)
- return true;
+ if (result == Success && data) {
+ Atom *atoms = (Atom *)data;
+ Atom wm_act_max_horz = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_HORZ", False);
+ Atom wm_act_max_vert = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_VERT", False);
+ bool found_wm_act_max_horz = false;
+ bool found_wm_act_max_vert = false;
+
+ for (uint64_t i = 0; i < len; i++) {
+ if (atoms[i] == wm_act_max_horz) {
+ found_wm_act_max_horz = true;
+ }
+ if (atoms[i] == wm_act_max_vert) {
+ found_wm_act_max_vert = true;
+ }
+
+ if (found_wm_act_max_horz || found_wm_act_max_vert) {
+ return true;
+ }
+ }
+ XFree(atoms);
}
+
return false;
}
-void OS_X11::set_window_maximized(bool p_enabled) {
- if (is_window_maximized() == p_enabled)
- return;
+void DisplayServerX11::_set_wm_maximized(WindowID p_window, bool p_enabled) {
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
// Using EWMH -- Extended Window Manager Hints
XEvent xev;
@@ -1603,7 +1162,7 @@ void OS_X11::set_window_maximized(bool p_enabled) {
memset(&xev, 0, sizeof(xev));
xev.type = ClientMessage;
- xev.xclient.window = x11_window;
+ xev.xclient.window = wd.x11_window;
xev.xclient.message_type = wm_state;
xev.xclient.format = 32;
xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
@@ -1612,178 +1171,446 @@ void OS_X11::set_window_maximized(bool p_enabled) {
XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
- if (p_enabled && is_window_maximize_allowed()) {
+ if (p_enabled && window_is_maximize_allowed(p_window)) {
// Wait for effective resizing (so the GLX context is too).
// Give up after 0.5s, it's not going to happen on this WM.
// https://github.com/godotengine/godot/issues/19978
- for (int attempt = 0; !is_window_maximized() && attempt < 50; attempt++) {
+ for (int attempt = 0; window_get_mode(p_window) != WINDOW_MODE_MAXIMIZED && attempt < 50; attempt++) {
usleep(10000);
}
}
-
- maximized = p_enabled;
}
-bool OS_X11::is_window_maximize_allowed() {
- Atom property = XInternAtom(x11_display, "_NET_WM_ALLOWED_ACTIONS", False);
- Atom type;
- int format;
- unsigned long len;
- unsigned long remaining;
- unsigned char *data = NULL;
-
- int result = XGetWindowProperty(
- x11_display,
- x11_window,
- property,
- 0,
- 1024,
- False,
- XA_ATOM,
- &type,
- &format,
- &len,
- &remaining,
- &data);
+void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
- if (result == Success) {
- Atom *atoms = (Atom *)data;
- Atom wm_act_max_horz = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_HORZ", False);
- Atom wm_act_max_vert = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_VERT", False);
- bool found_wm_act_max_horz = false;
- bool found_wm_act_max_vert = false;
+ if (p_enabled && !window_get_flag(WINDOW_FLAG_BORDERLESS, p_window)) {
+ // remove decorations if the window is not already borderless
+ Hints hints;
+ Atom property;
+ 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);
+ }
- for (uint64_t i = 0; i < len; i++) {
- if (atoms[i] == wm_act_max_horz)
- found_wm_act_max_horz = true;
- if (atoms[i] == wm_act_max_vert)
- found_wm_act_max_vert = true;
+ if (p_enabled && window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
+ // Set the window as resizable to prevent window managers to ignore the fullscreen state flag.
+ XSizeHints *xsh;
- if (found_wm_act_max_horz || found_wm_act_max_vert)
- return true;
- }
- XFree(atoms);
+ xsh = XAllocSizeHints();
+ xsh->flags = 0L;
+ XSetWMNormalHints(x11_display, wd.x11_window, xsh);
+ XFree(xsh);
}
- return false;
-}
-
-bool OS_X11::is_window_maximized() const {
// 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 = NULL;
- bool retval = false;
+ XEvent xev;
+ Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
+ Atom wm_fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False);
- int result = XGetWindowProperty(
- x11_display,
- x11_window,
- property,
- 0,
- 1024,
- False,
- XA_ATOM,
- &type,
- &format,
- &len,
- &remaining,
- &data);
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.xclient.window = wd.x11_window;
+ xev.xclient.message_type = wm_state;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
+ xev.xclient.data.l[1] = wm_fullscreen;
+ xev.xclient.data.l[2] = 0;
- if (result == Success) {
- 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;
+ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
- 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;
+ // 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 (found_wm_max_horz && found_wm_max_vert) {
- retval = true;
- break;
+ 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);
- XFree(data);
- return retval;
+ // put back or remove decorations according to the last set borderless state
+ Hints hints;
+ Atom property;
+ 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);
+ }
}
-void OS_X11::set_window_always_on_top(bool p_enabled) {
- if (is_window_always_on_top() == p_enabled)
- return;
+void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
- if (p_enabled && current_videomode.fullscreen) {
- // Fullscreen + Always-on-top requires a maximized window on some window managers (Metacity)
- set_window_maximized(true);
- }
- set_wm_above(p_enabled);
- if (!p_enabled && !current_videomode.fullscreen) {
- // Restore
- set_window_maximized(false);
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ WindowMode old_mode = window_get_mode(p_window);
+ if (old_mode == p_mode) {
+ return; // do nothing
}
+ //remove all "extra" modes
- current_videomode.always_on_top = p_enabled;
-}
+ switch (old_mode) {
+ case WINDOW_MODE_WINDOWED: {
+ //do nothing
+ } break;
+ case WINDOW_MODE_MINIMIZED: {
+ //Un-Minimize
+ // Using ICCCM -- Inter-Client Communication Conventions Manual
+ XEvent xev;
+ Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False);
+
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.xclient.window = wd.x11_window;
+ xev.xclient.message_type = wm_change;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = WM_NormalState;
+
+ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+
+ Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
+ Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False);
+
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.xclient.window = wd.x11_window;
+ xev.xclient.message_type = wm_state;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
+ xev.xclient.data.l[1] = wm_hidden;
+
+ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+ } break;
+ case WINDOW_MODE_FULLSCREEN: {
+ //Remove full-screen
+ _set_wm_fullscreen(p_window, false);
-bool OS_X11::is_window_always_on_top() const {
- return current_videomode.always_on_top;
-}
+ //un-maximize required for always on top
+ bool on_top = window_get_flag(WINDOW_FLAG_ALWAYS_ON_TOP, p_window);
+
+ wd.fullscreen = false;
-bool OS_X11::is_window_focused() const {
- return window_focused;
+ window_set_position(wd.last_position_before_fs, p_window);
+
+ if (on_top) {
+ _set_wm_maximized(p_window, false);
+ }
+
+ } break;
+ case WINDOW_MODE_MAXIMIZED: {
+ _set_wm_maximized(p_window, false);
+ } break;
+ }
+
+ switch (p_mode) {
+ case WINDOW_MODE_WINDOWED: {
+ //do nothing
+ } break;
+ case WINDOW_MODE_MINIMIZED: {
+ // Using ICCCM -- Inter-Client Communication Conventions Manual
+ XEvent xev;
+ Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False);
+
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.xclient.window = wd.x11_window;
+ xev.xclient.message_type = wm_change;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = WM_IconicState;
+
+ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+
+ Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
+ Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False);
+
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.xclient.window = wd.x11_window;
+ xev.xclient.message_type = wm_state;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
+ xev.xclient.data.l[1] = wm_hidden;
+
+ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+ } 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;
+ } break;
+ case WINDOW_MODE_MAXIMIZED: {
+ _set_wm_maximized(p_window, true);
+
+ } break;
+ }
}
-void OS_X11::set_borderless_window(bool p_borderless) {
+DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
- if (get_borderless_window() == p_borderless)
- return;
+ ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED);
+ const WindowData &wd = windows[p_window];
- if (!p_borderless && layered_window)
- set_window_per_pixel_transparency_enabled(false);
+ 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);
- current_videomode.borderless_window = p_borderless;
+ 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;
- Hints hints;
- Atom property;
- hints.flags = 2;
- hints.decorations = current_videomode.borderless_window ? 0 : 1;
- property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ 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;
+ }
- // Preserve window size
- set_window_size(Size2(current_videomode.width, current_videomode.height));
-}
+ if (found_wm_max_horz && found_wm_max_vert) {
+ retval = true;
+ break;
+ }
+ }
-bool OS_X11::get_borderless_window() {
+ XFree(data);
+ }
- bool borderless = current_videomode.borderless_window;
- Atom prop = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- if (prop != None) {
+ if (retval) {
+ return WINDOW_MODE_MAXIMIZED;
+ }
+ }
+ { // test minimzed
+ // Using ICCCM -- Inter-Client Communication Conventions Manual
+ Atom property = XInternAtom(x11_display, "WM_STATE", True);
Atom type;
int format;
unsigned long len;
unsigned long remaining;
- unsigned char *data = NULL;
- if (XGetWindowProperty(x11_display, x11_window, prop, 0, sizeof(Hints), False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
- if (data && (format == 32) && (len >= 5)) {
- borderless = !((Hints *)data)->decorations;
+ unsigned char *data = nullptr;
+
+ int result = XGetWindowProperty(
+ x11_display,
+ wd.x11_window,
+ property,
+ 0,
+ 32,
+ False,
+ AnyPropertyType,
+ &type,
+ &format,
+ &len,
+ &remaining,
+ &data);
+
+ if (result == Success && data) {
+ long *state = (long *)data;
+ if (state[0] == WM_IconicState) {
+ XFree(data);
+ return WINDOW_MODE_MINIMIZED;
}
XFree(data);
}
}
- return borderless;
+
+ // all other discarded, return windowed.
+
+ return WINDOW_MODE_WINDOWED;
}
-void OS_X11::request_attention() {
+void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ 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);
+
+ } break;
+ case WINDOW_FLAG_BORDERLESS: {
+ Hints hints;
+ Atom property;
+ 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);
+
+ // Preserve window size
+ window_set_size(window_get_size(p_window), p_window);
+
+ wd.borderless = p_enabled;
+ } break;
+ case WINDOW_FLAG_ALWAYS_ON_TOP: {
+ ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID, "Can't make a window transient if the 'on top' flag is active.");
+ if (p_enabled && wd.fullscreen) {
+ _set_wm_maximized(p_window, true);
+ }
+
+ Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
+ Atom wm_above = XInternAtom(x11_display, "_NET_WM_STATE_ABOVE", False);
+
+ XClientMessageEvent xev;
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.window = wd.x11_window;
+ xev.message_type = wm_state;
+ xev.format = 32;
+ xev.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
+ xev.data.l[1] = wm_above;
+ xev.data.l[3] = 1;
+ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xev);
+
+ if (!p_enabled && !wd.fullscreen) {
+ _set_wm_maximized(p_window, false);
+ }
+ wd.on_top = p_enabled;
+
+ } break;
+ case WINDOW_FLAG_TRANSPARENT: {
+ //todo reimplement
+ } break;
+ default: {
+ }
+ }
+}
+
+bool DisplayServerX11::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), false);
+ const WindowData &wd = windows[p_window];
+
+ switch (p_flag) {
+ case WINDOW_FLAG_RESIZE_DISABLED: {
+ return wd.resize_disabled;
+ } break;
+ case WINDOW_FLAG_BORDERLESS: {
+ bool borderless = wd.borderless;
+ Atom prop = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
+ if (prop != None) {
+ Atom type;
+ int format;
+ unsigned long len;
+ unsigned long remaining;
+ unsigned char *data = nullptr;
+ if (XGetWindowProperty(x11_display, wd.x11_window, prop, 0, sizeof(Hints), False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
+ if (data && (format == 32) && (len >= 5)) {
+ borderless = !((Hints *)data)->decorations;
+ }
+ if (data) {
+ XFree(data);
+ }
+ }
+ }
+ return borderless;
+ } break;
+ case WINDOW_FLAG_ALWAYS_ON_TOP: {
+ return wd.on_top;
+ } break;
+ case WINDOW_FLAG_TRANSPARENT: {
+ //todo reimplement
+ } break;
+ default: {
+ }
+ }
+
+ return false;
+}
+
+void DisplayServerX11::window_request_attention(WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
// Using EWMH -- Extended Window Manager Hints
//
// Sets the _NET_WM_STATE_DEMANDS_ATTENTION atom for WM_STATE
@@ -1795,7 +1622,7 @@ void OS_X11::request_attention() {
memset(&xev, 0, sizeof(xev));
xev.type = ClientMessage;
- xev.xclient.window = x11_window;
+ xev.xclient.window = wd.x11_window;
xev.xclient.message_type = wm_state;
xev.xclient.format = 32;
xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
@@ -1805,16 +1632,320 @@ void OS_X11::request_attention() {
XFlush(x11_display);
}
-void OS_X11::get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state) {
+void DisplayServerX11::window_move_to_foreground(WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ 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);
+ XFlush(x11_display);
+}
+
+bool DisplayServerX11::window_can_draw(WindowID p_window) const {
+ //this seems to be all that is provided by X11
+ return window_get_mode(p_window) != WINDOW_MODE_MINIMIZED;
+}
+
+bool DisplayServerX11::can_any_window_draw() const {
+ _THREAD_SAFE_METHOD_
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (window_get_mode(E->key()) != WINDOW_MODE_MINIMIZED) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void DisplayServerX11::window_set_ime_active(const bool p_active, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ wd.im_active = p_active;
+
+ if (!wd.xic) {
+ return;
+ }
+
+ if (p_active) {
+ XSetICFocus(wd.xic);
+ window_set_ime_position(wd.im_position, p_window);
+ } else {
+ XUnsetICFocus(wd.xic);
+ }
+}
+
+void DisplayServerX11::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ wd.im_position = p_pos;
+
+ if (!wd.xic) {
+ return;
+ }
+
+ ::XPoint spot;
+ 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);
+ XFree(preedit_attr);
+}
+
+void DisplayServerX11::cursor_set_shape(CursorShape p_shape) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
+
+ if (p_shape == current_cursor) {
+ return;
+ }
+
+ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
+ if (cursors[p_shape] != None) {
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ XDefineCursor(x11_display, E->get().x11_window, cursors[p_shape]);
+ }
+ } else if (cursors[CURSOR_ARROW] != None) {
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ XDefineCursor(x11_display, E->get().x11_window, cursors[CURSOR_ARROW]);
+ }
+ }
+ }
+
+ current_cursor = p_shape;
+}
+
+DisplayServerX11::CursorShape DisplayServerX11::cursor_get_shape() const {
+ return current_cursor;
+}
+
+void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
+ _THREAD_SAFE_METHOD_
+
+ 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;
+ Size2i texture_size;
+ Rect2i atlas_rect;
+
+ if (texture.is_valid()) {
+ image = texture->get_data();
+ }
+
+ if (!image.is_valid() && atlas_texture.is_valid()) {
+ texture = atlas_texture->get_atlas();
+
+ atlas_rect.size.width = texture->get_width();
+ atlas_rect.size.height = texture->get_height();
+ atlas_rect.position.x = atlas_texture->get_region().position.x;
+ atlas_rect.position.y = atlas_texture->get_region().position.y;
+
+ texture_size.width = atlas_texture->get_region().size.x;
+ texture_size.height = atlas_texture->get_region().size.y;
+ } else if (image.is_valid()) {
+ texture_size.width = texture->get_width();
+ texture_size.height = texture->get_height();
+ }
+
+ ERR_FAIL_COND(!texture.is_valid());
+ ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
+ ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
+ ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
+
+ image = texture->get_data();
+
+ ERR_FAIL_COND(!image.is_valid());
+
+ // Create the cursor structure
+ XcursorImage *cursor_image = XcursorImageCreate(texture_size.width, texture_size.height);
+ XcursorUInt image_size = texture_size.width * texture_size.height;
+ XcursorDim size = sizeof(XcursorPixel) * image_size;
+
+ cursor_image->version = 1;
+ cursor_image->size = size;
+ cursor_image->xhot = p_hotspot.x;
+ cursor_image->yhot = p_hotspot.y;
+
+ // allocate memory to contain the whole file
+ cursor_image->pixels = (XcursorPixel *)memalloc(size);
+
+ for (XcursorPixel index = 0; index < image_size; index++) {
+ int row_index = floor(index / texture_size.width) + atlas_rect.position.y;
+ int column_index = (index % int(texture_size.width)) + atlas_rect.position.x;
+
+ if (atlas_texture.is_valid()) {
+ column_index = MIN(column_index, atlas_rect.size.width - 1);
+ row_index = MIN(row_index, atlas_rect.size.height - 1);
+ }
+
+ *(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32();
+ }
+
+ ERR_FAIL_COND(cursor_image->pixels == nullptr);
+
+ // Save it for a further usage
+ cursors[p_shape] = XcursorImageLoadCursor(x11_display, cursor_image);
+
+ Vector<Variant> params;
+ params.push_back(p_cursor);
+ params.push_back(p_hotspot);
+ cursors_cache.insert(p_shape, params);
+
+ if (p_shape == current_cursor) {
+ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ XDefineCursor(x11_display, E->get().x11_window, cursors[p_shape]);
+ }
+ }
+ }
+
+ memfree(cursor_image->pixels);
+ XcursorImageDestroy(cursor_image);
+ } else {
+ // Reset to default system cursor
+ if (img[p_shape]) {
+ cursors[p_shape] = XcursorImageLoadCursor(x11_display, img[p_shape]);
+ }
+
+ CursorShape c = current_cursor;
+ current_cursor = CURSOR_MAX;
+ cursor_set_shape(c);
+
+ cursors_cache.erase(p_shape);
+ }
+}
+
+DisplayServerX11::LatinKeyboardVariant DisplayServerX11::get_latin_keyboard_variant() const {
+ _THREAD_SAFE_METHOD_
+
+ XkbDescRec *xkbdesc = XkbAllocKeyboard();
+ ERR_FAIL_COND_V(!xkbdesc, LATIN_KEYBOARD_QWERTY);
+
+ XkbGetNames(x11_display, XkbSymbolsNameMask, xkbdesc);
+ ERR_FAIL_COND_V(!xkbdesc->names, LATIN_KEYBOARD_QWERTY);
+ ERR_FAIL_COND_V(!xkbdesc->names->symbols, LATIN_KEYBOARD_QWERTY);
+
+ char *layout = XGetAtomName(x11_display, xkbdesc->names->symbols);
+ ERR_FAIL_COND_V(!layout, LATIN_KEYBOARD_QWERTY);
+
+ Vector<String> info = String(layout).split("+");
+ ERR_FAIL_INDEX_V(1, info.size(), LATIN_KEYBOARD_QWERTY);
+
+ 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;
+ }
+
+ return LATIN_KEYBOARD_QWERTY;
+}
+
+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;
+ 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);
+ }
+
+ XGetWindowProperty(p_display, p_window, p_property, 0, read_bytes, False, AnyPropertyType,
+ &actual_type, &actual_format, &nitems, &bytes_after,
+ &ret);
+
+ read_bytes *= 2;
+
+ } while (bytes_after != 0);
+
+ Property p = { ret, actual_format, (int)nitems, actual_type };
+
+ return p;
+}
+
+static Atom pick_target_from_list(Display *p_display, Atom *p_list, int p_count) {
+ static const char *target_type = "text/uri-list";
+
+ for (int i = 0; i < p_count; i++) {
+ Atom atom = p_list[i];
+
+ if (atom != None && String(XGetAtomName(p_display, atom)) == target_type) {
+ return atom;
+ }
+ }
+ return None;
+}
+
+static Atom pick_target_from_atoms(Display *p_disp, Atom p_t1, Atom p_t2, Atom p_t3) {
+ static const char *target_type = "text/uri-list";
+ if (p_t1 != None && String(XGetAtomName(p_disp, p_t1)) == target_type) {
+ return p_t1;
+ }
+
+ if (p_t2 != None && String(XGetAtomName(p_disp, p_t2)) == target_type) {
+ return p_t2;
+ }
+
+ if (p_t3 != None && String(XGetAtomName(p_disp, p_t3)) == target_type) {
+ return p_t3;
+ }
+
+ return None;
+}
+
+void DisplayServerX11::_get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state) {
state->set_shift((p_x11_state & ShiftMask));
state->set_control((p_x11_state & ControlMask));
state->set_alt((p_x11_state & Mod1Mask /*|| p_x11_state&Mod5Mask*/)); //altgr should not count as alt
state->set_metakey((p_x11_state & Mod4Mask));
}
-unsigned int OS_X11::get_mouse_button_state(unsigned int p_x11_button, int p_x11_type) {
-
+unsigned int DisplayServerX11::_get_mouse_button_state(unsigned int p_x11_button, int p_x11_type) {
unsigned int mask = 1 << (p_x11_button - 1);
if (p_x11_type == ButtonPress) {
@@ -1826,8 +1957,8 @@ unsigned int OS_X11::get_mouse_button_state(unsigned int p_x11_button, int p_x11
return last_button_state;
}
-void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
-
+void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, bool p_echo) {
+ WindowData wd = windows[p_window];
// X11 functions don't know what const is
XKeyEvent *xkeyevent = p_event;
@@ -1853,13 +1984,13 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
KeySym keysym_keycode = 0; // keysym used to find a keycode
KeySym keysym_unicode = 0; // keysym used to find unicode
- // XLookupString returns keysyms usable as nice scancodes/
+ // XLookupString returns keysyms usable as nice keycodes.
char str[256 + 1];
XKeyEvent xkeyevent_no_mod = *xkeyevent;
xkeyevent_no_mod.state &= ~ShiftMask;
xkeyevent_no_mod.state &= ~ControlMask;
- XLookupString(xkeyevent, str, 256, &keysym_unicode, NULL);
- XLookupString(&xkeyevent_no_mod, NULL, 0, &keysym_keycode, NULL);
+ XLookupString(xkeyevent, str, 256, &keysym_unicode, nullptr);
+ XLookupString(&xkeyevent_no_mod, nullptr, 0, &keysym_keycode, nullptr);
// Meanwhile, XLookupString returns keysyms useful for unicode.
@@ -1869,18 +2000,17 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
xmblen = 8;
}
- if (xkeyevent->type == KeyPress && xic) {
-
+ if (xkeyevent->type == KeyPress && wd.xic) {
Status status;
#ifdef X_HAVE_UTF8_STRING
int utf8len = 8;
char *utf8string = (char *)memalloc(sizeof(char) * utf8len);
- int utf8bytes = Xutf8LookupString(xic, xkeyevent, utf8string,
+ int utf8bytes = Xutf8LookupString(wd.xic, xkeyevent, utf8string,
utf8len - 1, &keysym_unicode, &status);
if (status == XBufferOverflow) {
utf8len = utf8bytes + 1;
utf8string = (char *)memrealloc(utf8string, utf8len);
- utf8bytes = Xutf8LookupString(xic, xkeyevent, utf8string,
+ utf8bytes = Xutf8LookupString(wd.xic, xkeyevent, utf8string,
utf8len - 1, &keysym_unicode, &status);
}
utf8string[utf8bytes] = '\0';
@@ -1890,8 +2020,9 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
unsigned int keycode = KeyMappingX11::get_keycode(keysym_keycode);
unsigned int physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode);
- if (keycode >= 'a' && keycode <= 'z')
+ if (keycode >= 'a' && keycode <= 'z') {
keycode -= 'a' - 'A';
+ }
String tmp;
tmp.parse_utf8(utf8string, utf8bytes);
@@ -1902,11 +2033,13 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
continue;
}
- if (keycode == 0)
+ if (keycode == 0) {
keycode = physical_keycode;
+ }
- get_key_modifier_state(xkeyevent->state, k);
+ _get_key_modifier_state(xkeyevent->state, k);
+ k->set_window_id(p_window);
k->set_unicode(tmp[i]);
k->set_pressed(keypress);
@@ -1924,7 +2057,7 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
k->set_shift(true);
}
- input->accumulate_input_event(k);
+ Input::get_singleton()->accumulate_input_event(k);
}
memfree(utf8string);
return;
@@ -1932,7 +2065,6 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
memfree(utf8string);
#else
do {
-
int mnbytes = XmbLookupString(xic, xkeyevent, xmbstring, xmblen - 1, &keysym_unicode, &status);
xmbstring[mnbytes] = '\0';
@@ -1944,7 +2076,7 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
#endif
}
- /* Phase 2, obtain a pigui keycode from the keysym */
+ /* Phase 2, obtain a Godot keycode from the keysym */
// KeyMappingX11 just translated the X11 keysym to a PIGUI
// keysym, so it works in all platforms the same.
@@ -1970,11 +2102,13 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
bool keypress = xkeyevent->type == KeyPress;
- if (physical_keycode == 0 && keycode == 0 && unicode == 0)
+ if (physical_keycode == 0 && keycode == 0 && unicode == 0) {
return;
+ }
- if (keycode == 0)
+ if (keycode == 0) {
keycode = physical_keycode;
+ }
/* Phase 5, determine modifier mask */
@@ -1986,8 +2120,9 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
Ref<InputEventKey> k;
k.instance();
+ k->set_window_id(p_window);
- get_key_modifier_state(xkeyevent->state, k);
+ _get_key_modifier_state(xkeyevent->state, k);
/* Phase 6, determine echo character */
@@ -1997,7 +2132,6 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
// difference in time is below a threshold.
if (xkeyevent->type != KeyPress) {
-
p_echo = false;
// make sure there are events pending,
@@ -2017,11 +2151,11 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
#undef ABSDIFF
if (peek_event.type == KeyPress && threshold < 5) {
KeySym rk;
- XLookupString((XKeyEvent *)&peek_event, str, 256, &rk, NULL);
+ XLookupString((XKeyEvent *)&peek_event, str, 256, &rk, nullptr);
if (rk == keysym_keycode) {
XEvent event;
XNextEvent(x11_display, &event); //erase next event
- handle_key_event((XKeyEvent *)&event, true);
+ _handle_key_event(p_window, (XKeyEvent *)&event, true);
return; //ignore current, echo next
}
}
@@ -2036,8 +2170,9 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
k->set_pressed(keypress);
- if (keycode >= 'a' && keycode <= 'z')
+ if (keycode >= 'a' && keycode <= 'z') {
keycode -= 'a' - 'A';
+ }
k->set_keycode(keycode);
k->set_physical_keycode(physical_keycode);
@@ -2054,14 +2189,15 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
//don't set mod state if modifier keys are released by themselves
//else event.is_action() will not work correctly here
if (!k->is_pressed()) {
- if (k->get_keycode() == KEY_SHIFT)
+ if (k->get_keycode() == KEY_SHIFT) {
k->set_shift(false);
- else if (k->get_keycode() == KEY_CONTROL)
+ } else if (k->get_keycode() == KEY_CONTROL) {
k->set_control(false);
- else if (k->get_keycode() == KEY_ALT)
+ } else if (k->get_keycode() == KEY_ALT) {
k->set_alt(false);
- else if (k->get_keycode() == KEY_META)
+ } else if (k->get_keycode() == KEY_META) {
k->set_metakey(false);
+ }
}
bool last_is_pressed = Input::get_singleton()->is_key_pressed(k->get_keycode());
@@ -2071,115 +2207,153 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
}
}
- //printf("key: %x\n",k->get_keycode());
- input->accumulate_input_event(k);
+ Input::get_singleton()->accumulate_input_event(k);
}
-struct Property {
- unsigned char *data;
- int format, nitems;
- Atom type;
-};
-
-static Property read_property(Display *p_display, Window p_window, Atom p_property) {
+void DisplayServerX11::_xim_destroy_callback(::XIM im, ::XPointer client_data,
+ ::XPointer call_data) {
+ WARN_PRINT("Input method stopped");
+ DisplayServerX11 *ds = reinterpret_cast<DisplayServerX11 *>(client_data);
+ ds->xim = nullptr;
- Atom actual_type;
- int actual_format;
- unsigned long nitems;
- unsigned long bytes_after;
- unsigned char *ret = 0;
+ for (Map<WindowID, WindowData>::Element *E = ds->windows.front(); E; E = E->next()) {
+ E->get().xic = nullptr;
+ }
+}
- int read_bytes = 1024;
+void DisplayServerX11::_window_changed(XEvent *event) {
+ WindowID window_id = MAIN_WINDOW_ID;
- //Keep trying to read the property until there are no
- //bytes unread.
- do {
- if (ret != 0)
- XFree(ret);
+ // Assign the event to the relevant window
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (event->xany.window == E->get().x11_window) {
+ window_id = E->key();
+ break;
+ }
+ }
- XGetWindowProperty(p_display, p_window, p_property, 0, read_bytes, False, AnyPropertyType,
- &actual_type, &actual_format, &nitems, &bytes_after,
- &ret);
+ Rect2i new_rect;
- read_bytes *= 2;
+ WindowData &wd = windows[window_id];
+ if (wd.x11_window != event->xany.window) { // Check if the correct window, in case it was not main window or anything else
+ return;
+ }
- } while (bytes_after != 0);
+ {
+ //the position in xconfigure is not useful here, obtain it manually
+ int x, y;
+ Window child;
+ XTranslateCoordinates(x11_display, wd.x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child);
+ new_rect.position.x = x;
+ new_rect.position.y = y;
- Property p = { ret, actual_format, (int)nitems, actual_type };
+ new_rect.size.width = event->xconfigure.width;
+ new_rect.size.height = event->xconfigure.height;
+ }
- return p;
-}
+ if (new_rect == Rect2i(wd.position, wd.size)) {
+ return;
+ }
+ if (wd.xic) {
+ // Not portable.
+ window_set_ime_position(Point2(0, 1));
+ }
-static Atom pick_target_from_list(Display *p_display, Atom *p_list, int p_count) {
+ wd.position = new_rect.position;
+ wd.size = new_rect.size;
- static const char *target_type = "text/uri-list";
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_resize(window_id, wd.size.width, wd.size.height);
+ }
+#endif
- for (int i = 0; i < p_count; i++) {
+ print_line("DisplayServer::_window_changed: " + itos(window_id) + " rect: " + new_rect);
+ if (!wd.rect_changed_callback.is_null()) {
+ Rect2i r = new_rect;
- Atom atom = p_list[i];
+ Variant rect = r;
- if (atom != None && String(XGetAtomName(p_display, atom)) == target_type)
- return atom;
+ Variant *rectp = &rect;
+ Variant ret;
+ Callable::CallError ce;
+ wd.rect_changed_callback.call((const Variant **)&rectp, 1, ret, ce);
}
- return None;
}
-static Atom pick_target_from_atoms(Display *p_disp, Atom p_t1, Atom p_t2, Atom p_t3) {
-
- static const char *target_type = "text/uri-list";
- if (p_t1 != None && String(XGetAtomName(p_disp, p_t1)) == target_type)
- return p_t1;
-
- if (p_t2 != None && String(XGetAtomName(p_disp, p_t2)) == target_type)
- return p_t2;
-
- if (p_t3 != None && String(XGetAtomName(p_disp, p_t3)) == target_type)
- return p_t3;
-
- return None;
+void DisplayServerX11::_dispatch_input_events(const Ref<InputEvent> &p_event) {
+ ((DisplayServerX11 *)(get_singleton()))->_dispatch_input_event(p_event);
}
-void OS_X11::_window_changed(XEvent *event) {
+void DisplayServerX11::_dispatch_input_event(const Ref<InputEvent> &p_event) {
+ Variant ev = p_event;
+ Variant *evp = &ev;
+ Variant ret;
+ Callable::CallError ce;
- if (xic) {
- // Not portable.
- set_ime_position(Point2(0, 1));
+ 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()));
+ Callable callable = windows[event_from_window->get_window_id()].input_event_callback;
+ if (callable.is_null()) {
+ return;
+ }
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ } else {
+ //send to all windows
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ Callable callable = E->get().input_event_callback;
+ if (callable.is_null()) {
+ continue;
+ }
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ }
}
- if ((event->xconfigure.width == current_videomode.width) &&
- (event->xconfigure.height == current_videomode.height))
- return;
-
- current_videomode.width = event->xconfigure.width;
- current_videomode.height = event->xconfigure.height;
+}
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
- context_vulkan->window_resize(0, current_videomode.width, current_videomode.height);
+void DisplayServerX11::_send_window_event(const WindowData &wd, WindowEvent p_event) {
+ if (!wd.event_callback.is_null()) {
+ Variant event = int(p_event);
+ Variant *eventp = &event;
+ Variant ret;
+ Callable::CallError ce;
+ wd.event_callback.call((const Variant **)&eventp, 1, ret, ce);
}
-#endif
}
-void OS_X11::process_xevents() {
-
- //printf("checking events %i\n", XPending(x11_display));
+void DisplayServerX11::process_events() {
+ _THREAD_SAFE_METHOD_
do_mouse_warp = false;
// Is the current mouse mode one where it needs to be grabbed.
bool mouse_mode_grab = mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED;
+ xi.pressure = 0;
+ xi.tilt = Vector2();
+ xi.pressure_supported = false;
+
while (XPending(x11_display) > 0) {
XEvent event;
XNextEvent(x11_display, &event);
+ WindowID window_id = MAIN_WINDOW_ID;
+
+ // Assign the event to the relevant window
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (event.xany.window == E->get().x11_window) {
+ window_id = E->key();
+ break;
+ }
+ }
+
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;
int index = event_data->detail;
Vector2 pos = Vector2(event_data->event_x, event_data->event_y);
@@ -2187,7 +2361,7 @@ void OS_X11::process_xevents() {
switch (event_data->evtype) {
case XI_HierarchyChanged:
case XI_DeviceChanged: {
- refresh_device_info();
+ _refresh_device_info();
} break;
case XI_RawMotion: {
XIRawEvent *raw_event = (XIRawEvent *)event_data;
@@ -2204,9 +2378,6 @@ void OS_X11::process_xevents() {
double rel_x = 0.0;
double rel_y = 0.0;
- double pressure = 0.0;
- double tilt_x = 0.0;
- double tilt_y = 0.0;
if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_ABSX)) {
rel_x = *values;
@@ -2219,24 +2390,41 @@ void OS_X11::process_xevents() {
}
if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_PRESSURE)) {
- pressure = *values;
+ Map<int, Vector2>::Element *pen_pressure = xi.pen_pressure_range.find(device_id);
+ if (pen_pressure) {
+ Vector2 pen_pressure_range = pen_pressure->value();
+ if (pen_pressure_range != Vector2()) {
+ xi.pressure_supported = true;
+ xi.pressure = (*values - pen_pressure_range[0]) /
+ (pen_pressure_range[1] - pen_pressure_range[0]);
+ }
+ }
+
values++;
}
if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_TILTX)) {
- tilt_x = *values;
+ Map<int, Vector2>::Element *pen_tilt_x = xi.pen_tilt_x_range.find(device_id);
+ if (pen_tilt_x) {
+ Vector2 pen_tilt_x_range = pen_tilt_x->value();
+ if (pen_tilt_x_range != Vector2()) {
+ xi.tilt.x = ((*values - pen_tilt_x_range[0]) / (pen_tilt_x_range[1] - pen_tilt_x_range[0])) * 2 - 1;
+ }
+ }
+
values++;
}
if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_TILTY)) {
- tilt_y = *values;
- }
+ Map<int, Vector2>::Element *pen_tilt_y = xi.pen_tilt_y_range.find(device_id);
+ if (pen_tilt_y) {
+ Vector2 pen_tilt_y_range = pen_tilt_y->value();
+ if (pen_tilt_y_range != Vector2()) {
+ xi.tilt.y = ((*values - pen_tilt_y_range[0]) / (pen_tilt_y_range[1] - pen_tilt_y_range[0])) * 2 - 1;
+ }
+ }
- Map<int, Vector3>::Element *pen_info = xi.pen_devices.find(device_id);
- if (pen_info) {
- Vector3 mult = pen_info->value();
- if (mult.x != 0.0) xi.pressure = pressure / mult.x;
- if ((mult.y != 0.0) && (mult.z != 0.0)) xi.tilt = Vector2(tilt_x / mult.y, tilt_y / mult.z);
+ values++;
}
// https://bugs.freedesktop.org/show_bug.cgi?id=71609
@@ -2272,48 +2460,49 @@ void OS_X11::process_xevents() {
//XIAllowTouchEvents(x11_display, event_data->deviceid, event_data->detail, x11_window, XIAcceptTouch);
case XI_TouchEnd: {
-
bool is_begin = event_data->evtype == XI_TouchBegin;
Ref<InputEventScreenTouch> st;
st.instance();
+ st->set_window_id(window_id);
st->set_index(index);
st->set_position(pos);
st->set_pressed(is_begin);
if (is_begin) {
- if (xi.state.has(index)) // Defensive
+ if (xi.state.has(index)) { // Defensive
break;
+ }
xi.state[index] = pos;
if (xi.state.size() == 1) {
// X11 may send a motion event when a touch gesture begins, that would result
// in a spurious mouse motion event being sent to Godot; remember it to be able to filter it out
xi.mouse_pos_to_filter = pos;
}
- input->accumulate_input_event(st);
+ Input::get_singleton()->accumulate_input_event(st);
} else {
- if (!xi.state.has(index)) // Defensive
+ if (!xi.state.has(index)) { // Defensive
break;
+ }
xi.state.erase(index);
- input->accumulate_input_event(st);
+ Input::get_singleton()->accumulate_input_event(st);
}
} break;
case XI_TouchUpdate: {
-
Map<int, Vector2>::Element *curr_pos_elem = xi.state.find(index);
if (!curr_pos_elem) { // Defensive
break;
}
if (curr_pos_elem->value() != pos) {
-
Ref<InputEventScreenDrag> sd;
sd.instance();
+ sd->set_window_id(window_id);
sd->set_index(index);
sd->set_position(pos);
sd->set_relative(pos - curr_pos_elem->value());
- input->accumulate_input_event(sd);
+ Input::get_singleton()->accumulate_input_event(sd);
curr_pos_elem->value() = pos;
}
@@ -2338,31 +2527,37 @@ void OS_X11::process_xevents() {
minimized = (visibility->state == VisibilityFullyObscured);
} break;
case LeaveNotify: {
- if (main_loop && !mouse_mode_grab)
- main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
+ if (!mouse_mode_grab) {
+ _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
} break;
case EnterNotify: {
- if (main_loop && !mouse_mode_grab)
- main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
+ if (!mouse_mode_grab) {
+ _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
+ }
} break;
case FocusIn:
minimized = false;
window_has_focus = true;
- main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
+ _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_IN);
window_focused = true;
if (mouse_mode_grab) {
// Show and update the cursor if confined and the window regained focus.
- if (mouse_mode == MOUSE_MODE_CONFINED)
- XUndefineCursor(x11_display, x11_window);
- else if (mouse_mode == MOUSE_MODE_CAPTURED) // or re-hide it in captured mode
- XDefineCursor(x11_display, x11_window, null_cursor);
-
- XGrabPointer(
- x11_display, x11_window, True,
- ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
- GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime);
+
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (mouse_mode == MOUSE_MODE_CONFINED) {
+ XUndefineCursor(x11_display, E->get().x11_window);
+ } else if (mouse_mode == MOUSE_MODE_CAPTURED) { // or re-hide it in captured mode
+ XDefineCursor(x11_display, E->get().x11_window, null_cursor);
+ }
+
+ XGrabPointer(
+ x11_display, E->get().x11_window, True,
+ ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
+ GrabModeAsync, GrabModeAsync, E->get().x11_window, None, CurrentTime);
+ }
}
#ifdef TOUCH_ENABLED
// Grab touch devices to avoid OS gesture interference
@@ -2370,22 +2565,24 @@ void OS_X11::process_xevents() {
XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask);
}*/
#endif
- if (xic) {
- XSetICFocus(xic);
+ if (windows[window_id].xic) {
+ XSetICFocus(windows[window_id].xic);
}
break;
case FocusOut:
window_has_focus = false;
- input->release_pressed_events();
- main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
+ Input::get_singleton()->release_pressed_events();
+ _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_OUT);
window_focused = false;
if (mouse_mode_grab) {
- //dear X11, I try, I really try, but you never work, you do whathever you want.
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
- // Show the cursor if we're in captured mode so it doesn't look weird.
- XUndefineCursor(x11_display, x11_window);
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ //dear X11, I try, I really try, but you never work, you do whathever you want.
+ if (mouse_mode == MOUSE_MODE_CAPTURED) {
+ // Show the cursor if we're in captured mode so it doesn't look weird.
+ XUndefineCursor(x11_display, E->get().x11_window);
+ }
}
XUngrabPointer(x11_display, CurrentTime);
}
@@ -2397,17 +2594,17 @@ void OS_X11::process_xevents() {
// Release every pointer to avoid sticky points
for (Map<int, Vector2>::Element *E = xi.state.front(); E; E = E->next()) {
-
Ref<InputEventScreenTouch> st;
st.instance();
st->set_index(E->key());
+ st->set_window_id(window_id);
st->set_position(E->get());
- input->accumulate_input_event(st);
+ Input::get_singleton()->accumulate_input_event(st);
}
xi.state.clear();
#endif
- if (xic) {
- XUnsetICFocus(xic);
+ if (windows[window_id].xic) {
+ XSetICFocus(windows[window_id].xic);
}
break;
@@ -2416,7 +2613,6 @@ void OS_X11::process_xevents() {
break;
case ButtonPress:
case ButtonRelease: {
-
/* exit in case of a mouse button press */
last_timestamp = event.xbutton.time;
if (mouse_mode == MOUSE_MODE_CAPTURED) {
@@ -2427,28 +2623,27 @@ void OS_X11::process_xevents() {
Ref<InputEventMouseButton> mb;
mb.instance();
- get_key_modifier_state(event.xbutton.state, mb);
+ mb->set_window_id(window_id);
+ _get_key_modifier_state(event.xbutton.state, mb);
mb->set_button_index(event.xbutton.button);
- if (mb->get_button_index() == 2)
+ if (mb->get_button_index() == 2) {
mb->set_button_index(3);
- else if (mb->get_button_index() == 3)
+ } else if (mb->get_button_index() == 3) {
mb->set_button_index(2);
- mb->set_button_mask(get_mouse_button_state(mb->get_button_index(), event.xbutton.type));
+ }
+ mb->set_button_mask(_get_mouse_button_state(mb->get_button_index(), event.xbutton.type));
mb->set_position(Vector2(event.xbutton.x, event.xbutton.y));
mb->set_global_position(mb->get_position());
mb->set_pressed((event.type == ButtonPress));
if (event.type == ButtonPress) {
-
- uint64_t diff = get_ticks_usec() / 1000 - last_click_ms;
+ uint64_t diff = OS::get_singleton()->get_ticks_usec() / 1000 - last_click_ms;
if (mb->get_button_index() == last_click_button_index) {
-
- if (diff < 400 && Point2(last_click_pos).distance_to(Point2(event.xbutton.x, event.xbutton.y)) < 5) {
-
+ if (diff < 400 && Vector2(last_click_pos).distance_to(Vector2(event.xbutton.x, event.xbutton.y)) < 5) {
last_click_ms = 0;
- last_click_pos = Point2(-100, -100);
+ last_click_pos = Point2i(-100, -100);
last_click_button_index = -1;
mb->set_doubleclick(true);
}
@@ -2459,21 +2654,20 @@ void OS_X11::process_xevents() {
if (!mb->is_doubleclick()) {
last_click_ms += diff;
- last_click_pos = Point2(event.xbutton.x, event.xbutton.y);
+ last_click_pos = Point2i(event.xbutton.x, event.xbutton.y);
}
}
- input->accumulate_input_event(mb);
+ Input::get_singleton()->accumulate_input_event(mb);
} break;
case MotionNotify: {
-
// The X11 API requires filtering one-by-one through the motion
// notify events, in order to figure out which event is the one
// generated by warping the mouse pointer.
while (true) {
- if (mouse_mode == MOUSE_MODE_CAPTURED && event.xmotion.x == current_videomode.width / 2 && event.xmotion.y == current_videomode.height / 2) {
+ if (mouse_mode == MOUSE_MODE_CAPTURED && event.xmotion.x == windows[MAIN_WINDOW_ID].size.width / 2 && event.xmotion.y == windows[MAIN_WINDOW_ID].size.height / 2) {
//this is likely the warp event since it was warped here
center = Vector2(event.xmotion.x, event.xmotion.y);
break;
@@ -2497,7 +2691,7 @@ void OS_X11::process_xevents() {
// Motion is also simple.
// A little hack is in order
// to be able to send relative motion events.
- Point2 pos(event.xmotion.x, event.xmotion.y);
+ Point2i pos(event.xmotion.x, event.xmotion.y);
// Avoidance of spurious mouse motion (see handling of touch)
bool filter = false;
@@ -2523,7 +2717,6 @@ void OS_X11::process_xevents() {
}
if (!last_mouse_pos_valid) {
-
last_mouse_pos = pos;
last_mouse_pos_valid = true;
}
@@ -2534,7 +2727,7 @@ void OS_X11::process_xevents() {
// RawMotion provides more precision than MotionNotify, which doesn't sense subpixel motion.
// Therefore, MotionNotify cannot be the authority on relative mouse motion.
// This means we need to take a combined approach...
- Point2 rel;
+ Point2i rel;
// Only use raw input if in capture mode. Otherwise use the classic behavior.
if (mouse_mode == MOUSE_MODE_CAPTURED) {
@@ -2548,24 +2741,29 @@ void OS_X11::process_xevents() {
xi.relative_motion.y = 0;
if (mouse_mode == MOUSE_MODE_CAPTURED) {
- pos = Point2i(current_videomode.width / 2, current_videomode.height / 2);
+ pos = Point2i(windows[MAIN_WINDOW_ID].size.width / 2, windows[MAIN_WINDOW_ID].size.height / 2);
}
Ref<InputEventMouseMotion> mm;
mm.instance();
- mm->set_pressure(xi.pressure);
+ mm->set_window_id(window_id);
+ if (xi.pressure_supported) {
+ mm->set_pressure(xi.pressure);
+ } else {
+ mm->set_pressure((mouse_get_button_state() & (1 << (BUTTON_LEFT - 1))) ? 1.0f : 0.0f);
+ }
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(get_mouse_button_state());
+ _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->set_mouse_position(posi);
- mm->set_speed(input->get_last_mouse_speed());
+ Input::get_singleton()->set_mouse_position(posi);
+ mm->set_speed(Input::get_singleton()->get_last_mouse_speed());
mm->set_relative(rel);
@@ -2575,21 +2773,20 @@ void OS_X11::process_xevents() {
// 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)
- input->accumulate_input_event(mm);
+ if (window_has_focus || !mouse_mode_grab) {
+ Input::get_singleton()->accumulate_input_event(mm);
+ }
} break;
case KeyPress:
case KeyRelease: {
-
last_timestamp = event.xkey.time;
// key event is a little complex, so
// it will be handled in its own function.
- handle_key_event((XKeyEvent *)&event);
+ _handle_key_event(window_id, (XKeyEvent *)&event);
} break;
case SelectionRequest: {
-
XSelectionRequestEvent *req;
XEvent e, respond;
e = event;
@@ -2601,7 +2798,7 @@ void OS_X11::process_xevents() {
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 = OS::get_clipboard().utf8();
+ CharString clip = clipboard_get().utf8();
XChangeProperty(x11_display,
req->requestor,
req->property,
@@ -2612,7 +2809,6 @@ void OS_X11::process_xevents() {
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);
@@ -2635,8 +2831,9 @@ void OS_X11::process_xevents() {
} else {
char *targetname = XGetAtomName(x11_display, req->target);
printf("No Target '%s'\n", targetname);
- if (targetname)
+ if (targetname) {
XFree(targetname);
+ }
respond.xselection.property = None;
}
@@ -2653,14 +2850,20 @@ void OS_X11::process_xevents() {
case SelectionNotify:
if (event.xselection.target == requested) {
-
- Property p = read_property(x11_display, x11_window, XInternAtom(x11_display, "PRIMARY", 0));
+ Property p = _read_property(x11_display, windows[window_id].x11_window, XInternAtom(x11_display, "PRIMARY", 0));
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();
}
- main_loop->drop_files(files);
+
+ if (!windows[window_id].drop_files_callback.is_null()) {
+ Variant v = files;
+ Variant *vp = &v;
+ Variant ret;
+ Callable::CallError ce;
+ windows[window_id].drop_files_callback.call((const Variant **)&vp, 1, ret, ce);
+ }
//Reply that all is well.
XClientMessageEvent m;
@@ -2670,7 +2873,7 @@ void OS_X11::process_xevents() {
m.window = xdnd_source_window;
m.message_type = xdnd_finished;
m.format = 32;
- m.data.l[0] = x11_window;
+ m.data.l[0] = windows[window_id].x11_window;
m.data.l[1] = 1;
m.data.l[2] = xdnd_action_copy; //We only ever copy.
@@ -2680,22 +2883,22 @@ void OS_X11::process_xevents() {
case ClientMessage:
- if ((unsigned int)event.xclient.data.l[0] == (unsigned int)wm_delete)
- main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
+ if ((unsigned int)event.xclient.data.l[0] == (unsigned int)wm_delete) {
+ _send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);
+ }
else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_enter) {
-
//File(s) have been dragged over the window, check for supported target (text/uri-list)
xdnd_version = (event.xclient.data.l[1] >> 24);
Window source = event.xclient.data.l[0];
bool more_than_3 = event.xclient.data.l[1] & 1;
if (more_than_3) {
- Property p = read_property(x11_display, source, XInternAtom(x11_display, "XdndTypeList", False));
+ Property p = _read_property(x11_display, source, XInternAtom(x11_display, "XdndTypeList", False));
requested = pick_target_from_list(x11_display, (Atom *)p.data, p.nitems);
- } else
+ } else {
requested = pick_target_from_atoms(x11_display, event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]);
+ }
} else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_position) {
-
//xdnd position event, reply with an XDND status message
//just depending on type of data for now
XClientMessageEvent m;
@@ -2705,7 +2908,7 @@ void OS_X11::process_xevents() {
m.window = event.xclient.data.l[0];
m.message_type = xdnd_status;
m.format = 32;
- m.data.l[0] = x11_window;
+ m.data.l[0] = windows[window_id].x11_window;
m.data.l[1] = (requested != None);
m.data.l[2] = 0; //empty rectangle
m.data.l[3] = 0;
@@ -2714,13 +2917,13 @@ void OS_X11::process_xevents() {
XSendEvent(x11_display, event.xclient.data.l[0], False, NoEventMask, (XEvent *)&m);
XFlush(x11_display);
} else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_drop) {
-
if (requested != None) {
xdnd_source_window = event.xclient.data.l[0];
- if (xdnd_version >= 1)
- XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), x11_window, event.xclient.data.l[2]);
- else
- XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), x11_window, CurrentTime);
+ if (xdnd_version >= 1) {
+ XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), windows[window_id].x11_window, event.xclient.data.l[2]);
+ } else {
+ XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), windows[window_id].x11_window, CurrentTime);
+ }
} else {
//Reply that we're not interested.
XClientMessageEvent m;
@@ -2730,7 +2933,7 @@ void OS_X11::process_xevents() {
m.window = event.xclient.data.l[0];
m.message_type = xdnd_finished;
m.format = 32;
- m.data.l[0] = x11_window;
+ m.data.l[0] = windows[window_id].x11_window;
m.data.l[1] = 0;
m.data.l[2] = None; //Failed.
XSendEvent(x11_display, event.xclient.data.l[0], False, NoEventMask, (XEvent *)&m);
@@ -2745,9 +2948,8 @@ void OS_X11::process_xevents() {
XFlush(x11_display);
if (do_mouse_warp) {
-
- XWarpPointer(x11_display, None, x11_window,
- 0, 0, 0, 0, (int)current_videomode.width / 2, (int)current_videomode.height / 2);
+ XWarpPointer(x11_display, None, windows[MAIN_WINDOW_ID].x11_window,
+ 0, 0, 0, 0, (int)windows[MAIN_WINDOW_ID].size.width / 2, (int)windows[MAIN_WINDOW_ID].size.height / 2);
/*
Window root, child;
@@ -2761,808 +2963,867 @@ void OS_X11::process_xevents() {
*/
}
- input->flush_accumulated_events();
+ Input::get_singleton()->flush_accumulated_events();
}
-MainLoop *OS_X11::get_main_loop() const {
-
- return main_loop;
+void DisplayServerX11::release_rendering_thread() {
}
-void OS_X11::delete_main_loop() {
-
- if (main_loop)
- memdelete(main_loop);
- main_loop = NULL;
+void DisplayServerX11::make_rendering_thread() {
}
-void OS_X11::set_main_loop(MainLoop *p_main_loop) {
-
- main_loop = p_main_loop;
- input->set_main_loop(p_main_loop);
+void DisplayServerX11::swap_buffers() {
}
-bool OS_X11::can_draw() const {
-
- return !minimized;
-};
-
-void OS_X11::set_clipboard(const String &p_text) {
-
- OS::set_clipboard(p_text);
-
- XSetSelectionOwner(x11_display, XA_PRIMARY, x11_window, CurrentTime);
- XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, CurrentTime);
-};
-
-static String _get_clipboard_impl(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard, Atom target) {
-
- 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;
- };
+void DisplayServerX11::_update_context(WindowData &wd) {
+ XClassHint *classHint = XAllocClassHint();
- if (Sown != None) {
- 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) {
+ if (classHint) {
+ CharString name_str;
+ switch (context) {
+ case CONTEXT_EDITOR:
+ name_str = "Godot_Editor";
break;
- };
- };
+ case CONTEXT_PROJECTMAN:
+ name_str = "Godot_ProjectList";
+ break;
+ case CONTEXT_ENGINE:
+ name_str = "Godot_Engine";
+ break;
+ }
- //
- // Do not get any data, see how much data is there
- //
- XGetWindowProperty(x11_display, x11_window,
- selection, // Tricky..
- 0, 0, // offset - len
- 0, // Delete 0==FALSE
- AnyPropertyType, //flag
- &type, // return type
- &format, // return format
- &len, &bytes_left, //that
- &data);
- // DATA is There
- if (bytes_left > 0) {
- 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");
- XFree(data);
+ CharString class_str;
+ if (context == CONTEXT_ENGINE) {
+ String config_name = GLOBAL_GET("application/config/name");
+ if (config_name.length() == 0) {
+ class_str = "Godot_Engine";
+ } else {
+ class_str = config_name.utf8();
+ }
+ } else {
+ class_str = "Godot";
}
- }
- return ret;
-}
+ classHint->res_class = class_str.ptrw();
+ classHint->res_name = name_str.ptrw();
-static String _get_clipboard(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard) {
- String ret;
- Atom utf8_atom = XInternAtom(x11_display, "UTF8_STRING", True);
- if (utf8_atom != None) {
- ret = _get_clipboard_impl(p_source, x11_window, x11_display, p_internal_clipboard, utf8_atom);
- }
- if (ret == "") {
- ret = _get_clipboard_impl(p_source, x11_window, x11_display, p_internal_clipboard, XA_STRING);
+ XSetClassHint(x11_display, wd.x11_window, classHint);
+ XFree(classHint);
}
- return ret;
}
-String OS_X11::get_clipboard() const {
+void DisplayServerX11::set_context(Context p_context) {
+ _THREAD_SAFE_METHOD_
- String ret;
- ret = _get_clipboard(XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, x11_display, OS::get_clipboard());
+ context = p_context;
- if (ret == "") {
- ret = _get_clipboard(XA_PRIMARY, x11_window, x11_display, OS::get_clipboard());
- };
-
- return ret;
-}
-
-String OS_X11::get_name() const {
-
- return "X11";
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ _update_context(E->get());
+ }
}
-Error OS_X11::shell_open(String p_uri) {
-
- Error ok;
- List<String> args;
- args.push_back(p_uri);
- ok = execute("xdg-open", args, false);
- if (ok == OK)
- return OK;
- ok = execute("gnome-open", args, false);
- if (ok == OK)
- return OK;
- ok = execute("kde-open", args, false);
- return ok;
+void DisplayServerX11::set_native_icon(const String &p_filename) {
+ WARN_PRINT("Native icon not supported by this display server.");
}
-bool OS_X11::_check_internal_feature_support(const String &p_feature) {
-
- return p_feature == "pc";
+bool g_set_icon_error = false;
+int set_icon_errorhandler(Display *dpy, XErrorEvent *ev) {
+ g_set_icon_error = true;
+ return 0;
}
-String OS_X11::get_config_path() const {
+void DisplayServerX11::set_icon(const Ref<Image> &p_icon) {
+ _THREAD_SAFE_METHOD_
- if (has_environment("XDG_CONFIG_HOME")) {
- return get_environment("XDG_CONFIG_HOME");
- } else if (has_environment("HOME")) {
- return get_environment("HOME").plus_file(".config");
- } else {
- return ".";
- }
-}
+ WindowData &wd = windows[MAIN_WINDOW_ID];
-String OS_X11::get_data_path() const {
+ int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&set_icon_errorhandler);
- if (has_environment("XDG_DATA_HOME")) {
- return get_environment("XDG_DATA_HOME");
- } else if (has_environment("HOME")) {
- return get_environment("HOME").plus_file(".local/share");
- } else {
- return get_config_path();
- }
-}
+ Atom net_wm_icon = XInternAtom(x11_display, "_NET_WM_ICON", False);
-String OS_X11::get_cache_path() const {
+ if (p_icon.is_valid()) {
+ Ref<Image> img = p_icon->duplicate();
+ img->convert(Image::FORMAT_RGBA8);
- if (has_environment("XDG_CACHE_HOME")) {
- return get_environment("XDG_CACHE_HOME");
- } else if (has_environment("HOME")) {
- return get_environment("HOME").plus_file(".cache");
- } else {
- return get_config_path();
- }
-}
+ while (true) {
+ int w = img->get_width();
+ int h = img->get_height();
-String OS_X11::get_system_dir(SystemDir p_dir) const {
+ if (g_set_icon_error) {
+ g_set_icon_error = false;
- String xdgparam;
+ WARN_PRINT("Icon too large, attempting to resize icon.");
- switch (p_dir) {
- case SYSTEM_DIR_DESKTOP: {
+ int new_width, new_height;
+ if (w > h) {
+ new_width = w / 2;
+ new_height = h * new_width / w;
+ } else {
+ new_height = h / 2;
+ new_width = w * new_height / h;
+ }
- xdgparam = "DESKTOP";
- } break;
- case SYSTEM_DIR_DCIM: {
+ w = new_width;
+ h = new_height;
- xdgparam = "PICTURES";
+ if (!w || !h) {
+ WARN_PRINT("Unable to set icon.");
+ break;
+ }
- } break;
- case SYSTEM_DIR_DOCUMENTS: {
+ img->resize(w, h, Image::INTERPOLATE_CUBIC);
+ }
- xdgparam = "DOCUMENTS";
+ // We're using long to have wordsize (32Bit build -> 32 Bits, 64 Bit build -> 64 Bits
+ Vector<long> pd;
- } break;
- case SYSTEM_DIR_DOWNLOADS: {
+ pd.resize(2 + w * h);
- xdgparam = "DOWNLOAD";
+ pd.write[0] = w;
+ pd.write[1] = h;
- } break;
- case SYSTEM_DIR_MOVIES: {
+ const uint8_t *r = img->get_data().ptr();
- xdgparam = "VIDEOS";
+ long *wr = &pd.write[2];
+ uint8_t const *pr = r;
- } break;
- case SYSTEM_DIR_MUSIC: {
+ for (int i = 0; i < w * h; i++) {
+ long v = 0;
+ // A R G B
+ v |= pr[3] << 24 | pr[0] << 16 | pr[1] << 8 | pr[2];
+ *wr++ = v;
+ pr += 4;
+ }
- xdgparam = "MUSIC";
+ XChangeProperty(x11_display, wd.x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size());
- } break;
- case SYSTEM_DIR_PICTURES: {
+ if (!g_set_icon_error) {
+ break;
+ }
+ }
+ } else {
+ XDeleteProperty(x11_display, wd.x11_window, net_wm_icon);
+ }
- xdgparam = "PICTURES";
+ XFlush(x11_display);
+ XSetErrorHandler(oldHandler);
+}
- } break;
- case SYSTEM_DIR_RINGTONES: {
+Vector<String> DisplayServerX11::get_rendering_drivers_func() {
+ Vector<String> drivers;
- xdgparam = "MUSIC";
+#ifdef VULKAN_ENABLED
+ drivers.push_back("vulkan");
+#endif
+#ifdef OPENGL_ENABLED
+ drivers.push_back("opengl");
+#endif
- } break;
- }
+ return drivers;
+}
- String pipe;
- List<String> arg;
- arg.push_back(xdgparam);
- Error err = const_cast<OS_X11 *>(this)->execute("xdg-user-dir", arg, true, NULL, &pipe);
- if (err != OK)
- return ".";
- return pipe.strip_edges();
+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));
}
-void OS_X11::move_window_to_foreground() {
+DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+ //Create window
- XEvent xev;
- Atom net_active_window = XInternAtom(x11_display, "_NET_ACTIVE_WINDOW", False);
+ long visualMask = VisualScreenMask;
+ int numberOfVisuals;
+ XVisualInfo vInfoTemplate = {};
+ vInfoTemplate.screen = DefaultScreen(x11_display);
+ XVisualInfo *visualInfo = XGetVisualInfo(x11_display, visualMask, &vInfoTemplate, &numberOfVisuals);
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = 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;
+ Colormap colormap = XCreateColormap(x11_display, RootWindow(x11_display, vInfoTemplate.screen), visualInfo->visual, AllocNone);
- XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
- XFlush(x11_display);
-}
+ XSetWindowAttributes windowAttributes = {};
+ windowAttributes.colormap = colormap;
+ windowAttributes.background_pixel = 0xFFFFFFFF;
+ windowAttributes.border_pixel = 0;
+ windowAttributes.event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask;
-void OS_X11::set_cursor_shape(CursorShape p_shape) {
+ unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask;
- ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
+ WindowID id;
+ {
+ 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);
- if (p_shape == current_cursor) {
- return;
- }
+ XMapWindow(x11_display, wd.x11_window);
- if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
- if (cursors[p_shape] != None) {
- XDefineCursor(x11_display, x11_window, cursors[p_shape]);
- } else if (cursors[CURSOR_ARROW] != None) {
- XDefineCursor(x11_display, x11_window, cursors[CURSOR_ARROW]);
+ //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);
}
- }
- current_cursor = p_shape;
-}
+ long im_event_mask = 0;
-OS::CursorShape OS_X11::get_cursor_shape() const {
+ {
+ XIEventMask all_event_mask;
+ XSetWindowAttributes new_attr;
- return current_cursor;
-}
+ new_attr.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask |
+ LeaveWindowMask | PointerMotionMask |
+ Button1MotionMask |
+ Button2MotionMask | Button3MotionMask |
+ Button4MotionMask | Button5MotionMask |
+ ButtonMotionMask | KeymapStateMask |
+ ExposureMask | VisibilityChangeMask |
+ StructureNotifyMask |
+ SubstructureNotifyMask | SubstructureRedirectMask |
+ FocusChangeMask | PropertyChangeMask |
+ ColormapChangeMask | OwnerGrabButtonMask |
+ im_event_mask;
-void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
+ XChangeWindowAttributes(x11_display, wd.x11_window, CWEventMask, &new_attr);
- if (p_cursor.is_valid()) {
+ static unsigned char all_mask_data[XIMaskLen(XI_LASTEVENT)] = {};
- Map<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);
+ all_event_mask.deviceid = XIAllDevices;
+ all_event_mask.mask_len = sizeof(all_mask_data);
+ all_event_mask.mask = all_mask_data;
- if (cursor_c) {
- if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {
- set_cursor_shape(p_shape);
- return;
+ XISetMask(all_event_mask.mask, XI_HierarchyChanged);
+
+#ifdef TOUCH_ENABLED
+ if (xi.touch_devices.size()) {
+ XISetMask(all_event_mask.mask, XI_TouchBegin);
+ XISetMask(all_event_mask.mask, XI_TouchUpdate);
+ XISetMask(all_event_mask.mask, XI_TouchEnd);
+ XISetMask(all_event_mask.mask, XI_TouchOwnership);
}
+#endif
- cursors_cache.erase(p_shape);
+ XISelectEvents(x11_display, wd.x11_window, &all_event_mask, 1);
}
- Ref<Texture2D> texture = p_cursor;
- Ref<AtlasTexture> atlas_texture = p_cursor;
- Ref<Image> image;
- Size2 texture_size;
- Rect2 atlas_rect;
+ /* 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 (texture.is_valid()) {
- image = texture->get_data();
+ if (xim && xim_style) {
+ 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");
+ XDestroyIC(wd.xic);
+ wd.xic = nullptr;
+ }
+ if (wd.xic) {
+ XUnsetICFocus(wd.xic);
+ } else {
+ WARN_PRINT("XCreateIC couldn't create wd.xic");
+ }
+ } else {
+ wd.xic = nullptr;
+ WARN_PRINT("XCreateIC couldn't create wd.xic");
}
- if (!image.is_valid() && atlas_texture.is_valid()) {
- texture = atlas_texture->get_atlas();
+ _update_context(wd);
- atlas_rect.size.width = texture->get_width();
- atlas_rect.size.height = texture->get_height();
- atlas_rect.position.x = atlas_texture->get_region().position.x;
- atlas_rect.position.y = atlas_texture->get_region().position.y;
+ id = window_id_counter++;
- texture_size.width = atlas_texture->get_region().size.x;
- texture_size.height = atlas_texture->get_region().size.y;
- } else if (image.is_valid()) {
- texture_size.width = texture->get_width();
- texture_size.height = texture->get_height();
- }
+ windows[id] = wd;
- ERR_FAIL_COND(!texture.is_valid());
- ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
- ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
- ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
+ {
+ if (p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT) {
+ XSizeHints *xsh;
+ xsh = XAllocSizeHints();
- image = texture->get_data();
+ 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;
- ERR_FAIL_COND(!image.is_valid());
+ XSetWMNormalHints(x11_display, wd.x11_window, xsh);
+ XFree(xsh);
+ }
- // Create the cursor structure
- XcursorImage *cursor_image = XcursorImageCreate(texture_size.width, texture_size.height);
- XcursorUInt image_size = texture_size.width * texture_size.height;
- XcursorDim size = sizeof(XcursorPixel) * image_size;
+ bool make_utility = false;
- cursor_image->version = 1;
- cursor_image->size = size;
- cursor_image->xhot = p_hotspot.x;
- cursor_image->yhot = p_hotspot.y;
+ 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);
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
- // allocate memory to contain the whole file
- cursor_image->pixels = (XcursorPixel *)memalloc(size);
+ make_utility = true;
+ }
+ if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
+ make_utility = true;
+ }
- for (XcursorPixel index = 0; index < image_size; index++) {
- int row_index = floor(index / texture_size.width) + atlas_rect.position.y;
- int column_index = (index % int(texture_size.width)) + atlas_rect.position.x;
+ 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
- if (atlas_texture.is_valid()) {
- column_index = MIN(column_index, atlas_rect.size.width - 1);
- row_index = MIN(row_index, atlas_rect.size.height - 1);
- }
+ Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
+ Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
- *(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32();
- }
+ XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
- ERR_FAIL_COND(cursor_image->pixels == NULL);
+ 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);
- // Save it for a further usage
- cursors[p_shape] = XcursorImageLoadCursor(x11_display, cursor_image);
+ 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;
- Vector<Variant> params;
- params.push_back(p_cursor);
- params.push_back(p_hotspot);
- cursors_cache.insert(p_shape, params);
+ 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 (p_shape == current_cursor) {
- if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
- XDefineCursor(x11_display, x11_window, cursors[p_shape]);
+ XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
}
}
- memfree(cursor_image->pixels);
- XcursorImageDestroy(cursor_image);
- } else {
- // Reset to default system cursor
- if (img[p_shape]) {
- cursors[p_shape] = XcursorImageLoadCursor(x11_display, img[p_shape]);
- }
-
- CursorShape c = current_cursor;
- current_cursor = CURSOR_MAX;
- set_cursor_shape(c);
-
- cursors_cache.erase(p_shape);
- }
-}
+ if (id != MAIN_WINDOW_ID) {
+ XSizeHints my_hints = XSizeHints();
-void OS_X11::release_rendering_thread() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->release_current();
- }
-#endif
-}
+ 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;
-void OS_X11::make_rendering_thread() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->make_current();
- }
-#endif
-}
+ XSetNormalHints(x11_display, wd.x11_window, &my_hints);
+ XMoveWindow(x11_display, wd.x11_window, p_rect.position.x, p_rect.position.y);
+ }
-void OS_X11::swap_buffers() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->swap_buffers();
- }
-#endif
- /* not needed for now
#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
- context_vulkan->swap_buffers();
- }
-#endif*/
-}
-
-void OS_X11::alert(const String &p_alert, const String &p_title) {
- const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" };
-
- String path = get_environment("PATH");
- Vector<String> path_elems = path.split(":", false);
- String program;
+ if (context_vulkan) {
+ Error err = context_vulkan->window_create(id, wd.x11_window, x11_display, p_rect.size.width, p_rect.size.height);
+ ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a Vulkan window");
+ }
+#endif
- for (int i = 0; i < path_elems.size(); i++) {
- for (uint64_t k = 0; k < sizeof(message_programs) / sizeof(char *); k++) {
- String tested_path = path_elems[i].plus_file(message_programs[k]);
+ //set_class_hint(x11_display, wd.x11_window);
+ XFlush(x11_display);
- if (FileAccess::exists(tested_path)) {
- program = tested_path;
- break;
- }
- }
+ XSync(x11_display, False);
+ //XSetErrorHandler(oldHandler);
- if (program.length())
- break;
+ XFree(visualInfo);
}
- List<String> args;
+ WindowData &wd = windows[id];
- if (program.ends_with("zenity")) {
- args.push_back("--error");
- args.push_back("--width");
- args.push_back("500");
- args.push_back("--title");
- args.push_back(p_title);
- args.push_back("--text");
- args.push_back(p_alert);
- }
+ window_set_mode(p_mode, id);
- if (program.ends_with("kdialog")) {
- args.push_back("--error");
- args.push_back(p_alert);
- args.push_back("--title");
- args.push_back(p_title);
- }
+ //sync size
+ {
+ XWindowAttributes xwa;
- if (program.ends_with("Xdialog")) {
- args.push_back("--title");
- args.push_back(p_title);
- args.push_back("--msgbox");
- args.push_back(p_alert);
- args.push_back("0");
- args.push_back("0");
- }
+ XSync(x11_display, False);
+ XGetWindowAttributes(x11_display, wd.x11_window, &xwa);
- if (program.ends_with("xmessage")) {
- args.push_back("-center");
- args.push_back("-title");
- args.push_back(p_title);
- args.push_back(p_alert);
- }
+ wd.position.x = xwa.x;
+ wd.position.y = xwa.y;
+ wd.size.width = xwa.width;
+ wd.size.height = xwa.height;
- if (program.length()) {
- execute(program, args, true);
- } else {
- print_line(p_alert);
+ print_line("DisplayServer::_create_window " + itos(id) + " want rect: " + p_rect + " got rect " + Rect2i(xwa.x, xwa.y, xwa.width, xwa.height));
}
-}
-bool g_set_icon_error = false;
-int set_icon_errorhandler(Display *dpy, XErrorEvent *ev) {
- g_set_icon_error = true;
- return 0;
+ //set cursor
+ if (cursors[current_cursor] != None) {
+ XDefineCursor(x11_display, wd.x11_window, cursors[current_cursor]);
+ }
+ return id;
}
-void OS_X11::set_icon(const Ref<Image> &p_icon) {
- int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&set_icon_errorhandler);
-
- Atom net_wm_icon = XInternAtom(x11_display, "_NET_WM_ICON", False);
+DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
- if (p_icon.is_valid()) {
- Ref<Image> img = p_icon->duplicate();
- img->convert(Image::FORMAT_RGBA8);
+ r_error = OK;
- while (true) {
- int w = img->get_width();
- int h = img->get_height();
+ current_cursor = CURSOR_ARROW;
+ mouse_mode = MOUSE_MODE_VISIBLE;
- if (g_set_icon_error) {
- g_set_icon_error = false;
+ for (int i = 0; i < CURSOR_MAX; i++) {
+ cursors[i] = None;
+ img[i] = nullptr;
+ }
- WARN_PRINT("Icon too large, attempting to resize icon.");
+ last_button_state = 0;
- int new_width, new_height;
- if (w > h) {
- new_width = w / 2;
- new_height = h * new_width / w;
- } else {
- new_height = h / 2;
- new_width = w * new_height / h;
- }
+ xmbstring = nullptr;
- w = new_width;
- h = new_height;
+ last_click_ms = 0;
+ last_click_button_index = -1;
+ last_click_pos = Point2i(-100, -100);
- if (!w || !h) {
- WARN_PRINT("Unable to set icon.");
- break;
- }
+ last_timestamp = 0;
+ last_mouse_pos_valid = false;
+ last_keyrelease_time = 0;
- img->resize(w, h, Image::INTERPOLATE_CUBIC);
- }
+ XInitThreads(); //always use threads
- // We're using long to have wordsize (32Bit build -> 32 Bits, 64 Bit build -> 64 Bits
- Vector<long> pd;
+ /** XLIB INITIALIZATION **/
+ x11_display = XOpenDisplay(nullptr);
- pd.resize(2 + w * h);
+ if (!x11_display) {
+ ERR_PRINT("X11 Display is not available");
+ r_error = ERR_UNAVAILABLE;
+ return;
+ }
- pd.write[0] = w;
- pd.write[1] = h;
+ char *modifiers = nullptr;
+ Bool xkb_dar = False;
+ XAutoRepeatOn(x11_display);
+ xkb_dar = XkbSetDetectableAutoRepeat(x11_display, True, nullptr);
- const uint8_t *r = img->get_data().ptr();
+ // Try to support IME if detectable auto-repeat is supported
+ if (xkb_dar == True) {
+#ifdef X_HAVE_UTF8_STRING
+ // Xutf8LookupString will be used later instead of XmbLookupString before
+ // the multibyte sequences can be converted to unicode string.
+ modifiers = XSetLocaleModifiers("");
+#endif
+ }
- long *wr = &pd.write[2];
- uint8_t const *pr = r;
+ if (modifiers == nullptr) {
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ WARN_PRINT("IME is disabled");
+ }
+ XSetLocaleModifiers("@im=none");
+ WARN_PRINT("Error setting locale modifiers");
+ }
- for (int i = 0; i < w * h; i++) {
- long v = 0;
- // A R G B
- v |= pr[3] << 24 | pr[0] << 16 | pr[1] << 8 | pr[2];
- *wr++ = v;
- pr += 4;
+ const char *err;
+ xrr_get_monitors = nullptr;
+ xrr_free_monitors = nullptr;
+ int xrandr_major = 0;
+ int xrandr_minor = 0;
+ int event_base, error_base;
+ xrandr_ext_ok = XRRQueryExtension(x11_display, &event_base, &error_base);
+ 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);
+ } else {
+ XRRQueryVersion(x11_display, &xrandr_major, &xrandr_minor);
+ if (((xrandr_major << 8) | xrandr_minor) >= 0x0105) {
+ xrr_get_monitors = (xrr_get_monitors_t)dlsym(xrandr_handle, "XRRGetMonitors");
+ if (!xrr_get_monitors) {
+ err = dlerror();
+ fprintf(stderr, "could not find symbol XRRGetMonitors\nError: %s\n", err);
+ } else {
+ xrr_free_monitors = (xrr_free_monitors_t)dlsym(xrandr_handle, "XRRFreeMonitors");
+ if (!xrr_free_monitors) {
+ err = dlerror();
+ fprintf(stderr, "could not find XRRFreeMonitors\nError: %s\n", err);
+ xrr_get_monitors = nullptr;
+ }
}
-
- XChangeProperty(x11_display, x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size());
-
- if (!g_set_icon_error)
- break;
}
- } else {
- XDeleteProperty(x11_display, x11_window, net_wm_icon);
}
- XFlush(x11_display);
- XSetErrorHandler(oldHandler);
-}
+ if (!_refresh_device_info()) {
+ alert("Your system does not support XInput 2.\n"
+ "Please upgrade your distribution.",
+ "Unable to initialize XInput");
+ r_error = ERR_UNAVAILABLE;
+ return;
+ }
-void OS_X11::force_process_input() {
- process_xevents(); // get rid of pending events
-#ifdef JOYDEV_ENABLED
- joypad->process_joypads();
-#endif
-}
+ xim = XOpenIM(x11_display, nullptr, nullptr, nullptr);
-void OS_X11::run() {
+ if (xim == nullptr) {
+ WARN_PRINT("XOpenIM failed");
+ xim_style = 0L;
+ } else {
+ ::XIMCallback im_destroy_callback;
+ im_destroy_callback.client_data = (::XPointer)(this);
+ im_destroy_callback.callback = (::XIMProc)(_xim_destroy_callback);
+ if (XSetIMValues(xim, XNDestroyCallback, &im_destroy_callback,
+ nullptr) != nullptr) {
+ WARN_PRINT("Error setting XIM destroy callback");
+ }
- force_quit = false;
+ ::XIMStyles *xim_styles = nullptr;
+ xim_style = 0L;
+ char *imvalret = XGetIMValues(xim, XNQueryInputStyle, &xim_styles, nullptr);
+ if (imvalret != nullptr || xim_styles == nullptr) {
+ fprintf(stderr, "Input method doesn't support any styles\n");
+ }
- if (!main_loop)
- return;
+ if (xim_styles) {
+ xim_style = 0L;
+ for (int i = 0; i < xim_styles->count_styles; i++) {
+ if (xim_styles->supported_styles[i] ==
+ (XIMPreeditNothing | XIMStatusNothing)) {
+ xim_style = xim_styles->supported_styles[i];
+ break;
+ }
+ }
- main_loop->init();
+ XFree(xim_styles);
+ }
+ XFree(imvalret);
+ }
- //uint64_t last_ticks=get_ticks_usec();
+ /* Atorm internment */
+ wm_delete = XInternAtom(x11_display, "WM_DELETE_WINDOW", true);
+ //Set Xdnd (drag & drop) support
+ xdnd_aware = XInternAtom(x11_display, "XdndAware", False);
+ xdnd_version = 5;
+ xdnd_enter = XInternAtom(x11_display, "XdndEnter", False);
+ xdnd_position = XInternAtom(x11_display, "XdndPosition", False);
+ xdnd_status = XInternAtom(x11_display, "XdndStatus", False);
+ xdnd_action_copy = XInternAtom(x11_display, "XdndActionCopy", False);
+ xdnd_drop = XInternAtom(x11_display, "XdndDrop", False);
+ xdnd_finished = XInternAtom(x11_display, "XdndFinished", False);
+ xdnd_selection = XInternAtom(x11_display, "XdndSelection", False);
- //int frames=0;
- //uint64_t frame=0;
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!
+ //TODO - do Vulkan and GLES2 support checks, driver selection and fallback
+ rendering_driver = p_rendering_driver;
- while (!force_quit) {
+#ifndef _MSC_VER
+#warning Forcing vulkan rendering driver because OpenGL not implemented yet
+#endif
+ rendering_driver = "vulkan";
- process_xevents(); // get rid of pending events
-#ifdef JOYDEV_ENABLED
- joypad->process_joypads();
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ context_vulkan = memnew(VulkanContextX11);
+ if (context_vulkan->initialize() != OK) {
+ memdelete(context_vulkan);
+ context_vulkan = nullptr;
+ r_error = ERR_CANT_CREATE;
+ ERR_FAIL_MSG("Could not initialize Vulkan");
+ }
+ }
#endif
- if (Main::iteration())
- break;
- };
+ // Init context and rendering device
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver == "opengl_es") {
+ if (getenv("DRI_PRIME") == nullptr) {
+ int use_prime = -1;
- main_loop->finish();
-}
+ if (getenv("PRIMUS_DISPLAY") ||
+ getenv("PRIMUS_libGLd") ||
+ getenv("PRIMUS_libGLa") ||
+ getenv("PRIMUS_libGL") ||
+ getenv("PRIMUS_LOAD_GLOBAL") ||
+ getenv("BUMBLEBEE_SOCKET")) {
+ print_verbose("Optirun/primusrun detected. Skipping GPU detection");
+ use_prime = 0;
+ }
-bool OS_X11::is_joy_known(int p_device) {
- return input->is_joy_mapped(p_device);
-}
+ if (getenv("LD_LIBRARY_PATH")) {
+ String ld_library_path(getenv("LD_LIBRARY_PATH"));
+ Vector<String> libraries = ld_library_path.split(":");
-String OS_X11::get_joy_guid(int p_device) const {
- return input->get_joy_guid_remapped(p_device);
-}
+ for (int i = 0; i < libraries.size(); ++i) {
+ if (FileAccess::exists(libraries[i] + "/libGL.so.1") ||
+ FileAccess::exists(libraries[i] + "/libGL.so")) {
+ print_verbose("Custom libGL override detected. Skipping GPU detection");
+ use_prime = 0;
+ }
+ }
+ }
-void OS_X11::_set_use_vsync(bool p_enable) {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- if (context_gles2)
- context_gles2->set_use_vsync(p_enable);
- }
-#endif
-}
+ if (use_prime == -1) {
+ print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic.");
+ use_prime = detect_prime();
+ }
-void OS_X11::set_context(int p_context) {
+ if (use_prime) {
+ print_line("Found discrete GPU, setting DRI_PRIME=1 to use it.");
+ print_line("Note: Set DRI_PRIME=0 in the environment to disable Godot from using the discrete GPU.");
+ setenv("DRI_PRIME", "1", 1);
+ }
+ }
- XClassHint *classHint = XAllocClassHint();
+ ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE;
- if (classHint) {
+ context_gles2 = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, opengl_api_type));
- CharString name_str;
- switch (p_context) {
- case CONTEXT_EDITOR:
- name_str = "Godot_Editor";
- break;
- case CONTEXT_PROJECTMAN:
- name_str = "Godot_ProjectList";
- break;
- case CONTEXT_ENGINE:
- name_str = "Godot_Engine";
- break;
+ if (context_gles2->initialize() != OK) {
+ memdelete(context_gles2);
+ context_gles2 = nullptr;
+ ERR_FAIL_V(ERR_UNAVAILABLE);
}
- CharString class_str;
- if (p_context == CONTEXT_ENGINE) {
- String config_name = GLOBAL_GET("application/config/name");
- if (config_name.length() == 0) {
- class_str = "Godot_Engine";
- } else {
- class_str = config_name.utf8();
- }
+ context_gles2->set_use_vsync(current_videomode.use_vsync);
+
+ if (RasterizerGLES2::is_viable() == OK) {
+ RasterizerGLES2::register_config();
+ RasterizerGLES2::make_current();
} else {
- class_str = "Godot";
+ memdelete(context_gles2);
+ context_gles2 = nullptr;
+ ERR_FAIL_V(ERR_UNAVAILABLE);
+ }
+ }
+#endif
+ 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, p_flags, Rect2i(window_position, p_resolution));
+ for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
+ if (p_flags & (1 << i)) {
+ window_set_flag(WindowFlags(i), true, main_window);
}
-
- classHint->res_class = class_str.ptrw();
- classHint->res_name = name_str.ptrw();
-
- XSetClassHint(x11_display, x11_window, classHint);
- XFree(classHint);
}
-}
-
-void OS_X11::disable_crash_handler() {
- crash_handler.disable();
-}
-bool OS_X11::is_disable_crash_handler() const {
- return crash_handler.is_disabled();
-}
+//create RenderingDevice if used
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ //temporary
+ rendering_device_vulkan = memnew(RenderingDeviceVulkan);
+ rendering_device_vulkan->initialize(context_vulkan);
-static String get_mountpoint(const String &p_path) {
- struct stat s;
- if (stat(p_path.utf8().get_data(), &s)) {
- return "";
+ RasterizerRD::make_current();
}
+#endif
-#ifdef HAVE_MNTENT
- dev_t dev = s.st_dev;
- FILE *fd = setmntent("/proc/mounts", "r");
- if (!fd) {
- return "";
+ /*
+ rendering_server = memnew(RenderingServerRaster);
+ if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
+ rendering_server = memnew(RenderingServerWrapMT(rendering_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
}
+ */
- struct mntent mnt;
- char buf[1024];
- size_t buflen = 1024;
- while (getmntent_r(fd, &mnt, buf, buflen)) {
- if (!stat(mnt.mnt_dir, &s) && s.st_dev == dev) {
- endmntent(fd);
- return String(mnt.mnt_dir);
- }
+ {
+ //set all event master mask
+ XIEventMask all_master_event_mask;
+ static unsigned char all_master_mask_data[XIMaskLen(XI_LASTEVENT)] = {};
+ all_master_event_mask.deviceid = XIAllMasterDevices;
+ all_master_event_mask.mask_len = sizeof(all_master_mask_data);
+ all_master_event_mask.mask = all_master_mask_data;
+ XISetMask(all_master_event_mask.mask, XI_DeviceChanged);
+ XISetMask(all_master_event_mask.mask, XI_RawMotion);
+ XISelectEvents(x11_display, DefaultRootWindow(x11_display), &all_master_event_mask, 1);
}
- endmntent(fd);
-#endif
- return "";
-}
+ // Disabled by now since grabbing also blocks mouse events
+ // (they are received as extended events instead of standard events)
+ /*XIClearMask(xi.touch_event_mask.mask, XI_TouchOwnership);
-Error OS_X11::move_to_trash(const String &p_path) {
- String trash_can = "";
- String mnt = get_mountpoint(p_path);
+ // Grab touch devices to avoid OS gesture interference
+ for (int i = 0; i < xi.touch_devices.size(); ++i) {
+ XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask);
+ }*/
- // If there is a directory "[Mountpoint]/.Trash-[UID]/files", use it as the trash can.
- if (mnt != "") {
- String path(mnt + "/.Trash-" + itos(getuid()) + "/files");
- struct stat s;
- if (!stat(path.utf8().get_data(), &s)) {
- trash_can = path;
- }
- }
+ cursor_size = XcursorGetDefaultSize(x11_display);
+ cursor_theme = XcursorGetTheme(x11_display);
- // Otherwise, if ${XDG_DATA_HOME} is defined, use "${XDG_DATA_HOME}/Trash/files" as the trash can.
- if (trash_can == "") {
- char *dhome = getenv("XDG_DATA_HOME");
- if (dhome) {
- trash_can = String(dhome) + "/Trash/files";
- }
+ if (!cursor_theme) {
+ print_verbose("XcursorGetTheme could not get cursor theme");
+ cursor_theme = "default";
}
- // Otherwise, if ${HOME} is defined, use "${HOME}/.local/share/Trash/files" as the trash can.
- if (trash_can == "") {
- char *home = getenv("HOME");
- if (home) {
- trash_can = String(home) + "/.local/share/Trash/files";
+ for (int i = 0; i < CURSOR_MAX; i++) {
+ static const char *cursor_file[] = {
+ "left_ptr",
+ "xterm",
+ "hand2",
+ "cross",
+ "watch",
+ "left_ptr_watch",
+ "fleur",
+ "dnd-move",
+ "crossed_circle",
+ "v_double_arrow",
+ "h_double_arrow",
+ "size_bdiag",
+ "size_fdiag",
+ "move",
+ "row_resize",
+ "col_resize",
+ "question_arrow"
+ };
+
+ img[i] = XcursorLibraryLoadImage(cursor_file[i], cursor_theme, cursor_size);
+ if (!img[i]) {
+ const char *fallback = nullptr;
+
+ switch (i) {
+ case CURSOR_POINTING_HAND:
+ fallback = "pointer";
+ break;
+ case CURSOR_CROSS:
+ fallback = "crosshair";
+ break;
+ case CURSOR_WAIT:
+ fallback = "wait";
+ break;
+ case CURSOR_BUSY:
+ fallback = "progress";
+ break;
+ case CURSOR_DRAG:
+ fallback = "grabbing";
+ break;
+ case CURSOR_CAN_DROP:
+ fallback = "hand1";
+ break;
+ case CURSOR_FORBIDDEN:
+ fallback = "forbidden";
+ break;
+ case CURSOR_VSIZE:
+ fallback = "ns-resize";
+ break;
+ case CURSOR_HSIZE:
+ fallback = "ew-resize";
+ break;
+ case CURSOR_BDIAGSIZE:
+ fallback = "fd_double_arrow";
+ break;
+ case CURSOR_FDIAGSIZE:
+ fallback = "bd_double_arrow";
+ break;
+ case CURSOR_MOVE:
+ img[i] = img[CURSOR_DRAG];
+ break;
+ case CURSOR_VSPLIT:
+ fallback = "sb_v_double_arrow";
+ break;
+ case CURSOR_HSPLIT:
+ fallback = "sb_h_double_arrow";
+ break;
+ case CURSOR_HELP:
+ fallback = "help";
+ break;
+ }
+ if (fallback != nullptr) {
+ img[i] = XcursorLibraryLoadImage(fallback, cursor_theme, cursor_size);
+ }
+ }
+ if (img[i]) {
+ cursors[i] = XcursorImageLoadCursor(x11_display, img[i]);
+ } else {
+ print_verbose("Failed loading custom cursor: " + String(cursor_file[i]));
}
}
- // 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;
- }
+ {
+ // Creating an empty/transparent cursor
- // 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);
+ // Create 1x1 bitmap
+ Pixmap cursormask = XCreatePixmap(x11_display,
+ RootWindow(x11_display, DefaultScreen(x11_display)), 1, 1, 1);
- // 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;
- }
+ // Fill with zero
+ XGCValues xgc;
+ xgc.function = GXclear;
+ GC gc = XCreateGC(x11_display, cursormask, GCFunction, &xgc);
+ XFillRectangle(x11_display, cursormask, gc, 0, 0, 1, 1);
- // The trash can is successfully created, now move the given resource to it.
- // 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, NULL, NULL, &retval);
+ // Color value doesn't matter. Mask zero means no foreground or background will be drawn
+ XColor col = {};
- // 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;
- }
+ Cursor cursor = XCreatePixmapCursor(x11_display,
+ cursormask, // source (using cursor mask as placeholder, since it'll all be ignored)
+ cursormask, // mask
+ &col, &col, 0, 0);
- return OK;
-}
+ XFreePixmap(x11_display, cursormask);
+ XFreeGC(x11_display, gc);
-OS::LatinKeyboardVariant OS_X11::get_latin_keyboard_variant() const {
+ if (cursor == None) {
+ ERR_PRINT("FAILED CREATING CURSOR");
+ }
- XkbDescRec *xkbdesc = XkbAllocKeyboard();
- ERR_FAIL_COND_V(!xkbdesc, LATIN_KEYBOARD_QWERTY);
+ null_cursor = cursor;
+ }
+ cursor_set_shape(CURSOR_BUSY);
- XkbGetNames(x11_display, XkbSymbolsNameMask, xkbdesc);
- ERR_FAIL_COND_V(!xkbdesc->names, LATIN_KEYBOARD_QWERTY);
- ERR_FAIL_COND_V(!xkbdesc->names->symbols, LATIN_KEYBOARD_QWERTY);
+ requested = None;
- char *layout = XGetAtomName(x11_display, xkbdesc->names->symbols);
- ERR_FAIL_COND_V(!layout, LATIN_KEYBOARD_QWERTY);
+ window_has_focus = true; // Set focus to true at init
- Vector<String> info = String(layout).split("+");
- ERR_FAIL_INDEX_V(1, info.size(), LATIN_KEYBOARD_QWERTY);
+ /*if (p_desired.layered) {
+ set_window_per_pixel_transparency_enabled(true);
+ }*/
- 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;
+ XEvent xevent;
+ while (XPending(x11_display) > 0) {
+ XNextEvent(x11_display, &xevent);
+ if (xevent.type == ConfigureNotify) {
+ _window_changed(&xevent);
+ }
}
- return LATIN_KEYBOARD_QWERTY;
+ _update_real_mouse_position(windows[MAIN_WINDOW_ID]);
+
+ r_error = OK;
}
-void OS_X11::update_real_mouse_position() {
- Window root_return, child_return;
- int root_x, root_y, win_x, win_y;
- unsigned int mask_return;
+DisplayServerX11::~DisplayServerX11() {
+ //destroy all windows
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+#ifdef VULKAN_ENABLED
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_destroy(E->key());
+ }
+#endif
- Bool xquerypointer_result = XQueryPointer(x11_display, x11_window, &root_return, &child_return, &root_x, &root_y,
- &win_x, &win_y, &mask_return);
+ if (E->get().xic) {
+ XDestroyIC(E->get().xic);
+ }
+ XUnmapWindow(x11_display, E->get().x11_window);
+ XDestroyWindow(x11_display, E->get().x11_window);
+ }
- if (xquerypointer_result) {
- if (win_x > 0 && win_y > 0 && win_x <= current_videomode.width && win_y <= current_videomode.height) {
+ //destroy drivers
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ if (rendering_device_vulkan) {
+ rendering_device_vulkan->finalize();
+ memdelete(rendering_device_vulkan);
+ }
- last_mouse_pos.x = win_x;
- last_mouse_pos.y = win_y;
- last_mouse_pos_valid = true;
- input->set_mouse_position(last_mouse_pos);
+ if (context_vulkan) {
+ memdelete(context_vulkan);
}
}
-}
+#endif
+
+ if (xrandr_handle) {
+ dlclose(xrandr_handle);
+ }
-OS_X11::OS_X11() {
+ for (int i = 0; i < CURSOR_MAX; i++) {
+ if (cursors[i] != None) {
+ XFreeCursor(x11_display, cursors[i]);
+ }
+ if (img[i] != nullptr) {
+ XcursorImageDestroy(img[i]);
+ }
+ };
-#ifdef PULSEAUDIO_ENABLED
- AudioDriverManager::add_driver(&driver_pulseaudio);
-#endif
+ if (xim) {
+ XCloseIM(xim);
+ }
-#ifdef ALSA_ENABLED
- AudioDriverManager::add_driver(&driver_alsa);
-#endif
+ XCloseDisplay(x11_display);
+ if (xmbstring) {
+ memfree(xmbstring);
+ }
+}
- xi.opcode = 0;
- xi.last_relative_time = 0;
- layered_window = false;
- minimized = false;
- window_focused = true;
- xim_style = 0L;
- mouse_mode = MOUSE_MODE_VISIBLE;
- last_position_before_fs = Vector2();
+void DisplayServerX11::register_x11_driver() {
+ register_create_function("x11", create_func, get_rendering_drivers_func);
}
+
+#endif // X11 enabled
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
new file mode 100644
index 0000000000..b5ea71f72a
--- /dev/null
+++ b/platform/linuxbsd/display_server_x11.h
@@ -0,0 +1,354 @@
+/*************************************************************************/
+/* display_server_x11.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 DISPLAY_SERVER_X11_H
+#define DISPLAY_SERVER_X11_H
+
+#ifdef X11_ENABLED
+
+#include "servers/display_server.h"
+
+#include "core/input/input.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_server.h"
+
+#if defined(OPENGL_ENABLED)
+#include "context_gl_x11.h"
+#endif
+
+#if defined(VULKAN_ENABLED)
+#include "drivers/vulkan/rendering_device_vulkan.h"
+#include "platform/linuxbsd/vulkan_context_x11.h"
+#endif
+
+#include <X11/Xcursor/Xcursor.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XInput2.h>
+#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;
+} xrr_monitor_info;
+
+#undef CursorShape
+
+class DisplayServerX11 : public DisplayServer {
+ //No need to register, it's platform-specific and nothing is added
+ //GDCLASS(DisplayServerX11, DisplayServer)
+
+ _THREAD_SAFE_CLASS_
+
+ Atom wm_delete;
+ Atom xdnd_enter;
+ Atom xdnd_position;
+ Atom xdnd_status;
+ Atom xdnd_action_copy;
+ Atom xdnd_drop;
+ Atom xdnd_finished;
+ Atom xdnd_selection;
+ Atom xdnd_aware;
+ Atom requested;
+ int xdnd_version;
+
+#if defined(OPENGL_ENABLED)
+ ContextGL_X11 *context_gles2;
+#endif
+#if defined(VULKAN_ENABLED)
+ VulkanContextX11 *context_vulkan;
+ RenderingDeviceVulkan *rendering_device_vulkan;
+#endif
+
+ struct WindowData {
+ Window x11_window;
+ ::XIC xic;
+
+ Size2i min_size;
+ Size2i max_size;
+ Point2i position;
+ Size2i size;
+ Point2i im_position;
+ bool im_active = false;
+ Callable rect_changed_callback;
+ Callable event_callback;
+ Callable input_event_callback;
+ Callable input_text_callback;
+ Callable drop_files_callback;
+
+ WindowID transient_parent = INVALID_WINDOW_ID;
+ Set<WindowID> transient_children;
+
+ ObjectID instance_id;
+
+ //better to guess on the fly, given WM can change it
+ //WindowMode mode;
+ bool fullscreen = false; //OS can't exit from this mode
+ bool on_top = false;
+ bool borderless = false;
+ bool resize_disabled = false;
+ Vector2i last_position_before_fs;
+ };
+
+ Map<WindowID, WindowData> windows;
+
+ WindowID window_id_counter = MAIN_WINDOW_ID;
+ WindowID _create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect);
+
+ String internal_clipboard;
+ Window xdnd_source_window;
+ ::Display *x11_display;
+ char *xmbstring;
+ int xmblen;
+ unsigned long last_timestamp;
+ ::Time last_keyrelease_time;
+ ::XIM xim;
+ ::XIMStyle xim_style;
+ static void _xim_destroy_callback(::XIM im, ::XPointer client_data,
+ ::XPointer call_data);
+
+ Point2i last_mouse_pos;
+ bool last_mouse_pos_valid;
+ Point2i last_click_pos;
+ uint64_t last_click_ms;
+ int last_click_button_index;
+ uint32_t last_button_state;
+
+ struct {
+ int opcode;
+ Vector<int> touch_devices;
+ Map<int, Vector2> absolute_devices;
+ Map<int, Vector2> pen_pressure_range;
+ Map<int, Vector2> pen_tilt_x_range;
+ Map<int, Vector2> pen_tilt_y_range;
+ XIEventMask all_event_mask;
+ Map<int, Vector2> state;
+ double pressure;
+ bool pressure_supported;
+ Vector2 tilt;
+ Vector2 mouse_pos_to_filter;
+ Vector2 relative_motion;
+ Vector2 raw_pos;
+ Vector2 old_raw_pos;
+ ::Time last_relative_time;
+ } xi;
+
+ bool _refresh_device_info();
+
+ unsigned int _get_mouse_button_state(unsigned int p_x11_button, int p_x11_type);
+ void _get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state);
+ void _flush_mouse_motion();
+
+ MouseMode mouse_mode;
+ Point2i center;
+
+ void _handle_key_event(WindowID p_window, XKeyEvent *p_event, bool p_echo = false);
+
+ bool minimized;
+ bool window_has_focus;
+ bool do_mouse_warp;
+
+ const char *cursor_theme;
+ int cursor_size;
+ XcursorImage *img[CURSOR_MAX];
+ Cursor cursors[CURSOR_MAX];
+ Cursor null_cursor;
+ CursorShape current_cursor;
+ Map<CursorShape, Vector<Variant>> cursors_cache;
+
+ bool layered_window;
+
+ String rendering_driver;
+ bool window_focused;
+ //void set_wm_border(bool p_enabled);
+ void set_wm_fullscreen(bool p_enabled);
+ void set_wm_above(bool p_enabled);
+
+ typedef xrr_monitor_info *(*xrr_get_monitors_t)(Display *dpy, Window window, Bool get_active, int *nmonitors);
+ typedef void (*xrr_free_monitors_t)(xrr_monitor_info *monitors);
+ xrr_get_monitors_t xrr_get_monitors;
+ xrr_free_monitors_t xrr_free_monitors;
+ void *xrandr_handle;
+ Bool xrandr_ext_ok;
+
+ struct Property {
+ unsigned char *data;
+ int format, nitems;
+ Atom type;
+ };
+ static Property _read_property(Display *p_display, Window p_window, Atom p_property);
+
+ void _update_real_mouse_position(const WindowData &wd);
+ void _set_wm_fullscreen(WindowID p_window, bool p_enabled);
+ void _set_wm_maximized(WindowID p_window, bool p_enabled);
+
+ void _update_context(WindowData &wd);
+
+ Context context = CONTEXT_ENGINE;
+
+ void _send_window_event(const WindowData &wd, WindowEvent p_event);
+ static void _dispatch_input_events(const Ref<InputEvent> &p_event);
+ void _dispatch_input_event(const Ref<InputEvent> &p_event);
+
+protected:
+ void _window_changed(XEvent *event);
+
+public:
+ virtual bool has_feature(Feature p_feature) const;
+ virtual String get_name() const;
+
+ virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
+
+ virtual void mouse_set_mode(MouseMode p_mode);
+ virtual MouseMode mouse_get_mode() const;
+
+ 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 clipboard_set(const String &p_text);
+ virtual String clipboard_get() const;
+
+ 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;
+ virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+
+ 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 delete_sub_window(WindowID p_id);
+
+ virtual WindowID get_window_at_screen_position(const Point2i &p_position) const;
+
+ 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_set_title(const String &p_title, 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);
+ 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 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 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 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_transient(WindowID p_window, WindowID p_parent);
+
+ 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_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_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID);
+ virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ 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_request_attention(WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual bool can_any_window_draw() const;
+
+ 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 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, const Vector2 &p_hotspot);
+
+ virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
+
+ virtual void process_events();
+
+ virtual void release_rendering_thread();
+ virtual void make_rendering_thread();
+ virtual void swap_buffers();
+
+ virtual void set_context(Context p_context);
+
+ virtual void set_native_icon(const String &p_filename);
+ virtual void set_icon(const Ref<Image> &p_icon);
+
+ 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_x11_driver();
+
+ DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ ~DisplayServerX11();
+};
+
+#endif // X11 enabled
+
+#endif // DISPLAY_SERVER_X11_H
diff --git a/platform/x11/export/export.cpp b/platform/linuxbsd/export/export.cpp
index 1c0c6ec096..86ea95c563 100644
--- a/platform/x11/export/export.cpp
+++ b/platform/linuxbsd/export/export.cpp
@@ -32,17 +32,16 @@
#include "core/os/file_access.h"
#include "editor/editor_export.h"
-#include "platform/x11/logo.gen.h"
+#include "platform/linuxbsd/logo.gen.h"
#include "scene/resources/texture.h"
static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size);
-void register_x11_exporter() {
-
+void register_linuxbsd_exporter() {
Ref<EditorExportPlatformPC> platform;
platform.instance();
- Ref<Image> img = memnew(Image(_x11_logo));
+ Ref<Image> img = memnew(Image(_linuxbsd_logo));
Ref<ImageTexture> logo;
logo.instance();
logo->create_from_image(img);
@@ -62,7 +61,6 @@ void register_x11_exporter() {
}
static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) {
-
// Patch the header of the "pck" section in the ELF file so that it corresponds to the embedded data
FileAccess *f = FileAccess::open(p_path, FileAccess::READ_WRITE);
@@ -139,7 +137,6 @@ static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start,
bool found = false;
for (int i = 0; i < num_sections; ++i) {
-
int64_t section_header_pos = section_table_pos + i * section_header_size;
f->seek(section_header_pos);
diff --git a/platform/x11/export/export.h b/platform/linuxbsd/export/export.h
index d94ea114a8..5ee81f485e 100644
--- a/platform/x11/export/export.h
+++ b/platform/linuxbsd/export/export.h
@@ -28,4 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-void register_x11_exporter();
+#ifndef LINUXBSD_EXPORT_H
+#define LINUXBSD_EXPORT_H
+
+void register_linuxbsd_exporter();
+
+#endif // LINUXBSD_EXPORT_H
diff --git a/platform/x11/godot_x11.cpp b/platform/linuxbsd/godot_linuxbsd.cpp
index 77b74184ad..3ed64e9d46 100644
--- a/platform/x11/godot_x11.cpp
+++ b/platform/linuxbsd/godot_linuxbsd.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* godot_x11.cpp */
+/* godot_linuxbsd.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -34,11 +34,10 @@
#include <unistd.h>
#include "main/main.h"
-#include "os_x11.h"
+#include "os_linuxbsd.h"
int main(int argc, char *argv[]) {
-
- OS_X11 os;
+ OS_LinuxBSD os;
setlocale(LC_CTYPE, "");
@@ -52,8 +51,9 @@ int main(int argc, char *argv[]) {
return 255;
}
- if (Main::start())
+ if (Main::start()) {
os.run(); // it is actually the OS that decides how to run
+ }
Main::cleanup();
if (ret) { // Previous getcwd was successful
diff --git a/platform/x11/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index a9fe7275c2..5edaf35c50 100644
--- a/platform/x11/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -54,12 +54,11 @@ JoypadLinux::Joypad::Joypad() {
dpad = 0;
devpath = "";
for (int i = 0; i < MAX_ABS; i++) {
- abs_info[i] = NULL;
+ abs_info[i] = nullptr;
}
}
JoypadLinux::Joypad::~Joypad() {
-
for (int i = 0; i < MAX_ABS; i++) {
if (abs_info[i]) {
memdelete(abs_info[i]);
@@ -71,7 +70,7 @@ void JoypadLinux::Joypad::reset() {
dpad = 0;
fd = -1;
- InputDefault::JoyAxis jx;
+ Input::JoyAxis jx;
jx.min = -1;
jx.value = 0.0f;
for (int i = 0; i < MAX_ABS; i++) {
@@ -80,7 +79,7 @@ void JoypadLinux::Joypad::reset() {
}
}
-JoypadLinux::JoypadLinux(InputDefault *in) {
+JoypadLinux::JoypadLinux(Input *in) {
exit_udev = false;
input = in;
joy_thread = Thread::create(joy_thread_func, this);
@@ -94,7 +93,6 @@ JoypadLinux::~JoypadLinux() {
}
void JoypadLinux::joy_thread_func(void *p_user) {
-
if (p_user) {
JoypadLinux *joy = (JoypadLinux *)p_user;
joy->run_joypad_thread();
@@ -115,7 +113,6 @@ void JoypadLinux::run_joypad_thread() {
#ifdef UDEV_ENABLED
void JoypadLinux::enumerate_joypads(udev *p_udev) {
-
udev_enumerate *enumerate;
udev_list_entry *devices, *dev_list_entry;
udev_device *dev;
@@ -126,13 +123,11 @@ void JoypadLinux::enumerate_joypads(udev *p_udev) {
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(dev_list_entry, devices) {
-
const char *path = udev_list_entry_get_name(dev_list_entry);
dev = udev_device_new_from_syspath(p_udev, path);
const char *devnode = udev_device_get_devnode(dev);
if (devnode) {
-
String devnode_str = devnode;
if (devnode_str.find(ignore_str) == -1) {
MutexLock lock(joy_mutex);
@@ -145,15 +140,13 @@ void JoypadLinux::enumerate_joypads(udev *p_udev) {
}
void JoypadLinux::monitor_joypads(udev *p_udev) {
-
- udev_device *dev = NULL;
+ udev_device *dev = nullptr;
udev_monitor *mon = udev_monitor_new_from_netlink(p_udev, "udev");
- udev_monitor_filter_add_match_subsystem_devtype(mon, "input", NULL);
+ udev_monitor_filter_add_match_subsystem_devtype(mon, "input", nullptr);
udev_monitor_enable_receiving(mon);
int fd = udev_monitor_get_fd(mon);
while (!exit_udev) {
-
fd_set fds;
struct timeval tv;
int ret;
@@ -163,7 +156,7 @@ void JoypadLinux::monitor_joypads(udev *p_udev) {
tv.tv_sec = 0;
tv.tv_usec = 0;
- ret = select(fd + 1, &fds, NULL, NULL, &tv);
+ ret = select(fd + 1, &fds, nullptr, nullptr, &tv);
/* Check if our file descriptor has received data. */
if (ret > 0 && FD_ISSET(fd, &fds)) {
@@ -172,15 +165,12 @@ void JoypadLinux::monitor_joypads(udev *p_udev) {
dev = udev_monitor_receive_device(mon);
if (dev && udev_device_get_devnode(dev) != 0) {
-
MutexLock lock(joy_mutex);
String action = udev_device_get_action(dev);
const char *devnode = udev_device_get_devnode(dev);
if (devnode) {
-
String devnode_str = devnode;
if (devnode_str.find(ignore_str) == -1) {
-
if (action == "add")
open_joypad(devnode);
else if (String(action) == "remove")
@@ -198,7 +188,6 @@ void JoypadLinux::monitor_joypads(udev *p_udev) {
#endif
void JoypadLinux::monitor_joypads() {
-
while (!exit_udev) {
{
MutexLock lock(joy_mutex);
@@ -216,9 +205,7 @@ void JoypadLinux::monitor_joypads() {
}
int JoypadLinux::get_joy_from_path(String p_path) const {
-
for (int i = 0; i < JOYPADS_MAX; i++) {
-
if (joypads[i].devpath == p_path) {
return i;
}
@@ -229,17 +216,16 @@ int JoypadLinux::get_joy_from_path(String p_path) const {
void JoypadLinux::close_joypad(int p_id) {
if (p_id == -1) {
for (int i = 0; i < JOYPADS_MAX; i++) {
-
close_joypad(i);
};
return;
- } else if (p_id < 0)
+ } else if (p_id < 0) {
return;
+ }
Joypad &joy = joypads[p_id];
if (joy.fd != -1) {
-
close(joy.fd);
joy.fd = -1;
attached_devices.remove(attached_devices.find(joy.devpath));
@@ -248,7 +234,6 @@ void JoypadLinux::close_joypad(int p_id) {
}
static String _hex_str(uint8_t p_byte) {
-
static const char *dict = "0123456789abcdef";
char ret[3];
ret[2] = 0;
@@ -260,7 +245,6 @@ static String _hex_str(uint8_t p_byte) {
}
void JoypadLinux::setup_joypad_properties(int p_id) {
-
Joypad *joy = &joypads[p_id];
unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
@@ -274,16 +258,12 @@ void JoypadLinux::setup_joypad_properties(int p_id) {
return;
}
for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
-
if (test_bit(i, keybit)) {
-
joy->key_map[i] = num_buttons++;
}
}
for (int i = BTN_MISC; i < BTN_JOYSTICK; ++i) {
-
if (test_bit(i, keybit)) {
-
joy->key_map[i] = num_buttons++;
}
}
@@ -294,12 +274,11 @@ void JoypadLinux::setup_joypad_properties(int p_id) {
continue;
}
if (test_bit(i, absbit)) {
-
joy->abs_map[i] = num_axes++;
joy->abs_info[i] = memnew(input_absinfo);
if (ioctl(joy->fd, EVIOCGABS(i), joy->abs_info[i]) < 0) {
memdelete(joy->abs_info[i]);
- joy->abs_info[i] = NULL;
+ joy->abs_info[i] = nullptr;
}
}
}
@@ -315,11 +294,9 @@ void JoypadLinux::setup_joypad_properties(int p_id) {
}
void JoypadLinux::open_joypad(const char *p_path) {
-
int joy_num = input->get_unused_joy_id();
int fd = open(p_path, O_RDWR | O_NONBLOCK);
if (fd != -1 && joy_num != -1) {
-
unsigned long evbit[NBITS(EV_MAX)] = { 0 };
unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
@@ -340,7 +317,10 @@ void JoypadLinux::open_joypad(const char *p_path) {
(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(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))) {
close(fd);
return;
}
@@ -366,7 +346,6 @@ void JoypadLinux::open_joypad(const char *p_path) {
setup_joypad_properties(joy_num);
sprintf(uid, "%04x%04x", BSWAP16(inpid.bustype), 0);
if (inpid.vendor && inpid.product && inpid.version) {
-
uint16_t vendor = BSWAP16(inpid.vendor);
uint16_t product = BSWAP16(inpid.product);
uint16_t version = BSWAP16(inpid.version);
@@ -377,7 +356,6 @@ void JoypadLinux::open_joypad(const char *p_path) {
String uidname = uid;
int uidlen = MIN(name.length(), 11);
for (int i = 0; i < uidlen; i++) {
-
uidname = uidname + _hex_str(name[i]);
}
uidname += "00";
@@ -433,11 +411,10 @@ void JoypadLinux::joypad_vibration_stop(int p_id, uint64_t p_timestamp) {
joy.ff_effect_timestamp = p_timestamp;
}
-InputDefault::JoyAxis JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const {
-
+Input::JoyAxis JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const {
int min = p_abs->minimum;
int max = p_abs->maximum;
- InputDefault::JoyAxis jx;
+ Input::JoyAxis jx;
if (min < 0) {
jx.min = -1;
@@ -454,13 +431,13 @@ InputDefault::JoyAxis JoypadLinux::axis_correct(const input_absinfo *p_abs, int
}
void JoypadLinux::process_joypads() {
-
if (joy_mutex.try_lock() != OK) {
return;
}
for (int i = 0; i < JOYPADS_MAX; i++) {
-
- if (joypads[i].fd == -1) continue;
+ if (joypads[i].fd == -1) {
+ continue;
+ }
input_event events[32];
Joypad *joy = &joypads[i];
@@ -470,13 +447,13 @@ void JoypadLinux::process_joypads() {
while ((len = read(joy->fd, events, (sizeof events))) > 0) {
len /= sizeof(events[0]);
for (int j = 0; j < len; j++) {
-
input_event &ev = events[j];
// ev may be tainted and out of MAX_KEY range, which will cause
// joy->key_map[ev.code] to crash
- if (ev.code >= MAX_KEY)
+ if (ev.code >= MAX_KEY) {
return;
+ }
switch (ev.type) {
case EV_KEY:
@@ -488,33 +465,38 @@ void JoypadLinux::process_joypads() {
switch (ev.code) {
case ABS_HAT0X:
if (ev.value != 0) {
- if (ev.value < 0)
- joy->dpad |= InputDefault::HAT_MASK_LEFT;
- else
- joy->dpad |= InputDefault::HAT_MASK_RIGHT;
- } else
- joy->dpad &= ~(InputDefault::HAT_MASK_LEFT | InputDefault::HAT_MASK_RIGHT);
+ if (ev.value < 0) {
+ joy->dpad |= Input::HAT_MASK_LEFT;
+ } else {
+ joy->dpad |= Input::HAT_MASK_RIGHT;
+ }
+ } else {
+ joy->dpad &= ~(Input::HAT_MASK_LEFT | Input::HAT_MASK_RIGHT);
+ }
input->joy_hat(i, joy->dpad);
break;
case ABS_HAT0Y:
if (ev.value != 0) {
- if (ev.value < 0)
- joy->dpad |= InputDefault::HAT_MASK_UP;
- else
- joy->dpad |= InputDefault::HAT_MASK_DOWN;
- } else
- joy->dpad &= ~(InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_DOWN);
+ if (ev.value < 0) {
+ joy->dpad |= Input::HAT_MASK_UP;
+ } else {
+ joy->dpad |= Input::HAT_MASK_DOWN;
+ }
+ } else {
+ joy->dpad &= ~(Input::HAT_MASK_UP | Input::HAT_MASK_DOWN);
+ }
input->joy_hat(i, joy->dpad);
break;
default:
- if (ev.code >= MAX_ABS)
+ if (ev.code >= MAX_ABS) {
return;
+ }
if (joy->abs_map[ev.code] != -1 && joy->abs_info[ev.code]) {
- InputDefault::JoyAxis value = axis_correct(joy->abs_info[ev.code], ev.value);
+ Input::JoyAxis value = axis_correct(joy->abs_info[ev.code], ev.value);
joy->curr_axis[joy->abs_map[ev.code]] = value;
}
break;
diff --git a/platform/x11/joypad_linux.h b/platform/linuxbsd/joypad_linux.h
index d5719b6dbe..0d175193a5 100644
--- a/platform/x11/joypad_linux.h
+++ b/platform/linuxbsd/joypad_linux.h
@@ -33,15 +33,15 @@
#define JOYPAD_LINUX_H
#ifdef JOYDEV_ENABLED
+#include "core/input/input.h"
#include "core/os/mutex.h"
#include "core/os/thread.h"
-#include "main/input_default.h"
struct input_absinfo;
class JoypadLinux {
public:
- JoypadLinux(InputDefault *in);
+ JoypadLinux(Input *in);
~JoypadLinux();
void process_joypads();
@@ -53,7 +53,7 @@ private:
};
struct Joypad {
- InputDefault::JoyAxis curr_axis[MAX_ABS];
+ Input::JoyAxis curr_axis[MAX_ABS];
int key_map[MAX_KEY];
int abs_map[MAX_ABS];
int dpad;
@@ -74,7 +74,7 @@ private:
bool exit_udev;
Mutex joy_mutex;
Thread *joy_thread;
- InputDefault *input;
+ Input *input;
Joypad joypads[JOYPADS_MAX];
Vector<String> attached_devices;
@@ -95,7 +95,7 @@ private:
void joypad_vibration_start(int p_id, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
void joypad_vibration_stop(int p_id, uint64_t p_timestamp);
- InputDefault::JoyAxis axis_correct(const input_absinfo *p_abs, int p_value) const;
+ Input::JoyAxis axis_correct(const input_absinfo *p_abs, int p_value) const;
};
#endif
diff --git a/platform/x11/key_mapping_x11.cpp b/platform/linuxbsd/key_mapping_x11.cpp
index 78bd2b71a0..77512b1a9e 100644
--- a/platform/x11/key_mapping_x11.cpp
+++ b/platform/linuxbsd/key_mapping_x11.cpp
@@ -33,7 +33,6 @@
/***** SCAN CODE CONVERSION ******/
struct _XTranslatePair {
-
KeySym keysym;
unsigned int keycode;
};
@@ -181,7 +180,6 @@ static _XTranslatePair _xkeysym_to_keycode[] = {
};
struct _TranslatePair {
-
unsigned int keysym;
unsigned int keycode;
};
@@ -301,10 +299,8 @@ static _TranslatePair _scancode_to_keycode[] = {
};
unsigned int KeyMappingX11::get_scancode(unsigned int p_code) {
-
unsigned int keycode = KEY_UNKNOWN;
for (int i = 0; _scancode_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
-
if (_scancode_to_keycode[i].keycode == p_code) {
keycode = _scancode_to_keycode[i].keysym;
break;
@@ -315,34 +311,34 @@ unsigned int KeyMappingX11::get_scancode(unsigned int p_code) {
}
unsigned int KeyMappingX11::get_keycode(KeySym p_keysym) {
-
// kinda bruteforce.. could optimize.
- if (p_keysym < 0x100) // Latin 1, maps 1-1
+ if (p_keysym < 0x100) { // Latin 1, maps 1-1
return p_keysym;
+ }
// look for special key
for (int idx = 0; _xkeysym_to_keycode[idx].keysym != 0; idx++) {
-
- if (_xkeysym_to_keycode[idx].keysym == p_keysym)
+ if (_xkeysym_to_keycode[idx].keysym == p_keysym) {
return _xkeysym_to_keycode[idx].keycode;
+ }
}
return 0;
}
KeySym KeyMappingX11::get_keysym(unsigned int p_code) {
-
// kinda bruteforce.. could optimize.
- if (p_code < 0x100) // Latin 1, maps 1-1
+ if (p_code < 0x100) { // Latin 1, maps 1-1
return p_code;
+ }
// look for special key
for (int idx = 0; _xkeysym_to_keycode[idx].keysym != 0; idx++) {
-
- if (_xkeysym_to_keycode[idx].keycode == p_code)
+ if (_xkeysym_to_keycode[idx].keycode == p_code) {
return _xkeysym_to_keycode[idx].keysym;
+ }
}
return 0;
@@ -353,7 +349,6 @@ KeySym KeyMappingX11::get_keysym(unsigned int p_code) {
// Tables taken from FOX toolkit
struct _XTranslateUnicodePair {
-
KeySym keysym;
unsigned int unicode;
};
@@ -1125,37 +1120,41 @@ static _XTranslateUnicodePair _xkeysym_to_unicode[_KEYSYM_MAX] = {
};
unsigned int KeyMappingX11::get_unicode_from_keysym(KeySym p_keysym) {
-
/* Latin-1 */
- if (p_keysym >= 0x20 && p_keysym <= 0x7e)
+ if (p_keysym >= 0x20 && p_keysym <= 0x7e) {
return p_keysym;
- if (p_keysym >= 0xa0 && p_keysym <= 0xff)
+ }
+ if (p_keysym >= 0xa0 && p_keysym <= 0xff) {
return p_keysym;
+ }
// keypad to latin1 is easy
- if (p_keysym >= 0xffaa && p_keysym <= 0xffb9)
+ if (p_keysym >= 0xffaa && p_keysym <= 0xffb9) {
return p_keysym - 0xff80;
+ }
/* Unicode (may be present)*/
- if ((p_keysym & 0xff000000) == 0x01000000)
+ if ((p_keysym & 0xff000000) == 0x01000000) {
return p_keysym & 0x00ffffff;
+ }
int middle, low = 0, high = _KEYSYM_MAX - 1;
do {
middle = (high + low) / 2;
- if (_xkeysym_to_unicode[middle].keysym == p_keysym)
+ if (_xkeysym_to_unicode[middle].keysym == p_keysym) {
return _xkeysym_to_unicode[middle].unicode;
- if (_xkeysym_to_unicode[middle].keysym <= p_keysym)
+ }
+ if (_xkeysym_to_unicode[middle].keysym <= p_keysym) {
low = middle + 1;
- else
+ } else {
high = middle - 1;
+ }
} while (high >= low);
return 0;
}
struct _XTranslateUnicodePairReverse {
-
unsigned int unicode;
KeySym keysym;
};
@@ -1919,24 +1918,27 @@ static _XTranslateUnicodePairReverse _unicode_to_xkeysym[_UNICODE_MAX] = {
};
KeySym KeyMappingX11::get_keysym_from_unicode(unsigned int p_unicode) {
-
/* Latin 1 */
- if (p_unicode >= 0x20 && p_unicode <= 0x7e)
+ if (p_unicode >= 0x20 && p_unicode <= 0x7e) {
return p_unicode;
+ }
- if (p_unicode >= 0xa0 && p_unicode <= 0xff)
+ if (p_unicode >= 0xa0 && p_unicode <= 0xff) {
return p_unicode;
+ }
int middle, low = 0, high = _UNICODE_MAX - 1;
do {
middle = (high + low) / 2;
- if (_unicode_to_xkeysym[middle].keysym == p_unicode)
+ if (_unicode_to_xkeysym[middle].keysym == p_unicode) {
return _unicode_to_xkeysym[middle].keysym;
- if (_unicode_to_xkeysym[middle].keysym <= p_unicode)
+ }
+ if (_unicode_to_xkeysym[middle].keysym <= p_unicode) {
low = middle + 1;
- else
+ } else {
high = middle - 1;
+ }
} while (high >= low);
// if not found, let's hope X understands it as unicode
diff --git a/platform/x11/key_mapping_x11.h b/platform/linuxbsd/key_mapping_x11.h
index 10db43bcc4..8f5e01a3c2 100644
--- a/platform/x11/key_mapping_x11.h
+++ b/platform/linuxbsd/key_mapping_x11.h
@@ -41,7 +41,7 @@
#include "core/os/keyboard.h"
class KeyMappingX11 {
- KeyMappingX11(){};
+ KeyMappingX11() {}
public:
static unsigned int get_keycode(KeySym p_keysym);
diff --git a/platform/x11/logo.png b/platform/linuxbsd/logo.png
index 078654b757..078654b757 100644
--- a/platform/x11/logo.png
+++ b/platform/linuxbsd/logo.png
Binary files differ
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
new file mode 100644
index 0000000000..09a5eca914
--- /dev/null
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -0,0 +1,363 @@
+/*************************************************************************/
+/* os_linuxbsd.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_linuxbsd.h"
+
+#include "core/os/dir_access.h"
+#include "core/print_string.h"
+#include "errno.h"
+
+#ifdef HAVE_MNTENT
+#include <mntent.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#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();
+
+ OS_Unix::initialize_core();
+}
+
+void OS_LinuxBSD::initialize_joypads() {
+#ifdef JOYDEV_ENABLED
+ joypad = memnew(JoypadLinux(Input::get_singleton()));
+#endif
+}
+
+String OS_LinuxBSD::get_unique_id() const {
+ static String machine_id;
+ if (machine_id.empty()) {
+ if (FileAccess *f = FileAccess::open("/etc/machine-id", FileAccess::READ)) {
+ while (machine_id.empty() && !f->eof_reached()) {
+ machine_id = f->get_line().strip_edges();
+ }
+ f->close();
+ memdelete(f);
+ }
+ }
+ return machine_id;
+}
+
+void OS_LinuxBSD::finalize() {
+ if (main_loop) {
+ memdelete(main_loop);
+ }
+ main_loop = nullptr;
+
+#ifdef ALSAMIDI_ENABLED
+ driver_alsamidi.close();
+#endif
+
+#ifdef JOYDEV_ENABLED
+ memdelete(joypad);
+#endif
+}
+
+MainLoop *OS_LinuxBSD::get_main_loop() const {
+ return main_loop;
+}
+
+void OS_LinuxBSD::delete_main_loop() {
+ if (main_loop) {
+ memdelete(main_loop);
+ }
+ main_loop = nullptr;
+}
+
+void OS_LinuxBSD::set_main_loop(MainLoop *p_main_loop) {
+ main_loop = p_main_loop;
+}
+
+String OS_LinuxBSD::get_name() const {
+#ifdef __linux__
+ return "Linux";
+#elif defined(__FreeBSD__)
+ return "FreeBSD";
+#elif defined(__NetBSD__)
+ return "NetBSD";
+#else
+ return "BSD";
+#endif
+}
+
+Error OS_LinuxBSD::shell_open(String p_uri) {
+ Error ok;
+ List<String> args;
+ args.push_back(p_uri);
+ ok = execute("xdg-open", args, false);
+ if (ok == OK) {
+ return OK;
+ }
+ ok = execute("gnome-open", args, false);
+ if (ok == OK) {
+ return OK;
+ }
+ ok = execute("kde-open", args, false);
+ return ok;
+}
+
+bool OS_LinuxBSD::_check_internal_feature_support(const String &p_feature) {
+ return p_feature == "pc";
+}
+
+String OS_LinuxBSD::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");
+ } else {
+ return ".";
+ }
+}
+
+String OS_LinuxBSD::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(".local/share");
+ } else {
+ return get_config_path();
+ }
+}
+
+String OS_LinuxBSD::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(".cache");
+ } else {
+ return get_config_path();
+ }
+}
+
+String OS_LinuxBSD::get_system_dir(SystemDir p_dir) const {
+ String xdgparam;
+
+ switch (p_dir) {
+ case SYSTEM_DIR_DESKTOP: {
+ xdgparam = "DESKTOP";
+ } break;
+ case SYSTEM_DIR_DCIM: {
+ xdgparam = "PICTURES";
+
+ } break;
+ case SYSTEM_DIR_DOCUMENTS: {
+ xdgparam = "DOCUMENTS";
+
+ } break;
+ case SYSTEM_DIR_DOWNLOADS: {
+ xdgparam = "DOWNLOAD";
+
+ } break;
+ case SYSTEM_DIR_MOVIES: {
+ xdgparam = "VIDEOS";
+
+ } break;
+ case SYSTEM_DIR_MUSIC: {
+ xdgparam = "MUSIC";
+
+ } break;
+ case SYSTEM_DIR_PICTURES: {
+ xdgparam = "PICTURES";
+
+ } break;
+ case SYSTEM_DIR_RINGTONES: {
+ xdgparam = "MUSIC";
+
+ } break;
+ }
+
+ String pipe;
+ List<String> arg;
+ arg.push_back(xdgparam);
+ Error err = const_cast<OS_LinuxBSD *>(this)->execute("xdg-user-dir", arg, true, nullptr, &pipe);
+ if (err != OK) {
+ return ".";
+ }
+ return pipe.strip_edges();
+}
+
+void OS_LinuxBSD::run() {
+ force_quit = false;
+
+ if (!main_loop) {
+ return;
+ }
+
+ main_loop->init();
+
+ //uint64_t last_ticks=get_ticks_usec();
+
+ //int frames=0;
+ //uint64_t frame=0;
+
+ while (!force_quit) {
+ DisplayServer::get_singleton()->process_events(); // get rid of pending events
+#ifdef JOYDEV_ENABLED
+ joypad->process_joypads();
+#endif
+ if (Main::iteration()) {
+ break;
+ }
+ };
+
+ main_loop->finish();
+}
+
+void OS_LinuxBSD::disable_crash_handler() {
+ crash_handler.disable();
+}
+
+bool OS_LinuxBSD::is_disable_crash_handler() const {
+ return crash_handler.is_disabled();
+}
+
+static String get_mountpoint(const String &p_path) {
+ struct stat s;
+ if (stat(p_path.utf8().get_data(), &s)) {
+ return "";
+ }
+
+#ifdef HAVE_MNTENT
+ dev_t dev = s.st_dev;
+ FILE *fd = setmntent("/proc/mounts", "r");
+ if (!fd) {
+ return "";
+ }
+
+ struct mntent mnt;
+ char buf[1024];
+ size_t buflen = 1024;
+ while (getmntent_r(fd, &mnt, buf, buflen)) {
+ if (!stat(mnt.mnt_dir, &s) && s.st_dev == dev) {
+ endmntent(fd);
+ return String(mnt.mnt_dir);
+ }
+ }
+
+ endmntent(fd);
+#endif
+ return "";
+}
+
+Error OS_LinuxBSD::move_to_trash(const String &p_path) {
+ String trash_can = "";
+ String mnt = get_mountpoint(p_path);
+
+ // If there is a directory "[Mountpoint]/.Trash-[UID]/files", use it as the trash can.
+ if (mnt != "") {
+ String path(mnt + "/.Trash-" + itos(getuid()) + "/files");
+ struct stat s;
+ if (!stat(path.utf8().get_data(), &s)) {
+ trash_can = path;
+ }
+ }
+
+ // Otherwise, if ${XDG_DATA_HOME} is defined, use "${XDG_DATA_HOME}/Trash/files" as the trash can.
+ if (trash_can == "") {
+ char *dhome = getenv("XDG_DATA_HOME");
+ if (dhome) {
+ trash_can = String(dhome) + "/Trash/files";
+ }
+ }
+
+ // Otherwise, if ${HOME} is defined, use "${HOME}/.local/share/Trash/files" as the trash can.
+ if (trash_can == "") {
+ char *home = getenv("HOME");
+ if (home) {
+ trash_can = String(home) + "/.local/share/Trash/files";
+ }
+ }
+
+ // 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;
+ }
+
+ // 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);
+
+ // 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 move the given resource to it.
+ // 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;
+ }
+
+ return OK;
+}
+
+OS_LinuxBSD::OS_LinuxBSD() {
+ main_loop = nullptr;
+ force_quit = false;
+
+#ifdef PULSEAUDIO_ENABLED
+ AudioDriverManager::add_driver(&driver_pulseaudio);
+#endif
+
+#ifdef ALSA_ENABLED
+ AudioDriverManager::add_driver(&driver_alsa);
+#endif
+
+#ifdef X11_ENABLED
+ DisplayServerX11::register_x11_driver();
+#endif
+}
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
new file mode 100644
index 0000000000..4295721c68
--- /dev/null
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -0,0 +1,105 @@
+/*************************************************************************/
+/* os_linuxbsd.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_LINUXBSD_H
+#define OS_LINUXBSD_H
+
+#include "core/input/input.h"
+#include "crash_handler_linuxbsd.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_server.h"
+
+class OS_LinuxBSD : public OS_Unix {
+ virtual void delete_main_loop();
+
+ bool force_quit;
+
+#ifdef JOYDEV_ENABLED
+ JoypadLinux *joypad;
+#endif
+
+#ifdef ALSA_ENABLED
+ AudioDriverALSA driver_alsa;
+#endif
+
+#ifdef ALSAMIDI_ENABLED
+ MIDIDriverALSAMidi driver_alsamidi;
+#endif
+
+#ifdef PULSEAUDIO_ENABLED
+ AudioDriverPulseAudio driver_pulseaudio;
+#endif
+
+ CrashHandler crash_handler;
+
+ MainLoop *main_loop;
+
+protected:
+ virtual void initialize();
+ virtual void finalize();
+
+ virtual void initialize_joypads();
+
+ virtual void set_main_loop(MainLoop *p_main_loop);
+
+public:
+ virtual String get_name() const;
+
+ virtual MainLoop *get_main_loop() const;
+
+ virtual String get_config_path() const;
+ virtual String get_data_path() const;
+ virtual String get_cache_path() const;
+
+ virtual String get_system_dir(SystemDir p_dir) const;
+
+ virtual Error shell_open(String p_uri);
+
+ virtual String get_unique_id() const;
+
+ virtual bool _check_internal_feature_support(const String &p_feature);
+
+ void run();
+
+ void disable_crash_handler();
+ bool is_disable_crash_handler() const;
+
+ virtual Error move_to_trash(const String &p_path);
+
+ OS_LinuxBSD();
+};
+
+#endif
diff --git a/platform/x11/pck_embed.ld b/platform/linuxbsd/pck_embed.ld
index 57a1994043..57a1994043 100644
--- a/platform/x11/pck_embed.ld
+++ b/platform/linuxbsd/pck_embed.ld
diff --git a/platform/x11/pck_embed.legacy.ld b/platform/linuxbsd/pck_embed.legacy.ld
index a23013ba7a..a23013ba7a 100644
--- a/platform/x11/pck_embed.legacy.ld
+++ b/platform/linuxbsd/pck_embed.legacy.ld
diff --git a/platform/x11/platform_config.h b/platform/linuxbsd/platform_config.h
index ac30519132..ac30519132 100644
--- a/platform/x11/platform_config.h
+++ b/platform/linuxbsd/platform_config.h
diff --git a/platform/x11/platform_x11_builders.py b/platform/linuxbsd/platform_linuxbsd_builders.py
index 5ff0c6fb14..58234f3748 100644
--- a/platform/x11/platform_x11_builders.py
+++ b/platform/linuxbsd/platform_linuxbsd_builders.py
@@ -7,11 +7,11 @@ import os
from platform_methods import subprocess_main
-def make_debug_x11(target, source, env):
- os.system('objcopy --only-keep-debug {0} {0}.debugsymbols'.format(target[0]))
- os.system('strip --strip-debug --strip-unneeded {0}'.format(target[0]))
- os.system('objcopy --add-gnu-debuglink={0}.debugsymbols {0}'.format(target[0]))
+def make_debug_linuxbsd(target, source, env):
+ os.system("objcopy --only-keep-debug {0} {0}.debugsymbols".format(target[0]))
+ os.system("strip --strip-debug --strip-unneeded {0}".format(target[0]))
+ os.system("objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(target[0]))
-if __name__ == '__main__':
+if __name__ == "__main__":
subprocess_main(globals())
diff --git a/platform/x11/vulkan_context_x11.cpp b/platform/linuxbsd/vulkan_context_x11.cpp
index 602dbc77a2..2eaa9f9446 100644
--- a/platform/x11/vulkan_context_x11.cpp
+++ b/platform/linuxbsd/vulkan_context_x11.cpp
@@ -35,19 +35,18 @@ const char *VulkanContextX11::_get_platform_surface_extension() const {
return VK_KHR_XLIB_SURFACE_EXTENSION_NAME;
}
-int VulkanContextX11::window_create(::Window p_window, Display *p_display, int p_width, int p_height) {
-
+Error VulkanContextX11::window_create(DisplayServer::WindowID p_window_id, ::Window p_window, Display *p_display, int p_width, int p_height) {
VkXlibSurfaceCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
- createInfo.pNext = NULL;
+ createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.dpy = p_display;
createInfo.window = p_window;
VkSurfaceKHR surface;
- VkResult err = vkCreateXlibSurfaceKHR(_get_instance(), &createInfo, NULL, &surface);
- ERR_FAIL_COND_V(err, -1);
- return _window_create(surface, p_width, p_height);
+ VkResult err = vkCreateXlibSurfaceKHR(_get_instance(), &createInfo, nullptr, &surface);
+ ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
+ return _window_create(p_window_id, surface, p_width, p_height);
}
VulkanContextX11::VulkanContextX11() {
diff --git a/platform/x11/vulkan_context_x11.h b/platform/linuxbsd/vulkan_context_x11.h
index 573f994ea6..af3d923cfe 100644
--- a/platform/x11/vulkan_context_x11.h
+++ b/platform/linuxbsd/vulkan_context_x11.h
@@ -35,11 +35,10 @@
#include <X11/Xlib.h>
class VulkanContextX11 : public VulkanContext {
-
virtual const char *_get_platform_surface_extension() const;
public:
- int window_create(::Window p_window, Display *p_display, int p_width, int p_height);
+ Error window_create(DisplayServer::WindowID p_window_id, ::Window p_window, Display *p_display, int p_width, int p_height);
VulkanContextX11();
~VulkanContextX11();
diff --git a/platform/osx/SCsub b/platform/osx/SCsub
index 0a4e0a45e1..ad62db358b 100644
--- a/platform/osx/SCsub
+++ b/platform/osx/SCsub
@@ -1,22 +1,22 @@
#!/usr/bin/env python
-Import('env')
+Import("env")
from platform_methods import run_in_subprocess
import platform_osx_builders
files = [
- 'crash_handler_osx.mm',
- 'os_osx.mm',
- 'godot_main_osx.mm',
- 'dir_access_osx.mm',
- 'joypad_osx.cpp',
- 'vulkan_context_osx.mm',
- 'context_gl_osx.mm'
+ "crash_handler_osx.mm",
+ "os_osx.mm",
+ "display_server_osx.mm",
+ "godot_main_osx.mm",
+ "dir_access_osx.mm",
+ "joypad_osx.cpp",
+ "vulkan_context_osx.mm",
+ "context_gl_osx.mm",
]
-prog = env.add_program('#bin/godot', files)
+prog = env.add_program("#bin/godot", files)
if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") 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 7e436c5e36..cce00fb35f 100644
--- a/platform/osx/context_gl_osx.h
+++ b/platform/osx/context_gl_osx.h
@@ -41,7 +41,6 @@
#include <CoreVideo/CoreVideo.h>
class ContextGL_OSX {
-
bool opengl_3_context;
bool use_vsync;
diff --git a/platform/osx/context_gl_osx.mm b/platform/osx/context_gl_osx.mm
index 91d1332d24..2319e9eb1f 100644
--- a/platform/osx/context_gl_osx.mm
+++ b/platform/osx/context_gl_osx.mm
@@ -33,42 +33,34 @@
#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED)
void ContextGL_OSX::release_current() {
-
[NSOpenGLContext clearCurrentContext];
}
void ContextGL_OSX::make_current() {
-
[context makeCurrentContext];
}
void ContextGL_OSX::update() {
-
[context update];
}
void ContextGL_OSX::set_opacity(GLint p_opacity) {
-
[context setValues:&p_opacity forParameter:NSOpenGLCPSurfaceOpacity];
}
int ContextGL_OSX::get_window_width() {
-
return OS::get_singleton()->get_video_mode().width;
}
int ContextGL_OSX::get_window_height() {
-
return OS::get_singleton()->get_video_mode().height;
}
void ContextGL_OSX::swap_buffers() {
-
[context flushBuffer];
}
void ContextGL_OSX::set_use_vsync(bool p_use) {
-
CGLContextObj ctx = CGLGetCurrentContext();
if (ctx) {
GLint swapInterval = p_use ? 1 : 0;
@@ -78,12 +70,10 @@ void ContextGL_OSX::set_use_vsync(bool p_use) {
}
bool ContextGL_OSX::is_using_vsync() const {
-
return use_vsync;
}
Error ContextGL_OSX::initialize() {
-
framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
ERR_FAIL_COND_V(!framework, ERR_CANT_CREATE);
@@ -161,7 +151,6 @@ Error ContextGL_OSX::initialize() {
}
ContextGL_OSX::ContextGL_OSX(id p_view, bool p_opengl_3_context) {
-
opengl_3_context = p_opengl_3_context;
window_view = p_view;
use_vsync = false;
diff --git a/platform/osx/crash_handler_osx.h b/platform/osx/crash_handler_osx.h
index abd9812596..9970f6045a 100644
--- a/platform/osx/crash_handler_osx.h
+++ b/platform/osx/crash_handler_osx.h
@@ -32,7 +32,6 @@
#define CRASH_HANDLER_OSX_H
class CrashHandler {
-
bool disabled;
public:
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index 12ca5c10dc..29aa8ece19 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -14,7 +14,7 @@ def get_name():
def can_build():
- if (sys.platform == "darwin" or ("OSXCROSS_ROOT" in os.environ)):
+ if sys.platform == "darwin" or ("OSXCROSS_ROOT" in os.environ):
return True
return False
@@ -24,52 +24,55 @@ def get_opts():
from SCons.Variables import BoolVariable, EnumVariable
return [
- ('osxcross_sdk', 'OSXCross SDK version', 'darwin14'),
- ('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)', False),
- EnumVariable('debug_symbols', 'Add debugging symbols to release builds', 'yes', ('yes', 'no', 'full')),
- 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),
- BoolVariable('use_tsan', 'Use LLVM/GCC compiler thread sanitizer (TSAN))', False),
+ ("osxcross_sdk", "OSXCross SDK version", "darwin14"),
+ ("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)",
+ False,
+ ),
+ EnumVariable("debug_symbols", "Add debugging symbols to release builds", "yes", ("yes", "no", "full")),
+ 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),
+ BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False),
]
def get_flags():
- return [
- ]
+ 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'])
- else: #optimize for size
- env.Prepend(CCFLAGS=['-Os','-ftree-vectorize', '-msse2'])
-
- if (env["debug_symbols"] == "yes"):
- env.Prepend(CCFLAGS=['-g1'])
- if (env["debug_symbols"] == "full"):
- env.Prepend(CCFLAGS=['-g2'])
-
- elif (env["target"] == "release_debug"):
- if (env["optimize"] == "speed"): #optimize for speed (default)
- env.Prepend(CCFLAGS=['-O2'])
- 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"):
- env.Prepend(CCFLAGS=['-g2'])
-
- elif (env["target"] == "debug"):
- env.Prepend(CCFLAGS=['-g3'])
- env.Prepend(CPPDEFINES=['DEBUG_ENABLED', 'DEBUG_MEMORY_ENABLED'])
+ ## Build type
+
+ if env["target"] == "release":
+ if env["optimize"] == "speed": # optimize for speed (default)
+ env.Prepend(CCFLAGS=["-O3", "-fomit-frame-pointer", "-ftree-vectorize", "-msse2"])
+ else: # optimize for size
+ env.Prepend(CCFLAGS=["-Os", "-ftree-vectorize", "-msse2"])
+
+ if env["debug_symbols"] == "yes":
+ env.Prepend(CCFLAGS=["-g1"])
+ if env["debug_symbols"] == "full":
+ env.Prepend(CCFLAGS=["-g2"])
+
+ elif env["target"] == "release_debug":
+ if env["optimize"] == "speed": # optimize for speed (default)
+ env.Prepend(CCFLAGS=["-O2"])
+ 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":
+ env.Prepend(CCFLAGS=["-g2"])
+
+ elif env["target"] == "debug":
+ env.Prepend(CCFLAGS=["-g3"])
+ env.Prepend(CPPDEFINES=["DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED"])
## Architecture
@@ -83,86 +86,109 @@ def configure(env):
if "OSXCROSS_ROOT" in os.environ:
env["osxcross"] = True
- 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'):
+ 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"
- env['AS'] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-as"
- env.Append(CPPDEFINES=['__MACPORTS__']) #hack to fix libvpx MM256_BROADCASTSI128_SI256 define
+ env["AR"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-ar"
+ env["RANLIB"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-ranlib"
+ env["AS"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-as"
+ env.Append(CPPDEFINES=["__MACPORTS__"]) # hack to fix libvpx MM256_BROADCASTSI128_SI256 define
else:
- env['CC'] = 'clang'
- env['CXX'] = 'clang++'
+ env["CC"] = "clang"
+ env["CXX"] = "clang++"
- detect_darwin_sdk_path('osx', env)
- env.Append(CCFLAGS=['-isysroot', '$MACOS_SDK_PATH'])
- env.Append(LINKFLAGS=['-isysroot', '$MACOS_SDK_PATH'])
+ detect_darwin_sdk_path("osx", env)
+ env.Append(CCFLAGS=["-isysroot", "$MACOS_SDK_PATH"])
+ env.Append(LINKFLAGS=["-isysroot", "$MACOS_SDK_PATH"])
- else: # osxcross build
+ else: # osxcross build
root = os.environ.get("OSXCROSS_ROOT", 0)
basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-"
ccache_path = os.environ.get("CCACHE")
if ccache_path is None:
- env['CC'] = basecmd + "cc"
- env['CXX'] = basecmd + "c++"
+ env["CC"] = basecmd + "cc"
+ env["CXX"] = basecmd + "c++"
else:
# there aren't any ccache wrappers available for OS X cross-compile,
# to enable caching we need to prepend the path to the ccache binary
- env['CC'] = ccache_path + ' ' + basecmd + "cc"
- env['CXX'] = ccache_path + ' ' + basecmd + "c++"
- env['AR'] = basecmd + "ar"
- env['RANLIB'] = basecmd + "ranlib"
- 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"] = ccache_path + " " + basecmd + "cc"
+ env["CXX"] = ccache_path + " " + basecmd + "c++"
+ env["AR"] = basecmd + "ar"
+ env["RANLIB"] = basecmd + "ranlib"
+ 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']:
+ if env["use_ubsan"] or env["use_asan"] or env["use_tsan"]:
env.extra_suffix += "s"
- if env['use_ubsan']:
- env.Append(CCFLAGS=['-fsanitize=undefined'])
- env.Append(LINKFLAGS=['-fsanitize=undefined'])
+ 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_asan"]:
+ env.Append(CCFLAGS=["-fsanitize=address"])
+ env.Append(LINKFLAGS=["-fsanitize=address"])
- if env['use_tsan']:
- env.Append(CCFLAGS=['-fsanitize=thread'])
- env.Append(LINKFLAGS=['-fsanitize=thread'])
+ if env["use_tsan"]:
+ env.Append(CCFLAGS=["-fsanitize=thread"])
+ env.Append(LINKFLAGS=["-fsanitize=thread"])
## Dependencies
- if env['builtin_libtheora']:
+ if env["builtin_libtheora"]:
env["x86_libtheora_opt_gcc"] = True
## Flags
- env.Prepend(CPPPATH=['#platform/osx'])
- env.Append(CPPDEFINES=['OSX_ENABLED', 'UNIX_ENABLED', 'APPLE_STYLE_KEYS', 'COREAUDIO_ENABLED', 'COREMIDI_ENABLED'])
- env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreMIDI', '-framework', 'IOKit', '-framework', 'ForceFeedback', '-framework', 'CoreVideo', '-framework', 'AVFoundation', '-framework', 'CoreMedia'])
- env.Append(LIBS=['pthread', 'z'])
-
- env.Append(CPPDEFINES=['VULKAN_ENABLED'])
- env.Append(LINKFLAGS=['-framework', 'Metal', '-framework', 'QuartzCore', '-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'])
-
- #env.Append(CPPDEFINES=['GLES_ENABLED', 'OPENGL_ENABLED'])
-
- env.Append(CCFLAGS=['-mmacosx-version-min=10.12'])
- env.Append(LINKFLAGS=['-mmacosx-version-min=10.12'])
+ env.Prepend(CPPPATH=["#platform/osx"])
+ env.Append(CPPDEFINES=["OSX_ENABLED", "UNIX_ENABLED", "APPLE_STYLE_KEYS", "COREAUDIO_ENABLED", "COREMIDI_ENABLED"])
+ env.Append(
+ LINKFLAGS=[
+ "-framework",
+ "Cocoa",
+ "-framework",
+ "Carbon",
+ "-framework",
+ "AudioUnit",
+ "-framework",
+ "CoreAudio",
+ "-framework",
+ "CoreMIDI",
+ "-framework",
+ "IOKit",
+ "-framework",
+ "ForceFeedback",
+ "-framework",
+ "CoreVideo",
+ "-framework",
+ "AVFoundation",
+ "-framework",
+ "CoreMedia",
+ ]
+ )
+ env.Append(LIBS=["pthread", "z"])
+
+ env.Append(CPPDEFINES=["VULKAN_ENABLED"])
+ env.Append(LINKFLAGS=["-framework", "Metal", "-framework", "QuartzCore", "-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"])
+
+ # 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.mm b/platform/osx/dir_access_osx.mm
index 66ea380903..7791ba5407 100644
--- a/platform/osx/dir_access_osx.mm
+++ b/platform/osx/dir_access_osx.mm
@@ -38,7 +38,6 @@
#include <Foundation/Foundation.h>
String DirAccessOSX::fix_unicode_name(const char *p_name) const {
-
String fname;
NSString *nsstr = [[NSString stringWithUTF8String:p_name] precomposedStringWithCanonicalMapping];
diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h
new file mode 100644
index 0000000000..8133dfe2c4
--- /dev/null
+++ b/platform/osx/display_server_osx.h
@@ -0,0 +1,308 @@
+/*************************************************************************/
+/* display_server_osx.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 DISPLAY_SERVER_OSX_H
+#define DISPLAY_SERVER_OSX_H
+
+#define BitMap _QDBitMap // Suppress deprecated QuickDraw definition.
+
+#include "core/input/input.h"
+#include "servers/display_server.h"
+
+#if defined(OPENGL_ENABLED)
+#include "context_gl_osx.h"
+//TODO - reimplement OpenGLES
+#endif
+
+#if defined(VULKAN_ENABLED)
+#include "drivers/vulkan/rendering_device_vulkan.h"
+#include "platform/osx/vulkan_context_osx.h"
+#endif
+
+#include <AppKit/AppKit.h>
+#include <AppKit/NSCursor.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <CoreVideo/CoreVideo.h>
+
+#undef BitMap
+#undef CursorShape
+
+class DisplayServerOSX : public DisplayServer {
+ GDCLASS(DisplayServerOSX, DisplayServer)
+
+ _THREAD_SAFE_CLASS_
+
+public:
+#if defined(OPENGL_ENABLED)
+ ContextGL_OSX *context_gles2;
+#endif
+#if defined(VULKAN_ENABLED)
+ VulkanContextOSX *context_vulkan;
+ RenderingDeviceVulkan *rendering_device_vulkan;
+#endif
+
+ const NSMenu *_get_menu_root(const String &p_menu_root) const;
+ NSMenu *_get_menu_root(const String &p_menu_root);
+
+ NSMenu *apple_menu = nullptr;
+ NSMenu *dock_menu = nullptr;
+ Map<String, NSMenu *> submenu;
+
+ 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;
+ };
+
+ Vector<KeyEvent> key_event_buffer;
+ int key_event_pos;
+
+ struct WindowData {
+ id window_delegate;
+ id window_object;
+ id window_view;
+
+#if defined(OPENGL_ENABLED)
+ ContextGL_OSX *context_gles2 = nullptr;
+#endif
+ Point2i mouse_pos;
+
+ Size2i min_size;
+ Size2i max_size;
+ Size2i size;
+
+ bool mouse_down_control = false;
+
+ bool im_active = false;
+ Size2i im_position;
+
+ Callable rect_changed_callback;
+ Callable event_callback;
+ Callable input_event_callback;
+ Callable input_text_callback;
+ Callable drop_files_callback;
+
+ ObjectID instance_id;
+
+ WindowID transient_parent = INVALID_WINDOW_ID;
+ Set<WindowID> transient_children;
+
+ bool layered_window = false;
+ bool fullscreen = false;
+ bool on_top = false;
+ bool borderless = false;
+ bool resize_disabled = false;
+ };
+
+ Point2i im_selection;
+ String im_text;
+
+ Map<WindowID, WindowData> windows;
+
+ WindowID window_id_counter = MAIN_WINDOW_ID;
+
+ WindowID _create_window(WindowMode p_mode, const Rect2i &p_rect);
+ void _update_window(WindowData p_wd);
+ void _send_window_event(const WindowData &wd, WindowEvent p_event);
+ static void _dispatch_input_events(const Ref<InputEvent> &p_event);
+ void _dispatch_input_event(const Ref<InputEvent> &p_event);
+ WindowID _find_window_id(id p_window);
+
+ 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;
+
+ void _push_input(const Ref<InputEvent> &p_event);
+ void _process_key_events();
+ void _release_pressed_events();
+
+ String rendering_driver;
+
+ id delegate;
+ id autoreleasePool;
+ CGEventSourceRef eventSource;
+
+ CursorShape cursor_shape;
+ NSCursor *cursors[CURSOR_MAX];
+ Map<CursorShape, Vector<Variant>> cursors_cache;
+
+ MouseMode mouse_mode;
+ Point2i last_mouse_pos;
+ uint32_t last_button_state;
+
+ bool window_focused;
+ bool drop_events;
+ bool in_dispatch_input_event = false;
+
+public:
+ virtual bool has_feature(Feature p_feature) const;
+ virtual String get_name() const;
+
+ 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 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 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 int global_menu_get_item_count(const String &p_menu_root) const;
+
+ 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 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 mouse_set_mode(MouseMode p_mode);
+ virtual MouseMode mouse_get_mode() const;
+
+ 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 clipboard_set(const String &p_text);
+ virtual String clipboard_get() const;
+
+ 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 Vector<int> get_window_list() const;
+
+ virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i & = Rect2i());
+ virtual void delete_sub_window(WindowID p_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);
+ 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_title(const String &p_title, 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);
+
+ 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 void window_set_transient(WindowID p_window, WindowID p_parent);
+
+ 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_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_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_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID);
+ virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ 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_request_attention(WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual bool can_any_window_draw() const;
+
+ 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 WindowID get_window_at_screen_position(const Point2i &p_position) const;
+
+ 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 Point2i ime_get_selection() const;
+ virtual String ime_get_text() const;
+
+ 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 bool get_swap_ok_cancel();
+
+ virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
+
+ virtual void process_events();
+ virtual void force_process_and_drop_events();
+
+ virtual void release_rendering_thread();
+ virtual void make_rendering_thread();
+ virtual void swap_buffers();
+
+ virtual void set_native_icon(const String &p_filename);
+ virtual void set_icon(const Ref<Image> &p_icon);
+
+ virtual void console_set_visible(bool p_enabled);
+ virtual bool is_console_visible() 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_osx_driver();
+
+ DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ ~DisplayServerOSX();
+};
+
+#endif // DISPLAY_SERVER_OSX_H
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
new file mode 100644
index 0000000000..9a1191490c
--- /dev/null
+++ b/platform/osx/display_server_osx.mm
@@ -0,0 +1,3664 @@
+/*************************************************************************/
+/* display_server_osx.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. */
+/*************************************************************************/
+
+#include "display_server_osx.h"
+
+#include "os_osx.h"
+
+#include "core/io/marshalls.h"
+#include "core/os/keyboard.h"
+#include "main/main.h"
+#include "scene/resources/texture.h"
+
+#include <Carbon/Carbon.h>
+#include <Cocoa/Cocoa.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/hid/IOHIDKeys.h>
+#include <IOKit/hid/IOHIDLib.h>
+
+#if defined(OPENGL_ENABLED)
+#include "drivers/gles2/rasterizer_gles2.h"
+//TODO - reimplement OpenGLES
+#endif
+
+#if defined(VULKAN_ENABLED)
+#include "servers/rendering/rasterizer_rd/rasterizer_rd.h"
+
+#include <QuartzCore/CAMetalLayer.h>
+#endif
+
+#ifndef NSAppKitVersionNumber10_14
+#define NSAppKitVersionNumber10_14 1671
+#endif
+
+#define DS_OSX ((DisplayServerOSX *)(DisplayServerOSX::get_singleton()))
+
+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));
+ r_state->set_alt((p_osx_state & NSEventModifierFlagOption));
+ r_state->set_metakey((p_osx_state & NSEventModifierFlagCommand));
+}
+
+static Vector2i _get_mouse_pos(DisplayServerOSX::WindowData &p_wd, NSPoint p_locationInWindow, CGFloat p_backingScaleFactor) {
+ 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;
+ DS_OSX->last_mouse_pos = p_wd.mouse_pos;
+ Input::get_singleton()->set_mouse_position(p_wd.mouse_pos);
+ return p_wd.mouse_pos;
+}
+
+static void _push_to_key_event_buffer(const DisplayServerOSX::KeyEvent &p_event) {
+ Vector<DisplayServerOSX::KeyEvent> &buffer = DS_OSX->key_event_buffer;
+ if (DS_OSX->key_event_pos >= buffer.size()) {
+ buffer.resize(1 + DS_OSX->key_event_pos);
+ }
+ buffer.write[DS_OSX->key_event_pos++] = p_event;
+}
+
+static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
+ if ([NSCursor respondsToSelector:selector]) {
+ id object = [NSCursor performSelector:selector];
+ if ([object isKindOfClass:[NSCursor class]]) {
+ return object;
+ }
+ }
+ if (fallback) {
+ // Fallback should be a reasonable default, no need to check.
+ return [NSCursor performSelector:fallback];
+ }
+ return [NSCursor arrowCursor];
+}
+
+/*************************************************************************/
+/* GodotApplication */
+/*************************************************************************/
+
+@interface GodotApplication : NSApplication
+@end
+
+@implementation GodotApplication
+
+- (void)sendEvent:(NSEvent *)event {
+ // special case handling of command-period, which is traditionally a special
+ // shortcut in macOS and doesn't arrive at our regular keyDown handler.
+ if ([event type] == NSEventTypeKeyDown) {
+ if (([event modifierFlags] & NSEventModifierFlagCommand) && [event keyCode] == 0x2f) {
+ Ref<InputEventKey> k;
+ k.instance();
+
+ _get_key_modifier_state([event modifierFlags], k);
+ k->set_window_id(DisplayServerOSX::INVALID_WINDOW_ID);
+ k->set_pressed(true);
+ k->set_keycode(KEY_PERIOD);
+ k->set_physical_keycode(KEY_PERIOD);
+ k->set_echo([event isARepeat]);
+
+ Input::get_singleton()->accumulate_input_event(k);
+ }
+ }
+
+ // 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))
+ [[self keyWindow] sendEvent:event];
+ else
+ [super sendEvent:event];
+}
+
+@end
+
+/*************************************************************************/
+/* GlobalMenuItem */
+/*************************************************************************/
+
+@interface GlobalMenuItem : NSObject {
+@public
+ Callable callback;
+ Variant meta;
+ bool checkable;
+}
+
+@end
+
+@implementation GlobalMenuItem
+@end
+
+/*************************************************************************/
+/* GodotApplicationDelegate */
+/*************************************************************************/
+
+@interface GodotApplicationDelegate : NSObject
+- (void)forceUnbundledWindowActivationHackStep1;
+- (void)forceUnbundledWindowActivationHackStep2;
+- (void)forceUnbundledWindowActivationHackStep3;
+@end
+
+@implementation GodotApplicationDelegate
+
+- (void)forceUnbundledWindowActivationHackStep1 {
+ // Step1: Switch focus to macOS Dock.
+ // Required to perform step 2, TransformProcessType will fail if app is already the in focus.
+ for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) {
+ [app activateWithOptions:NSApplicationActivateIgnoringOtherApps];
+ break;
+ }
+ [self performSelector:@selector(forceUnbundledWindowActivationHackStep2) withObject:nil afterDelay:0.02];
+}
+
+- (void)forceUnbundledWindowActivationHackStep2 {
+ // Step 2: Register app as foreground process.
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ (void)TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ [self performSelector:@selector(forceUnbundledWindowActivationHackStep3) withObject:nil afterDelay:0.02];
+}
+
+- (void)forceUnbundledWindowActivationHackStep3 {
+ // Step 3: Switch focus back to app window.
+ [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification *)notice {
+ NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
+ if (nsappname == nil) {
+ // If executable is not a bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored).
+ [self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02];
+ }
+}
+
+- (void)globalMenuCallback:(id)sender {
+ if (![sender representedObject])
+ return;
+
+ GlobalMenuItem *value = [sender representedObject];
+
+ if (value) {
+ if (value->checkable) {
+ if ([sender state] == NSControlStateValueOff) {
+ [sender setState:NSControlStateValueOn];
+ } else {
+ [sender setState:NSControlStateValueOff];
+ }
+ }
+
+ if (value->callback != Callable()) {
+ Variant tag = value->meta;
+ Variant *tagp = &tag;
+ Variant ret;
+ Callable::CallError ce;
+ value->callback.call((const Variant **)&tagp, 1, ret, ce);
+ }
+ }
+}
+
+- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
+ return DS_OSX->dock_menu;
+}
+
+- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
+ // Note: may be called called before main loop init!
+ char *utfs = strdup([filename UTF8String]);
+ ((OS_OSX *)(OS_OSX::get_singleton()))->open_with_filename.parse_utf8(utfs);
+ free(utfs);
+
+#ifdef TOOLS_ENABLED
+ // Open new instance
+ if (OS_OSX::get_singleton()->get_main_loop()) {
+ 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);
+ }
+#endif
+ return YES;
+}
+
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
+ DS_OSX->_send_window_event(DS_OSX->windows[DisplayServerOSX::MAIN_WINDOW_ID], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
+ return NSTerminateCancel;
+}
+
+- (void)showAbout:(id)sender {
+ if (OS_OSX::get_singleton()->get_main_loop())
+ OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT);
+}
+
+@end
+
+/*************************************************************************/
+/* GodotWindowDelegate */
+/*************************************************************************/
+
+@interface GodotWindowDelegate : NSObject {
+ DisplayServerOSX::WindowID window_id;
+}
+
+- (void)windowWillClose:(NSNotification *)notification;
+- (void)setWindowID:(DisplayServerOSX::WindowID)wid;
+
+@end
+
+@implementation GodotWindowDelegate
+
+- (void)setWindowID:(DisplayServerOSX::WindowID)wid {
+ window_id = wid;
+}
+
+- (BOOL)windowShouldClose:(id)sender {
+ if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
+ return YES;
+ }
+ DS_OSX->_send_window_event(DS_OSX->windows[window_id], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
+ return NO;
+}
+
+- (void)windowWillClose:(NSNotification *)notification {
+ if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
+ return;
+ }
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ while (wd.transient_children.size()) {
+ 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.
+ DS_OSX->window_set_transient(window_id, DisplayServerOSX::INVALID_WINDOW_ID);
+ } else if ((window_id != DisplayServerOSX::MAIN_WINDOW_ID) && (DS_OSX->windows.size() == 1)) {
+ DisplayServerOSX::WindowData &pwd = DS_OSX->windows[DisplayServerOSX::MAIN_WINDOW_ID];
+ [pwd.window_object makeKeyAndOrderFront:nil]; // Move focus back to main window if there is no parent or other windows left.
+ }
+
+#ifdef VULKAN_ENABLED
+ if (DS_OSX->rendering_driver == "vulkan") {
+ DS_OSX->context_vulkan->window_destroy(window_id);
+ }
+#endif
+}
+
+- (void)windowDidEnterFullScreen:(NSNotification *)notification {
+ if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
+ return;
+ }
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ wd.fullscreen = true;
+
+ [wd.window_object setContentMinSize:NSMakeSize(0, 0)];
+ [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
+}
+
+- (void)windowDidExitFullScreen:(NSNotification *)notification {
+ if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
+ return;
+ }
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ wd.fullscreen = false;
+
+ if (wd.min_size != Size2i()) {
+ Size2i size = wd.min_size / DS_OSX->_display_scale([wd.window_object screen]);
+ [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]);
+ [wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
+ }
+
+ if (wd.resize_disabled)
+ [wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
+}
+
+- (void)windowDidChangeBackingProperties:(NSNotification *)notification {
+ if (!DisplayServerOSX::get_singleton())
+ return;
+
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ 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 NSRect contentRect = [wd.window_view frame];
+
+ wd.size.width = contentRect.size.width * newDisplayScale;
+ wd.size.height = contentRect.size.height * newDisplayScale;
+
+ 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]);
+ }
+#endif
+ //Force window resize event
+ [self windowDidResize:notification];
+ }
+}
+
+- (void)windowDidResize:(NSNotification *)notification {
+ if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
+ return;
+ }
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+#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
+
+ if (!wd.rect_changed_callback.is_null()) {
+ Variant size = Rect2i(DS_OSX->window_get_position(window_id), DS_OSX->window_get_size(window_id));
+ Variant *sizep = &size;
+ Variant ret;
+ Callable::CallError ce;
+ wd.rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce);
+ }
+
+ if (OS_OSX::get_singleton()->get_main_loop()) {
+ Main::force_redraw();
+ //Event retrieval blocks until resize is over. Call Main::iteration() directly.
+ if (!Main::is_iterating()) { //avoid cyclic loop
+ Main::iteration();
+ }
+ }
+}
+
+- (void)windowDidMove:(NSNotification *)notification {
+ if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
+ return;
+ }
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ DS_OSX->_release_pressed_events();
+
+ if (!wd.rect_changed_callback.is_null()) {
+ Variant size = Rect2i(DS_OSX->window_get_position(window_id), DS_OSX->window_get_size(window_id));
+ Variant *sizep = &size;
+ Variant ret;
+ Callable::CallError ce;
+ wd.rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce);
+ }
+}
+
+- (void)windowDidBecomeKey:(NSNotification *)notification {
+ if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
+ return;
+ }
+ 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);
+ Input::get_singleton()->set_mouse_position(wd.mouse_pos);
+
+ DS_OSX->window_focused = true;
+ DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN);
+}
+
+- (void)windowDidResignKey:(NSNotification *)notification {
+ if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
+ return;
+ }
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ DS_OSX->window_focused = false;
+
+ DS_OSX->_release_pressed_events();
+ DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_OUT);
+}
+
+- (void)windowDidMiniaturize:(NSNotification *)notification {
+ if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
+ return;
+ }
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ DS_OSX->window_focused = false;
+
+ DS_OSX->_release_pressed_events();
+ DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_OUT);
+}
+
+- (void)windowDidDeminiaturize:(NSNotification *)notification {
+ if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
+ return;
+ }
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ DS_OSX->window_focused = true;
+ DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN);
+}
+
+@end
+
+/*************************************************************************/
+/* GodotContentView */
+/*************************************************************************/
+
+@interface GodotContentView : NSView <NSTextInputClient> {
+ DisplayServerOSX::WindowID window_id;
+ NSTrackingArea *trackingArea;
+ NSMutableAttributedString *markedText;
+ bool imeInputEventInProgress;
+}
+
+- (void)cancelComposition;
+- (CALayer *)makeBackingLayer;
+- (BOOL)wantsUpdateLayer;
+- (void)updateLayer;
+- (void)setWindowID:(DisplayServerOSX::WindowID)wid;
+
+@end
+
+@implementation GodotContentView
+
+- (void)setWindowID:(DisplayServerOSX::WindowID)wid {
+ window_id = wid;
+}
+
++ (void)initialize {
+ if (self == [GodotContentView class]) {
+ // nothing left to do here at the moment..
+ }
+}
+
+- (CALayer *)makeBackingLayer {
+#if defined(VULKAN_ENABLED)
+ if (DS_OSX->rendering_driver == "vulkan") {
+ CALayer *layer = [[CAMetalLayer class] layer];
+ return layer;
+ }
+#endif
+ return [super makeBackingLayer];
+}
+
+- (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();
+ //TODO - reimplement OpenGLES
+ }
+#endif
+}
+
+- (BOOL)wantsUpdateLayer {
+ return YES;
+}
+
+- (id)init {
+ self = [super init];
+ trackingArea = nil;
+ imeInputEventInProgress = false;
+ [self updateTrackingAreas];
+ [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
+ markedText = [[NSMutableAttributedString alloc] init];
+ return self;
+}
+
+- (void)dealloc {
+ [trackingArea release];
+ [markedText release];
+ [super dealloc];
+}
+
+static const NSRange kEmptyRange = { NSNotFound, 0 };
+
+- (BOOL)hasMarkedText {
+ return (markedText.length > 0);
+}
+
+- (NSRange)markedRange {
+ return NSMakeRange(0, markedText.length);
+}
+
+- (NSRange)selectedRange {
+ return kEmptyRange;
+}
+
+- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange {
+ if ([aString isKindOfClass:[NSAttributedString class]]) {
+ [markedText initWithAttributedString:aString];
+ } else {
+ [markedText initWithString:aString];
+ }
+ if (markedText.length == 0) {
+ [self unmarkText];
+ return;
+ }
+
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ if (wd.im_active) {
+ imeInputEventInProgress = true;
+ DS_OSX->im_text.parse_utf8([[markedText mutableString] UTF8String]);
+ DS_OSX->im_selection = Point2i(selectedRange.location, selectedRange.length);
+
+ OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
+ }
+}
+
+- (void)doCommandBySelector:(SEL)aSelector {
+ if ([self respondsToSelector:aSelector])
+ [self performSelector:aSelector];
+}
+
+- (void)unmarkText {
+ imeInputEventInProgress = false;
+ [[markedText mutableString] setString:@""];
+
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ if (wd.im_active) {
+ DS_OSX->im_text = String();
+ DS_OSX->im_selection = Point2i();
+
+ OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
+ }
+}
+
+- (NSArray *)validAttributesForMarkedText {
+ return [NSArray array];
+}
+
+- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
+ return nil;
+}
+
+- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
+ return 0;
+}
+
+- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
+ ERR_FAIL_COND_V(!DS_OSX->windows.has(window_id), NSMakeRect(0, 0, 0, 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);
+ NSPoint pointOnScreen = [wd.window_object convertRectToScreen:pointInWindowRect].origin;
+
+ return NSMakeRect(pointOnScreen.x, pointOnScreen.y, 0, 0);
+}
+
+- (void)cancelComposition {
+ [self unmarkText];
+ NSTextInputContext *currentInputContext = [NSTextInputContext currentInputContext];
+ [currentInputContext discardMarkedText];
+}
+
+- (void)insertText:(id)aString {
+ [self insertText:aString replacementRange:NSMakeRange(0, 0)];
+}
+
+- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
+ NSEvent *event = [NSApp currentEvent];
+
+ NSString *characters;
+ if ([aString isKindOfClass:[NSAttributedString class]]) {
+ characters = [aString string];
+ } else {
+ 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) {
+ NSTextInputContext *currentInputContext = [NSTextInputContext currentInputContext];
+ [currentInputContext discardMarkedText];
+ [self cancelComposition];
+ return;
+ }
+
+ for (i = 0; i < length; i++) {
+ const unichar codepoint = [characters characterAtIndex:i];
+ if ((codepoint & 0xFF00) == 0xF700)
+ continue;
+
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = true;
+ ke.echo = false;
+ ke.raw = false; // IME input event
+ ke.keycode = 0;
+ ke.physical_keycode = 0;
+ ke.unicode = codepoint;
+
+ _push_to_key_event_buffer(ke);
+ }
+ [self cancelComposition];
+}
+
+- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
+ return NSDragOperationCopy;
+}
+
+- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
+ return NSDragOperationCopy;
+}
+
+- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
+ 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()) {
+ Variant v = files;
+ Variant *vp = &v;
+ Variant ret;
+ Callable::CallError ce;
+ wd.drop_files_callback.call((const Variant **)&vp, 1, ret, ce);
+ }
+
+ return NO;
+}
+
+- (BOOL)isOpaque {
+ return YES;
+}
+
+- (BOOL)canBecomeKeyView {
+ return YES;
+}
+
+- (BOOL)acceptsFirstResponder {
+ return YES;
+}
+
+- (void)cursorUpdate:(NSEvent *)event {
+ DisplayServer::CursorShape p_shape = DS_OSX->cursor_shape;
+ DS_OSX->cursor_shape = DisplayServer::CURSOR_MAX;
+ DS_OSX->cursor_set_shape(p_shape);
+}
+
+static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, int index, int mask, bool pressed) {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ if (pressed) {
+ DS_OSX->last_button_state |= mask;
+ } else {
+ DS_OSX->last_button_state &= ~mask;
+ }
+
+ 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);
+ _get_key_modifier_state([event modifierFlags], mb);
+ mb->set_button_index(index);
+ mb->set_pressed(pressed);
+ mb->set_position(pos);
+ mb->set_global_position(pos);
+ mb->set_button_mask(DS_OSX->last_button_state);
+ if (index == BUTTON_LEFT && pressed) {
+ mb->set_doubleclick([event clickCount] == 2);
+ }
+
+ Input::get_singleton()->accumulate_input_event(mb);
+}
+
+- (void)mouseDown:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ if (([event modifierFlags] & NSEventModifierFlagControl)) {
+ wd.mouse_down_control = true;
+ _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true);
+ } else {
+ wd.mouse_down_control = false;
+ _mouseDownEvent(window_id, event, BUTTON_LEFT, BUTTON_MASK_LEFT, true);
+ }
+}
+
+- (void)mouseDragged:(NSEvent *)event {
+ [self mouseMoved:event];
+}
+
+- (void)mouseUp:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ if (wd.mouse_down_control) {
+ _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false);
+ } else {
+ _mouseDownEvent(window_id, event, BUTTON_LEFT, BUTTON_MASK_LEFT, false);
+ }
+}
+
+- (void)mouseMoved:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ 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);
+ mm->set_position(pos);
+ mm->set_pressure([event pressure]);
+ if ([event subtype] == NSEventSubtypeTabletPoint) {
+ const NSPoint p = [event tilt];
+ mm->set_tilt(Vector2(p.x, p.y));
+ }
+ 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;
+ mm->set_relative(relativeMotion);
+ _get_key_modifier_state([event modifierFlags], mm);
+
+ Input::get_singleton()->set_mouse_position(wd.mouse_pos);
+ Input::get_singleton()->accumulate_input_event(mm);
+}
+
+- (void)rightMouseDown:(NSEvent *)event {
+ _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true);
+}
+
+- (void)rightMouseDragged:(NSEvent *)event {
+ [self mouseMoved:event];
+}
+
+- (void)rightMouseUp:(NSEvent *)event {
+ _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false);
+}
+
+- (void)otherMouseDown:(NSEvent *)event {
+ if ((int)[event buttonNumber] == 2) {
+ _mouseDownEvent(window_id, event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, true);
+ } else if ((int)[event buttonNumber] == 3) {
+ _mouseDownEvent(window_id, event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, true);
+ } else if ((int)[event buttonNumber] == 4) {
+ _mouseDownEvent(window_id, event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, true);
+ } else {
+ return;
+ }
+}
+
+- (void)otherMouseDragged:(NSEvent *)event {
+ [self mouseMoved:event];
+}
+
+- (void)otherMouseUp:(NSEvent *)event {
+ if ((int)[event buttonNumber] == 2) {
+ _mouseDownEvent(window_id, event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, false);
+ } else if ((int)[event buttonNumber] == 3) {
+ _mouseDownEvent(window_id, event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, false);
+ } else if ((int)[event buttonNumber] == 4) {
+ _mouseDownEvent(window_id, event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, false);
+ } else {
+ return;
+ }
+}
+
+- (void)mouseExited:(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)
+ 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)
+ 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;
+ DS_OSX->cursor_set_shape(p_shape);
+}
+
+- (void)magnifyWithEvent:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ Ref<InputEventMagnifyGesture> ev;
+ 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_factor([event magnification] + 1.0);
+
+ Input::get_singleton()->accumulate_input_event(ev);
+}
+
+- (void)viewDidChangeBackingProperties {
+ // nothing left to do here
+}
+
+- (void)updateTrackingAreas {
+ if (trackingArea != nil) {
+ [self removeTrackingArea:trackingArea];
+ [trackingArea release];
+ }
+
+ NSTrackingAreaOptions options =
+ NSTrackingMouseEnteredAndExited |
+ NSTrackingActiveInKeyWindow |
+ NSTrackingCursorUpdate |
+ NSTrackingInVisibleRect;
+
+ trackingArea = [[NSTrackingArea alloc]
+ initWithRect:[self bounds]
+ options:options
+ owner:self
+ userInfo:nil];
+
+ [self addTrackingArea:trackingArea];
+ [super updateTrackingAreas];
+}
+
+static bool isNumpadKey(unsigned int key) {
+ static const unsigned int table[] = {
+ 0x41, /* kVK_ANSI_KeypadDecimal */
+ 0x43, /* kVK_ANSI_KeypadMultiply */
+ 0x45, /* kVK_ANSI_KeypadPlus */
+ 0x47, /* kVK_ANSI_KeypadClear */
+ 0x4b, /* kVK_ANSI_KeypadDivide */
+ 0x4c, /* kVK_ANSI_KeypadEnter */
+ 0x4e, /* kVK_ANSI_KeypadMinus */
+ 0x51, /* kVK_ANSI_KeypadEquals */
+ 0x52, /* kVK_ANSI_Keypad0 */
+ 0x53, /* kVK_ANSI_Keypad1 */
+ 0x54, /* kVK_ANSI_Keypad2 */
+ 0x55, /* kVK_ANSI_Keypad3 */
+ 0x56, /* kVK_ANSI_Keypad4 */
+ 0x57, /* kVK_ANSI_Keypad5 */
+ 0x58, /* kVK_ANSI_Keypad6 */
+ 0x59, /* kVK_ANSI_Keypad7 */
+ 0x5b, /* kVK_ANSI_Keypad8 */
+ 0x5c, /* kVK_ANSI_Keypad9 */
+ 0x5f, /* kVK_JIS_KeypadComma */
+ 0x00
+ };
+ for (int i = 0; table[i] != 0; i++) {
+ if (key == table[i])
+ return true;
+ }
+ return false;
+}
+
+// Translates a OS X keycode to a Godot keycode
+//
+static int translateKey(unsigned int key) {
+ // Keyboard symbol translation table
+ static const unsigned int table[128] = {
+ /* 00 */ KEY_A,
+ /* 01 */ KEY_S,
+ /* 02 */ KEY_D,
+ /* 03 */ KEY_F,
+ /* 04 */ KEY_H,
+ /* 05 */ KEY_G,
+ /* 06 */ KEY_Z,
+ /* 07 */ KEY_X,
+ /* 08 */ KEY_C,
+ /* 09 */ KEY_V,
+ /* 0a */ KEY_SECTION, /* ISO Section */
+ /* 0b */ KEY_B,
+ /* 0c */ KEY_Q,
+ /* 0d */ KEY_W,
+ /* 0e */ KEY_E,
+ /* 0f */ KEY_R,
+ /* 10 */ KEY_Y,
+ /* 11 */ KEY_T,
+ /* 12 */ KEY_1,
+ /* 13 */ KEY_2,
+ /* 14 */ KEY_3,
+ /* 15 */ KEY_4,
+ /* 16 */ KEY_6,
+ /* 17 */ KEY_5,
+ /* 18 */ KEY_EQUAL,
+ /* 19 */ KEY_9,
+ /* 1a */ KEY_7,
+ /* 1b */ KEY_MINUS,
+ /* 1c */ KEY_8,
+ /* 1d */ KEY_0,
+ /* 1e */ KEY_BRACERIGHT,
+ /* 1f */ KEY_O,
+ /* 20 */ KEY_U,
+ /* 21 */ KEY_BRACELEFT,
+ /* 22 */ KEY_I,
+ /* 23 */ KEY_P,
+ /* 24 */ KEY_ENTER,
+ /* 25 */ KEY_L,
+ /* 26 */ KEY_J,
+ /* 27 */ KEY_APOSTROPHE,
+ /* 28 */ KEY_K,
+ /* 29 */ KEY_SEMICOLON,
+ /* 2a */ KEY_BACKSLASH,
+ /* 2b */ KEY_COMMA,
+ /* 2c */ KEY_SLASH,
+ /* 2d */ KEY_N,
+ /* 2e */ KEY_M,
+ /* 2f */ KEY_PERIOD,
+ /* 30 */ KEY_TAB,
+ /* 31 */ KEY_SPACE,
+ /* 32 */ KEY_QUOTELEFT,
+ /* 33 */ KEY_BACKSPACE,
+ /* 34 */ KEY_UNKNOWN,
+ /* 35 */ KEY_ESCAPE,
+ /* 36 */ KEY_META,
+ /* 37 */ KEY_META,
+ /* 38 */ KEY_SHIFT,
+ /* 39 */ KEY_CAPSLOCK,
+ /* 3a */ KEY_ALT,
+ /* 3b */ KEY_CONTROL,
+ /* 3c */ KEY_SHIFT,
+ /* 3d */ KEY_ALT,
+ /* 3e */ KEY_CONTROL,
+ /* 3f */ KEY_UNKNOWN, /* Function */
+ /* 40 */ KEY_UNKNOWN, /* F17 */
+ /* 41 */ KEY_KP_PERIOD,
+ /* 42 */ KEY_UNKNOWN,
+ /* 43 */ KEY_KP_MULTIPLY,
+ /* 44 */ KEY_UNKNOWN,
+ /* 45 */ KEY_KP_ADD,
+ /* 46 */ KEY_UNKNOWN,
+ /* 47 */ KEY_NUMLOCK, /* Really KeypadClear... */
+ /* 48 */ KEY_VOLUMEUP, /* VolumeUp */
+ /* 49 */ KEY_VOLUMEDOWN, /* VolumeDown */
+ /* 4a */ KEY_VOLUMEMUTE, /* Mute */
+ /* 4b */ KEY_KP_DIVIDE,
+ /* 4c */ KEY_KP_ENTER,
+ /* 4d */ KEY_UNKNOWN,
+ /* 4e */ KEY_KP_SUBTRACT,
+ /* 4f */ KEY_UNKNOWN, /* F18 */
+ /* 50 */ KEY_UNKNOWN, /* F19 */
+ /* 51 */ KEY_EQUAL, /* KeypadEqual */
+ /* 52 */ KEY_KP_0,
+ /* 53 */ KEY_KP_1,
+ /* 54 */ KEY_KP_2,
+ /* 55 */ KEY_KP_3,
+ /* 56 */ KEY_KP_4,
+ /* 57 */ KEY_KP_5,
+ /* 58 */ KEY_KP_6,
+ /* 59 */ KEY_KP_7,
+ /* 5a */ KEY_UNKNOWN, /* F20 */
+ /* 5b */ KEY_KP_8,
+ /* 5c */ KEY_KP_9,
+ /* 5d */ KEY_YEN, /* JIS Yen */
+ /* 5e */ KEY_UNDERSCORE, /* JIS Underscore */
+ /* 5f */ KEY_COMMA, /* JIS KeypadComma */
+ /* 60 */ KEY_F5,
+ /* 61 */ KEY_F6,
+ /* 62 */ KEY_F7,
+ /* 63 */ KEY_F3,
+ /* 64 */ KEY_F8,
+ /* 65 */ KEY_F9,
+ /* 66 */ KEY_UNKNOWN, /* JIS Eisu */
+ /* 67 */ KEY_F11,
+ /* 68 */ KEY_UNKNOWN, /* JIS Kana */
+ /* 69 */ KEY_F13,
+ /* 6a */ KEY_F16,
+ /* 6b */ KEY_F14,
+ /* 6c */ KEY_UNKNOWN,
+ /* 6d */ KEY_F10,
+ /* 6e */ KEY_MENU,
+ /* 6f */ KEY_F12,
+ /* 70 */ KEY_UNKNOWN,
+ /* 71 */ KEY_F15,
+ /* 72 */ KEY_INSERT, /* Really Help... */
+ /* 73 */ KEY_HOME,
+ /* 74 */ KEY_PAGEUP,
+ /* 75 */ KEY_DELETE,
+ /* 76 */ KEY_F4,
+ /* 77 */ KEY_END,
+ /* 78 */ KEY_F2,
+ /* 79 */ KEY_PAGEDOWN,
+ /* 7a */ KEY_F1,
+ /* 7b */ KEY_LEFT,
+ /* 7c */ KEY_RIGHT,
+ /* 7d */ KEY_DOWN,
+ /* 7e */ KEY_UP,
+ /* 7f */ KEY_UNKNOWN,
+ };
+
+ if (key >= 128)
+ return KEY_UNKNOWN;
+
+ return table[key];
+}
+
+struct _KeyCodeMap {
+ UniChar kchar;
+ int kcode;
+};
+
+static const _KeyCodeMap _keycodes[55] = {
+ { '`', KEY_QUOTELEFT },
+ { '~', KEY_ASCIITILDE },
+ { '0', KEY_0 },
+ { '1', KEY_1 },
+ { '2', KEY_2 },
+ { '3', KEY_3 },
+ { '4', KEY_4 },
+ { '5', KEY_5 },
+ { '6', KEY_6 },
+ { '7', KEY_7 },
+ { '8', KEY_8 },
+ { '9', KEY_9 },
+ { '-', KEY_MINUS },
+ { '_', KEY_UNDERSCORE },
+ { '=', KEY_EQUAL },
+ { '+', KEY_PLUS },
+ { 'q', KEY_Q },
+ { 'w', KEY_W },
+ { 'e', KEY_E },
+ { 'r', KEY_R },
+ { 't', KEY_T },
+ { 'y', KEY_Y },
+ { 'u', KEY_U },
+ { 'i', KEY_I },
+ { 'o', KEY_O },
+ { 'p', KEY_P },
+ { '[', KEY_BRACELEFT },
+ { ']', KEY_BRACERIGHT },
+ { '{', KEY_BRACELEFT },
+ { '}', KEY_BRACERIGHT },
+ { 'a', KEY_A },
+ { 's', KEY_S },
+ { 'd', KEY_D },
+ { 'f', KEY_F },
+ { 'g', KEY_G },
+ { 'h', KEY_H },
+ { 'j', KEY_J },
+ { 'k', KEY_K },
+ { 'l', KEY_L },
+ { ';', KEY_SEMICOLON },
+ { ':', KEY_COLON },
+ { '\'', KEY_APOSTROPHE },
+ { '\"', KEY_QUOTEDBL },
+ { '\\', KEY_BACKSLASH },
+ { '#', KEY_NUMBERSIGN },
+ { 'z', KEY_Z },
+ { 'x', KEY_X },
+ { 'c', KEY_C },
+ { 'v', KEY_V },
+ { 'b', KEY_B },
+ { 'n', KEY_N },
+ { 'm', KEY_M },
+ { ',', KEY_COMMA },
+ { '.', KEY_PERIOD },
+ { '/', KEY_SLASH }
+};
+
+static int remapKey(unsigned int key, unsigned int state) {
+ if (isNumpadKey(key))
+ return translateKey(key);
+
+ TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
+ if (!currentKeyboard)
+ return translateKey(key);
+
+ CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
+ if (!layoutData)
+ return translateKey(key);
+
+ const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
+
+ UInt32 keysDown = 0;
+ UniChar chars[4];
+ UniCharCount realLength;
+
+ OSStatus err = UCKeyTranslate(keyboardLayout,
+ key,
+ kUCKeyActionDisplay,
+ (state >> 8) & 0xFF,
+ LMGetKbdType(),
+ kUCKeyTranslateNoDeadKeysBit,
+ &keysDown,
+ sizeof(chars) / sizeof(chars[0]),
+ &realLength,
+ chars);
+
+ if (err != noErr) {
+ return translateKey(key);
+ }
+
+ for (unsigned int i = 0; i < 55; i++) {
+ if (_keycodes[i].kchar == chars[0]) {
+ return _keycodes[i].kcode;
+ }
+ }
+ return translateKey(key);
+}
+
+- (void)keyDown:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ // Ignore all input if IME input is in progress
+ if (!imeInputEventInProgress) {
+ NSString *characters = [event characters];
+ NSUInteger length = [characters length];
+
+ 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++) {
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = true;
+ ke.echo = [event isARepeat];
+ ke.keycode = remapKey([event keyCode], [event modifierFlags]);
+ ke.physical_keycode = translateKey([event keyCode]);
+ ke.raw = true;
+ ke.unicode = [characters characterAtIndex:i];
+
+ _push_to_key_event_buffer(ke);
+ }
+ } else {
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = true;
+ ke.echo = [event isARepeat];
+ ke.keycode = remapKey([event keyCode], [event modifierFlags]);
+ ke.physical_keycode = translateKey([event keyCode]);
+ ke.raw = false;
+ ke.unicode = 0;
+
+ _push_to_key_event_buffer(ke);
+ }
+ }
+
+ // Pass events to IME handler
+ if (wd.im_active)
+ [self interpretKeyEvents:[NSArray arrayWithObject:event]];
+}
+
+- (void)flagsChanged:(NSEvent *)event {
+ // Ignore all input if IME input is in progress
+ if (!imeInputEventInProgress) {
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.echo = false;
+ ke.raw = true;
+
+ int key = [event keyCode];
+ int mod = [event modifierFlags];
+
+ if (key == 0x36 || key == 0x37) {
+ if (mod & NSEventModifierFlagCommand) {
+ mod &= ~NSEventModifierFlagCommand;
+ ke.pressed = true;
+ } else {
+ ke.pressed = false;
+ }
+ } else if (key == 0x38 || key == 0x3c) {
+ if (mod & NSEventModifierFlagShift) {
+ mod &= ~NSEventModifierFlagShift;
+ ke.pressed = true;
+ } else {
+ ke.pressed = false;
+ }
+ } else if (key == 0x3a || key == 0x3d) {
+ if (mod & NSEventModifierFlagOption) {
+ mod &= ~NSEventModifierFlagOption;
+ ke.pressed = true;
+ } else {
+ ke.pressed = false;
+ }
+ } else if (key == 0x3b || key == 0x3e) {
+ if (mod & NSEventModifierFlagControl) {
+ mod &= ~NSEventModifierFlagControl;
+ ke.pressed = true;
+ } else {
+ ke.pressed = false;
+ }
+ } else {
+ return;
+ }
+
+ ke.osx_state = mod;
+ ke.keycode = remapKey(key, mod);
+ ke.physical_keycode = translateKey(key);
+ ke.unicode = 0;
+
+ _push_to_key_event_buffer(ke);
+ }
+}
+
+- (void)keyUp:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ // Ignore all input if IME input is in progress
+ if (!imeInputEventInProgress) {
+ NSString *characters = [event characters];
+ NSUInteger length = [characters length];
+
+ // 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++) {
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = false;
+ ke.echo = [event isARepeat];
+ ke.keycode = remapKey([event keyCode], [event modifierFlags]);
+ ke.physical_keycode = translateKey([event keyCode]);
+ ke.raw = true;
+ ke.unicode = [characters characterAtIndex:i];
+
+ _push_to_key_event_buffer(ke);
+ }
+ } else {
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = false;
+ ke.echo = [event isARepeat];
+ ke.keycode = remapKey([event keyCode], [event modifierFlags]);
+ ke.physical_keycode = translateKey([event keyCode]);
+ ke.raw = true;
+ ke.unicode = 0;
+
+ _push_to_key_event_buffer(ke);
+ }
+ }
+}
+
+inline void sendScrollEvent(DisplayServer::WindowID window_id, int button, double factor, int modifierFlags) {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ unsigned int mask = 1 << (button - 1);
+
+ Ref<InputEventMouseButton> sc;
+ sc.instance();
+
+ sc->set_window_id(window_id);
+ _get_key_modifier_state(modifierFlags, sc);
+ sc->set_button_index(button);
+ sc->set_factor(factor);
+ sc->set_pressed(true);
+ sc->set_position(wd.mouse_pos);
+ sc->set_global_position(wd.mouse_pos);
+ DS_OSX->last_button_state |= mask;
+ sc->set_button_mask(DS_OSX->last_button_state);
+
+ Input::get_singleton()->accumulate_input_event(sc);
+
+ sc.instance();
+ sc->set_window_id(window_id);
+ sc->set_button_index(button);
+ sc->set_factor(factor);
+ sc->set_pressed(false);
+ sc->set_position(wd.mouse_pos);
+ sc->set_global_position(wd.mouse_pos);
+ DS_OSX->last_button_state &= ~mask;
+ sc->set_button_mask(DS_OSX->last_button_state);
+
+ Input::get_singleton()->accumulate_input_event(sc);
+}
+
+inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy, int modifierFlags) {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ Ref<InputEventPanGesture> pg;
+ pg.instance();
+
+ pg->set_window_id(window_id);
+ _get_key_modifier_state(modifierFlags, pg);
+ pg->set_position(wd.mouse_pos);
+ pg->set_delta(Vector2(-dx, -dy));
+
+ Input::get_singleton()->accumulate_input_event(pg);
+}
+
+- (void)scrollWheel:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ double deltaX, deltaY;
+
+ const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [[event window] backingScaleFactor] : 1.0;
+ _get_mouse_pos(wd, [event locationInWindow], backingScaleFactor);
+
+ deltaX = [event scrollingDeltaX];
+ deltaY = [event scrollingDeltaY];
+
+ if ([event hasPreciseScrollingDeltas]) {
+ deltaX *= 0.03;
+ deltaY *= 0.03;
+ }
+
+ if ([event phase] != NSEventPhaseNone || [event momentumPhase] != NSEventPhaseNone) {
+ sendPanEvent(window_id, deltaX, deltaY, [event modifierFlags]);
+ } else {
+ if (fabs(deltaX)) {
+ sendScrollEvent(window_id, 0 > deltaX ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_LEFT, fabs(deltaX * 0.3), [event modifierFlags]);
+ }
+ if (fabs(deltaY)) {
+ sendScrollEvent(window_id, 0 < deltaY ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN, fabs(deltaY * 0.3), [event modifierFlags]);
+ }
+ }
+}
+
+@end
+
+/*************************************************************************/
+/* GodotWindow */
+/*************************************************************************/
+
+@interface GodotWindow : NSWindow {
+}
+
+@end
+
+@implementation GodotWindow
+
+- (BOOL)canBecomeKeyWindow {
+ // Required for NSBorderlessWindowMask windows
+ return YES;
+}
+
+@end
+
+/*************************************************************************/
+/* DisplayServerOSX */
+/*************************************************************************/
+
+bool DisplayServerOSX::has_feature(Feature p_feature) const {
+ switch (p_feature) {
+ case FEATURE_GLOBAL_MENU:
+ case FEATURE_SUBWINDOWS:
+ //case FEATURE_TOUCHSCREEN:
+ case FEATURE_MOUSE:
+ case FEATURE_MOUSE_WARP:
+ case FEATURE_CLIPBOARD:
+ case FEATURE_CURSOR_SHAPE:
+ case FEATURE_CUSTOM_CURSOR_SHAPE:
+ case FEATURE_NATIVE_DIALOG:
+ //case FEATURE_CONSOLE_WINDOW:
+ case FEATURE_IME:
+ case FEATURE_WINDOW_TRANSPARENCY:
+ case FEATURE_HIDPI:
+ case FEATURE_ICON:
+ case FEATURE_NATIVE_ICON:
+ case FEATURE_SWAP_BUFFERS:
+ return true;
+ default: {
+ }
+ }
+ return false;
+}
+
+String DisplayServerOSX::get_name() const {
+ return "OSX";
+}
+
+const NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) const {
+ const NSMenu *menu = NULL;
+ if (p_menu_root == "") {
+ // Main menu.x
+ menu = [NSApp mainMenu];
+ } else if (p_menu_root.to_lower() == "_dock") {
+ // macOS dock menu.
+ menu = dock_menu;
+ } else {
+ // Submenu.
+ if (submenu.has(p_menu_root)) {
+ menu = submenu[p_menu_root];
+ }
+ }
+ if (menu == apple_menu) {
+ // Do not allow to change Apple menu.
+ return NULL;
+ }
+ return menu;
+}
+
+NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) {
+ NSMenu *menu = NULL;
+ if (p_menu_root == "") {
+ // Main menu.
+ menu = [NSApp mainMenu];
+ } else if (p_menu_root.to_lower() == "_dock") {
+ // macOS dock menu.
+ menu = dock_menu;
+ } else {
+ // Submenu.
+ if (!submenu.has(p_menu_root)) {
+ NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]];
+ submenu[p_menu_root] = n_menu;
+ }
+ menu = submenu[p_menu_root];
+ }
+ if (menu == apple_menu) {
+ // Do not allow to change Apple menu.
+ return NULL;
+ }
+ return menu;
+}
+
+void DisplayServerOSX::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
+ GlobalMenuItem *obj = [[[GlobalMenuItem alloc] init] autorelease];
+ obj->callback = p_callback;
+ obj->meta = p_tag;
+ obj->checkable = false;
+ [menu_item setRepresentedObject:obj];
+ }
+}
+
+void DisplayServerOSX::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
+ GlobalMenuItem *obj = [[[GlobalMenuItem alloc] init] autorelease];
+ obj->callback = p_callback;
+ obj->meta = p_tag;
+ obj->checkable = true;
+ [menu_item setRepresentedObject:obj];
+ }
+}
+
+void DisplayServerOSX::global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ NSMenu *sub_menu = _get_menu_root(p_submenu);
+ if (menu && sub_menu) {
+ if (sub_menu == menu) {
+ ERR_PRINT("Can't set submenu to self!");
+ return;
+ }
+ if ([sub_menu supermenu]) {
+ ERR_PRINT("Can't set submenu to menu that is already a submenu of some other menu!");
+ return;
+ }
+ NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@""];
+ [menu setSubmenu:sub_menu forItem:menu_item];
+ }
+}
+
+void DisplayServerOSX::global_menu_add_separator(const String &p_menu_root) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ [menu addItem:[NSMenuItem separatorItem]];
+ }
+}
+
+bool DisplayServerOSX::global_menu_is_item_checked(const String &p_menu_root, int p_idx) const {
+ _THREAD_SAFE_METHOD_
+
+ const NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ return ([menu_item state] == NSControlStateValueOn);
+ }
+ }
+ return false;
+}
+
+bool DisplayServerOSX::global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const {
+ _THREAD_SAFE_METHOD_
+
+ const NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ GlobalMenuItem *obj = [menu_item representedObject];
+ if (obj) {
+ return obj->checkable;
+ }
+ }
+ }
+ return false;
+}
+
+Callable DisplayServerOSX::global_menu_get_item_callback(const String &p_menu_root, int p_idx) {
+ _THREAD_SAFE_METHOD_
+
+ const NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ GlobalMenuItem *obj = [menu_item representedObject];
+ if (obj) {
+ return obj->callback;
+ }
+ }
+ }
+ return Callable();
+}
+
+Variant DisplayServerOSX::global_menu_get_item_tag(const String &p_menu_root, int p_idx) {
+ _THREAD_SAFE_METHOD_
+
+ const NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ GlobalMenuItem *obj = [menu_item representedObject];
+ if (obj) {
+ return obj->meta;
+ }
+ }
+ }
+ return Variant();
+}
+
+String DisplayServerOSX::global_menu_get_item_text(const String &p_menu_root, int p_idx) {
+ _THREAD_SAFE_METHOD_
+
+ const NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ char *utfs = strdup([[menu_item title] UTF8String]);
+ String ret;
+ ret.parse_utf8(utfs);
+ free(utfs);
+ return ret;
+ }
+ }
+ return String();
+}
+
+String DisplayServerOSX::global_menu_get_item_submenu(const String &p_menu_root, int p_idx) {
+ _THREAD_SAFE_METHOD_
+
+ const NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ const NSMenu *sub_menu = [menu_item submenu];
+ if (sub_menu) {
+ for (Map<String, NSMenu *>::Element *E = submenu.front(); E; E = E->next()) {
+ if (E->get() == sub_menu)
+ return E->key();
+ }
+ }
+ }
+ }
+ return String();
+}
+
+void DisplayServerOSX::global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+ return;
+ }
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ if (p_checked) {
+ [menu_item setState:NSControlStateValueOn];
+ } else {
+ [menu_item setState:NSControlStateValueOff];
+ }
+ }
+ }
+}
+
+void DisplayServerOSX::global_menu_set_item_checkable(const String &p_menu_root, int p_idx, bool p_checkable) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+ return;
+ }
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ GlobalMenuItem *obj = [menu_item representedObject];
+ obj->checkable = p_checkable;
+ }
+ }
+}
+
+void DisplayServerOSX::global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+ return;
+ }
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ GlobalMenuItem *obj = [menu_item representedObject];
+ obj->callback = p_callback;
+ }
+ }
+}
+
+void DisplayServerOSX::global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+ return;
+ }
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ GlobalMenuItem *obj = [menu_item representedObject];
+ obj->meta = p_tag;
+ }
+ }
+}
+
+void DisplayServerOSX::global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+ return;
+ }
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ [menu_item setTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]];
+ }
+ }
+}
+
+void DisplayServerOSX::global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ NSMenu *sub_menu = _get_menu_root(p_submenu);
+ if (menu && sub_menu) {
+ if (sub_menu == menu) {
+ ERR_PRINT("Can't set submenu to self!");
+ return;
+ }
+ if ([sub_menu supermenu]) {
+ ERR_PRINT("Can't set submenu to menu that is already a submenu of some other menu!");
+ return;
+ }
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+ return;
+ }
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ [menu setSubmenu:sub_menu forItem:menu_item];
+ }
+ }
+}
+
+int DisplayServerOSX::global_menu_get_item_count(const String &p_menu_root) const {
+ _THREAD_SAFE_METHOD_
+
+ const NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ return [menu numberOfItems];
+ } else {
+ return 0;
+ }
+}
+
+void DisplayServerOSX::global_menu_remove_item(const String &p_menu_root, int p_idx) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not delete Apple menu.
+ return;
+ }
+ [menu removeItemAtIndex:p_idx];
+ }
+}
+
+void DisplayServerOSX::global_menu_clear(const String &p_menu_root) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ [menu removeAllItems];
+ // Restore Apple menu.
+ if (menu == [NSApp mainMenu]) {
+ NSMenuItem *menu_item = [menu addItemWithTitle:@"" action:nil keyEquivalent:@""];
+ [menu setSubmenu:apple_menu forItem:menu_item];
+ }
+ }
+}
+
+void DisplayServerOSX::alert(const String &p_alert, const String &p_title) {
+ _THREAD_SAFE_METHOD_
+
+ NSAlert *window = [[NSAlert alloc] init];
+ NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
+ NSString *ns_alert = [NSString stringWithUTF8String:p_alert.utf8().get_data()];
+
+ [window addButtonWithTitle:@"OK"];
+ [window setMessageText:ns_title];
+ [window setInformativeText:ns_alert];
+ [window setAlertStyle:NSAlertStyleWarning];
+
+ [window runModal];
+ [window release];
+}
+
+Error DisplayServerOSX::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {
+ _THREAD_SAFE_METHOD_
+
+ NSAlert *window = [[NSAlert alloc] init];
+ NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
+ NSString *ns_description = [NSString stringWithUTF8String:p_description.utf8().get_data()];
+
+ for (int i = 0; i < p_buttons.size(); i++) {
+ NSString *ns_button = [NSString stringWithUTF8String:p_buttons[i].utf8().get_data()];
+ [window addButtonWithTitle:ns_button];
+ }
+ [window setMessageText:ns_title];
+ [window setInformativeText:ns_description];
+ [window setAlertStyle:NSAlertStyleInformational];
+
+ int button_pressed;
+ NSInteger ret = [window runModal];
+ if (ret == NSAlertFirstButtonReturn) {
+ button_pressed = 0;
+ } else if (ret == NSAlertSecondButtonReturn) {
+ button_pressed = 1;
+ } else if (ret == NSAlertThirdButtonReturn) {
+ button_pressed = 2;
+ } else {
+ button_pressed = 2 + (ret - NSAlertThirdButtonReturn);
+ }
+
+ if (!p_callback.is_null()) {
+ Variant button = button_pressed;
+ Variant *buttonp = &button;
+ Variant ret;
+ Callable::CallError ce;
+ p_callback.call((const Variant **)&buttonp, 1, ret, ce);
+ }
+
+ [window release];
+ return OK;
+}
+
+Error DisplayServerOSX::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) {
+ _THREAD_SAFE_METHOD_
+
+ NSAlert *window = [[NSAlert alloc] init];
+ NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
+ NSString *ns_description = [NSString stringWithUTF8String:p_description.utf8().get_data()];
+ NSTextField *input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 250, 30)];
+
+ [window addButtonWithTitle:@"OK"];
+ [window setMessageText:ns_title];
+ [window setInformativeText:ns_description];
+ [window setAlertStyle:NSAlertStyleInformational];
+
+ [input setStringValue:[NSString stringWithUTF8String:p_partial.utf8().get_data()]];
+ [window setAccessoryView:input];
+
+ [window runModal];
+
+ char *utfs = strdup([[input stringValue] UTF8String]);
+ String ret;
+ ret.parse_utf8(utfs);
+ free(utfs);
+
+ if (!p_callback.is_null()) {
+ Variant text = ret;
+ Variant *textp = &text;
+ Variant ret;
+ Callable::CallError ce;
+ p_callback.call((const Variant **)&textp, 1, ret, ce);
+ }
+
+ [window release];
+ return OK;
+}
+
+void DisplayServerOSX::mouse_set_mode(MouseMode p_mode) {
+ _THREAD_SAFE_METHOD_
+
+ 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);
+ CGAssociateMouseAndMouseCursorPosition(false);
+ } else if (p_mode == MOUSE_MODE_HIDDEN) {
+ CGDisplayHideCursor(kCGDirectMainDisplay);
+ CGAssociateMouseAndMouseCursorPosition(true);
+ } else {
+ CGDisplayShowCursor(kCGDirectMainDisplay);
+ CGAssociateMouseAndMouseCursorPosition(true);
+ }
+
+ mouse_mode = p_mode;
+}
+
+DisplayServer::MouseMode DisplayServerOSX::mouse_get_mode() const {
+ return mouse_mode;
+}
+
+void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) {
+ _THREAD_SAFE_METHOD_
+
+ if (mouse_mode == MOUSE_MODE_CAPTURED) {
+ last_mouse_pos = p_to;
+ } else {
+ WindowData &wd = windows[MAIN_WINDOW_ID];
+
+ //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);
+ NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin;
+
+ //point in scren coords
+ CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y };
+
+ //do the warping
+ CGEventSourceRef lEventRef = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
+ CGEventSourceSetLocalEventsSuppressionInterval(lEventRef, 0.0);
+ CGAssociateMouseAndMouseCursorPosition(false);
+ CGWarpMouseCursorPosition(lMouseWarpPos);
+ CGAssociateMouseAndMouseCursorPosition(true);
+ }
+}
+
+Point2i DisplayServerOSX::mouse_get_position() const {
+ return last_mouse_pos;
+}
+
+Point2i DisplayServerOSX::mouse_get_absolute_position() const {
+ _THREAD_SAFE_METHOD_
+
+ const NSPoint mouse_pos = [NSEvent mouseLocation];
+
+ 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 DisplayServerOSX::mouse_get_button_state() const {
+ return last_button_state;
+}
+
+void DisplayServerOSX::clipboard_set(const String &p_text) {
+ _THREAD_SAFE_METHOD_
+
+ NSString *copiedString = [NSString stringWithUTF8String:p_text.utf8().get_data()];
+ NSArray *copiedStringArray = [NSArray arrayWithObject:copiedString];
+
+ NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+ [pasteboard clearContents];
+ [pasteboard writeObjects:copiedStringArray];
+}
+
+String DisplayServerOSX::clipboard_get() const {
+ _THREAD_SAFE_METHOD_
+
+ NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+ NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
+ NSDictionary *options = [NSDictionary dictionary];
+
+ BOOL ok = [pasteboard canReadObjectForClasses:classArray options:options];
+
+ if (!ok) {
+ return "";
+ }
+
+ NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options];
+ NSString *string = [objectsToPaste objectAtIndex:0];
+
+ char *utfs = strdup([string UTF8String]);
+ String ret;
+ ret.parse_utf8(utfs);
+ free(utfs);
+
+ return ret;
+}
+
+int DisplayServerOSX::get_screen_count() const {
+ _THREAD_SAFE_METHOD_
+
+ NSArray *screenArray = [NSScreen screens];
+ return [screenArray count];
+}
+
+// Returns the native top-left screen coordinate of the smallest rectangle
+// that encompasses all screens. Needed in get_screen_position(),
+// window_get_position, and window_set_position()
+// to convert between OS X native screen coordinates and the ones expected by Godot
+
+static bool displays_arrangement_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;
+}
+
+Point2i DisplayServerOSX::_get_screens_origin() const {
+ static Point2i origin;
+
+ if (displays_arrangement_dirty) {
+ origin = Point2i();
+
+ for (int i = 0; i < get_screen_count(); i++) {
+ Point2i position = _get_native_screen_position(i);
+ if (position.x < origin.x) {
+ origin.x = position.x;
+ }
+ if (position.y > origin.y) {
+ origin.y = position.y;
+ }
+ }
+ displays_arrangement_dirty = false;
+ }
+
+ return origin;
+}
+
+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();
+}
+
+Point2i DisplayServerOSX::screen_get_position(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
+ }
+
+ Point2i position = _get_native_screen_position(p_screen) - _get_screens_origin();
+ // OS X native y-coordinate relative to _get_screens_origin() is negative,
+ // Godot expects a positive value
+ position.y *= -1;
+ return position;
+}
+
+Size2i DisplayServerOSX::screen_get_size(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
+ }
+
+ 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();
+}
+
+int DisplayServerOSX::screen_get_dpi(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
+ }
+
+ 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;
+ }
+
+ return 96;
+}
+
+float DisplayServerOSX::screen_get_scale(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ 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]);
+ }
+
+ return 1.f;
+}
+
+Rect2i DisplayServerOSX::screen_get_usable_rect(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
+ }
+
+ NSArray *screenArray = [NSScreen screens];
+ if ((NSUInteger)p_screen < [screenArray count]) {
+ float displayScale = _display_scale([screenArray objectAtIndex:p_screen]);
+ NSRect nsrect = [[screenArray objectAtIndex:p_screen] visibleFrame];
+
+ Point2i position = Point2i(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * displayScale - _get_screens_origin();
+ position.y *= -1;
+ Size2i size = Size2i(nsrect.size.width, nsrect.size.height) * displayScale;
+
+ return Rect2i(position, size);
+ }
+
+ return Rect2i();
+}
+
+Vector<DisplayServer::WindowID> DisplayServerOSX::get_window_list() const {
+ _THREAD_SAFE_METHOD_
+
+ Vector<int> ret;
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+
+DisplayServer::WindowID DisplayServerOSX::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+ _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::_send_window_event(const WindowData &wd, WindowEvent p_event) {
+ _THREAD_SAFE_METHOD_
+
+ if (!wd.event_callback.is_null()) {
+ Variant event = int(p_event);
+ Variant *eventp = &event;
+ Variant ret;
+ Callable::CallError ce;
+ wd.event_callback.call((const Variant **)&eventp, 1, ret, ce);
+ }
+}
+
+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)
+ return E->key();
+ }
+ return INVALID_WINDOW_ID;
+}
+
+void DisplayServerOSX::_update_window(WindowData p_wd) {
+ bool borderless_full = false;
+
+ if (p_wd.borderless) {
+ NSRect frameRect = [p_wd.window_object frame];
+ NSRect screenRect = [[p_wd.window_object screen] frame];
+
+ // Check if our window covers up the screen
+ if (frameRect.origin.x <= screenRect.origin.x && frameRect.origin.y <= frameRect.origin.y &&
+ frameRect.size.width >= screenRect.size.width && frameRect.size.height >= screenRect.size.height) {
+ borderless_full = true;
+ }
+ }
+
+ if (borderless_full) {
+ // If the window covers up the screen set the level to above the main menu and hide on deactivate
+ [p_wd.window_object setLevel:NSMainMenuWindowLevel + 1];
+ [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];
+ [p_wd.window_object setHidesOnDeactivate:NO];
+ }
+}
+
+void DisplayServerOSX::delete_sub_window(WindowID p_id) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_id));
+ ERR_FAIL_COND_MSG(p_id == MAIN_WINDOW_ID, "Main window can't be deleted");
+
+ WindowData &wd = windows[p_id];
+
+ [wd.window_object setContentView:nil];
+ [wd.window_object close];
+}
+
+void DisplayServerOSX::window_set_title(const String &p_title, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ [wd.window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]];
+}
+
+void DisplayServerOSX::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.rect_changed_callback = p_callable;
+}
+
+void DisplayServerOSX::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.event_callback = p_callable;
+}
+
+void DisplayServerOSX::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.input_event_callback = p_callable;
+}
+
+void DisplayServerOSX::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.input_text_callback = p_callable;
+}
+
+void DisplayServerOSX::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.drop_files_callback = p_callable;
+}
+
+int DisplayServerOSX::window_get_current_screen(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(!windows.has(p_window), -1);
+ const WindowData &wd = windows[p_window];
+
+ const NSUInteger index = [[NSScreen screens] indexOfObject:[wd.window_object screen]];
+ return (index == NSNotFound) ? 0 : index;
+}
+
+void DisplayServerOSX::window_set_current_screen(int p_screen, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+ Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));
+ window_set_position(wpos + screen_get_position(p_screen), p_window);
+}
+
+void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent) {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND(p_window == p_parent);
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd_window = windows[p_window];
+
+ ERR_FAIL_COND(wd_window.transient_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));
+
+ WindowData &wd_parent = windows[wd_window.transient_parent];
+
+ wd_window.transient_parent = INVALID_WINDOW_ID;
+ wd_parent.transient_children.erase(p_window);
+
+ [wd_window.window_object setParentWindow:nil];
+ } 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");
+ WindowData &wd_parent = windows[p_parent];
+
+ wd_window.transient_parent = p_parent;
+ wd_parent.transient_children.insert(p_window);
+
+ [wd_window.window_object setParentWindow:wd_parent.window_object];
+ }
+}
+
+Point2i DisplayServerOSX::window_get_position(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Point2i());
+ const WindowData &wd = windows[p_window];
+
+ 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;
+ pos -= _get_screens_origin();
+ // OS X native y-coordinate relative to _get_screens_origin() is negative,
+ // Godot expects a positive value
+ pos.y *= -1;
+ return pos;
+}
+
+void DisplayServerOSX::window_set_position(const Point2i &p_position, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ Point2i position = p_position;
+ // OS X native y-coordinate relative to _get_screens_origin() is negative,
+ // Godot passes a positive value
+ position.y *= -1;
+ position += _get_screens_origin();
+
+ 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];
+
+ _update_window(wd);
+ _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream], displayScale);
+}
+
+void DisplayServerOSX::window_set_max_size(const Size2i p_size, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if ((p_size != Size2i()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {
+ ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
+ return;
+ }
+ wd.max_size = p_size;
+
+ if ((wd.max_size != Size2i()) && !wd.fullscreen) {
+ Size2i size = wd.max_size / _display_scale([wd.window_object screen]);
+ [wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
+ } else {
+ [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
+ }
+}
+
+Size2i DisplayServerOSX::window_get_max_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+ return wd.max_size;
+}
+
+void DisplayServerOSX::window_set_min_size(const Size2i p_size, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if ((p_size != Size2i()) && (wd.max_size != Size2i()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {
+ ERR_PRINT("Minimum window size can't be larger than maximum window size!");
+ return;
+ }
+ wd.min_size = p_size;
+
+ if ((wd.min_size != Size2i()) && !wd.fullscreen) {
+ Size2i size = wd.min_size / _display_scale([wd.window_object screen]);
+ [wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)];
+ } else {
+ [wd.window_object setContentMinSize:NSMakeSize(0, 0)];
+ }
+}
+
+Size2i DisplayServerOSX::window_get_min_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+
+ return wd.min_size;
+}
+
+void DisplayServerOSX::window_set_size(const Size2i p_size, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ Size2i size = p_size / _display_scale([wd.window_object screen]);
+
+ 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;
+ }
+ }
+
+ NSRect frame = [wd.window_object frame];
+ [wd.window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, size.x, size.y) display:YES];
+
+ _update_window(wd);
+}
+
+Size2i DisplayServerOSX::window_get_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+ return wd.size;
+}
+
+Size2i DisplayServerOSX::window_get_real_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ 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]);
+}
+
+bool DisplayServerOSX::window_is_maximize_allowed(WindowID p_window) const {
+ return true;
+}
+
+void DisplayServerOSX::_set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window) {
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ 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];
+#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;
+ } else {
+ [wd.window_object setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1]];
+ [wd.window_object setOpaque:YES];
+ [wd.window_object setHasShadow: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;
+ }
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver == "opengl_es") {
+ //TODO - reimplement OpenGLES
+ wd.context_gles2->update();
+ }
+#endif
+ NSRect frameRect = [wd.window_object frame];
+ [wd.window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:YES];
+ [wd.window_object setFrame:frameRect display:YES];
+ }
+}
+
+void DisplayServerOSX::window_set_mode(WindowMode p_mode, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ WindowMode old_mode = window_get_mode(p_window);
+ if (old_mode == p_mode) {
+ return; // do nothing
+ }
+
+ switch (old_mode) {
+ case WINDOW_MODE_WINDOWED: {
+ //do nothing
+ } break;
+ case WINDOW_MODE_MINIMIZED: {
+ [wd.window_object deminiaturize:nil];
+ } break;
+ case WINDOW_MODE_FULLSCREEN: {
+ if (wd.layered_window)
+ _set_window_per_pixel_transparency_enabled(true, p_window);
+ 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]);
+ [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]);
+ [wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
+ }
+ [wd.window_object toggleFullScreen:nil];
+ wd.fullscreen = false;
+ } break;
+ case WINDOW_MODE_MAXIMIZED: {
+ if ([wd.window_object isZoomed]) {
+ [wd.window_object zoom:nil];
+ }
+ } break;
+ }
+
+ switch (p_mode) {
+ case WINDOW_MODE_WINDOWED: {
+ //do nothing
+ } break;
+ case WINDOW_MODE_MINIMIZED: {
+ [wd.window_object performMiniaturize:nil];
+ } break;
+ case WINDOW_MODE_FULLSCREEN: {
+ if (wd.layered_window)
+ _set_window_per_pixel_transparency_enabled(false, p_window);
+ if (wd.resize_disabled) //fullscreen window should be resizable to work
+ [wd.window_object setStyleMask:[wd.window_object styleMask] | NSWindowStyleMaskResizable];
+ [wd.window_object setContentMinSize:NSMakeSize(0, 0)];
+ [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
+ [wd.window_object toggleFullScreen:nil];
+ wd.fullscreen = true;
+ } break;
+ case WINDOW_MODE_MAXIMIZED: {
+ if (![wd.window_object isZoomed]) {
+ [wd.window_object zoom:nil];
+ }
+ } break;
+ }
+}
+
+DisplayServer::WindowMode DisplayServerOSX::window_get_mode(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED);
+ const WindowData &wd = windows[p_window];
+
+ if (wd.fullscreen) { //if fullscreen, it's not in another mode
+ return WINDOW_MODE_FULLSCREEN;
+ }
+ if ([wd.window_object isZoomed] && !wd.resize_disabled) {
+ return WINDOW_MODE_MAXIMIZED;
+ }
+ if ([wd.window_object respondsToSelector:@selector(isMiniaturized)]) {
+ if ([wd.window_object isMiniaturized]) {
+ return WINDOW_MODE_MINIMIZED;
+ }
+ }
+
+ // all other discarded, return windowed.
+ return WINDOW_MODE_WINDOWED;
+}
+
+void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ 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
+ return;
+ if (p_enabled) {
+ [wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
+ } else {
+ [wd.window_object setStyleMask:[wd.window_object styleMask] | NSWindowStyleMaskResizable];
+ }
+ } break;
+ case WINDOW_FLAG_BORDERLESS: {
+ // OrderOut prevents a lose focus bug with the window
+ [wd.window_object orderOut:nil];
+ wd.borderless = p_enabled;
+ if (p_enabled) {
+ [wd.window_object setStyleMask:NSWindowStyleMaskBorderless];
+ } else {
+ if (wd.layered_window)
+ _set_window_per_pixel_transparency_enabled(false, p_window);
+ [wd.window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (wd.resize_disabled ? 0 : NSWindowStyleMaskResizable)];
+ // Force update of the window styles
+ NSRect frameRect = [wd.window_object frame];
+ [wd.window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO];
+ [wd.window_object setFrame:frameRect display:NO];
+ }
+ _update_window(wd);
+ [wd.window_object makeKeyAndOrderFront:nil];
+ } break;
+ case WINDOW_FLAG_ALWAYS_ON_TOP: {
+ wd.on_top = p_enabled;
+ if (p_enabled) {
+ [wd.window_object setLevel:NSFloatingWindowLevel];
+ } else {
+ [wd.window_object setLevel:NSNormalWindowLevel];
+ }
+ } break;
+ case WINDOW_FLAG_TRANSPARENT: {
+ wd.layered_window = p_enabled;
+ if (p_enabled) {
+ [wd.window_object setStyleMask:NSWindowStyleMaskBorderless]; // force borderless
+ } else if (!wd.borderless) {
+ [wd.window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (wd.resize_disabled ? 0 : NSWindowStyleMaskResizable)];
+ }
+ _set_window_per_pixel_transparency_enabled(p_enabled, p_window);
+
+ } break;
+ default: {
+ }
+ }
+}
+
+bool DisplayServerOSX::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), false);
+ const WindowData &wd = windows[p_window];
+
+ switch (p_flag) {
+ case WINDOW_FLAG_RESIZE_DISABLED: {
+ return wd.resize_disabled;
+ } break;
+ case WINDOW_FLAG_BORDERLESS: {
+ return [wd.window_object styleMask] == NSWindowStyleMaskBorderless;
+ } break;
+ case WINDOW_FLAG_ALWAYS_ON_TOP: {
+ return [wd.window_object level] == NSFloatingWindowLevel;
+ } break;
+ case WINDOW_FLAG_TRANSPARENT: {
+ return wd.layered_window;
+ } break;
+ default: {
+ }
+ }
+
+ return false;
+}
+
+void DisplayServerOSX::window_request_attention(WindowID p_window) {
+ // It's app global, ignore window id.
+ [NSApp requestUserAttention:NSCriticalRequest];
+}
+
+void DisplayServerOSX::window_move_to_foreground(WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ const WindowData &wd = windows[p_window];
+
+ [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
+ [wd.window_object makeKeyAndOrderFront:nil];
+}
+
+bool DisplayServerOSX::window_can_draw(WindowID p_window) const {
+ return window_get_mode(p_window) != WINDOW_MODE_MINIMIZED;
+}
+
+bool DisplayServerOSX::can_any_window_draw() const {
+ _THREAD_SAFE_METHOD_
+
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (window_get_mode(E->key()) != WINDOW_MODE_MINIMIZED) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void DisplayServerOSX::window_set_ime_active(const bool p_active, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ wd.im_active = p_active;
+
+ if (!p_active)
+ [wd.window_view cancelComposition];
+}
+
+void DisplayServerOSX::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ wd.im_position = p_pos;
+}
+
+bool DisplayServerOSX::get_swap_ok_cancel() {
+ return true;
+}
+
+void DisplayServerOSX::cursor_set_shape(CursorShape p_shape) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
+
+ if (cursor_shape == p_shape)
+ return;
+
+ if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
+ cursor_shape = p_shape;
+ return;
+ }
+
+ if (cursors[p_shape] != NULL) {
+ [cursors[p_shape] set];
+ } else {
+ switch (p_shape) {
+ case CURSOR_ARROW:
+ [[NSCursor arrowCursor] set];
+ break;
+ case CURSOR_IBEAM:
+ [[NSCursor IBeamCursor] set];
+ break;
+ case CURSOR_POINTING_HAND:
+ [[NSCursor pointingHandCursor] set];
+ break;
+ case CURSOR_CROSS:
+ [[NSCursor crosshairCursor] set];
+ break;
+ case CURSOR_WAIT:
+ [[NSCursor arrowCursor] set];
+ break;
+ case CURSOR_BUSY:
+ [[NSCursor arrowCursor] set];
+ break;
+ case CURSOR_DRAG:
+ [[NSCursor closedHandCursor] set];
+ break;
+ case CURSOR_CAN_DROP:
+ [[NSCursor openHandCursor] set];
+ break;
+ case CURSOR_FORBIDDEN:
+ [[NSCursor operationNotAllowedCursor] set];
+ break;
+ case CURSOR_VSIZE:
+ [_cursorFromSelector(@selector(_windowResizeNorthSouthCursor), @selector(resizeUpDownCursor)) set];
+ break;
+ case CURSOR_HSIZE:
+ [_cursorFromSelector(@selector(_windowResizeEastWestCursor), @selector(resizeLeftRightCursor)) set];
+ break;
+ case CURSOR_BDIAGSIZE:
+ [_cursorFromSelector(@selector(_windowResizeNorthEastSouthWestCursor)) set];
+ break;
+ case CURSOR_FDIAGSIZE:
+ [_cursorFromSelector(@selector(_windowResizeNorthWestSouthEastCursor)) set];
+ break;
+ case CURSOR_MOVE:
+ [[NSCursor arrowCursor] set];
+ break;
+ case CURSOR_VSPLIT:
+ [[NSCursor resizeUpDownCursor] set];
+ break;
+ case CURSOR_HSPLIT:
+ [[NSCursor resizeLeftRightCursor] set];
+ break;
+ case CURSOR_HELP:
+ [_cursorFromSelector(@selector(_helpCursor)) set];
+ break;
+ default: {
+ }
+ }
+ }
+
+ cursor_shape = p_shape;
+}
+
+DisplayServerOSX::CursorShape DisplayServerOSX::cursor_get_shape() const {
+ return cursor_shape;
+}
+
+void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
+ _THREAD_SAFE_METHOD_
+
+ 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;
+ Size2 texture_size;
+ Rect2 atlas_rect;
+
+ if (texture.is_valid()) {
+ image = texture->get_data();
+ }
+
+ if (!image.is_valid() && atlas_texture.is_valid()) {
+ texture = atlas_texture->get_atlas();
+
+ atlas_rect.size.width = texture->get_width();
+ atlas_rect.size.height = texture->get_height();
+ atlas_rect.position.x = atlas_texture->get_region().position.x;
+ atlas_rect.position.y = atlas_texture->get_region().position.y;
+
+ texture_size.width = atlas_texture->get_region().size.x;
+ texture_size.height = atlas_texture->get_region().size.y;
+ } else if (image.is_valid()) {
+ texture_size.width = texture->get_width();
+ texture_size.height = texture->get_height();
+ }
+
+ ERR_FAIL_COND(!texture.is_valid());
+ ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
+ ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
+ ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
+
+ image = texture->get_data();
+
+ ERR_FAIL_COND(!image.is_valid());
+
+ NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
+ initWithBitmapDataPlanes:NULL
+ pixelsWide:int(texture_size.width)
+ pixelsHigh:int(texture_size.height)
+ bitsPerSample:8
+ samplesPerPixel:4
+ hasAlpha:YES
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bytesPerRow:int(texture_size.width) * 4
+ bitsPerPixel:32];
+
+ ERR_FAIL_COND(imgrep == nil);
+ uint8_t *pixels = [imgrep bitmapData];
+
+ int len = int(texture_size.width * texture_size.height);
+
+ for (int i = 0; i < len; i++) {
+ int row_index = floor(i / texture_size.width) + atlas_rect.position.y;
+ int column_index = (i % int(texture_size.width)) + atlas_rect.position.x;
+
+ if (atlas_texture.is_valid()) {
+ column_index = MIN(column_index, atlas_rect.size.width - 1);
+ row_index = MIN(row_index, atlas_rect.size.height - 1);
+ }
+
+ uint32_t color = image->get_pixel(column_index, row_index).to_argb32();
+
+ uint8_t alpha = (color >> 24) & 0xFF;
+ pixels[i * 4 + 0] = ((color >> 16) & 0xFF) * alpha / 255;
+ pixels[i * 4 + 1] = ((color >> 8) & 0xFF) * alpha / 255;
+ pixels[i * 4 + 2] = ((color)&0xFF) * alpha / 255;
+ pixels[i * 4 + 3] = alpha;
+ }
+
+ NSImage *nsimage = [[NSImage alloc] initWithSize:NSMakeSize(texture_size.width, texture_size.height)];
+ [nsimage addRepresentation:imgrep];
+
+ NSCursor *cursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSMakePoint(p_hotspot.x, p_hotspot.y)];
+
+ [cursors[p_shape] release];
+ cursors[p_shape] = cursor;
+
+ Vector<Variant> params;
+ params.push_back(p_cursor);
+ params.push_back(p_hotspot);
+ cursors_cache.insert(p_shape, params);
+
+ if (p_shape == cursor_shape) {
+ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
+ [cursor set];
+ }
+ }
+
+ [imgrep release];
+ [nsimage release];
+ } else {
+ // Reset to default system cursor
+ if (cursors[p_shape] != NULL) {
+ [cursors[p_shape] release];
+ cursors[p_shape] = NULL;
+ }
+
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ cursor_set_shape(c);
+
+ cursors_cache.erase(p_shape);
+ }
+}
+
+static bool keyboard_layout_dirty = true;
+static void keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) {
+ 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;
+
+ CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
+ if (!layoutData)
+ return nil;
+
+ const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
+
+ OSStatus err;
+ CFMutableStringRef output = CFStringCreateMutable(NULL, 0);
+
+ for (int i = 0; i < length; ++i) {
+ UInt32 keysDown = 0;
+ UniChar chars[4];
+ UniCharCount realLength;
+
+ err = UCKeyTranslate(keyboardLayout,
+ keyCode[i],
+ kUCKeyActionDisplay,
+ 0,
+ LMGetKbdType(),
+ kUCKeyTranslateNoDeadKeysBit,
+ &keysDown,
+ sizeof(chars) / sizeof(chars[0]),
+ &realLength,
+ chars);
+
+ if (err != noErr) {
+ CFRelease(output);
+ return nil;
+ }
+
+ CFStringAppendCharacters(output, chars, 1);
+ }
+
+ return (NSString *)output;
+}
+
+DisplayServerOSX::LatinKeyboardVariant DisplayServerOSX::get_latin_keyboard_variant() const {
+ _THREAD_SAFE_METHOD_
+
+ static LatinKeyboardVariant layout = LATIN_KEYBOARD_QWERTY;
+
+ if (keyboard_layout_dirty) {
+ layout = LATIN_KEYBOARD_QWERTY;
+
+ 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);
+
+ 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;
+ }
+
+ [test release];
+
+ keyboard_layout_dirty = false;
+ return layout;
+ }
+
+ return layout;
+}
+
+void DisplayServerOSX::_push_input(const Ref<InputEvent> &p_event) {
+ Ref<InputEvent> ev = p_event;
+ Input::get_singleton()->accumulate_input_event(ev);
+}
+
+void DisplayServerOSX::_release_pressed_events() {
+ _THREAD_SAFE_METHOD_
+ if (Input::get_singleton()) {
+ Input::get_singleton()->release_pressed_events();
+ }
+}
+
+void DisplayServerOSX::_process_key_events() {
+ Ref<InputEventKey> k;
+ for (int i = 0; i < key_event_pos; i++) {
+ const KeyEvent &ke = key_event_buffer[i];
+ if (ke.raw) {
+ // Non IME input - no composite characters, pass events as is
+ k.instance();
+
+ k->set_window_id(ke.window_id);
+ _get_key_modifier_state(ke.osx_state, k);
+ k->set_pressed(ke.pressed);
+ k->set_echo(ke.echo);
+ k->set_keycode(ke.keycode);
+ k->set_physical_keycode(ke.physical_keycode);
+ k->set_unicode(ke.unicode);
+
+ _push_input(k);
+ } else {
+ // IME input
+ if ((i == 0 && ke.keycode == 0) || (i > 0 && key_event_buffer[i - 1].keycode == 0)) {
+ k.instance();
+
+ k->set_window_id(ke.window_id);
+ _get_key_modifier_state(ke.osx_state, k);
+ k->set_pressed(ke.pressed);
+ k->set_echo(ke.echo);
+ k->set_keycode(0);
+ k->set_physical_keycode(0);
+ k->set_unicode(ke.unicode);
+
+ _push_input(k);
+ }
+ if (ke.keycode != 0) {
+ k.instance();
+
+ k->set_window_id(ke.window_id);
+ _get_key_modifier_state(ke.osx_state, k);
+ k->set_pressed(ke.pressed);
+ k->set_echo(ke.echo);
+ k->set_keycode(ke.keycode);
+ k->set_physical_keycode(ke.physical_keycode);
+
+ if (i + 1 < key_event_pos && key_event_buffer[i + 1].keycode == 0) {
+ k->set_unicode(key_event_buffer[i + 1].unicode);
+ }
+
+ _push_input(k);
+ }
+ }
+ }
+
+ key_event_pos = 0;
+}
+
+void DisplayServerOSX::process_events() {
+ _THREAD_SAFE_METHOD_
+
+ while (true) {
+ NSEvent *event = [NSApp
+ nextEventMatchingMask:NSEventMaskAny
+ untilDate:[NSDate distantPast]
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+
+ if (event == nil)
+ break;
+
+ [NSApp sendEvent:event];
+ }
+
+ if (!drop_events) {
+ _process_key_events();
+ Input::get_singleton()->flush_accumulated_events();
+ }
+
+ [autoreleasePool drain];
+ autoreleasePool = [[NSAutoreleasePool alloc] init];
+}
+
+void DisplayServerOSX::force_process_and_drop_events() {
+ _THREAD_SAFE_METHOD_
+
+ drop_events = true;
+ process_events();
+ drop_events = false;
+}
+
+void DisplayServerOSX::set_native_icon(const String &p_filename) {
+ _THREAD_SAFE_METHOD_
+
+ FileAccess *f = FileAccess::open(p_filename, FileAccess::READ);
+ ERR_FAIL_COND(!f);
+
+ Vector<uint8_t> data;
+ uint32_t len = f->get_len();
+ data.resize(len);
+ f->get_buffer((uint8_t *)&data.write[0], len);
+ memdelete(f);
+
+ NSData *icon_data = [[[NSData alloc] initWithBytes:&data.write[0] length:len] autorelease];
+ ERR_FAIL_COND_MSG(!icon_data, "Error reading icon data.");
+
+ NSImage *icon = [[[NSImage alloc] initWithData:icon_data] autorelease];
+ ERR_FAIL_COND_MSG(!icon, "Error loading icon.");
+
+ [NSApp setApplicationIconImage:icon];
+}
+
+void DisplayServerOSX::set_icon(const Ref<Image> &p_icon) {
+ _THREAD_SAFE_METHOD_
+
+ Ref<Image> img = p_icon;
+ img = img->duplicate();
+ img->convert(Image::FORMAT_RGBA8);
+ NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
+ initWithBitmapDataPlanes:NULL
+ pixelsWide:img->get_width()
+ pixelsHigh:img->get_height()
+ bitsPerSample:8
+ samplesPerPixel:4
+ hasAlpha:YES
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bytesPerRow:img->get_width() * 4
+ bitsPerPixel:32];
+ ERR_FAIL_COND(imgrep == nil);
+ uint8_t *pixels = [imgrep bitmapData];
+
+ int len = img->get_width() * img->get_height();
+ const uint8_t *r = img->get_data().ptr();
+
+ /* Premultiply the alpha channel */
+ for (int i = 0; i < len; i++) {
+ uint8_t alpha = r[i * 4 + 3];
+ pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255);
+ pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255);
+ pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255);
+ pixels[i * 4 + 3] = alpha;
+ }
+
+ NSImage *nsimg = [[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())];
+ ERR_FAIL_COND(nsimg == nil);
+
+ [nsimg addRepresentation:imgrep];
+ [NSApp setApplicationIconImage:nsimg];
+
+ [imgrep release];
+ [nsimg release];
+}
+
+Vector<String> DisplayServerOSX::get_rendering_drivers_func() {
+ Vector<String> drivers;
+
+#ifdef VULKAN_ENABLED
+ drivers.push_back("vulkan");
+#endif
+#ifdef OPENGL_ENABLED
+ drivers.push_back("opengl");
+#endif
+
+ return drivers;
+}
+
+Point2i DisplayServerOSX::ime_get_selection() const {
+ return im_selection;
+}
+
+String DisplayServerOSX::ime_get_text() const {
+ return im_text;
+}
+
+DisplayServer::WindowID DisplayServerOSX::get_window_at_screen_position(const Point2i &p_position) const {
+ 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)) {
+ return E->key();
+ }
+ }
+ return INVALID_WINDOW_ID;
+}
+
+void DisplayServerOSX::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].instance_id = p_instance;
+}
+
+ObjectID DisplayServerOSX::window_get_attached_instance_id(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), ObjectID());
+ return windows[p_window].instance_id;
+}
+
+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));
+}
+
+DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, const Rect2i &p_rect) {
+ WindowID id;
+ {
+ 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];
+
+ Point2i position = p_rect.position;
+ // OS X native y-coordinate relative to _get_screens_origin() is negative,
+ // Godot passes a positive value
+ position.y *= -1;
+ position += _get_screens_origin();
+
+ // 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)
+ styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable
+ backing:NSBackingStoreBuffered
+ defer:NO];
+ ERR_FAIL_COND_V_MSG(wd.window_object == nil, INVALID_WINDOW_ID, "Can't create a window");
+
+ 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_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
+ [wd.window_object setContentView:wd.window_view];
+ [wd.window_object setDelegate:wd.window_delegate];
+ [wd.window_object setAcceptsMouseMovedEvents:YES];
+ [wd.window_object setRestorable:NO];
+
+ if ([wd.window_object respondsToSelector:@selector(setTabbingMode:)])
+ [wd.window_object setTabbingMode:NSWindowTabbingModeDisallowed];
+
+#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 (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++;
+ windows[id] = wd;
+ }
+
+ 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;
+
+#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();
+ }
+#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
+
+ return id;
+}
+
+void DisplayServerOSX::_dispatch_input_events(const Ref<InputEvent> &p_event) {
+ ((DisplayServerOSX *)(get_singleton()))->_dispatch_input_event(p_event);
+}
+
+void DisplayServerOSX::_dispatch_input_event(const Ref<InputEvent> &p_event) {
+ _THREAD_SAFE_METHOD_
+ if (!in_dispatch_input_event) {
+ in_dispatch_input_event = true;
+
+ Variant ev = p_event;
+ Variant *evp = &ev;
+ Variant ret;
+ Callable::CallError ce;
+
+ 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
+ if (windows.has(event_from_window->get_window_id())) {
+ Callable callable = windows[event_from_window->get_window_id()].input_event_callback;
+ if (callable.is_null()) {
+ return;
+ }
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ }
+ } else {
+ //send to all windows
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ Callable callable = E->get().input_event_callback;
+ if (callable.is_null()) {
+ continue;
+ }
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ }
+ }
+
+ in_dispatch_input_event = false;
+ }
+}
+
+void DisplayServerOSX::release_rendering_thread() {
+ //TODO - reimplement OpenGLES
+}
+
+void DisplayServerOSX::make_rendering_thread() {
+ //TODO - reimplement OpenGLES
+}
+
+void DisplayServerOSX::swap_buffers() {
+ //TODO - reimplement OpenGLES
+}
+
+void DisplayServerOSX::console_set_visible(bool p_enabled) {
+ //TODO - open terminal and redirect
+}
+
+bool DisplayServerOSX::is_console_visible() const {
+ return isatty(STDIN_FILENO);
+}
+
+DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
+
+ r_error = OK;
+ drop_events = false;
+
+ memset(cursors, 0, sizeof(cursors));
+ cursor_shape = CURSOR_ARROW;
+
+ key_event_pos = 0;
+ mouse_mode = MOUSE_MODE_VISIBLE;
+ last_button_state = 0;
+
+ autoreleasePool = [[NSAutoreleasePool alloc] init];
+
+ eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ ERR_FAIL_COND(!eventSource);
+
+ CGEventSourceSetLocalEventsSuppressionInterval(eventSource, 0.0);
+
+ // Implicitly create shared NSApplication instance
+ [GodotApplication sharedApplication];
+
+ // In case we are unbundled, make us a proper UI application
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
+
+ keyboard_layout_dirty = true;
+ displays_arrangement_dirty = true;
+
+ // Register to be notified on keyboard layout changes
+ CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
+ NULL, keyboard_layout_changed,
+ kTISNotifySelectedKeyboardInputSourceChanged, NULL,
+ CFNotificationSuspensionBehaviorDeliverImmediately);
+
+ // Register to be notified on displays arrangement changes
+ CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, NULL);
+
+ // Menu bar setup must go between sharedApplication above and
+ // finishLaunching below, in order to properly emulate the behavior
+ // of NSApplicationMain
+ NSMenuItem *menu_item;
+ NSString *title;
+
+ NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
+ if (nsappname == nil)
+ nsappname = [[NSProcessInfo processInfo] processName];
+
+ // Setup Dock menu
+ dock_menu = [[NSMenu alloc] initWithTitle:@"_dock"];
+
+ // Setup Apple menu
+ apple_menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
+ title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname];
+ [apple_menu addItemWithTitle:title action:@selector(showAbout:) keyEquivalent:@""];
+
+ [apple_menu addItem:[NSMenuItem separatorItem]];
+
+ NSMenu *services = [[NSMenu alloc] initWithTitle:@""];
+ menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Services", nil) action:nil keyEquivalent:@""];
+ [apple_menu setSubmenu:services forItem:menu_item];
+ [NSApp setServicesMenu:services];
+ [services release];
+
+ [apple_menu addItem:[NSMenuItem separatorItem]];
+
+ title = [NSString stringWithFormat:NSLocalizedString(@"Hide %@", nil), nsappname];
+ [apple_menu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
+
+ menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Hide Others", nil) action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
+ [menu_item setKeyEquivalentModifierMask:(NSEventModifierFlagOption | NSEventModifierFlagCommand)];
+
+ [apple_menu addItemWithTitle:NSLocalizedString(@"Show all", nil) action:@selector(unhideAllApplications:) keyEquivalent:@""];
+
+ [apple_menu addItem:[NSMenuItem separatorItem]];
+
+ title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname];
+ [apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
+
+ // Setup menu bar
+ NSMenu *main_menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
+ menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""];
+ [main_menu setSubmenu:apple_menu forItem:menu_item];
+ [NSApp setMainMenu:main_menu];
+
+ [NSApp finishLaunching];
+
+ delegate = [[GodotApplicationDelegate alloc] init];
+ ERR_FAIL_COND(!delegate);
+ [NSApp setDelegate:delegate];
+
+ //process application:openFile: event
+ while (true) {
+ NSEvent *event = [NSApp
+ nextEventMatchingMask:NSEventMaskAny
+ untilDate:[NSDate distantPast]
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+
+ if (event == nil)
+ break;
+
+ [NSApp sendEvent:event];
+ }
+
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!
+ //TODO - do Vulkan and GLES2 support checks, driver selection and fallback
+ rendering_driver = p_rendering_driver;
+
+#ifndef _MSC_VER
+#warning Forcing vulkan rendering driver because OpenGL not implemented yet
+#endif
+ rendering_driver = "vulkan";
+
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver == "opengl_es") {
+ //TODO - reimplement OpenGLES
+ }
+#endif
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ context_vulkan = memnew(VulkanContextOSX);
+ if (context_vulkan->initialize() != OK) {
+ memdelete(context_vulkan);
+ context_vulkan = NULL;
+ r_error = ERR_CANT_CREATE;
+ ERR_FAIL_MSG("Could not initialize Vulkan");
+ }
+ }
+#endif
+
+ 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, Rect2i(window_position, p_resolution));
+ 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];
+
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ rendering_device_vulkan = memnew(RenderingDeviceVulkan);
+ rendering_device_vulkan->initialize(context_vulkan);
+
+ RasterizerRD::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() {
+ if (dock_menu) {
+ [dock_menu release];
+ }
+
+ for (Map<String, NSMenu *>::Element *E = submenu.front(); E; E = E->next()) {
+ [E->get() release];
+ }
+
+ //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];
+ }
+
+ //destroy drivers
+#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
+
+ CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, kTISNotifySelectedKeyboardInputSourceChanged, NULL);
+ CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL);
+
+ cursors_cache.clear();
+
+ //visual_server->finish();
+ //memdelete(visual_server);
+}
+
+void DisplayServerOSX::register_osx_driver() {
+ register_create_function("osx", create_func, get_rendering_drivers_func);
+}
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index dbe52da912..784fba75ec 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -46,7 +46,6 @@
#include <sys/stat.h>
class EditorExportPlatformOSX : public EditorExportPlatform {
-
GDCLASS(EditorExportPlatformOSX, EditorExportPlatform);
int version_code;
@@ -58,6 +57,7 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
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);
#ifdef OSX_ENABLED
bool use_codesign() const { return true; }
@@ -89,7 +89,6 @@ public:
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
virtual void get_platform_features(List<String> *r_features) {
-
r_features->push_back("pc");
r_features->push_back("s3tc");
r_features->push_back("OSX");
@@ -117,7 +116,6 @@ void EditorExportPlatformOSX::get_preset_features(const Ref<EditorExportPreset>
}
void EditorExportPlatformOSX::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"), ""));
@@ -148,7 +146,6 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
}
void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source, Vector<uint8_t> &p_dest) {
-
int src_len = p_size * p_size;
Vector<uint8_t> result;
@@ -163,7 +160,6 @@ void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source,
uint8_t cur = p_source.ptr()[i * 4 + p_ch];
if (i < src_len - 2) {
-
if ((p_source.ptr()[(i + 1) * 4 + p_ch] == cur) && (p_source.ptr()[(i + 2) * 4 + p_ch] == cur)) {
if (buf_size > 0) {
result.write[res_size++] = (uint8_t)(buf_size - 1);
@@ -215,7 +211,6 @@ void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source,
}
void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data) {
-
Ref<ImageTexture> it = memnew(ImageTexture);
Vector<uint8_t> data;
@@ -320,7 +315,6 @@ void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_
}
void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary) {
-
String str;
String strnew;
str.parse_utf8((const char *)plist.ptr(), plist.size());
@@ -370,6 +364,7 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset
**/
Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
+#ifdef OSX_ENABLED
List<String> args;
if (p_preset->get("codesign/timestamp")) {
@@ -380,8 +375,7 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese
args.push_back("runtime");
}
- if (p_preset->get("codesign/entitlements") != "") {
- /* this should point to our entitlements.plist file that sandboxes our application, I don't know if this should also be placed in our app bundle */
+ if ((p_preset->get("codesign/entitlements") != "") && (p_path.get_extension() != "dmg")) {
args.push_back("--entitlements");
args.push_back(p_preset->get("codesign/entitlements"));
}
@@ -402,7 +396,7 @@ 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, NULL, &str, NULL, true);
+ Error err = OS::get_singleton()->execute("codesign", args, true, nullptr, &str, nullptr, true);
ERR_FAIL_COND_V(err != OK, err);
print_line("codesign (" + p_path + "): " + str);
@@ -414,6 +408,7 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese
EditorNode::add_io_error("codesign: invalid entitlements file");
return FAILED;
}
+#endif
return OK;
}
@@ -435,7 +430,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, NULL, &str, NULL, true);
+ Error err = OS::get_singleton()->execute("hdiutil", args, true, nullptr, &str, nullptr, true);
ERR_FAIL_COND_V(err != OK, err);
print_line("hdiutil returned: " + str);
@@ -458,10 +453,11 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
EditorProgress ep("export", "Exporting for OSX", 3, true);
- if (p_debug)
+ if (p_debug) {
src_pkg_name = p_preset->get("custom_template/debug");
- else
+ } else {
src_pkg_name = p_preset->get("custom_template/release");
+ }
if (src_pkg_name == "") {
String err;
@@ -476,7 +472,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
return ERR_FILE_BAD_PATH;
}
- FileAccess *src_f = NULL;
+ FileAccess *src_f = nullptr;
zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
if (ep.step("Creating app", 0)) {
@@ -485,7 +481,6 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io);
if (!src_pkg_zip) {
-
EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name);
return ERR_FILE_NOT_FOUND;
}
@@ -495,80 +490,69 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + ".64";
String pkg_name;
- if (p_preset->get("application/name") != "")
+ if (p_preset->get("application/name") != "") {
pkg_name = p_preset->get("application/name"); // app_name
- else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "")
+ } else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") {
pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name"));
- else
+ } else {
pkg_name = "Unnamed";
+ }
String pkg_name_safe = OS::get_singleton()->get_safe_dir_name(pkg_name);
Error err = OK;
String tmp_app_path_name = "";
- zlib_filefunc_def io2 = io;
- FileAccess *dst_f = NULL;
- io2.opaque = &dst_f;
- zipFile dst_pkg_zip = NULL;
- DirAccess *tmp_app_path = NULL;
+ DirAccess *tmp_app_path = nullptr;
String export_format = use_dmg() && p_path.ends_with("dmg") ? "dmg" : "zip";
- if (export_format == "dmg") {
- // We're on OSX so we can export to DMG, but first we create our application bundle
- tmp_app_path_name = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".app");
- print_line("Exporting to " + tmp_app_path_name);
- tmp_app_path = DirAccess::create_for_path(tmp_app_path_name);
- if (!tmp_app_path) {
- err = ERR_CANT_CREATE;
- }
- // Create our folder structure or rely on unzip?
- 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");
- }
+ // Create our application bundle.
+ tmp_app_path_name = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".app");
+ print_line("Exporting to " + tmp_app_path_name);
+ tmp_app_path = DirAccess::create_for_path(tmp_app_path_name);
+ if (!tmp_app_path) {
+ err = ERR_CANT_CREATE;
+ }
- 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");
- }
+ // 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");
+ }
- 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");
- }
- } else {
- // Open our destination zip file
- dst_pkg_zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
- if (!dst_pkg_zip) {
- err = ERR_CANT_CREATE;
- }
+ 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");
}
- // Now process our template
+ 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");
+ }
+
+ // Now process our template.
bool found_binary = false;
int total_size = 0;
while (ret == UNZ_OK && err == OK) {
bool is_execute = false;
- //get filename
+ // Get filename.
unz_file_info info;
char fname[16384];
- ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, NULL, 0, NULL, 0);
+ ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0);
String file = fname;
Vector<uint8_t> data;
data.resize(info.uncompressed_size);
- //read
+ // Read.
unzOpenCurrentFile(src_pkg_zip);
unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size());
unzCloseCurrentFile(src_pkg_zip);
- //write
-
+ // Write.
file = file.replace_first("osx_template.app/", "");
if (file == "Contents/Info.plist") {
@@ -578,7 +562,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
if (file.begins_with("Contents/MacOS/godot_")) {
if (file != "Contents/MacOS/" + binary_to_use) {
ret = unzGoToNextFile(src_pkg_zip);
- continue; //ignore!
+ continue; // skip
}
found_binary = true;
is_execute = true;
@@ -586,12 +570,13 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
}
if (file == "Contents/Resources/icon.icns") {
- //see if there is an icon
+ // See if there is an icon.
String iconpath;
- if (p_preset->get("application/icon") != "")
+ if (p_preset->get("application/icon") != "") {
iconpath = p_preset->get("application/icon");
- else
+ } else {
iconpath = ProjectSettings::get_singleton()->get("application/config/icon");
+ }
if (iconpath != "") {
if (iconpath.get_extension() == "icns") {
@@ -614,18 +599,17 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
}
if (data.size() > 0) {
-
if (file.find("/data.mono.osx.64.release_debug/") != -1) {
if (!p_debug) {
ret = unzGoToNextFile(src_pkg_zip);
- continue; //skip
+ continue; // skip
}
file = file.replace("/data.mono.osx.64.release_debug/", "/data_" + pkg_name_safe + "/");
}
if (file.find("/data.mono.osx.64.release/") != -1) {
if (p_debug) {
ret = unzGoToNextFile(src_pkg_zip);
- continue; //skip
+ continue; // skip
}
file = file.replace("/data.mono.osx.64.release/", "/data_" + pkg_name_safe + "/");
}
@@ -633,62 +617,31 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
print_line("ADDING: " + file + " size: " + itos(data.size()));
total_size += data.size();
- if (export_format == "dmg") {
- // 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());
- }
- if (err == OK) {
- // write the file, need to add chmod
- FileAccess *f = FileAccess::open(file, FileAccess::WRITE);
- if (f) {
- f->store_buffer(data.ptr(), data.size());
- f->close();
- if (is_execute) {
- // Chmod with 0755 if the file is executable
- FileAccess::set_unix_permissions(file, 0755);
- }
- memdelete(f);
- } else {
- err = ERR_CANT_CREATE;
+ // 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());
+ }
+ if (err == OK) {
+ FileAccess *f = FileAccess::open(file, FileAccess::WRITE);
+ if (f) {
+ f->store_buffer(data.ptr(), data.size());
+ f->close();
+ if (is_execute) {
+ // chmod with 0755 if the file is executable.
+ FileAccess::set_unix_permissions(file, 0755);
}
+ memdelete(f);
+ } else {
+ err = ERR_CANT_CREATE;
}
- } else {
- // add it to our zip file
- file = pkg_name + ".app/" + file;
-
- zip_fileinfo fi;
- fi.tmz_date.tm_hour = info.tmu_date.tm_hour;
- fi.tmz_date.tm_min = info.tmu_date.tm_min;
- fi.tmz_date.tm_sec = info.tmu_date.tm_sec;
- fi.tmz_date.tm_mon = info.tmu_date.tm_mon;
- fi.tmz_date.tm_mday = info.tmu_date.tm_mday;
- fi.tmz_date.tm_year = info.tmu_date.tm_year;
- fi.dosDate = info.dosDate;
- fi.internal_fa = info.internal_fa;
- fi.external_fa = info.external_fa;
-
- zipOpenNewFileInZip(dst_pkg_zip,
- file.utf8().get_data(),
- &fi,
- NULL,
- 0,
- NULL,
- 0,
- NULL,
- Z_DEFLATED,
- Z_DEFAULT_COMPRESSION);
-
- zipWriteInFileInZip(dst_pkg_zip, data.ptr(), data.size());
- zipCloseFileInZip(dst_pkg_zip);
}
}
ret = unzGoToNextFile(src_pkg_zip);
}
- // we're done with our source zip
+ // We're done with our source zip.
unzClose(src_pkg_zip);
if (!found_binary) {
@@ -701,124 +654,131 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
return ERR_SKIP;
}
- if (export_format == "dmg") {
- String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck";
- Vector<SharedObject> shared_objects;
- err = save_pack(p_preset, pack_path, &shared_objects);
+ String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck";
+ Vector<SharedObject> shared_objects;
+ err = save_pack(p_preset, pack_path, &shared_objects);
- // see if we can code sign our new package
- bool sign_enabled = p_preset->get("codesign/enable");
+ // See if we can code sign our new package.
+ bool sign_enabled = p_preset->get("codesign/enable");
- if (err == OK) {
- DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- for (int i = 0; i < shared_objects.size(); i++) {
- err = da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file());
- if (err == OK && sign_enabled) {
- err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file());
- }
+ if (err == OK) {
+ DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ for (int i = 0; i < shared_objects.size(); i++) {
+ err = da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file());
+ if (err == OK && sign_enabled) {
+ err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file());
}
- memdelete(da);
}
+ memdelete(da);
+ }
- if (err == OK && sign_enabled) {
- if (ep.step("Code signing bundle", 2)) {
- return ERR_SKIP;
- }
-
- // the order in which we code sign is important, this is a bit of a shame or we could do this in our loop that extracts the files from our ZIP
-
- // start with our application
- err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name);
-
- ///@TODO we should check the contents of /Contents/Frameworks for frameworks to sign
+ if (err == OK && sign_enabled) {
+ if (ep.step("Code signing bundle", 2)) {
+ return ERR_SKIP;
}
+ err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name);
+ }
- // and finally create a DMG
+ if (export_format == "dmg") {
+ // Create a DMG.
if (err == OK) {
if (ep.step("Making DMG", 3)) {
return ERR_SKIP;
}
err = _create_dmg(p_path, pkg_name, tmp_app_path_name);
}
-
- // Clean up temporary .app dir
- OS::get_singleton()->move_to_trash(tmp_app_path_name);
-
- } else { // pck
-
- String pack_path = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".pck");
-
- Vector<SharedObject> shared_objects;
- err = save_pack(p_preset, pack_path, &shared_objects);
-
- if (err == OK) {
- zipOpenNewFileInZip(dst_pkg_zip,
- (pkg_name + ".app/Contents/Resources/" + pkg_name + ".pck").utf8().get_data(),
- NULL,
- NULL,
- 0,
- NULL,
- 0,
- NULL,
- Z_DEFLATED,
- Z_DEFAULT_COMPRESSION);
-
- FileAccess *pf = FileAccess::open(pack_path, FileAccess::READ);
- if (pf) {
- const int BSIZE = 16384;
- uint8_t buf[BSIZE];
-
- while (true) {
-
- int r = pf->get_buffer(buf, BSIZE);
- if (r <= 0)
- break;
- zipWriteInFileInZip(dst_pkg_zip, buf, r);
- }
-
- zipCloseFileInZip(dst_pkg_zip);
- memdelete(pf);
- } else {
- err = ERR_CANT_OPEN;
+ // Sign DMG.
+ if (err == OK && sign_enabled) {
+ if (ep.step("Code signing DMG", 3)) {
+ return ERR_SKIP;
}
+ err = _code_sign(p_preset, p_path);
}
-
+ } else {
+ // Create ZIP.
if (err == OK) {
- //add shared objects
- for (int i = 0; i < shared_objects.size(); i++) {
- Vector<uint8_t> file = FileAccess::get_file_as_array(shared_objects[i].path);
- ERR_CONTINUE(file.empty());
-
- zipOpenNewFileInZip(dst_pkg_zip,
- (pkg_name + ".app/Contents/Frameworks/").plus_file(shared_objects[i].path.get_file()).utf8().get_data(),
- NULL,
- NULL,
- 0,
- NULL,
- 0,
- NULL,
- Z_DEFLATED,
- Z_DEFAULT_COMPRESSION);
-
- zipWriteInFileInZip(dst_pkg_zip, file.ptr(), file.size());
- zipCloseFileInZip(dst_pkg_zip);
+ if (ep.step("Making ZIP", 3)) {
+ return ERR_SKIP;
+ }
+ if (FileAccess::exists(p_path)) {
+ OS::get_singleton()->move_to_trash(p_path);
}
- }
- // Clean up generated file.
- DirAccess::remove_file_or_error(pack_path);
+ FileAccess *dst_f = nullptr;
+ zlib_filefunc_def io_dst = zipio_create_io_from_file(&dst_f);
+ zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
+
+ _zip_folder_recursive(zip, EditorSettings::get_singleton()->get_cache_dir(), pkg_name + ".app", pkg_name);
+
+ zipClose(zip, nullptr);
+ }
}
- }
- if (dst_pkg_zip) {
- zipClose(dst_pkg_zip, NULL);
+ // Clean up temporary .app dir.
+ OS::get_singleton()->move_to_trash(tmp_app_path_name);
}
return err;
}
-bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) {
+ String dir = p_root_path.plus_file(p_folder);
+ DirAccess *da = DirAccess::open(dir);
+ da->list_dir_begin();
+ String f;
+ while ((f = da->get_next()) != "") {
+ if (f == "." || f == "..") {
+ continue;
+ }
+ if (da->current_is_dir()) {
+ _zip_folder_recursive(p_zip, p_root_path, p_folder.plus_file(f), p_pkg_name);
+ } else {
+ bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name));
+
+ OS::Time time = OS::get_singleton()->get_time();
+ OS::Date date = OS::get_singleton()->get_date();
+
+ zip_fileinfo zipfi;
+ 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_sec = time.sec;
+ zipfi.tmz_date.tm_year = date.year;
+ zipfi.dosDate = 0;
+ zipfi.external_fa = (is_executable ? 0755 : 0644) << 16L;
+ zipfi.internal_fa = 0;
+
+ zipOpenNewFileInZip4(p_zip,
+ p_folder.plus_file(f).utf8().get_data(),
+ &zipfi,
+ nullptr,
+ 0,
+ nullptr,
+ 0,
+ nullptr,
+ Z_DEFLATED,
+ Z_DEFAULT_COMPRESSION,
+ 0,
+ -MAX_WBITS,
+ DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY,
+ nullptr,
+ 0,
+ 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
+ 0);
+
+ Vector<uint8_t> array = FileAccess::get_file_as_array(dir.plus_file(f));
+ zipWriteInFileInZip(p_zip, array.ptr(), array.size());
+ zipCloseFileInZip(p_zip);
+ }
+ }
+ da->list_dir_end();
+ memdelete(da);
+}
+
+bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
String err;
bool valid = false;
@@ -843,13 +803,13 @@ bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset
valid = dvalid || rvalid;
r_missing_templates = !valid;
- if (!err.empty())
+ if (!err.empty()) {
r_error = err;
+ }
return valid;
}
EditorExportPlatformOSX::EditorExportPlatformOSX() {
-
Ref<Image> img = memnew(Image(_osx_logo));
logo.instance();
logo->create_from_image(img);
@@ -859,7 +819,6 @@ EditorExportPlatformOSX::~EditorExportPlatformOSX() {
}
void register_osx_exporter() {
-
Ref<EditorExportPlatformOSX> platform;
platform.instance();
diff --git a/platform/osx/export/export.h b/platform/osx/export/export.h
index 7b8832cb01..4ddcec09fb 100644
--- a/platform/osx/export/export.h
+++ b/platform/osx/export/export.h
@@ -28,4 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef OSX_EXPORT_H
+#define OSX_EXPORT_H
+
void register_osx_exporter();
+
+#endif // OSX_EXPORT_H
diff --git a/platform/osx/godot_main_osx.mm b/platform/osx/godot_main_osx.mm
index eacd2b5cc6..93d0d6168c 100644
--- a/platform/osx/godot_main_osx.mm
+++ b/platform/osx/godot_main_osx.mm
@@ -36,7 +36,6 @@
#include <unistd.h>
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);
diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp
index e9f46fb5a4..d342d30097 100644
--- a/platform/osx/joypad_osx.cpp
+++ b/platform/osx/joypad_osx.cpp
@@ -34,13 +34,13 @@
#define GODOT_JOY_LOOP_RUN_MODE CFSTR("GodotJoypad")
-static JoypadOSX *self = NULL;
+static JoypadOSX *self = nullptr;
joypad::joypad() {
- device_ref = NULL;
- ff_device = NULL;
- ff_axes = NULL;
- ff_directions = NULL;
+ device_ref = nullptr;
+ ff_device = nullptr;
+ ff_axes = nullptr;
+ ff_directions = nullptr;
ffservice = 0;
ff_timestamp = 0;
id = 0;
@@ -53,7 +53,7 @@ joypad::joypad() {
ff_effect.dwTriggerButton = FFEB_NOTRIGGER;
ff_effect.dwStartDelay = 0;
ff_effect.dwTriggerRepeatInterval = 0;
- ff_effect.lpEnvelope = NULL;
+ ff_effect.lpEnvelope = nullptr;
ff_effect.cbTypeSpecificParams = sizeof(FFCONSTANTFORCE);
ff_effect.lpvTypeSpecificParams = &ff_constant_force;
ff_effect.dwSize = sizeof(ff_effect);
@@ -98,6 +98,7 @@ int joypad::get_hid_element_state(rec_element *p_element) const {
}
return value;
}
+
void joypad::add_hid_element(IOHIDElementRef p_element) {
const CFTypeID elementTypeID = p_element ? CFGetTypeID(p_element) : 0;
@@ -105,7 +106,7 @@ void joypad::add_hid_element(IOHIDElementRef p_element) {
const IOHIDElementCookie cookie = IOHIDElementGetCookie(p_element);
const uint32_t usagePage = IOHIDElementGetUsagePage(p_element);
const uint32_t usage = IOHIDElementGetUsage(p_element);
- Vector<rec_element> *list = NULL;
+ Vector<rec_element> *list = nullptr;
switch (IOHIDElementGetType(p_element)) {
case kIOHIDElementTypeInput_Misc:
@@ -240,7 +241,6 @@ static bool is_joypad(IOHIDDeviceRef p_device_ref) {
}
void JoypadOSX::_device_added(IOReturn p_res, IOHIDDeviceRef p_device) {
-
if (p_res != kIOReturnSuccess || have_device(p_device)) {
return;
}
@@ -249,7 +249,7 @@ void JoypadOSX::_device_added(IOReturn p_res, IOHIDDeviceRef p_device) {
if (is_joypad(p_device)) {
configure_joypad(p_device, &new_joypad);
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
- if (IOHIDDeviceGetService != NULL) {
+ if (IOHIDDeviceGetService != nullptr) {
#endif
const io_service_t ioservice = IOHIDDeviceGetService(p_device);
if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK) && new_joypad.config_force_feedback(ioservice)) {
@@ -264,7 +264,6 @@ void JoypadOSX::_device_added(IOReturn p_res, IOHIDDeviceRef p_device) {
}
void JoypadOSX::_device_removed(IOReturn p_res, IOHIDDeviceRef p_device) {
-
int device = get_joy_ref(p_device);
ERR_FAIL_COND(device == -1);
@@ -274,7 +273,6 @@ void JoypadOSX::_device_removed(IOReturn p_res, IOHIDDeviceRef p_device) {
}
static String _hex_str(uint8_t p_byte) {
-
static const char *dict = "0123456789abcdef";
char ret[3];
ret[2] = 0;
@@ -286,7 +284,6 @@ static String _hex_str(uint8_t p_byte) {
}
bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) {
-
p_joy->device_ref = p_device_ref;
/* get device name */
String name;
@@ -330,7 +327,7 @@ bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) {
input->joy_connection_changed(id, true, name, guid);
}
- CFArrayRef array = IOHIDDeviceCopyMatchingElements(p_device_ref, NULL, kIOHIDOptionsTypeNone);
+ CFArrayRef array = IOHIDDeviceCopyMatchingElements(p_device_ref, nullptr, kIOHIDOptionsTypeNone);
if (array) {
p_joy->add_hid_elements(array);
CFRelease(array);
@@ -346,7 +343,6 @@ bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) {
} \
}
bool joypad::config_force_feedback(io_service_t p_service) {
-
HRESULT ret = FFCreateDevice(p_service, &ff_device);
ERR_FAIL_COND_V(ret != FF_OK, false);
@@ -368,13 +364,13 @@ bool joypad::config_force_feedback(io_service_t p_service) {
#define TEST_FF(ff) (features.supportedEffects & (ff))
bool joypad::check_ff_features() {
-
FFCAPABILITIES features;
HRESULT ret = FFDeviceGetForceFeedbackCapabilities(ff_device, &features);
if (ret == FF_OK && (features.supportedEffects & FFCAP_ET_CONSTANTFORCE)) {
uint32_t val;
ret = FFDeviceGetForceFeedbackProperty(ff_device, FFPROP_FFGAIN, &val, sizeof(val));
- if (ret != FF_OK) return false;
+ if (ret != FF_OK)
+ return false;
int num_axes = features.numFfAxes;
ff_axes = (DWORD *)memalloc(sizeof(DWORD) * num_axes);
ff_directions = (LONG *)memalloc(sizeof(LONG) * num_axes);
@@ -395,38 +391,38 @@ bool joypad::check_ff_features() {
static int process_hat_value(int p_min, int p_max, int p_value) {
int range = (p_max - p_min + 1);
int value = p_value - p_min;
- int hat_value = InputDefault::HAT_MASK_CENTER;
+ int hat_value = Input::HAT_MASK_CENTER;
if (range == 4) {
value *= 2;
}
switch (value) {
case 0:
- hat_value = InputDefault::HAT_MASK_UP;
+ hat_value = Input::HAT_MASK_UP;
break;
case 1:
- hat_value = InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_RIGHT;
+ hat_value = Input::HAT_MASK_UP | Input::HAT_MASK_RIGHT;
break;
case 2:
- hat_value = InputDefault::HAT_MASK_RIGHT;
+ hat_value = Input::HAT_MASK_RIGHT;
break;
case 3:
- hat_value = InputDefault::HAT_MASK_DOWN | InputDefault::HAT_MASK_RIGHT;
+ hat_value = Input::HAT_MASK_DOWN | Input::HAT_MASK_RIGHT;
break;
case 4:
- hat_value = InputDefault::HAT_MASK_DOWN;
+ hat_value = Input::HAT_MASK_DOWN;
break;
case 5:
- hat_value = InputDefault::HAT_MASK_DOWN | InputDefault::HAT_MASK_LEFT;
+ hat_value = Input::HAT_MASK_DOWN | Input::HAT_MASK_LEFT;
break;
case 6:
- hat_value = InputDefault::HAT_MASK_LEFT;
+ hat_value = Input::HAT_MASK_LEFT;
break;
case 7:
- hat_value = InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_LEFT;
+ hat_value = Input::HAT_MASK_UP | Input::HAT_MASK_LEFT;
break;
default:
- hat_value = InputDefault::HAT_MASK_CENTER;
+ hat_value = Input::HAT_MASK_CENTER;
break;
}
return hat_value;
@@ -438,8 +434,8 @@ void JoypadOSX::poll_joypads() const {
}
}
-static const InputDefault::JoyAxis axis_correct(int p_value, int p_min, int p_max) {
- InputDefault::JoyAxis jx;
+static const Input::JoyAxis axis_correct(int p_value, int p_min, int p_max) {
+ Input::JoyAxis jx;
if (p_min < 0) {
jx.min = -1;
if (p_value < 0) {
@@ -509,14 +505,16 @@ void JoypadOSX::joypad_vibration_stop(int p_id, uint64_t p_timestamp) {
int JoypadOSX::get_joy_index(int p_id) const {
for (int i = 0; i < device_list.size(); i++) {
- if (device_list[i].id == p_id) return i;
+ if (device_list[i].id == p_id)
+ return i;
}
return -1;
}
int JoypadOSX::get_joy_ref(IOHIDDeviceRef p_device) const {
for (int i = 0; i < device_list.size(); i++) {
- if (device_list[i].device_ref == p_device) return i;
+ if (device_list[i].device_ref == p_device)
+ return i;
}
return -1;
}
@@ -531,7 +529,7 @@ bool JoypadOSX::have_device(IOHIDDeviceRef p_device) const {
}
static CFDictionaryRef create_match_dictionary(const UInt32 page, const UInt32 usage, int *okay) {
- CFDictionaryRef retval = NULL;
+ CFDictionaryRef retval = nullptr;
CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
const void *keys[2] = { (void *)CFSTR(kIOHIDDeviceUsagePageKey), (void *)CFSTR(kIOHIDDeviceUsageKey) };
@@ -556,14 +554,13 @@ static CFDictionaryRef create_match_dictionary(const UInt32 page, const UInt32 u
}
void JoypadOSX::config_hid_manager(CFArrayRef p_matching_array) const {
-
CFRunLoopRef runloop = CFRunLoopGetCurrent();
IOReturn ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone);
ERR_FAIL_COND(ret != kIOReturnSuccess);
IOHIDManagerSetDeviceMatchingMultiple(hid_manager, p_matching_array);
- IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, joypad_added_callback, NULL);
- IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, joypad_removed_callback, NULL);
+ IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, joypad_added_callback, nullptr);
+ IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, joypad_removed_callback, nullptr);
IOHIDManagerScheduleWithRunLoop(hid_manager, runloop, GODOT_JOY_LOOP_RUN_MODE);
while (CFRunLoopRunInMode(GODOT_JOY_LOOP_RUN_MODE, 0, TRUE) == kCFRunLoopRunHandledSource) {
@@ -571,9 +568,9 @@ void JoypadOSX::config_hid_manager(CFArrayRef p_matching_array) const {
}
}
-JoypadOSX::JoypadOSX() {
+JoypadOSX::JoypadOSX(Input *in) {
self = this;
- input = (InputDefault *)Input::get_singleton();
+ input = in;
int okay = 1;
const void *vals[] = {
@@ -582,7 +579,7 @@ JoypadOSX::JoypadOSX() {
(void *)create_match_dictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
};
const size_t n_elements = sizeof(vals) / sizeof(vals[0]);
- CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, n_elements, &kCFTypeArrayCallBacks) : NULL;
+ CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, n_elements, &kCFTypeArrayCallBacks) : nullptr;
for (size_t i = 0; i < n_elements; i++) {
if (vals[i]) {
@@ -592,7 +589,7 @@ JoypadOSX::JoypadOSX() {
if (array) {
hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
- if (hid_manager != NULL) {
+ if (hid_manager != nullptr) {
config_hid_manager(array);
}
CFRelease(array);
@@ -600,7 +597,6 @@ JoypadOSX::JoypadOSX() {
}
JoypadOSX::~JoypadOSX() {
-
for (int i = 0; i < device_list.size(); i++) {
device_list.write[i].free();
}
@@ -608,5 +604,5 @@ JoypadOSX::~JoypadOSX() {
IOHIDManagerUnscheduleFromRunLoop(hid_manager, CFRunLoopGetCurrent(), GODOT_JOY_LOOP_RUN_MODE);
IOHIDManagerClose(hid_manager, kIOHIDOptionsTypeNone);
CFRelease(hid_manager);
- hid_manager = NULL;
+ hid_manager = nullptr;
}
diff --git a/platform/osx/joypad_osx.h b/platform/osx/joypad_osx.h
index 2c076b3680..dc238e68e4 100644
--- a/platform/osx/joypad_osx.h
+++ b/platform/osx/joypad_osx.h
@@ -40,7 +40,7 @@
#include <ForceFeedback/ForceFeedbackConstants.h>
#include <IOKit/hid/IOHIDLib.h>
-#include "main/input_default.h"
+#include "core/input/input.h"
struct rec_element {
IOHIDElementRef ref;
@@ -88,13 +88,12 @@ struct joypad {
};
class JoypadOSX {
-
enum {
JOYPADS_MAX = 16,
};
private:
- InputDefault *input;
+ Input *input;
IOHIDManagerRef hid_manager;
Vector<joypad> device_list;
@@ -118,7 +117,7 @@ public:
void _device_added(IOReturn p_res, IOHIDDeviceRef p_device);
void _device_removed(IOReturn p_res, IOHIDDeviceRef p_device);
- JoypadOSX();
+ JoypadOSX(Input *in);
~JoypadOSX();
};
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index e865c3078f..9204a145bf 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -31,57 +31,20 @@
#ifndef OS_OSX_H
#define OS_OSX_H
-#define BitMap _QDBitMap // Suppress deprecated QuickDraw definition.
-
-#include "core/os/input.h"
+#include "core/input/input.h"
#include "crash_handler_osx.h"
#include "drivers/coreaudio/audio_driver_coreaudio.h"
#include "drivers/coremidi/midi_driver_coremidi.h"
#include "drivers/unix/os_unix.h"
#include "joypad_osx.h"
-#include "main/input_default.h"
#include "servers/audio_server.h"
-#include "servers/visual/rasterizer.h"
-#include "servers/visual/visual_server_wrap_mt.h"
-#include "servers/visual_server.h"
-
-#if defined(OPENGL_ENABLED)
-#include "context_gl_osx.h"
-#endif
-
-#if defined(VULKAN_ENABLED)
-#include "drivers/vulkan/rendering_device_vulkan.h"
-#include "platform/osx/vulkan_context_osx.h"
-#endif
-
-#include <AppKit/AppKit.h>
-#include <AppKit/NSCursor.h>
-#include <ApplicationServices/ApplicationServices.h>
-#include <CoreVideo/CoreVideo.h>
-
-#undef BitMap
-#undef CursorShape
class OS_OSX : public OS_Unix {
-public:
- struct KeyEvent {
- unsigned int osx_state;
- bool pressed;
- bool echo;
- bool raw;
- uint32_t keycode;
- uint32_t physical_keycode;
- uint32_t unicode;
- };
-
- Vector<KeyEvent> key_event_buffer;
- int key_event_pos;
+ virtual void delete_main_loop();
bool force_quit;
- VisualServer *visual_server;
- List<String> args;
- MainLoop *main_loop;
+ JoypadOSX *joypad_osx;
#ifdef COREAUDIO_ENABLED
AudioDriverCoreAudio audio_driver;
@@ -90,143 +53,27 @@ public:
MIDIDriverCoreMidi midi_driver;
#endif
- InputDefault *input;
- JoypadOSX *joypad_osx;
-
- /* objc */
-
- CGEventSourceRef eventSource;
-
- void process_events();
- void process_key_events();
-
- // pthread_key_t current;
- bool mouse_grab;
- Point2 mouse_pos;
-
- id delegate;
- id window_delegate;
- id window_object;
- id window_view;
- id autoreleasePool;
- id cursor;
-
-#if defined(OPENGL_ENABLED)
- ContextGL_OSX *context_gles2;
-#endif
-
-#if defined(VULKAN_ENABLED)
- VulkanContextOSX *context_vulkan;
- RenderingDeviceVulkan *rendering_device_vulkan;
-#endif
-
- bool layered_window;
-
- CursorShape cursor_shape;
- NSCursor *cursors[CURSOR_MAX];
- Map<CursorShape, Vector<Variant>> cursors_cache;
- MouseMode mouse_mode;
-
- String title;
- bool minimized;
- bool maximized;
- bool zoomed;
- bool resizable;
- bool window_focused;
-
- Size2 window_size;
- Rect2 restore_rect;
-
- String open_with_filename;
-
- Point2 im_position;
- bool im_active;
- String im_text;
- Point2 im_selection;
-
- Size2 min_size;
- Size2 max_size;
-
CrashHandler crash_handler;
- float _mouse_scale(float p_scale) {
- if (_display_scale() > 1.0)
- return p_scale;
- else
- return 1.0;
- }
-
- float _display_scale() const;
- float _display_scale(id screen) const;
-
- void _update_window();
-
- int video_driver_index;
- virtual int get_current_video_driver() const;
-
- struct GlobalMenuItem {
- String label;
- Variant signal;
- Variant meta;
-
- GlobalMenuItem() {
- //NOP
- }
-
- GlobalMenuItem(const String &p_label, const Variant &p_signal, const Variant &p_meta) {
- label = p_label;
- signal = p_signal;
- meta = p_meta;
- }
- };
-
- Map<String, Vector<GlobalMenuItem>> global_menus;
+ MainLoop *main_loop;
- void _update_global_menu();
+public:
+ String open_with_filename;
protected:
virtual void initialize_core();
- virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
+ virtual void initialize();
virtual void finalize();
+ virtual void initialize_joypads();
+
virtual void set_main_loop(MainLoop *p_main_loop);
- virtual void delete_main_loop();
public:
- static OS_OSX *singleton;
-
- void global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta);
- void global_menu_add_separator(const String &p_menu);
- void global_menu_remove_item(const String &p_menu, int p_idx);
- void global_menu_clear(const String &p_menu);
-
- void wm_minimized(bool p_minimized);
-
virtual String get_name() const;
- 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 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 void set_mouse_show(bool p_show);
- virtual void set_mouse_grab(bool p_grab);
- virtual bool is_mouse_grab_enabled() const;
- virtual void warp_mouse_position(const Point2 &p_to);
- virtual Point2 get_mouse_position() const;
- virtual int get_mouse_button_state() const;
- void update_real_mouse_position();
- virtual void set_window_title(const String &p_title);
-
- virtual Size2 get_window_size() const;
- virtual Size2 get_real_window_size() const;
-
- virtual void set_native_icon(const String &p_filename);
- virtual void set_icon(const Ref<Image> &p_icon);
-
virtual MainLoop *get_main_loop() const;
virtual String get_config_path() const;
@@ -237,95 +84,24 @@ public:
virtual String get_system_dir(SystemDir p_dir) const;
- virtual bool can_draw() const;
-
- virtual void set_clipboard(const String &p_text);
- virtual String get_clipboard() const;
-
- virtual void release_rendering_thread();
- virtual void make_rendering_thread();
- virtual void swap_buffers();
-
Error shell_open(String p_uri);
- void push_input(const Ref<InputEvent> &p_event);
String get_locale() 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 LatinKeyboardVariant get_latin_keyboard_variant() const;
-
- virtual void move_window_to_foreground();
-
- 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 int get_screen_dpi(int p_screen = -1) const;
-
- virtual Point2 get_window_position() const;
- virtual void set_window_position(const Point2 &p_position);
- virtual Size2 get_max_window_size() const;
- virtual Size2 get_min_window_size() const;
- virtual void set_min_window_size(const Size2 p_size);
- virtual void set_max_window_size(const Size2 p_size);
- virtual void set_window_size(const Size2 p_size);
- 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_window_always_on_top(bool p_enabled);
- virtual bool is_window_always_on_top() const;
- virtual bool is_window_focused() const;
- virtual void request_attention();
- virtual String get_joy_guid(int p_device) const;
-
- virtual void set_borderless_window(bool p_borderless);
- virtual bool get_borderless_window();
-
- virtual bool get_window_per_pixel_transparency_enabled() const;
- virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
-
- virtual void set_ime_active(const bool p_active);
- virtual void set_ime_position(const Point2 &p_pos);
- virtual Point2 get_ime_selection() const;
- virtual String get_ime_text() const;
-
- virtual String get_unique_id() const;
+ virtual String get_unique_id() const; //++
virtual bool _check_internal_feature_support(const String &p_feature);
- virtual void _set_use_vsync(bool p_enable);
- //virtual bool is_vsync_enabled() const;
-
void run();
- void set_mouse_mode(MouseMode p_mode);
- MouseMode get_mouse_mode() const;
-
void disable_crash_handler();
bool is_disable_crash_handler() const;
virtual Error move_to_trash(const String &p_path);
- void force_process_input();
-
OS_OSX();
-
-private:
- Point2 get_native_screen_position(int p_screen) const;
- Point2 get_native_window_position() const;
- void set_native_window_position(const Point2 &p_position);
- Point2 get_screens_origin() const;
};
#endif
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 8ba8ca8a33..dba96ccfcd 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -30,1396 +30,73 @@
#include "os_osx.h"
-#include "core/os/keyboard.h"
-#include "core/print_string.h"
#include "core/version_generated.gen.h"
-#include "dir_access_osx.h"
-
-#if defined(OPENGL_ENABLED)
-#include "drivers/gles2/rasterizer_gles2.h"
-#endif
-
-#if defined(VULKAN_ENABLED)
-#include "servers/visual/rasterizer_rd/rasterizer_rd.h"
-#endif
+#include "dir_access_osx.h"
+#include "display_server_osx.h"
#include "main/main.h"
-#include "servers/visual/visual_server_raster.h"
-#include "servers/visual/visual_server_wrap_mt.h"
-
-#include <mach-o/dyld.h>
-
-#include <Carbon/Carbon.h>
-#import <Cocoa/Cocoa.h>
-#include <IOKit/IOCFPlugIn.h>
-#include <IOKit/IOKitLib.h>
-#include <IOKit/hid/IOHIDKeys.h>
-#include <IOKit/hid/IOHIDLib.h>
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
-#include <os/log.h>
-#endif
-
-#import <QuartzCore/CAMetalLayer.h>
-#include <vulkan/vulkan_metal.h>
#include <dlfcn.h>
-#include <fcntl.h>
#include <libproc.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200
-#define NSEventMaskAny NSAnyEventMask
-#define NSEventTypeKeyDown NSKeyDown
-#define NSEventTypeKeyUp NSKeyUp
-#define NSEventModifierFlagShift NSShiftKeyMask
-#define NSEventModifierFlagCommand NSCommandKeyMask
-#define NSEventModifierFlagControl NSControlKeyMask
-#define NSEventModifierFlagOption NSAlternateKeyMask
-#define NSWindowStyleMaskTitled NSTitledWindowMask
-#define NSWindowStyleMaskResizable NSResizableWindowMask
-#define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask
-#define NSWindowStyleMaskClosable NSClosableWindowMask
-#define NSWindowStyleMaskBorderless NSBorderlessWindowMask
-#endif
-
-#ifndef NSAppKitVersionNumber10_12
-#define NSAppKitVersionNumber10_12 1504
-#endif
-#ifndef NSAppKitVersionNumber10_14
-#define NSAppKitVersionNumber10_14 1671
-#endif
-
-static void get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> state) {
-
- state->set_shift((p_osx_state & NSEventModifierFlagShift));
- state->set_control((p_osx_state & NSEventModifierFlagControl));
- state->set_alt((p_osx_state & NSEventModifierFlagOption));
- state->set_metakey((p_osx_state & NSEventModifierFlagCommand));
-}
-
-static void push_to_key_event_buffer(const OS_OSX::KeyEvent &p_event) {
-
- Vector<OS_OSX::KeyEvent> &buffer = OS_OSX::singleton->key_event_buffer;
- if (OS_OSX::singleton->key_event_pos >= buffer.size()) {
- buffer.resize(1 + OS_OSX::singleton->key_event_pos);
- }
- buffer.write[OS_OSX::singleton->key_event_pos++] = p_event;
-}
-
-static int mouse_x = 0;
-static int mouse_y = 0;
-static int button_mask = 0;
-static bool mouse_down_control = false;
-
-static Vector2 get_mouse_pos(NSPoint locationInWindow, CGFloat backingScaleFactor) {
-
- const NSRect contentRect = [OS_OSX::singleton->window_view frame];
- const NSPoint p = locationInWindow;
- const float s = OS_OSX::singleton->_mouse_scale(backingScaleFactor);
- mouse_x = p.x * s;
- mouse_y = (contentRect.size.height - p.y) * s;
- return Vector2(mouse_x, mouse_y);
-}
-
-static NSCursor *cursorFromSelector(SEL selector, SEL fallback = nil) {
- if ([NSCursor respondsToSelector:selector]) {
- id object = [NSCursor performSelector:selector];
- if ([object isKindOfClass:[NSCursor class]]) {
- return object;
- }
- }
- if (fallback) {
- // Fallback should be a reasonable default, no need to check.
- return [NSCursor performSelector:fallback];
- }
- return [NSCursor arrowCursor];
-}
-
-@interface GodotApplication : NSApplication
-@end
-
-@implementation GodotApplication
-
-- (void)sendEvent:(NSEvent *)event {
-
- // special case handling of command-period, which is traditionally a special
- // shortcut in macOS and doesn't arrive at our regular keyDown handler.
- if ([event type] == NSEventTypeKeyDown) {
- if (([event modifierFlags] & NSEventModifierFlagCommand) && [event keyCode] == 0x2f) {
-
- Ref<InputEventKey> k;
- k.instance();
-
- get_key_modifier_state([event modifierFlags], k);
- k->set_pressed(true);
- k->set_keycode(KEY_PERIOD);
- k->set_physical_keycode(KEY_PERIOD);
- k->set_echo([event isARepeat]);
-
- OS_OSX::singleton->push_input(k);
- }
- }
-
- // 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))
- [[self keyWindow] sendEvent:event];
- else
- [super sendEvent:event];
-}
-
-@end
-
-@interface GodotApplicationDelegate : NSObject
-- (void)forceUnbundledWindowActivationHackStep1;
-- (void)forceUnbundledWindowActivationHackStep2;
-- (void)forceUnbundledWindowActivationHackStep3;
-@end
-
-@implementation GodotApplicationDelegate
-
-- (void)forceUnbundledWindowActivationHackStep1 {
- // Step1: Switch focus to macOS Dock.
- // Required to perform step 2, TransformProcessType will fail if app is already the in focus.
- for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) {
- [app activateWithOptions:NSApplicationActivateIgnoringOtherApps];
- break;
- }
- [self performSelector:@selector(forceUnbundledWindowActivationHackStep2) withObject:nil afterDelay:0.02];
-}
-
-- (void)forceUnbundledWindowActivationHackStep2 {
- // Step 2: Register app as foreground process.
- ProcessSerialNumber psn = { 0, kCurrentProcess };
- (void)TransformProcessType(&psn, kProcessTransformToForegroundApplication);
-
- [self performSelector:@selector(forceUnbundledWindowActivationHackStep3) withObject:nil afterDelay:0.02];
-}
-
-- (void)forceUnbundledWindowActivationHackStep3 {
- // Step 3: Switch focus back to app window.
- [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
-}
-
-- (void)applicationDidFinishLaunching:(NSNotification *)notice {
- NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
- if (nsappname == nil) {
- // If executable is not a bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored).
- [self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02];
- }
-}
-
-- (void)globalMenuCallback:(id)sender {
-
- if (![sender representedObject])
- return;
-
- OS_OSX::GlobalMenuItem *item = (OS_OSX::GlobalMenuItem *)[[sender representedObject] pointerValue];
-
- if (!item)
- return;
-
- OS_OSX::singleton->main_loop->global_menu_action(item->signal, item->meta);
-}
-
-- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
-
- NSMenu *menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
-
- Vector<OS_OSX::GlobalMenuItem> &E = OS_OSX::singleton->global_menus["_dock"];
- for (int i = 0; i < E.size(); i++) {
- if (E[i].label == String()) {
- [menu addItem:[NSMenuItem separatorItem]];
- } else {
- NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:E[i].label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
- [menu_item setRepresentedObject:[NSValue valueWithPointer:&(E[i])]];
- }
- }
-
- return menu;
-}
-
-- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
- // Note: may be called called before main loop init!
- char *utfs = strdup([filename UTF8String]);
- OS_OSX::singleton->open_with_filename.parse_utf8(utfs);
- free(utfs);
-
-#ifdef TOOLS_ENABLED
- // Open new instance
- if (OS_OSX::singleton->get_main_loop()) {
- List<String> args;
- args.push_back(OS_OSX::singleton->open_with_filename);
- String exec = OS::get_singleton()->get_executable_path();
-
- OS::ProcessID pid = 0;
- OS::get_singleton()->execute(exec, args, false, &pid);
- }
-#endif
- return YES;
-}
-
-- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
-
- return NSTerminateCancel;
-}
-
-- (void)showAbout:(id)sender {
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT);
-}
-
-@end
-
-@interface GodotWindowDelegate : NSObject {
- //_Godotwindow* window;
-}
-
-- (void)windowWillClose:(NSNotification *)notification;
-
-@end
-
-@implementation GodotWindowDelegate
-
-- (BOOL)windowShouldClose:(id)sender {
- //_GodotInputWindowCloseRequest(window);
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
-
- return NO;
-}
-
-- (void)windowWillClose:(NSNotification *)notification {
-#if defined(VULKAN_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_VULKAN) {
-
- if (OS_OSX::singleton->rendering_device_vulkan) {
- OS_OSX::singleton->rendering_device_vulkan->finalize();
- memdelete(OS_OSX::singleton->rendering_device_vulkan);
- OS_OSX::singleton->rendering_device_vulkan = NULL;
- }
-
- if (OS_OSX::singleton->context_vulkan) {
- memdelete(OS_OSX::singleton->context_vulkan);
- OS_OSX::singleton->context_vulkan = NULL;
- }
- }
-#endif
-}
-
-- (void)windowDidEnterFullScreen:(NSNotification *)notification {
- OS_OSX::singleton->zoomed = true;
-
- [OS_OSX::singleton->window_object setContentMinSize:NSMakeSize(0, 0)];
- [OS_OSX::singleton->window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
-}
-
-- (void)windowDidExitFullScreen:(NSNotification *)notification {
- OS_OSX::singleton->zoomed = false;
-
- if (OS_OSX::singleton->min_size != Size2()) {
- Size2 size = OS_OSX::singleton->min_size / OS_OSX::singleton->_display_scale();
- [OS_OSX::singleton->window_object setContentMinSize:NSMakeSize(size.x, size.y)];
- }
- if (OS_OSX::singleton->max_size != Size2()) {
- Size2 size = OS_OSX::singleton->max_size / OS_OSX::singleton->_display_scale();
- [OS_OSX::singleton->window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
- }
-
- if (!OS_OSX::singleton->resizable)
- [OS_OSX::singleton->window_object setStyleMask:[OS_OSX::singleton->window_object styleMask] & ~NSWindowStyleMaskResizable];
-}
-
-- (void)windowDidChangeBackingProperties:(NSNotification *)notification {
- if (!OS_OSX::singleton)
- return;
-
- NSWindow *window = (NSWindow *)[notification object];
- CGFloat newBackingScaleFactor = [window backingScaleFactor];
- CGFloat oldBackingScaleFactor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue];
-
-#if defined(OPENGL_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_GLES2) {
- if (OS_OSX::singleton->is_hidpi_allowed()) {
- [OS_OSX::singleton->window_view setWantsBestResolutionOpenGLSurface:YES];
- } else {
- [OS_OSX::singleton->window_view setWantsBestResolutionOpenGLSurface:NO];
- }
- }
-#endif
-
- if (newBackingScaleFactor != oldBackingScaleFactor) {
- //Set new display scale and window size
- float newDisplayScale = OS_OSX::singleton->is_hidpi_allowed() ? newBackingScaleFactor : 1.0;
-
- const NSRect contentRect = [OS_OSX::singleton->window_view frame];
- const NSRect fbRect = contentRect;
-
- OS_OSX::singleton->window_size.width = fbRect.size.width * newDisplayScale;
- OS_OSX::singleton->window_size.height = fbRect.size.height * newDisplayScale;
-
-#if defined(VULKAN_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_VULKAN) {
- CALayer *layer = [OS_OSX::singleton->window_view layer];
- layer.contentsScale = OS_OSX::singleton->_display_scale();
- }
-#endif
- //Update context
- if (OS_OSX::singleton->main_loop) {
- //Force window resize event
- [self windowDidResize:notification];
- }
- }
-}
-
-- (void)windowDidResize:(NSNotification *)notification {
-
-#if defined(OPENGL_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_GLES2) {
- OS_OSX::singleton->context_gles2->update();
- }
-#endif
- const NSRect contentRect = [OS_OSX::singleton->window_view frame];
- const NSRect fbRect = contentRect;
-
- float displayScale = OS_OSX::singleton->_display_scale();
- OS_OSX::singleton->window_size.width = fbRect.size.width * displayScale;
- OS_OSX::singleton->window_size.height = fbRect.size.height * displayScale;
-
-#if defined(VULKAN_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_VULKAN) {
- CALayer *layer = [OS_OSX::singleton->window_view layer];
- layer.contentsScale = OS_OSX::singleton->_display_scale();
- OS_OSX::singleton->context_vulkan->window_resize(0, OS_OSX::singleton->window_size.width, OS_OSX::singleton->window_size.height);
- }
-#endif
-
- if (OS_OSX::singleton->main_loop) {
- Main::force_redraw();
- //Event retrieval blocks until resize is over. Call Main::iteration() directly.
- if (!Main::is_iterating()) { //avoid cyclic loop
- Main::iteration();
- }
- }
-}
-
-- (void)windowDidMove:(NSNotification *)notification {
-
- if (OS_OSX::singleton->get_main_loop()) {
- OS_OSX::singleton->input->release_pressed_events();
- }
-}
-
-- (void)windowDidBecomeKey:(NSNotification *)notification {
- if (OS_OSX::singleton->get_main_loop()) {
- get_mouse_pos(
- [OS_OSX::singleton->window_object mouseLocationOutsideOfEventStream],
- [OS_OSX::singleton->window_view backingScaleFactor]);
- OS_OSX::singleton->input->set_mouse_position(Point2(mouse_x, mouse_y));
-
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
- }
-
- OS_OSX::singleton->window_focused = true;
-}
-
-- (void)windowDidResignKey:(NSNotification *)notification {
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
-
- OS_OSX::singleton->window_focused = false;
-}
-
-- (void)windowDidMiniaturize:(NSNotification *)notification {
- OS_OSX::singleton->wm_minimized(true);
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
-
- OS_OSX::singleton->window_focused = false;
-};
-
-- (void)windowDidDeminiaturize:(NSNotification *)notification {
- OS_OSX::singleton->wm_minimized(false);
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
-
- OS_OSX::singleton->window_focused = true;
-};
-
-@end
-
-@interface GodotContentView : NSView <NSTextInputClient> {
- NSTrackingArea *trackingArea;
- NSMutableAttributedString *markedText;
- bool imeInputEventInProgress;
-}
-- (void)cancelComposition;
-
-- (CALayer *)makeBackingLayer;
-
-- (BOOL)wantsUpdateLayer;
-- (void)updateLayer;
-
-@end
-
-@implementation GodotContentView
-
-+ (void)initialize {
- if (self == [GodotContentView class]) {
- // nothing left to do here at the moment..
- }
-}
-
-- (CALayer *)makeBackingLayer {
-#if defined(VULKAN_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_VULKAN) {
- CALayer *layer = [[CAMetalLayer class] layer];
- layer.contentsScale = OS_OSX::singleton->_display_scale();
- return layer;
- }
-#endif
- return [super makeBackingLayer];
-}
-
-- (void)updateLayer {
-#if defined(VULKAN_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_VULKAN) {
- [super updateLayer];
- }
-#endif
-#if defined(OPENGL_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_GLES2) {
- OS_OSX::singleton->context_gles2->update();
- }
-#endif
-}
-
-- (BOOL)wantsUpdateLayer {
- return YES;
-}
-
-- (id)init {
- self = [super init];
- trackingArea = nil;
- imeInputEventInProgress = false;
- [self updateTrackingAreas];
- [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
- markedText = [[NSMutableAttributedString alloc] init];
- return self;
-}
-
-- (void)dealloc {
- [trackingArea release];
- [markedText release];
- [super dealloc];
-}
-
-static const NSRange kEmptyRange = { NSNotFound, 0 };
-
-- (BOOL)hasMarkedText {
- return (markedText.length > 0);
-}
-
-- (NSRange)markedRange {
- return NSMakeRange(0, markedText.length);
-}
-
-- (NSRange)selectedRange {
- return kEmptyRange;
-}
-
-- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange {
- if ([aString isKindOfClass:[NSAttributedString class]]) {
- [markedText initWithAttributedString:aString];
- } else {
- [markedText initWithString:aString];
- }
- if (markedText.length == 0) {
- [self unmarkText];
- return;
- }
- if (OS_OSX::singleton->im_active) {
- imeInputEventInProgress = true;
- OS_OSX::singleton->im_text.parse_utf8([[markedText mutableString] UTF8String]);
- OS_OSX::singleton->im_selection = Point2(selectedRange.location, selectedRange.length);
-
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
- }
-}
-
-- (void)doCommandBySelector:(SEL)aSelector {
- if ([self respondsToSelector:aSelector])
- [self performSelector:aSelector];
-}
-
-- (void)unmarkText {
- imeInputEventInProgress = false;
- [[markedText mutableString] setString:@""];
- if (OS_OSX::singleton->im_active) {
- OS_OSX::singleton->im_text = String();
- OS_OSX::singleton->im_selection = Point2();
-
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
- }
-}
-
-- (NSArray *)validAttributesForMarkedText {
- return [NSArray array];
-}
-
-- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
- return nil;
-}
-
-- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
- return 0;
-}
-
-- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
- const NSRect contentRect = [OS_OSX::singleton->window_view frame];
- float displayScale = OS_OSX::singleton->_display_scale();
- NSRect pointInWindowRect = NSMakeRect(OS_OSX::singleton->im_position.x / displayScale, contentRect.size.height - (OS_OSX::singleton->im_position.y / displayScale) - 1, 0, 0);
- NSPoint pointOnScreen = [[OS_OSX::singleton->window_view window] convertRectToScreen:pointInWindowRect].origin;
-
- return NSMakeRect(pointOnScreen.x, pointOnScreen.y, 0, 0);
-}
-
-- (void)cancelComposition {
- [self unmarkText];
- NSTextInputContext *currentInputContext = [NSTextInputContext currentInputContext];
- [currentInputContext discardMarkedText];
-}
-
-- (void)insertText:(id)aString {
- [self insertText:aString replacementRange:NSMakeRange(0, 0)];
-}
-
-- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
- NSEvent *event = [NSApp currentEvent];
-
- NSString *characters;
- if ([aString isKindOfClass:[NSAttributedString class]]) {
- characters = [aString string];
- } else {
- 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) {
- NSTextInputContext *currentInputContext = [NSTextInputContext currentInputContext];
- [currentInputContext discardMarkedText];
- [self cancelComposition];
- return;
- }
-
- for (i = 0; i < length; i++) {
- const unichar codepoint = [characters characterAtIndex:i];
- if ((codepoint & 0xFF00) == 0xF700)
- continue;
-
- OS_OSX::KeyEvent ke;
-
- ke.osx_state = [event modifierFlags];
- ke.pressed = true;
- ke.echo = false;
- ke.raw = false; // IME input event
- ke.keycode = 0;
- ke.physical_keycode = 0;
- ke.unicode = codepoint;
-
- push_to_key_event_buffer(ke);
- }
- [self cancelComposition];
-}
-
-- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
- return NSDragOperationCopy;
-}
-
-- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
- return NSDragOperationCopy;
-}
-
-- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
-
- 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 (files.size()) {
- OS_OSX::singleton->main_loop->drop_files(files, 0);
- OS_OSX::singleton->move_window_to_foreground();
- }
-
- return NO;
-}
-
-- (BOOL)isOpaque {
- return YES;
-}
-
-- (BOOL)canBecomeKeyView {
- return YES;
-}
-
-- (BOOL)acceptsFirstResponder {
- return YES;
-}
-
-- (void)cursorUpdate:(NSEvent *)event {
- OS::CursorShape p_shape = OS_OSX::singleton->cursor_shape;
- OS_OSX::singleton->cursor_shape = OS::CURSOR_MAX;
- OS_OSX::singleton->set_cursor_shape(p_shape);
-}
-
-static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
- if (pressed) {
- button_mask |= mask;
- } else {
- button_mask &= ~mask;
- }
-
- Ref<InputEventMouseButton> mb;
- mb.instance();
- const CGFloat backingScaleFactor = [[event window] backingScaleFactor];
- const Vector2 pos = get_mouse_pos([event locationInWindow], backingScaleFactor);
- get_key_modifier_state([event modifierFlags], mb);
- mb->set_button_index(index);
- mb->set_pressed(pressed);
- mb->set_position(pos);
- mb->set_global_position(pos);
- mb->set_button_mask(button_mask);
- if (index == BUTTON_LEFT && pressed) {
- mb->set_doubleclick([event clickCount] == 2);
- }
- OS_OSX::singleton->push_input(mb);
-}
-
-- (void)mouseDown:(NSEvent *)event {
- if (([event modifierFlags] & NSEventModifierFlagControl)) {
- mouse_down_control = true;
- _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true);
- } else {
- mouse_down_control = false;
- _mouseDownEvent(event, BUTTON_LEFT, BUTTON_MASK_LEFT, true);
- }
-}
-
-- (void)mouseDragged:(NSEvent *)event {
- [self mouseMoved:event];
-}
-
-- (void)mouseUp:(NSEvent *)event {
- if (mouse_down_control) {
- _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false);
- } else {
- _mouseDownEvent(event, BUTTON_LEFT, BUTTON_MASK_LEFT, false);
- }
-}
-
-- (void)mouseMoved:(NSEvent *)event {
-
- Ref<InputEventMouseMotion> mm;
- mm.instance();
-
- mm->set_button_mask(button_mask);
- const CGFloat backingScaleFactor = [[event window] backingScaleFactor];
- const Vector2 pos = get_mouse_pos([event locationInWindow], backingScaleFactor);
- mm->set_position(pos);
- mm->set_pressure([event pressure]);
- if ([event subtype] == NSEventSubtypeTabletPoint) {
- const NSPoint p = [event tilt];
- mm->set_tilt(Vector2(p.x, p.y));
- }
- mm->set_global_position(pos);
- mm->set_speed(OS_OSX::singleton->input->get_last_mouse_speed());
- Vector2 relativeMotion = Vector2();
- relativeMotion.x = [event deltaX] * OS_OSX::singleton -> _mouse_scale(backingScaleFactor);
- relativeMotion.y = [event deltaY] * OS_OSX::singleton -> _mouse_scale(backingScaleFactor);
- mm->set_relative(relativeMotion);
- get_key_modifier_state([event modifierFlags], mm);
-
- OS_OSX::singleton->input->set_mouse_position(Point2(mouse_x, mouse_y));
- OS_OSX::singleton->push_input(mm);
-}
-
-- (void)rightMouseDown:(NSEvent *)event {
- _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true);
-}
-
-- (void)rightMouseDragged:(NSEvent *)event {
- [self mouseMoved:event];
-}
-
-- (void)rightMouseUp:(NSEvent *)event {
- _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false);
-}
-
-- (void)otherMouseDown:(NSEvent *)event {
-
- if ((int)[event buttonNumber] == 2) {
- _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, true);
-
- } else if ((int)[event buttonNumber] == 3) {
- _mouseDownEvent(event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, true);
-
- } else if ((int)[event buttonNumber] == 4) {
- _mouseDownEvent(event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, true);
-
- } else {
- return;
- }
-}
-
-- (void)otherMouseDragged:(NSEvent *)event {
- [self mouseMoved:event];
-}
-
-- (void)otherMouseUp:(NSEvent *)event {
-
- if ((int)[event buttonNumber] == 2) {
- _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, false);
-
- } else if ((int)[event buttonNumber] == 3) {
- _mouseDownEvent(event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, false);
-
- } else if ((int)[event buttonNumber] == 4) {
- _mouseDownEvent(event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, false);
-
- } else {
- return;
- }
-}
-
-- (void)mouseExited:(NSEvent *)event {
- if (!OS_OSX::singleton)
- return;
-
- if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED)
- OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
-}
-
-- (void)mouseEntered:(NSEvent *)event {
- if (!OS_OSX::singleton)
- return;
- if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED)
- OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
-
- OS::CursorShape p_shape = OS_OSX::singleton->cursor_shape;
- OS_OSX::singleton->cursor_shape = OS::CURSOR_MAX;
- OS_OSX::singleton->set_cursor_shape(p_shape);
-}
-
-- (void)magnifyWithEvent:(NSEvent *)event {
- Ref<InputEventMagnifyGesture> ev;
- ev.instance();
- get_key_modifier_state([event modifierFlags], ev);
- ev->set_position(get_mouse_pos([event locationInWindow], [[event window] backingScaleFactor]));
- ev->set_factor([event magnification] + 1.0);
- OS_OSX::singleton->push_input(ev);
-}
-
-- (void)viewDidChangeBackingProperties {
- // nothing left to do here
-}
-
-- (void)updateTrackingAreas {
- if (trackingArea != nil) {
- [self removeTrackingArea:trackingArea];
- [trackingArea release];
- }
-
- NSTrackingAreaOptions options =
- NSTrackingMouseEnteredAndExited |
- NSTrackingActiveInKeyWindow |
- NSTrackingCursorUpdate |
- NSTrackingInVisibleRect;
-
- trackingArea = [[NSTrackingArea alloc]
- initWithRect:[self bounds]
- options:options
- owner:self
- userInfo:nil];
-
- [self addTrackingArea:trackingArea];
- [super updateTrackingAreas];
-}
-
-static bool isNumpadKey(unsigned int key) {
-
- static const unsigned int table[] = {
- 0x41, /* kVK_ANSI_KeypadDecimal */
- 0x43, /* kVK_ANSI_KeypadMultiply */
- 0x45, /* kVK_ANSI_KeypadPlus */
- 0x47, /* kVK_ANSI_KeypadClear */
- 0x4b, /* kVK_ANSI_KeypadDivide */
- 0x4c, /* kVK_ANSI_KeypadEnter */
- 0x4e, /* kVK_ANSI_KeypadMinus */
- 0x51, /* kVK_ANSI_KeypadEquals */
- 0x52, /* kVK_ANSI_Keypad0 */
- 0x53, /* kVK_ANSI_Keypad1 */
- 0x54, /* kVK_ANSI_Keypad2 */
- 0x55, /* kVK_ANSI_Keypad3 */
- 0x56, /* kVK_ANSI_Keypad4 */
- 0x57, /* kVK_ANSI_Keypad5 */
- 0x58, /* kVK_ANSI_Keypad6 */
- 0x59, /* kVK_ANSI_Keypad7 */
- 0x5b, /* kVK_ANSI_Keypad8 */
- 0x5c, /* kVK_ANSI_Keypad9 */
- 0x5f, /* kVK_JIS_KeypadComma */
- 0x00
- };
- for (int i = 0; table[i] != 0; i++) {
- if (key == table[i])
- return true;
- }
- return false;
-}
-
-// Translates a OS X keycode to a Godot keycode
-//
-static int translateKey(unsigned int key) {
-
- // Keyboard symbol translation table
- static const unsigned int table[128] = {
- /* 00 */ KEY_A,
- /* 01 */ KEY_S,
- /* 02 */ KEY_D,
- /* 03 */ KEY_F,
- /* 04 */ KEY_H,
- /* 05 */ KEY_G,
- /* 06 */ KEY_Z,
- /* 07 */ KEY_X,
- /* 08 */ KEY_C,
- /* 09 */ KEY_V,
- /* 0a */ KEY_SECTION, /* ISO Section */
- /* 0b */ KEY_B,
- /* 0c */ KEY_Q,
- /* 0d */ KEY_W,
- /* 0e */ KEY_E,
- /* 0f */ KEY_R,
- /* 10 */ KEY_Y,
- /* 11 */ KEY_T,
- /* 12 */ KEY_1,
- /* 13 */ KEY_2,
- /* 14 */ KEY_3,
- /* 15 */ KEY_4,
- /* 16 */ KEY_6,
- /* 17 */ KEY_5,
- /* 18 */ KEY_EQUAL,
- /* 19 */ KEY_9,
- /* 1a */ KEY_7,
- /* 1b */ KEY_MINUS,
- /* 1c */ KEY_8,
- /* 1d */ KEY_0,
- /* 1e */ KEY_BRACERIGHT,
- /* 1f */ KEY_O,
- /* 20 */ KEY_U,
- /* 21 */ KEY_BRACELEFT,
- /* 22 */ KEY_I,
- /* 23 */ KEY_P,
- /* 24 */ KEY_ENTER,
- /* 25 */ KEY_L,
- /* 26 */ KEY_J,
- /* 27 */ KEY_APOSTROPHE,
- /* 28 */ KEY_K,
- /* 29 */ KEY_SEMICOLON,
- /* 2a */ KEY_BACKSLASH,
- /* 2b */ KEY_COMMA,
- /* 2c */ KEY_SLASH,
- /* 2d */ KEY_N,
- /* 2e */ KEY_M,
- /* 2f */ KEY_PERIOD,
- /* 30 */ KEY_TAB,
- /* 31 */ KEY_SPACE,
- /* 32 */ KEY_QUOTELEFT,
- /* 33 */ KEY_BACKSPACE,
- /* 34 */ KEY_UNKNOWN,
- /* 35 */ KEY_ESCAPE,
- /* 36 */ KEY_META,
- /* 37 */ KEY_META,
- /* 38 */ KEY_SHIFT,
- /* 39 */ KEY_CAPSLOCK,
- /* 3a */ KEY_ALT,
- /* 3b */ KEY_CONTROL,
- /* 3c */ KEY_SHIFT,
- /* 3d */ KEY_ALT,
- /* 3e */ KEY_CONTROL,
- /* 3f */ KEY_UNKNOWN, /* Function */
- /* 40 */ KEY_UNKNOWN, /* F17 */
- /* 41 */ KEY_KP_PERIOD,
- /* 42 */ KEY_UNKNOWN,
- /* 43 */ KEY_KP_MULTIPLY,
- /* 44 */ KEY_UNKNOWN,
- /* 45 */ KEY_KP_ADD,
- /* 46 */ KEY_UNKNOWN,
- /* 47 */ KEY_NUMLOCK, /* Really KeypadClear... */
- /* 48 */ KEY_VOLUMEUP, /* VolumeUp */
- /* 49 */ KEY_VOLUMEDOWN, /* VolumeDown */
- /* 4a */ KEY_VOLUMEMUTE, /* Mute */
- /* 4b */ KEY_KP_DIVIDE,
- /* 4c */ KEY_KP_ENTER,
- /* 4d */ KEY_UNKNOWN,
- /* 4e */ KEY_KP_SUBTRACT,
- /* 4f */ KEY_UNKNOWN, /* F18 */
- /* 50 */ KEY_UNKNOWN, /* F19 */
- /* 51 */ KEY_EQUAL, /* KeypadEqual */
- /* 52 */ KEY_KP_0,
- /* 53 */ KEY_KP_1,
- /* 54 */ KEY_KP_2,
- /* 55 */ KEY_KP_3,
- /* 56 */ KEY_KP_4,
- /* 57 */ KEY_KP_5,
- /* 58 */ KEY_KP_6,
- /* 59 */ KEY_KP_7,
- /* 5a */ KEY_UNKNOWN, /* F20 */
- /* 5b */ KEY_KP_8,
- /* 5c */ KEY_KP_9,
- /* 5d */ KEY_YEN, /* JIS Yen */
- /* 5e */ KEY_UNDERSCORE, /* JIS Underscore */
- /* 5f */ KEY_COMMA, /* JIS KeypadComma */
- /* 60 */ KEY_F5,
- /* 61 */ KEY_F6,
- /* 62 */ KEY_F7,
- /* 63 */ KEY_F3,
- /* 64 */ KEY_F8,
- /* 65 */ KEY_F9,
- /* 66 */ KEY_UNKNOWN, /* JIS Eisu */
- /* 67 */ KEY_F11,
- /* 68 */ KEY_UNKNOWN, /* JIS Kana */
- /* 69 */ KEY_F13,
- /* 6a */ KEY_F16,
- /* 6b */ KEY_F14,
- /* 6c */ KEY_UNKNOWN,
- /* 6d */ KEY_F10,
- /* 6e */ KEY_MENU,
- /* 6f */ KEY_F12,
- /* 70 */ KEY_UNKNOWN,
- /* 71 */ KEY_F15,
- /* 72 */ KEY_INSERT, /* Really Help... */
- /* 73 */ KEY_HOME,
- /* 74 */ KEY_PAGEUP,
- /* 75 */ KEY_DELETE,
- /* 76 */ KEY_F4,
- /* 77 */ KEY_END,
- /* 78 */ KEY_F2,
- /* 79 */ KEY_PAGEDOWN,
- /* 7a */ KEY_F1,
- /* 7b */ KEY_LEFT,
- /* 7c */ KEY_RIGHT,
- /* 7d */ KEY_DOWN,
- /* 7e */ KEY_UP,
- /* 7f */ KEY_UNKNOWN,
- };
-
- if (key >= 128)
- return KEY_UNKNOWN;
-
- return table[key];
-}
-
-struct _KeyCodeMap {
- UniChar kchar;
- int kcode;
-};
-
-static const _KeyCodeMap _keycodes[55] = {
- { '`', KEY_QUOTELEFT },
- { '~', KEY_ASCIITILDE },
- { '0', KEY_0 },
- { '1', KEY_1 },
- { '2', KEY_2 },
- { '3', KEY_3 },
- { '4', KEY_4 },
- { '5', KEY_5 },
- { '6', KEY_6 },
- { '7', KEY_7 },
- { '8', KEY_8 },
- { '9', KEY_9 },
- { '-', KEY_MINUS },
- { '_', KEY_UNDERSCORE },
- { '=', KEY_EQUAL },
- { '+', KEY_PLUS },
- { 'q', KEY_Q },
- { 'w', KEY_W },
- { 'e', KEY_E },
- { 'r', KEY_R },
- { 't', KEY_T },
- { 'y', KEY_Y },
- { 'u', KEY_U },
- { 'i', KEY_I },
- { 'o', KEY_O },
- { 'p', KEY_P },
- { '[', KEY_BRACELEFT },
- { ']', KEY_BRACERIGHT },
- { '{', KEY_BRACELEFT },
- { '}', KEY_BRACERIGHT },
- { 'a', KEY_A },
- { 's', KEY_S },
- { 'd', KEY_D },
- { 'f', KEY_F },
- { 'g', KEY_G },
- { 'h', KEY_H },
- { 'j', KEY_J },
- { 'k', KEY_K },
- { 'l', KEY_L },
- { ';', KEY_SEMICOLON },
- { ':', KEY_COLON },
- { '\'', KEY_APOSTROPHE },
- { '\"', KEY_QUOTEDBL },
- { '\\', KEY_BACKSLASH },
- { '#', KEY_NUMBERSIGN },
- { 'z', KEY_Z },
- { 'x', KEY_X },
- { 'c', KEY_C },
- { 'v', KEY_V },
- { 'b', KEY_B },
- { 'n', KEY_N },
- { 'm', KEY_M },
- { ',', KEY_COMMA },
- { '.', KEY_PERIOD },
- { '/', KEY_SLASH }
-};
-
-static int remapKey(unsigned int key, unsigned int state) {
-
- if (isNumpadKey(key))
- return translateKey(key);
-
- TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
- if (!currentKeyboard)
- return translateKey(key);
-
- CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
- if (!layoutData)
- return translateKey(key);
-
- const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
-
- UInt32 keysDown = 0;
- UniChar chars[4];
- UniCharCount realLength;
-
- OSStatus err = UCKeyTranslate(keyboardLayout,
- key,
- kUCKeyActionDisplay,
- (state >> 8) & 0xFF,
- LMGetKbdType(),
- kUCKeyTranslateNoDeadKeysBit,
- &keysDown,
- sizeof(chars) / sizeof(chars[0]),
- &realLength,
- chars);
-
- if (err != noErr) {
- return translateKey(key);
- }
-
- for (unsigned int i = 0; i < 55; i++) {
- if (_keycodes[i].kchar == chars[0]) {
- return _keycodes[i].kcode;
- }
- }
- return translateKey(key);
-}
-
-- (void)keyDown:(NSEvent *)event {
-
- // Ignore all input if IME input is in progress
- if (!imeInputEventInProgress) {
- NSString *characters = [event characters];
- NSUInteger length = [characters length];
-
- if (!OS_OSX::singleton->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++) {
- OS_OSX::KeyEvent ke;
-
- ke.osx_state = [event modifierFlags];
- ke.pressed = true;
- ke.echo = [event isARepeat];
- ke.keycode = remapKey([event keyCode], [event modifierFlags]);
- ke.physical_keycode = translateKey([event keyCode]);
- ke.raw = true;
- ke.unicode = [characters characterAtIndex:i];
-
- push_to_key_event_buffer(ke);
- }
- } else {
- OS_OSX::KeyEvent ke;
-
- ke.osx_state = [event modifierFlags];
- ke.pressed = true;
- ke.echo = [event isARepeat];
- ke.keycode = remapKey([event keyCode], [event modifierFlags]);
- ke.physical_keycode = translateKey([event keyCode]);
- ke.raw = false;
- ke.unicode = 0;
-
- push_to_key_event_buffer(ke);
- }
- }
-
- // Pass events to IME handler
- if (OS_OSX::singleton->im_active)
- [self interpretKeyEvents:[NSArray arrayWithObject:event]];
-}
-
-- (void)flagsChanged:(NSEvent *)event {
-
- // Ignore all input if IME input is in progress
- if (!imeInputEventInProgress) {
- OS_OSX::KeyEvent ke;
-
- ke.echo = false;
- ke.raw = true;
+#include <mach-o/dyld.h>
+#include <os/log.h>
- int key = [event keyCode];
- int mod = [event modifierFlags];
+/*************************************************************************/
+/* OSXTerminalLogger */
+/*************************************************************************/
- if (key == 0x36 || key == 0x37) {
- if (mod & NSEventModifierFlagCommand) {
- mod &= ~NSEventModifierFlagCommand;
- ke.pressed = true;
- } else {
- ke.pressed = false;
- }
- } else if (key == 0x38 || key == 0x3c) {
- if (mod & NSEventModifierFlagShift) {
- mod &= ~NSEventModifierFlagShift;
- ke.pressed = true;
- } else {
- ke.pressed = false;
- }
- } else if (key == 0x3a || key == 0x3d) {
- if (mod & NSEventModifierFlagOption) {
- mod &= ~NSEventModifierFlagOption;
- ke.pressed = true;
- } else {
- ke.pressed = false;
- }
- } else if (key == 0x3b || key == 0x3e) {
- if (mod & NSEventModifierFlagControl) {
- mod &= ~NSEventModifierFlagControl;
- ke.pressed = true;
- } else {
- ke.pressed = false;
- }
- } else {
+class OSXTerminalLogger : public StdLogger {
+public:
+ virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR) {
+ if (!should_log(true)) {
return;
}
- ke.osx_state = mod;
- ke.keycode = remapKey(key, mod);
- ke.physical_keycode = translateKey(key);
- ke.unicode = 0;
-
- push_to_key_event_buffer(ke);
- }
-}
-
-- (void)keyUp:(NSEvent *)event {
-
- // Ignore all input if IME input is in progress
- if (!imeInputEventInProgress) {
- NSString *characters = [event characters];
- NSUInteger length = [characters length];
-
- // Fallback unicode character handler used if IME is not active
- if (!OS_OSX::singleton->im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode], [event modifierFlags]))) {
- for (NSUInteger i = 0; i < length; i++) {
- OS_OSX::KeyEvent ke;
-
- ke.osx_state = [event modifierFlags];
- ke.pressed = false;
- ke.echo = [event isARepeat];
- ke.keycode = remapKey([event keyCode], [event modifierFlags]);
- ke.physical_keycode = translateKey([event keyCode]);
- ke.raw = true;
- ke.unicode = [characters characterAtIndex:i];
-
- push_to_key_event_buffer(ke);
- }
- } else {
- OS_OSX::KeyEvent ke;
-
- ke.osx_state = [event modifierFlags];
- ke.pressed = false;
- ke.echo = [event isARepeat];
- ke.keycode = remapKey([event keyCode], [event modifierFlags]);
- ke.physical_keycode = translateKey([event keyCode]);
- ke.raw = true;
- ke.unicode = 0;
-
- push_to_key_event_buffer(ke);
- }
- }
-}
-
-inline void sendScrollEvent(int button, double factor, int modifierFlags) {
-
- unsigned int mask = 1 << (button - 1);
- Vector2 mouse_pos = Vector2(mouse_x, mouse_y);
-
- Ref<InputEventMouseButton> sc;
- sc.instance();
-
- get_key_modifier_state(modifierFlags, sc);
- sc->set_button_index(button);
- sc->set_factor(factor);
- sc->set_pressed(true);
- sc->set_position(mouse_pos);
- sc->set_global_position(mouse_pos);
- button_mask |= mask;
- sc->set_button_mask(button_mask);
- OS_OSX::singleton->push_input(sc);
-
- sc.instance();
- sc->set_button_index(button);
- sc->set_factor(factor);
- sc->set_pressed(false);
- sc->set_position(mouse_pos);
- sc->set_global_position(mouse_pos);
- button_mask &= ~mask;
- sc->set_button_mask(button_mask);
- OS_OSX::singleton->push_input(sc);
-}
-
-inline void sendPanEvent(double dx, double dy, int modifierFlags) {
-
- Ref<InputEventPanGesture> pg;
- pg.instance();
-
- get_key_modifier_state(modifierFlags, pg);
- Vector2 mouse_pos = Vector2(mouse_x, mouse_y);
- pg->set_position(mouse_pos);
- pg->set_delta(Vector2(-dx, -dy));
- OS_OSX::singleton->push_input(pg);
-}
-
-- (void)scrollWheel:(NSEvent *)event {
- double deltaX, deltaY;
-
- get_mouse_pos([event locationInWindow], [[event window] backingScaleFactor]);
-
- deltaX = [event scrollingDeltaX];
- deltaY = [event scrollingDeltaY];
-
- if ([event hasPreciseScrollingDeltas]) {
- deltaX *= 0.03;
- deltaY *= 0.03;
- }
-
- if ([event phase] != NSEventPhaseNone || [event momentumPhase] != NSEventPhaseNone) {
- sendPanEvent(deltaX, deltaY, [event modifierFlags]);
- } else {
- if (fabs(deltaX)) {
- sendScrollEvent(0 > deltaX ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_LEFT, fabs(deltaX * 0.3), [event modifierFlags]);
- }
- if (fabs(deltaY)) {
- sendScrollEvent(0 < deltaY ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN, fabs(deltaY * 0.3), [event modifierFlags]);
- }
- }
-}
-
-@end
-
-@interface GodotWindow : NSWindow {
-}
-@end
-
-@implementation GodotWindow
-
-- (BOOL)canBecomeKeyWindow {
- // Required for NSBorderlessWindowMask windows
- return YES;
-}
-
-@end
-
-void OS_OSX::_update_global_menu() {
-
- NSMenu *main_menu = [NSApp mainMenu];
+ const char *err_details;
+ if (p_rationale && p_rationale[0])
+ err_details = p_rationale;
+ else
+ err_details = p_code;
- for (int i = 1; i < [main_menu numberOfItems]; i++) {
- [main_menu removeItemAtIndex:i];
- }
- for (Map<String, Vector<GlobalMenuItem>>::Element *E = global_menus.front(); E; E = E->next()) {
- if (E->key() != "_dock") {
- NSMenu *menu = [[[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:E->key().utf8().get_data()]] autorelease];
- for (int i = 0; i < E->get().size(); i++) {
- if (E->get()[i].label == String()) {
- [menu addItem:[NSMenuItem separatorItem]];
- } else {
- NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:E->get()[i].label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
- [menu_item setRepresentedObject:[NSValue valueWithPointer:&(E->get()[i])]];
- }
- }
- NSMenuItem *menu_item = [main_menu addItemWithTitle:[NSString stringWithUTF8String:E->key().utf8().get_data()] action:nil keyEquivalent:@""];
- [main_menu setSubmenu:menu forItem:menu_item];
+ switch (p_type) {
+ case ERR_WARNING:
+ os_log_info(OS_LOG_DEFAULT,
+ "WARNING: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
+ logf_error("\E[1;33mWARNING:\E[0;93m %s\n", err_details);
+ logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
+ break;
+ case ERR_SCRIPT:
+ os_log_error(OS_LOG_DEFAULT,
+ "SCRIPT ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
+ logf_error("\E[1;35mSCRIPT ERROR:\E[0;95m %s\n", err_details);
+ logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
+ break;
+ case ERR_SHADER:
+ os_log_error(OS_LOG_DEFAULT,
+ "SHADER ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
+ logf_error("\E[1;36mSHADER ERROR:\E[0;96m %s\n", err_details);
+ logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
+ break;
+ case ERR_ERROR:
+ default:
+ os_log_error(OS_LOG_DEFAULT,
+ "ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
+ logf_error("\E[1;31mERROR:\E[0;91m %s\n", err_details);
+ logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
+ break;
}
}
-}
-
-void OS_OSX::global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta) {
-
- global_menus[p_menu].push_back(GlobalMenuItem(p_label, p_signal, p_meta));
- _update_global_menu();
-}
-
-void OS_OSX::global_menu_add_separator(const String &p_menu) {
-
- global_menus[p_menu].push_back(GlobalMenuItem());
- _update_global_menu();
-}
-
-void OS_OSX::global_menu_remove_item(const String &p_menu, int p_idx) {
-
- ERR_FAIL_INDEX(p_idx, global_menus[p_menu].size());
-
- global_menus[p_menu].remove(p_idx);
- _update_global_menu();
-}
-
-void OS_OSX::global_menu_clear(const String &p_menu) {
-
- global_menus[p_menu].clear();
- _update_global_menu();
-}
-
-Point2 OS_OSX::get_ime_selection() const {
-
- return im_selection;
-}
-
-String OS_OSX::get_ime_text() const {
+};
- return im_text;
-}
+/*************************************************************************/
+/* OS_OSX */
+/*************************************************************************/
String OS_OSX::get_unique_id() const {
-
static String serial_number;
if (serial_number.empty()) {
@@ -1442,22 +119,7 @@ String OS_OSX::get_unique_id() const {
return serial_number;
}
-void OS_OSX::set_ime_active(const bool p_active) {
-
- im_active = p_active;
- if (!im_active)
- [window_view cancelComposition];
-}
-
-void OS_OSX::set_ime_position(const Point2 &p_pos) {
-
- im_position = p_pos;
-}
-
void OS_OSX::initialize_core() {
-
- crash_handler.initialize();
-
OS_Unix::initialize_core();
DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_RESOURCES);
@@ -1465,221 +127,32 @@ void OS_OSX::initialize_core() {
DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_FILESYSTEM);
}
-static bool keyboard_layout_dirty = true;
-static void keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) {
- keyboard_layout_dirty = true;
-}
-
-static bool displays_arrangement_dirty = true;
-static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) {
- displays_arrangement_dirty = true;
+void OS_OSX::initialize_joypads() {
+ joypad_osx = memnew(JoypadOSX(Input::get_singleton()));
}
-int OS_OSX::get_current_video_driver() const {
- return video_driver_index;
-}
-
-Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
-
- /*** OSX INITIALIZATION ***/
- /*** OSX INITIALIZATION ***/
- /*** OSX INITIALIZATION ***/
-
- keyboard_layout_dirty = true;
- displays_arrangement_dirty = true;
-
- // Register to be notified on keyboard layout changes
- CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
- NULL, keyboard_layout_changed,
- kTISNotifySelectedKeyboardInputSourceChanged, NULL,
- CFNotificationSuspensionBehaviorDeliverImmediately);
-
- // Register to be notified on displays arrangement changes
- CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, NULL);
-
- //!!!!!!!!!!!!!!!!!!!!!!!!!!
- //TODO - do Vulkan and GLES2 support checks, driver selection and fallback
- video_driver_index = p_video_driver;
- print_verbose("Driver: " + String(get_video_driver_name(video_driver_index)) + " [" + itos(video_driver_index) + "]");
- //!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- //Create window
-
- window_delegate = [[GodotWindowDelegate alloc] init];
-
- // Don't use accumulation buffer support; it's not accelerated
- // Aux buffers probably aren't accelerated either
-
- unsigned int styleMask;
-
- if (p_desired.borderless_window) {
- styleMask = NSWindowStyleMaskBorderless;
- } else {
- resizable = p_desired.resizable;
- styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (p_desired.resizable ? NSWindowStyleMaskResizable : 0);
- }
-
- window_object = [[GodotWindow alloc]
- initWithContentRect:NSMakeRect(0, 0, p_desired.width, p_desired.height)
- styleMask:styleMask
- backing:NSBackingStoreBuffered
- defer:NO];
-
- ERR_FAIL_COND_V(window_object == nil, ERR_UNAVAILABLE);
-
- window_view = [[GodotContentView alloc] init];
- if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_14) {
- [window_view setWantsLayer:TRUE];
- }
-
- float displayScale = 1.0;
- if (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]);
- }
- }
-
- window_size.width = p_desired.width * displayScale;
- window_size.height = p_desired.height * displayScale;
-
- if (displayScale > 1.0) {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- [window_view setWantsBestResolutionOpenGLSurface:YES];
- }
-#endif
- [window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
- } else {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- [window_view setWantsBestResolutionOpenGLSurface:NO];
- }
-#endif
- }
-
- [window_object setContentView:window_view];
- [window_object setDelegate:window_delegate];
- [window_object setAcceptsMouseMovedEvents:YES];
- [(NSWindow *)window_object center];
-
- [window_object setRestorable:NO];
-
- // Init context and rendering device
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
-
- context_gles2 = memnew(ContextGL_OSX(window_view, false));
-
- if (context_gles2->initialize() != OK) {
- memdelete(context_gles2);
- context_gles2 = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
-
- context_gles2->set_use_vsync(p_desired.use_vsync);
-
- if (RasterizerGLES2::is_viable() == OK) {
- RasterizerGLES2::register_config();
- RasterizerGLES2::make_current();
- } else {
- memdelete(context_gles2);
- context_gles2 = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
-
- context_vulkan = memnew(VulkanContextOSX);
- if (context_vulkan->initialize() != OK) {
- memdelete(context_vulkan);
- context_vulkan = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
- if (context_vulkan->window_create(window_view, get_video_mode().width, get_video_mode().height) == -1) {
- memdelete(context_vulkan);
- context_vulkan = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
-
- rendering_device_vulkan = memnew(RenderingDeviceVulkan);
- rendering_device_vulkan->initialize(context_vulkan);
-
- RasterizerRD::make_current();
- }
-#endif
-
- [NSApp activateIgnoringOtherApps:YES];
-
- _update_window();
-
- [window_object makeKeyAndOrderFront:nil];
-
- if (p_desired.fullscreen)
- zoomed = true;
-
- 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();
- AudioDriverManager::initialize(p_audio_driver);
-
- input = memnew(InputDefault);
- joypad_osx = memnew(JoypadOSX);
-
- _ensure_user_data_dir();
-
- restore_rect = Rect2(get_window_position(), get_window_size());
-
- if (p_desired.layered) {
- set_window_per_pixel_transparency_enabled(true);
- }
-
- update_real_mouse_position();
+void OS_OSX::initialize() {
+ crash_handler.initialize();
- return OK;
+ initialize_core();
+ //ensure_user_data_dir();
}
void OS_OSX::finalize() {
-
#ifdef COREMIDI_ENABLED
midi_driver.close();
#endif
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
-
- if (context_gles2)
- memdelete(context_gles2);
- }
-#endif
-
- CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, kTISNotifySelectedKeyboardInputSourceChanged, NULL);
- CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL);
-
delete_main_loop();
memdelete(joypad_osx);
- memdelete(input);
-
- cursors_cache.clear();
- visual_server->finish();
- memdelete(visual_server);
}
void OS_OSX::set_main_loop(MainLoop *p_main_loop) {
-
main_loop = p_main_loop;
- input->set_main_loop(p_main_loop);
}
void OS_OSX::delete_main_loop() {
-
if (!main_loop)
return;
memdelete(main_loop);
@@ -1687,89 +160,10 @@ void OS_OSX::delete_main_loop() {
}
String OS_OSX::get_name() const {
-
- return "OSX";
-}
-
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
-class OSXTerminalLogger : public StdLogger {
-public:
- virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR) {
- if (!should_log(true)) {
- return;
- }
-
- const char *err_details;
- if (p_rationale && p_rationale[0])
- err_details = p_rationale;
- else
- err_details = p_code;
-
- switch (p_type) {
- case ERR_WARNING:
- if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
- os_log_info(OS_LOG_DEFAULT,
- "WARNING: %{public}s\nat: %{public}s (%{public}s:%i)",
- err_details, p_function, p_file, p_line);
- }
- logf_error("\E[1;33mWARNING:\E[0;93m %s\n", err_details);
- logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
- break;
- case ERR_SCRIPT:
- if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
- os_log_error(OS_LOG_DEFAULT,
- "SCRIPT ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
- err_details, p_function, p_file, p_line);
- }
- logf_error("\E[1;35mSCRIPT ERROR:\E[0;95m %s\n", err_details);
- logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
- break;
- case ERR_SHADER:
- if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
- os_log_error(OS_LOG_DEFAULT,
- "SHADER ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
- err_details, p_function, p_file, p_line);
- }
- logf_error("\E[1;36mSHADER ERROR:\E[0;96m %s\n", err_details);
- logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
- break;
- case ERR_ERROR:
- default:
- if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_12) {
- os_log_error(OS_LOG_DEFAULT,
- "ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
- err_details, p_function, p_file, p_line);
- }
- logf_error("\E[1;31mERROR:\E[0;91m %s\n", err_details);
- logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
- break;
- }
- }
-};
-
-#else
-
-typedef UnixTerminalLogger OSXTerminalLogger;
-#endif
-
-void OS_OSX::alert(const String &p_alert, const String &p_title) {
- // Set OS X-compliant variables
- NSAlert *window = [[NSAlert alloc] init];
- NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
- NSString *ns_alert = [NSString stringWithUTF8String:p_alert.utf8().get_data()];
-
- [window addButtonWithTitle:@"OK"];
- [window setMessageText:ns_title];
- [window setInformativeText:ns_alert];
- [window setAlertStyle:NSAlertStyleWarning];
-
- // Display it, then release
- [window runModal];
- [window release];
+ return "macOS";
}
Error OS_OSX::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
-
String path = p_path;
if (!FileAccess::exists(path)) {
@@ -1787,293 +181,11 @@ Error OS_OSX::open_dynamic_library(const String p_path, void *&p_library_handle,
return OK;
}
-void OS_OSX::set_cursor_shape(CursorShape p_shape) {
-
- if (cursor_shape == p_shape)
- return;
-
- if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
- cursor_shape = p_shape;
- return;
- }
-
- if (cursors[p_shape] != NULL) {
- [cursors[p_shape] set];
- } else {
- switch (p_shape) {
- case CURSOR_ARROW: [[NSCursor arrowCursor] set]; break;
- case CURSOR_IBEAM: [[NSCursor IBeamCursor] set]; break;
- case CURSOR_POINTING_HAND: [[NSCursor pointingHandCursor] set]; break;
- case CURSOR_CROSS: [[NSCursor crosshairCursor] set]; break;
- case CURSOR_WAIT: [[NSCursor arrowCursor] set]; break;
- case CURSOR_BUSY: [[NSCursor arrowCursor] set]; break;
- case CURSOR_DRAG: [[NSCursor closedHandCursor] set]; break;
- case CURSOR_CAN_DROP: [[NSCursor openHandCursor] set]; break;
- case CURSOR_FORBIDDEN: [[NSCursor operationNotAllowedCursor] set]; break;
- case CURSOR_VSIZE: [cursorFromSelector(@selector(_windowResizeNorthSouthCursor), @selector(resizeUpDownCursor)) set]; break;
- case CURSOR_HSIZE: [cursorFromSelector(@selector(_windowResizeEastWestCursor), @selector(resizeLeftRightCursor)) set]; break;
- case CURSOR_BDIAGSIZE: [cursorFromSelector(@selector(_windowResizeNorthEastSouthWestCursor)) set]; break;
- case CURSOR_FDIAGSIZE: [cursorFromSelector(@selector(_windowResizeNorthWestSouthEastCursor)) set]; break;
- case CURSOR_MOVE: [[NSCursor arrowCursor] set]; break;
- case CURSOR_VSPLIT: [[NSCursor resizeUpDownCursor] set]; break;
- case CURSOR_HSPLIT: [[NSCursor resizeLeftRightCursor] set]; break;
- case CURSOR_HELP: [cursorFromSelector(@selector(_helpCursor)) set]; break;
- default: {
- };
- }
- }
-
- cursor_shape = p_shape;
-}
-
-OS::CursorShape OS_OSX::get_cursor_shape() const {
-
- return cursor_shape;
-}
-
-void OS_OSX::set_custom_mouse_cursor(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) {
- set_cursor_shape(p_shape);
- return;
- }
-
- cursors_cache.erase(p_shape);
- }
-
- Ref<Texture2D> texture = p_cursor;
- Ref<AtlasTexture> atlas_texture = p_cursor;
- Ref<Image> image;
- Size2 texture_size;
- Rect2 atlas_rect;
-
- if (texture.is_valid()) {
- image = texture->get_data();
- }
-
- if (!image.is_valid() && atlas_texture.is_valid()) {
- texture = atlas_texture->get_atlas();
-
- atlas_rect.size.width = texture->get_width();
- atlas_rect.size.height = texture->get_height();
- atlas_rect.position.x = atlas_texture->get_region().position.x;
- atlas_rect.position.y = atlas_texture->get_region().position.y;
-
- texture_size.width = atlas_texture->get_region().size.x;
- texture_size.height = atlas_texture->get_region().size.y;
- } else if (image.is_valid()) {
- texture_size.width = texture->get_width();
- texture_size.height = texture->get_height();
- }
-
- ERR_FAIL_COND(!texture.is_valid());
- ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
- ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
- ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
-
- image = texture->get_data();
-
- ERR_FAIL_COND(!image.is_valid());
-
- NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
- initWithBitmapDataPlanes:NULL
- pixelsWide:int(texture_size.width)
- pixelsHigh:int(texture_size.height)
- bitsPerSample:8
- samplesPerPixel:4
- hasAlpha:YES
- isPlanar:NO
- colorSpaceName:NSDeviceRGBColorSpace
- bytesPerRow:int(texture_size.width) * 4
- bitsPerPixel:32];
-
- ERR_FAIL_COND(imgrep == nil);
- uint8_t *pixels = [imgrep bitmapData];
-
- int len = int(texture_size.width * texture_size.height);
-
- for (int i = 0; i < len; i++) {
- int row_index = floor(i / texture_size.width) + atlas_rect.position.y;
- int column_index = (i % int(texture_size.width)) + atlas_rect.position.x;
-
- if (atlas_texture.is_valid()) {
- column_index = MIN(column_index, atlas_rect.size.width - 1);
- row_index = MIN(row_index, atlas_rect.size.height - 1);
- }
-
- uint32_t color = image->get_pixel(column_index, row_index).to_argb32();
-
- uint8_t alpha = (color >> 24) & 0xFF;
- pixels[i * 4 + 0] = ((color >> 16) & 0xFF) * alpha / 255;
- pixels[i * 4 + 1] = ((color >> 8) & 0xFF) * alpha / 255;
- pixels[i * 4 + 2] = ((color)&0xFF) * alpha / 255;
- pixels[i * 4 + 3] = alpha;
- }
-
- NSImage *nsimage = [[NSImage alloc] initWithSize:NSMakeSize(texture_size.width, texture_size.height)];
- [nsimage addRepresentation:imgrep];
-
- NSCursor *cursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSMakePoint(p_hotspot.x, p_hotspot.y)];
-
- [cursors[p_shape] release];
- cursors[p_shape] = cursor;
-
- Vector<Variant> params;
- params.push_back(p_cursor);
- params.push_back(p_hotspot);
- cursors_cache.insert(p_shape, params);
-
- if (p_shape == cursor_shape) {
- if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
- [cursor set];
- }
- }
-
- [imgrep release];
- [nsimage release];
- } else {
- // Reset to default system cursor
- if (cursors[p_shape] != NULL) {
- [cursors[p_shape] release];
- cursors[p_shape] = NULL;
- }
-
- CursorShape c = cursor_shape;
- cursor_shape = CURSOR_MAX;
- set_cursor_shape(c);
-
- cursors_cache.erase(p_shape);
- }
-}
-
-void OS_OSX::set_mouse_show(bool p_show) {
-}
-
-void OS_OSX::set_mouse_grab(bool p_grab) {
-}
-
-bool OS_OSX::is_mouse_grab_enabled() const {
-
- return mouse_grab;
-}
-
-void OS_OSX::warp_mouse_position(const Point2 &p_to) {
-
- //copied from windows impl with osx native calls
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
- mouse_x = p_to.x;
- mouse_y = p_to.y;
- } else { //set OS position
-
- //local point in window coords
- const NSRect contentRect = [window_view frame];
- float displayScale = _display_scale();
- NSRect pointInWindowRect = NSMakeRect(p_to.x / displayScale, contentRect.size.height - (p_to.y / displayScale) - 1, 0, 0);
- NSPoint pointOnScreen = [[window_view window] convertRectToScreen:pointInWindowRect].origin;
-
- //point in scren coords
- CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y };
-
- //do the warping
- CGEventSourceRef lEventRef = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
- CGEventSourceSetLocalEventsSuppressionInterval(lEventRef, 0.0);
- CGAssociateMouseAndMouseCursorPosition(false);
- CGWarpMouseCursorPosition(lMouseWarpPos);
- CGAssociateMouseAndMouseCursorPosition(true);
- }
-}
-
-void OS_OSX::update_real_mouse_position() {
-
- get_mouse_pos([window_object mouseLocationOutsideOfEventStream], [window_view backingScaleFactor]);
- input->set_mouse_position(Point2(mouse_x, mouse_y));
-}
-
-Point2 OS_OSX::get_mouse_position() const {
-
- return Vector2(mouse_x, mouse_y);
-}
-
-int OS_OSX::get_mouse_button_state() const {
- return button_mask;
-}
-
-void OS_OSX::set_window_title(const String &p_title) {
- title = p_title;
-
- [window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]];
-}
-
-void OS_OSX::set_native_icon(const String &p_filename) {
-
- FileAccess *f = FileAccess::open(p_filename, FileAccess::READ);
- ERR_FAIL_COND(!f);
-
- Vector<uint8_t> data;
- uint32_t len = f->get_len();
- data.resize(len);
- f->get_buffer((uint8_t *)&data.write[0], len);
- memdelete(f);
-
- NSData *icon_data = [[[NSData alloc] initWithBytes:&data.write[0] length:len] autorelease];
- ERR_FAIL_COND_MSG(!icon_data, "Error reading icon data.");
-
- NSImage *icon = [[[NSImage alloc] initWithData:icon_data] autorelease];
- ERR_FAIL_COND_MSG(!icon, "Error loading icon.");
-
- [NSApp setApplicationIconImage:icon];
-}
-
-void OS_OSX::set_icon(const Ref<Image> &p_icon) {
-
- Ref<Image> img = p_icon;
- img = img->duplicate();
- img->convert(Image::FORMAT_RGBA8);
- NSBitmapImageRep *imgrep = [[[NSBitmapImageRep alloc]
- initWithBitmapDataPlanes:NULL
- pixelsWide:img->get_width()
- pixelsHigh:img->get_height()
- bitsPerSample:8
- samplesPerPixel:4
- hasAlpha:YES
- isPlanar:NO
- colorSpaceName:NSDeviceRGBColorSpace
- bytesPerRow:img->get_width() * 4
- bitsPerPixel:32] autorelease];
- ERR_FAIL_COND(imgrep == nil);
- uint8_t *pixels = [imgrep bitmapData];
-
- int len = img->get_width() * img->get_height();
- const uint8_t *r = img->get_data().ptr();
-
- /* Premultiply the alpha channel */
- for (int i = 0; i < len; i++) {
- uint8_t alpha = r[i * 4 + 3];
- pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255);
- pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255);
- pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255);
- pixels[i * 4 + 3] = alpha;
- }
-
- NSImage *nsimg = [[[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())] autorelease];
- ERR_FAIL_COND(nsimg == nil);
- [nsimg addRepresentation:imgrep];
-
- [NSApp setApplicationIconImage:nsimg];
-}
-
MainLoop *OS_OSX::get_main_loop() const {
-
return main_loop;
}
String OS_OSX::get_config_path() const {
-
if (has_environment("XDG_CONFIG_HOME")) {
return get_environment("XDG_CONFIG_HOME");
} else if (has_environment("HOME")) {
@@ -2084,7 +196,6 @@ String OS_OSX::get_config_path() const {
}
String OS_OSX::get_data_path() const {
-
if (has_environment("XDG_DATA_HOME")) {
return get_environment("XDG_DATA_HOME");
} else {
@@ -2093,7 +204,6 @@ String OS_OSX::get_data_path() const {
}
String OS_OSX::get_cache_path() const {
-
if (has_environment("XDG_CACHE_HOME")) {
return get_environment("XDG_CACHE_HOME");
} else if (has_environment("HOME")) {
@@ -2104,7 +214,6 @@ String OS_OSX::get_cache_path() const {
}
String OS_OSX::get_bundle_resource_dir() const {
-
NSBundle *main = [NSBundle mainBundle];
NSString *resourcePath = [main resourcePath];
@@ -2118,12 +227,10 @@ String OS_OSX::get_bundle_resource_dir() const {
// Get properly capitalized engine name for system paths
String OS_OSX::get_godot_dir_name() const {
-
return String(VERSION_SHORT_NAME).capitalize();
}
String OS_OSX::get_system_dir(SystemDir p_dir) const {
-
NSSearchPathDirectory id;
bool found = true;
@@ -2153,10 +260,8 @@ String OS_OSX::get_system_dir(SystemDir p_dir) const {
String ret;
if (found) {
-
NSArray *paths = NSSearchPathForDirectoriesInDomains(id, NSUserDomainMask, YES);
if (paths && [paths count] >= 1) {
-
char *utfs = strdup([[paths firstObject] UTF8String]);
ret.parse_utf8(utfs);
free(utfs);
@@ -2166,62 +271,7 @@ String OS_OSX::get_system_dir(SystemDir p_dir) const {
return ret;
}
-bool OS_OSX::can_draw() const {
-
- return true;
-}
-
-void OS_OSX::set_clipboard(const String &p_text) {
-
- NSString *copiedString = [NSString stringWithUTF8String:p_text.utf8().get_data()];
- NSArray *copiedStringArray = [NSArray arrayWithObject:copiedString];
-
- NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
- [pasteboard clearContents];
- [pasteboard writeObjects:copiedStringArray];
-}
-
-String OS_OSX::get_clipboard() const {
-
- NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
- NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
- NSDictionary *options = [NSDictionary dictionary];
-
- BOOL ok = [pasteboard canReadObjectForClasses:classArray options:options];
-
- if (!ok) {
- return "";
- }
-
- NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options];
- NSString *string = [objectsToPaste objectAtIndex:0];
-
- char *utfs = strdup([string UTF8String]);
- String ret;
- ret.parse_utf8(utfs);
- free(utfs);
-
- return ret;
-}
-
-void OS_OSX::release_rendering_thread() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->release_current();
- }
-#endif
-}
-
-void OS_OSX::make_rendering_thread() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->make_current();
- }
-#endif
-}
-
Error OS_OSX::shell_open(String p_uri) {
-
[[NSWorkspace sharedWorkspace] openURL:[[NSURL alloc] initWithString:[[NSString stringWithUTF8String:p_uri.utf8().get_data()] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]]]];
return OK;
}
@@ -2231,491 +281,7 @@ String OS_OSX::get_locale() const {
return [locale_code UTF8String];
}
-void OS_OSX::swap_buffers() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->swap_buffers();
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
- context_vulkan->swap_buffers();
- }
-#endif
-}
-
-void OS_OSX::wm_minimized(bool p_minimized) {
-
- minimized = p_minimized;
-};
-
-void OS_OSX::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
-}
-
-OS::VideoMode OS_OSX::get_video_mode(int p_screen) const {
-
- VideoMode vm;
- vm.width = window_size.width;
- vm.height = window_size.height;
-
- return vm;
-}
-
-void OS_OSX::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
-}
-
-int OS_OSX::get_screen_count() const {
- NSArray *screenArray = [NSScreen screens];
- return [screenArray count];
-};
-
-// Returns the native top-left screen coordinate of the smallest rectangle
-// that encompasses all screens. Needed in get_screen_position(),
-// get_window_position, and set_window_position()
-// to convert between OS X native screen coordinates and the ones expected by Godot
-Point2 OS_OSX::get_screens_origin() const {
- static Point2 origin;
-
- if (displays_arrangement_dirty) {
- origin = Point2();
-
- for (int i = 0; i < get_screen_count(); i++) {
- Point2 position = get_native_screen_position(i);
- if (position.x < origin.x) {
- origin.x = position.x;
- }
- if (position.y > origin.y) {
- origin.y = position.y;
- }
- }
-
- displays_arrangement_dirty = false;
- }
-
- return origin;
-}
-
-static int get_screen_index(NSScreen *screen) {
- const NSUInteger index = [[NSScreen screens] indexOfObject:screen];
- return index == NSNotFound ? 0 : index;
-}
-
-int OS_OSX::get_current_screen() const {
- if (window_object) {
- return get_screen_index([window_object screen]);
- } else {
- return get_screen_index([NSScreen mainScreen]);
- }
-};
-
-void OS_OSX::set_current_screen(int p_screen) {
- Vector2 wpos = get_window_position() - get_screen_position(get_current_screen());
- set_window_position(wpos + get_screen_position(p_screen));
-};
-
-Point2 OS_OSX::get_native_screen_position(int p_screen) const {
- if (p_screen < 0) {
- p_screen = get_current_screen();
- }
-
- 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 Point2(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * display_scale;
- }
-
- return Point2();
-}
-
-Point2 OS_OSX::get_screen_position(int p_screen) const {
- Point2 position = get_native_screen_position(p_screen) - get_screens_origin();
- // OS X native y-coordinate relative to get_screens_origin() is negative,
- // Godot expects a positive value
- position.y *= -1;
- return position;
-}
-
-int OS_OSX::get_screen_dpi(int p_screen) const {
- if (p_screen < 0) {
- p_screen = get_current_screen();
- }
-
- 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;
- }
-
- return 72;
-}
-
-Size2 OS_OSX::get_screen_size(int p_screen) const {
- if (p_screen < 0) {
- p_screen = get_current_screen();
- }
-
- 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 Size2(nsrect.size.width, nsrect.size.height) * displayScale;
- }
-
- return Size2();
-}
-
-void OS_OSX::_update_window() {
- bool borderless_full = false;
-
- if (get_borderless_window()) {
- NSRect frameRect = [window_object frame];
- NSRect screenRect = [[window_object screen] frame];
-
- // Check if our window covers up the screen
- if (frameRect.origin.x <= screenRect.origin.x && frameRect.origin.y <= frameRect.origin.y &&
- frameRect.size.width >= screenRect.size.width && frameRect.size.height >= screenRect.size.height) {
- borderless_full = true;
- }
- }
-
- if (borderless_full) {
- // If the window covers up the screen set the level to above the main menu and hide on deactivate
- [window_object setLevel:NSMainMenuWindowLevel + 1];
- [window_object setHidesOnDeactivate:YES];
- } else {
- // Reset these when our window is not a borderless window that covers up the screen
- [window_object setLevel:NSNormalWindowLevel];
- [window_object setHidesOnDeactivate:NO];
- }
-}
-
-float OS_OSX::_display_scale() const {
- if (window_object) {
- return _display_scale([window_object screen]);
- } else {
- return _display_scale([NSScreen mainScreen]);
- }
-}
-
-float OS_OSX::_display_scale(id screen) const {
- if (is_hidpi_allowed()) {
- if ([screen respondsToSelector:@selector(backingScaleFactor)]) {
- return fmax(1.0, [screen backingScaleFactor]);
- }
- }
- return 1.0;
-}
-
-Point2 OS_OSX::get_native_window_position() const {
-
- NSRect nsrect = [window_object frame];
- Point2 pos;
- float display_scale = _display_scale();
-
- // 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;
-
- return pos;
-};
-
-Point2 OS_OSX::get_window_position() const {
- Point2 position = get_native_window_position() - get_screens_origin();
- // OS X native y-coordinate relative to get_screens_origin() is negative,
- // Godot expects a positive value
- position.y *= -1;
- return position;
-}
-
-void OS_OSX::set_native_window_position(const Point2 &p_position) {
-
- NSPoint pos;
- float displayScale = _display_scale();
-
- pos.x = p_position.x / displayScale;
- pos.y = p_position.y / displayScale;
-
- [window_object setFrameTopLeftPoint:pos];
-
- _update_window();
-};
-
-void OS_OSX::set_window_position(const Point2 &p_position) {
- Point2 position = p_position;
- // OS X native y-coordinate relative to get_screens_origin() is negative,
- // Godot passes a positive value
- position.y *= -1;
- set_native_window_position(get_screens_origin() + position);
-
- update_real_mouse_position();
-};
-
-Size2 OS_OSX::get_window_size() const {
-
- return window_size;
-};
-
-Size2 OS_OSX::get_real_window_size() const {
-
- NSRect frame = [window_object frame];
- return Size2(frame.size.width, frame.size.height) * _display_scale();
-}
-
-Size2 OS_OSX::get_max_window_size() const {
- return max_size;
-}
-
-Size2 OS_OSX::get_min_window_size() const {
- return min_size;
-}
-
-void OS_OSX::set_min_window_size(const Size2 p_size) {
-
- if ((p_size != Size2()) && (max_size != Size2()) && ((p_size.x > max_size.x) || (p_size.y > max_size.y))) {
- ERR_PRINT("Minimum window size can't be larger than maximum window size!");
- return;
- }
- min_size = p_size;
-
- if ((min_size != Size2()) && !zoomed) {
- Size2 size = min_size / _display_scale();
- [window_object setContentMinSize:NSMakeSize(size.x, size.y)];
- } else {
- [window_object setContentMinSize:NSMakeSize(0, 0)];
- }
-}
-
-void OS_OSX::set_max_window_size(const Size2 p_size) {
-
- if ((p_size != Size2()) && ((p_size.x < min_size.x) || (p_size.y < min_size.y))) {
- ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
- return;
- }
- max_size = p_size;
-
- if ((max_size != Size2()) && !zoomed) {
- Size2 size = max_size / _display_scale();
- [window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
- } else {
- [window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
- }
-}
-
-void OS_OSX::set_window_size(const Size2 p_size) {
-
- Size2 size = p_size / _display_scale();
-
- if (get_borderless_window() == false) {
- // 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;
- } else {
- if (floor(NSAppKitVersionNumber) < NSAppKitVersionNumber10_12) {
- size.y += [[NSStatusBar systemStatusBar] thickness];
- }
- }
- }
-
- NSRect frame = [window_object frame];
- [window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, size.x, size.y) display:YES];
-
- _update_window();
-};
-
-void OS_OSX::set_window_fullscreen(bool p_enabled) {
-
- if (zoomed != p_enabled) {
- if (layered_window)
- set_window_per_pixel_transparency_enabled(false);
- if (!resizable)
- [window_object setStyleMask:[window_object styleMask] | NSWindowStyleMaskResizable];
- if (p_enabled) {
- [window_object setContentMinSize:NSMakeSize(0, 0)];
- [window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
- } else {
- if (min_size != Size2()) {
- Size2 size = min_size / _display_scale();
- [window_object setContentMinSize:NSMakeSize(size.x, size.y)];
- }
- if (max_size != Size2()) {
- Size2 size = max_size / _display_scale();
- [window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
- }
- }
- [window_object toggleFullScreen:nil];
- }
- zoomed = p_enabled;
-};
-
-bool OS_OSX::is_window_fullscreen() const {
-
- return zoomed;
-};
-
-void OS_OSX::set_window_resizable(bool p_enabled) {
-
- if (p_enabled)
- [window_object setStyleMask:[window_object styleMask] | NSWindowStyleMaskResizable];
- else if (!zoomed)
- [window_object setStyleMask:[window_object styleMask] & ~NSWindowStyleMaskResizable];
-
- resizable = p_enabled;
-};
-
-bool OS_OSX::is_window_resizable() const {
-
- return [window_object styleMask] & NSWindowStyleMaskResizable;
-};
-
-void OS_OSX::set_window_minimized(bool p_enabled) {
-
- if (p_enabled)
- [window_object performMiniaturize:nil];
- else
- [window_object deminiaturize:nil];
-};
-
-bool OS_OSX::is_window_minimized() const {
-
- if ([window_object respondsToSelector:@selector(isMiniaturized)])
- return [window_object isMiniaturized];
-
- return minimized;
-};
-
-void OS_OSX::set_window_maximized(bool p_enabled) {
-
- if (p_enabled) {
- restore_rect = Rect2(get_window_position(), get_window_size());
- [window_object setFrame:[[[NSScreen screens] objectAtIndex:get_current_screen()] visibleFrame] display:YES];
- } else {
- set_window_size(restore_rect.size);
- set_window_position(restore_rect.position);
- };
- maximized = p_enabled;
-};
-
-bool OS_OSX::is_window_maximized() const {
-
- // don't know
- return maximized;
-};
-
-void OS_OSX::move_window_to_foreground() {
-
- [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
- [window_object makeKeyAndOrderFront:nil];
-}
-
-void OS_OSX::set_window_always_on_top(bool p_enabled) {
- if (is_window_always_on_top() == p_enabled)
- return;
-
- if (p_enabled)
- [window_object setLevel:NSFloatingWindowLevel];
- else
- [window_object setLevel:NSNormalWindowLevel];
-}
-
-bool OS_OSX::is_window_always_on_top() const {
- return [window_object level] == NSFloatingWindowLevel;
-}
-
-bool OS_OSX::is_window_focused() const {
- return window_focused;
-}
-
-void OS_OSX::request_attention() {
-
- [NSApp requestUserAttention:NSCriticalRequest];
-}
-
-bool OS_OSX::get_window_per_pixel_transparency_enabled() const {
-
- if (!is_layered_allowed()) return false;
- return layered_window;
-}
-
-void OS_OSX::set_window_per_pixel_transparency_enabled(bool p_enabled) {
-
- if (!is_layered_allowed()) return;
- if (layered_window != p_enabled) {
- if (p_enabled) {
- set_borderless_window(true);
- [window_object setBackgroundColor:[NSColor clearColor]];
- [window_object setOpaque:NO];
- [window_object setHasShadow:NO];
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->set_opacity(0);
- }
-#endif
- layered_window = true;
- } else {
- [window_object setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1]];
- [window_object setOpaque:YES];
- [window_object setHasShadow:YES];
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->set_opacity(1);
- }
-#endif
- layered_window = false;
- }
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->update();
- }
-#endif
- NSRect frame = [window_object frame];
- [window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, 1, 1) display:YES];
- [window_object setFrame:frame display:YES];
- }
-}
-
-void OS_OSX::set_borderless_window(bool p_borderless) {
-
- // OrderOut prevents a lose focus bug with the window
- [window_object orderOut:nil];
-
- if (p_borderless) {
- [window_object setStyleMask:NSWindowStyleMaskBorderless];
- } else {
- if (layered_window)
- set_window_per_pixel_transparency_enabled(false);
-
- [window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (resizable ? NSWindowStyleMaskResizable : 0)];
-
- // Force update of the window styles
- NSRect frameRect = [window_object frame];
- [window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO];
- [window_object setFrame:frameRect display:NO];
-
- // Restore the window title
- [window_object setTitle:[NSString stringWithUTF8String:title.utf8().get_data()]];
- }
-
- _update_window();
-
- [window_object makeKeyAndOrderFront:nil];
-}
-
-bool OS_OSX::get_borderless_window() {
-
- return [window_object styleMask] == NSWindowStyleMaskBorderless;
-}
-
String OS_OSX::get_executable_path() const {
-
int ret;
pid_t pid;
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
@@ -2732,177 +298,7 @@ String OS_OSX::get_executable_path() const {
}
}
-// Returns string representation of keys, if they are printable.
-//
-static NSString *createStringForKeys(const CGKeyCode *keyCode, int length) {
-
- TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
- if (!currentKeyboard)
- return nil;
-
- CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
- if (!layoutData)
- return nil;
-
- const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
-
- OSStatus err;
- CFMutableStringRef output = CFStringCreateMutable(NULL, 0);
-
- for (int i = 0; i < length; ++i) {
-
- UInt32 keysDown = 0;
- UniChar chars[4];
- UniCharCount realLength;
-
- err = UCKeyTranslate(keyboardLayout,
- keyCode[i],
- kUCKeyActionDisplay,
- 0,
- LMGetKbdType(),
- kUCKeyTranslateNoDeadKeysBit,
- &keysDown,
- sizeof(chars) / sizeof(chars[0]),
- &realLength,
- chars);
-
- if (err != noErr) {
- CFRelease(output);
- return nil;
- }
-
- CFStringAppendCharacters(output, chars, 1);
- }
-
- //CFStringUppercase(output, NULL);
-
- return (NSString *)output;
-}
-
-OS::LatinKeyboardVariant OS_OSX::get_latin_keyboard_variant() const {
-
- static LatinKeyboardVariant layout = LATIN_KEYBOARD_QWERTY;
-
- if (keyboard_layout_dirty) {
-
- layout = LATIN_KEYBOARD_QWERTY;
-
- 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);
-
- 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;
- }
-
- [test release];
-
- keyboard_layout_dirty = false;
- return layout;
- }
-
- return layout;
-}
-
-void OS_OSX::process_events() {
-
- while (true) {
- NSEvent *event = [NSApp
- nextEventMatchingMask:NSEventMaskAny
- untilDate:[NSDate distantPast]
- inMode:NSDefaultRunLoopMode
- dequeue:YES];
-
- if (event == nil)
- break;
-
- [NSApp sendEvent:event];
- }
- process_key_events();
-
- [autoreleasePool drain];
- autoreleasePool = [[NSAutoreleasePool alloc] init];
-
- input->flush_accumulated_events();
-}
-
-void OS_OSX::process_key_events() {
-
- Ref<InputEventKey> k;
- for (int i = 0; i < key_event_pos; i++) {
-
- const KeyEvent &ke = key_event_buffer[i];
-
- if (ke.raw) {
- // Non IME input - no composite characters, pass events as is
- k.instance();
-
- get_key_modifier_state(ke.osx_state, k);
- k->set_pressed(ke.pressed);
- k->set_echo(ke.echo);
- k->set_keycode(ke.keycode);
- k->set_physical_keycode(ke.physical_keycode);
- k->set_unicode(ke.unicode);
-
- push_input(k);
- } else {
- // IME input
- if ((i == 0 && ke.keycode == 0) || (i > 0 && key_event_buffer[i - 1].keycode == 0)) {
- k.instance();
-
- get_key_modifier_state(ke.osx_state, k);
- k->set_pressed(ke.pressed);
- k->set_echo(ke.echo);
- k->set_keycode(0);
- k->set_physical_keycode(0);
- k->set_unicode(ke.unicode);
-
- push_input(k);
- }
- if (ke.keycode != 0) {
- k.instance();
-
- get_key_modifier_state(ke.osx_state, k);
- k->set_pressed(ke.pressed);
- k->set_echo(ke.echo);
- k->set_keycode(ke.keycode);
- k->set_physical_keycode(ke.physical_keycode);
-
- if (i + 1 < key_event_pos && key_event_buffer[i + 1].keycode == 0) {
- k->set_unicode(key_event_buffer[i + 1].unicode);
- }
-
- push_input(k);
- }
- }
- }
-
- key_event_pos = 0;
-}
-
-void OS_OSX::push_input(const Ref<InputEvent> &p_event) {
-
- Ref<InputEvent> ev = p_event;
- input->accumulate_input_event(ev);
-}
-
-void OS_OSX::force_process_input() {
-
- process_events(); // get rid of pending events
- joypad_osx->process_joypads();
-}
-
void OS_OSX::run() {
-
force_quit = false;
if (!main_loop)
@@ -2910,23 +306,12 @@ void OS_OSX::run() {
main_loop->init();
- if (zoomed) {
- zoomed = false;
- set_window_fullscreen(true);
- }
-
- //uint64_t last_ticks=get_ticks_usec();
-
- //int frames=0;
- //uint64_t frame=0;
-
bool quit = false;
-
while (!force_quit && !quit) {
-
@try {
-
- process_events(); // get rid of pending events
+ if (DisplayServer::get_singleton()) {
+ DisplayServer::get_singleton()->process_events(); // get rid of pending events
+ }
joypad_osx->process_joypads();
if (Main::iteration() == true) {
@@ -2936,41 +321,9 @@ void OS_OSX::run() {
ERR_PRINT("NSException: " + String([exception reason].UTF8String));
}
};
-
main_loop->finish();
}
-void OS_OSX::set_mouse_mode(MouseMode p_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);
- CGAssociateMouseAndMouseCursorPosition(false);
- } else if (p_mode == MOUSE_MODE_HIDDEN) {
- CGDisplayHideCursor(kCGDirectMainDisplay);
- CGAssociateMouseAndMouseCursorPosition(true);
- } else {
- CGDisplayShowCursor(kCGDirectMainDisplay);
- CGAssociateMouseAndMouseCursorPosition(true);
- }
-
- mouse_mode = p_mode;
-}
-
-OS::MouseMode OS_OSX::get_mouse_mode() const {
-
- return mouse_mode;
-}
-
-String OS_OSX::get_joy_guid(int p_device) const {
- return input->get_joy_guid_remapped(p_device);
-}
-
Error OS_OSX::move_to_trash(const String &p_path) {
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())];
@@ -2984,123 +337,19 @@ Error OS_OSX::move_to_trash(const String &p_path) {
return OK;
}
-void OS_OSX::_set_use_vsync(bool p_enable) {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- if (context_gles2)
- context_gles2->set_use_vsync(p_enable);
- }
-#endif
-}
-
-OS_OSX *OS_OSX::singleton = NULL;
-
OS_OSX::OS_OSX() {
-
- memset(cursors, 0, sizeof(cursors));
- key_event_pos = 0;
- mouse_mode = OS::MOUSE_MODE_VISIBLE;
main_loop = NULL;
- singleton = this;
- im_active = false;
- im_position = Point2();
- layered_window = false;
- autoreleasePool = [[NSAutoreleasePool alloc] init];
-
- eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
- ERR_FAIL_COND(!eventSource);
-
- CGEventSourceSetLocalEventsSuppressionInterval(eventSource, 0.0);
-
- // Implicitly create shared NSApplication instance
- [GodotApplication sharedApplication];
-
- // In case we are unbundled, make us a proper UI application
- [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
-
- // Menu bar setup must go between sharedApplication above and
- // finishLaunching below, in order to properly emulate the behavior
- // of NSApplicationMain
- NSMenuItem *menu_item;
- NSString *title;
-
- NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
- if (nsappname == nil)
- nsappname = [[NSProcessInfo processInfo] processName];
-
- // Setup Apple menu
- NSMenu *apple_menu = [[NSMenu alloc] initWithTitle:@""];
- title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname];
- [apple_menu addItemWithTitle:title action:@selector(showAbout:) keyEquivalent:@""];
-
- [apple_menu addItem:[NSMenuItem separatorItem]];
-
- NSMenu *services = [[NSMenu alloc] initWithTitle:@""];
- menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Services", nil) action:nil keyEquivalent:@""];
- [apple_menu setSubmenu:services forItem:menu_item];
- [NSApp setServicesMenu:services];
- [services release];
-
- [apple_menu addItem:[NSMenuItem separatorItem]];
-
- title = [NSString stringWithFormat:NSLocalizedString(@"Hide %@", nil), nsappname];
- [apple_menu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
-
- menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Hide Others", nil) action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
- [menu_item setKeyEquivalentModifierMask:(NSEventModifierFlagOption | NSEventModifierFlagCommand)];
-
- [apple_menu addItemWithTitle:NSLocalizedString(@"Show all", nil) action:@selector(unhideAllApplications:) keyEquivalent:@""];
-
- [apple_menu addItem:[NSMenuItem separatorItem]];
-
- title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname];
- [apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
-
- // Setup menu bar
- NSMenu *main_menu = [[NSMenu alloc] initWithTitle:@""];
- menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""];
- [main_menu setSubmenu:apple_menu forItem:menu_item];
- [NSApp setMainMenu:main_menu];
-
- [main_menu release];
- [apple_menu release];
-
- [NSApp finishLaunching];
-
- delegate = [[GodotApplicationDelegate alloc] init];
- ERR_FAIL_COND(!delegate);
- [NSApp setDelegate:delegate];
-
- cursor_shape = CURSOR_ARROW;
-
- maximized = false;
- minimized = false;
- window_size = Vector2(1024, 600);
- zoomed = false;
- resizable = false;
- window_focused = true;
+ force_quit = false;
Vector<Logger *> loggers;
loggers.push_back(memnew(OSXTerminalLogger));
_set_logger(memnew(CompositeLogger(loggers)));
- //process application:openFile: event
- while (true) {
- NSEvent *event = [NSApp
- nextEventMatchingMask:NSEventMaskAny
- untilDate:[NSDate distantPast]
- inMode:NSDefaultRunLoopMode
- dequeue:YES];
-
- if (event == nil)
- break;
-
- [NSApp sendEvent:event];
- }
-
#ifdef COREAUDIO_ENABLED
AudioDriverManager::add_driver(&audio_driver);
#endif
+
+ DisplayServerOSX::register_osx_driver();
}
bool OS_OSX::_check_internal_feature_support(const String &p_feature) {
diff --git a/platform/osx/platform_osx_builders.py b/platform/osx/platform_osx_builders.py
index 81997f674b..953ed479db 100644
--- a/platform/osx/platform_osx_builders.py
+++ b/platform/osx/platform_osx_builders.py
@@ -8,14 +8,14 @@ from platform_methods import subprocess_main
def make_debug_osx(target, source, env):
- if (env["macports_clang"] != 'no'):
+ if env["macports_clang"] != "no":
mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local")
mpclangver = env["macports_clang"]
- os.system(mpprefix + '/libexec/llvm-' + mpclangver + '/bin/llvm-dsymutil {0} -o {0}.dSYM'.format(target[0]))
+ os.system(mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-dsymutil {0} -o {0}.dSYM".format(target[0]))
else:
- os.system('dsymutil {0} -o {0}.dSYM'.format(target[0]))
- os.system('strip -u -r {0}'.format(target[0]))
+ os.system("dsymutil {0} -o {0}.dSYM".format(target[0]))
+ os.system("strip -u -r {0}".format(target[0]))
-if __name__ == '__main__':
+if __name__ == "__main__":
subprocess_main(globals())
diff --git a/platform/osx/vulkan_context_osx.h b/platform/osx/vulkan_context_osx.h
index 619e91d1f6..e996f176a9 100644
--- a/platform/osx/vulkan_context_osx.h
+++ b/platform/osx/vulkan_context_osx.h
@@ -35,11 +35,10 @@
#include <AppKit/AppKit.h>
class VulkanContextOSX : public VulkanContext {
-
virtual const char *_get_platform_surface_extension() const;
public:
- int window_create(id p_window, int p_width, int p_height);
+ Error window_create(DisplayServer::WindowID p_window_id, id p_window, int p_width, int p_height);
VulkanContextOSX();
~VulkanContextOSX();
diff --git a/platform/osx/vulkan_context_osx.mm b/platform/osx/vulkan_context_osx.mm
index c132bd334a..ec8745ff01 100644
--- a/platform/osx/vulkan_context_osx.mm
+++ b/platform/osx/vulkan_context_osx.mm
@@ -35,8 +35,7 @@ const char *VulkanContextOSX::_get_platform_surface_extension() const {
return VK_MVK_MACOS_SURFACE_EXTENSION_NAME;
}
-int VulkanContextOSX::window_create(id p_window, int p_width, int p_height) {
-
+Error VulkanContextOSX::window_create(DisplayServer::WindowID p_window_id, id p_window, int p_width, int p_height) {
VkMacOSSurfaceCreateInfoMVK createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
createInfo.pNext = NULL;
@@ -45,8 +44,8 @@ int VulkanContextOSX::window_create(id p_window, int p_width, int p_height) {
VkSurfaceKHR surface;
VkResult err = vkCreateMacOSSurfaceMVK(_get_instance(), &createInfo, NULL, &surface);
- ERR_FAIL_COND_V(err, -1);
- return _window_create(surface, p_width, p_height);
+ ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
+ return _window_create(p_window_id, surface, p_width, p_height);
}
VulkanContextOSX::VulkanContextOSX() {
diff --git a/platform/server/SCsub b/platform/server/SCsub
index 8364164114..15b9af4d25 100644
--- a/platform/server/SCsub
+++ b/platform/server/SCsub
@@ -2,15 +2,15 @@
import sys
-Import('env')
+Import("env")
-common_server = [\
- "os_server.cpp",\
+common_server = [
+ "os_server.cpp",
]
if sys.platform == "darwin":
- common_server.append("#platform/osx/crash_handler_osx.mm")
+ common_server.append("#platform/osx/crash_handler_osx.mm")
else:
- common_server.append("#platform/x11/crash_handler_x11.cpp")
+ common_server.append("#platform/x11/crash_handler_x11.cpp")
-prog = env.add_program('#bin/godot_server', ['godot_server.cpp'] + common_server)
+prog = env.add_program("#bin/godot_server", ["godot_server.cpp"] + common_server)
diff --git a/platform/server/detect.py b/platform/server/detect.py
index db9ba8d036..a73810cdf4 100644
--- a/platform/server/detect.py
+++ b/platform/server/detect.py
@@ -5,6 +5,7 @@ import sys
# This file is mostly based on platform/x11/detect.py.
# If editing this file, make sure to apply relevant changes here too.
+
def is_active():
return True
@@ -14,14 +15,14 @@ def get_name():
def get_program_suffix():
- if (sys.platform == "darwin"):
+ if sys.platform == "darwin":
return "osx"
- return "x11"
+ return "linuxbsd"
def can_build():
- if (os.name != "posix"):
+ if os.name != "posix":
return False
return True
@@ -29,17 +30,18 @@ def can_build():
def get_opts():
from SCons.Variables import BoolVariable, EnumVariable
+
return [
- BoolVariable('use_llvm', 'Use the LLVM compiler', False),
- BoolVariable('use_static_cpp', 'Link libgcc and libstdc++ statically for better portability', False),
- 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('separate_debug_symbols', 'Create a separate file containing debugging symbols', False),
- BoolVariable('execinfo', 'Use libexecinfo on systems where glibc is not available', False),
+ BoolVariable("use_llvm", "Use the LLVM compiler", False),
+ BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", False),
+ 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("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
+ BoolVariable("execinfo", "Use libexecinfo on systems where glibc is not available", False),
]
@@ -52,89 +54,89 @@ def configure(env):
## Build type
- if (env["target"] == "release"):
- if (env["optimize"] == "speed"): #optimize for speed (default)
- env.Prepend(CCFLAGS=['-O3'])
- else: #optimize for size
- env.Prepend(CCFLAGS=['-Os'])
-
- if (env["debug_symbols"] == "yes"):
- env.Prepend(CCFLAGS=['-g1'])
- if (env["debug_symbols"] == "full"):
- env.Prepend(CCFLAGS=['-g2'])
-
- elif (env["target"] == "release_debug"):
- if (env["optimize"] == "speed"): #optimize for speed (default)
- env.Prepend(CCFLAGS=['-O2'])
- 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"):
- env.Prepend(CCFLAGS=['-g2'])
-
- elif (env["target"] == "debug"):
- env.Prepend(CCFLAGS=['-g3'])
- env.Prepend(CPPDEFINES=['DEBUG_ENABLED', 'DEBUG_MEMORY_ENABLED'])
- env.Append(LINKFLAGS=['-rdynamic'])
+ if env["target"] == "release":
+ if env["optimize"] == "speed": # optimize for speed (default)
+ env.Prepend(CCFLAGS=["-O3"])
+ else: # optimize for size
+ env.Prepend(CCFLAGS=["-Os"])
+
+ if env["debug_symbols"] == "yes":
+ env.Prepend(CCFLAGS=["-g1"])
+ if env["debug_symbols"] == "full":
+ env.Prepend(CCFLAGS=["-g2"])
+
+ elif env["target"] == "release_debug":
+ if env["optimize"] == "speed": # optimize for speed (default)
+ env.Prepend(CCFLAGS=["-O2"])
+ 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":
+ env.Prepend(CCFLAGS=["-g2"])
+
+ elif env["target"] == "debug":
+ env.Prepend(CCFLAGS=["-g3"])
+ env.Prepend(CPPDEFINES=["DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED"])
+ env.Append(LINKFLAGS=["-rdynamic"])
## Architecture
- is64 = sys.maxsize > 2**32
- if (env["bits"] == "default"):
+ is64 = sys.maxsize > 2 ** 32
+ if env["bits"] == "default":
env["bits"] = "64" if is64 else "32"
## Compiler configuration
- if 'CXX' in env and 'clang' in os.path.basename(env['CXX']):
+ if "CXX" in env and "clang" in os.path.basename(env["CXX"]):
# Convenience check to enforce the use_llvm overrides when CXX is clang(++)
- env['use_llvm'] = True
+ env["use_llvm"] = True
- if env['use_llvm']:
- if ('clang++' not in os.path.basename(env['CXX'])):
+ if env["use_llvm"]:
+ 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.Append(CPPDEFINES=["TYPED_METHOD_BIND"])
env.extra_suffix = ".llvm" + env.extra_suffix
- if env['use_coverage']:
- env.Append(CCFLAGS=['-ftest-coverage', '-fprofile-arcs'])
- env.Append(LINKFLAGS=['-ftest-coverage', '-fprofile-arcs'])
+ if env["use_coverage"]:
+ env.Append(CCFLAGS=["-ftest-coverage", "-fprofile-arcs"])
+ env.Append(LINKFLAGS=["-ftest-coverage", "-fprofile-arcs"])
- if env['use_ubsan'] or env['use_asan'] or env['use_lsan'] or env['use_tsan']:
+ if env["use_ubsan"] or env["use_asan"] or env["use_lsan"] or env["use_tsan"]:
env.extra_suffix += "s"
- if env['use_ubsan']:
- env.Append(CCFLAGS=['-fsanitize=undefined'])
- env.Append(LINKFLAGS=['-fsanitize=undefined'])
+ 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_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_lsan"]:
+ env.Append(CCFLAGS=["-fsanitize=leak"])
+ env.Append(LINKFLAGS=["-fsanitize=leak"])
- if env['use_tsan']:
- env.Append(CCFLAGS=['-fsanitize=thread'])
- env.Append(LINKFLAGS=['-fsanitize=thread'])
+ if env["use_tsan"]:
+ env.Append(CCFLAGS=["-fsanitize=thread"])
+ env.Append(LINKFLAGS=["-fsanitize=thread"])
- if env['use_lto']:
- env.Append(CCFLAGS=['-flto'])
- if not env['use_llvm'] and env.GetOption("num_jobs") > 1:
- env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))])
+ if env["use_lto"]:
+ env.Append(CCFLAGS=["-flto"])
+ if not env["use_llvm"] and env.GetOption("num_jobs") > 1:
+ env.Append(LINKFLAGS=["-flto=" + str(env.GetOption("num_jobs"))])
else:
- env.Append(LINKFLAGS=['-flto'])
- if not env['use_llvm']:
- env['RANLIB'] = 'gcc-ranlib'
- env['AR'] = 'gcc-ar'
+ env.Append(LINKFLAGS=["-flto"])
+ if not env["use_llvm"]:
+ env["RANLIB"] = "gcc-ranlib"
+ env["AR"] = "gcc-ar"
- env.Append(CCFLAGS=['-pipe'])
- env.Append(LINKFLAGS=['-pipe'])
+ env.Append(CCFLAGS=["-pipe"])
+ env.Append(LINKFLAGS=["-pipe"])
## Dependencies
@@ -142,109 +144,114 @@ 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']:
- env['builtin_freetype'] = True
- env['builtin_libpng'] = True
- env['builtin_zlib'] = True
+ 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_freetype"]:
+ env.ParseConfig("pkg-config freetype2 --cflags --libs")
- if not env['builtin_libpng']:
- env.ParseConfig('pkg-config libpng16 --cflags --libs')
+ if not env["builtin_libpng"]:
+ env.ParseConfig("pkg-config libpng16 --cflags --libs")
- if not env['builtin_bullet']:
+ if not env["builtin_bullet"]:
# We need at least version 2.89
import subprocess
- bullet_version = subprocess.check_output(['pkg-config', 'bullet', '--modversion']).strip()
+
+ bullet_version = subprocess.check_output(["pkg-config", "bullet", "--modversion"]).strip()
if str(bullet_version) < "2.89":
# 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.89"))
+ print(
+ "Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(
+ bullet_version, "2.89"
+ )
+ )
sys.exit(255)
- env.ParseConfig('pkg-config bullet --cflags --libs')
+ env.ParseConfig("pkg-config bullet --cflags --libs")
if False: # not env['builtin_assimp']:
# FIXME: Add min version check
- env.ParseConfig('pkg-config assimp --cflags --libs')
+ env.ParseConfig("pkg-config assimp --cflags --libs")
- if not env['builtin_enet']:
- env.ParseConfig('pkg-config libenet --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_squish"]:
+ env.ParseConfig("pkg-config libsquish --cflags --libs")
- if not env['builtin_zstd']:
- env.ParseConfig('pkg-config libzstd --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_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")
else:
- list_of_x86 = ['x86_64', 'x86', 'i386', 'i586']
+ 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_libvpx']:
- env.ParseConfig('pkg-config vpx --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_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_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 not env["builtin_libogg"]:
+ env.ParseConfig("pkg-config ogg --cflags --libs")
- if not env['builtin_libwebp']:
- env.ParseConfig('pkg-config libwebp --cflags --libs')
+ if not env["builtin_libwebp"]:
+ env.ParseConfig("pkg-config libwebp --cflags --libs")
- if not env['builtin_mbedtls']:
+ 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'])
+ env.Append(LIBS=["mbedtls", "mbedcrypto", "mbedx509"])
- if not env['builtin_wslay']:
- env.ParseConfig('pkg-config libwslay --cflags --libs')
+ if not env["builtin_wslay"]:
+ env.ParseConfig("pkg-config libwslay --cflags --libs")
- if not env['builtin_miniupnpc']:
+ if not env["builtin_miniupnpc"]:
# No pkgconfig file so far, hardcode default paths.
env.Prepend(CPPPATH=["/usr/include/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')
+ if not env["builtin_pcre2"]:
+ env.ParseConfig("pkg-config libpcre2-32 --cflags --libs")
## Flags
# Linkflags below this line should typically stay the last ones
- if not env['builtin_zlib']:
- env.ParseConfig('pkg-config zlib --cflags --libs')
+ if not env["builtin_zlib"]:
+ env.ParseConfig("pkg-config zlib --cflags --libs")
- env.Prepend(CPPPATH=['#platform/server'])
- env.Append(CPPDEFINES=['SERVER_ENABLED', 'UNIX_ENABLED'])
+ env.Prepend(CPPPATH=["#platform/server"])
+ env.Append(CPPDEFINES=["SERVER_ENABLED", "UNIX_ENABLED"])
- if (platform.system() == "Darwin"):
- env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-lz', '-framework', 'IOKit'])
+ if platform.system() == "Darwin":
+ env.Append(LINKFLAGS=["-framework", "Cocoa", "-framework", "Carbon", "-lz", "-framework", "IOKit"])
- env.Append(LIBS=['pthread'])
+ env.Append(LIBS=["pthread"])
- if (platform.system() == "Linux"):
- env.Append(LIBS=['dl'])
+ if platform.system() == "Linux":
+ env.Append(LIBS=["dl"])
- if (platform.system().find("BSD") >= 0):
+ if platform.system().find("BSD") >= 0:
env["execinfo"] = True
if env["execinfo"]:
- env.Append(LIBS=['execinfo'])
+ env.Append(LIBS=["execinfo"])
# Link those statically for portability
- if env['use_static_cpp']:
- env.Append(LINKFLAGS=['-static-libgcc', '-static-libstdc++'])
+ if env["use_static_cpp"]:
+ env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"])
diff --git a/platform/server/godot_server.cpp b/platform/server/godot_server.cpp
index df49bfaebf..32bd943ac3 100644
--- a/platform/server/godot_server.cpp
+++ b/platform/server/godot_server.cpp
@@ -32,7 +32,6 @@
#include "os_server.h"
int main(int argc, char *argv[]) {
-
OS_Server os;
Error err = Main::setup(argv[0], argc - 1, &argv[1]);
diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp
index 3257ec261c..fbe526ef6d 100644
--- a/platform/server/os_server.cpp
+++ b/platform/server/os_server.cpp
@@ -33,7 +33,7 @@
#include "core/print_string.h"
#include "drivers/dummy/rasterizer_dummy.h"
#include "drivers/dummy/texture_loader_dummy.h"
-#include "servers/visual/visual_server_raster.h"
+#include "servers/rendering/rendering_server_raster.h"
#include "main/main.h"
@@ -42,11 +42,10 @@
#include <unistd.h>
int OS_Server::get_video_driver_count() const {
-
return 1;
}
-const char *OS_Server::get_video_driver_name(int p_driver) const {
+const char *OS_Server::get_video_driver_name(int p_driver) const {
return "Dummy";
}
@@ -55,7 +54,6 @@ int OS_Server::get_audio_driver_count() const {
}
const char *OS_Server::get_audio_driver_name(int p_driver) const {
-
return "Dummy";
}
@@ -64,24 +62,22 @@ int OS_Server::get_current_video_driver() const {
}
void OS_Server::initialize_core() {
-
crash_handler.initialize();
OS_Unix::initialize_core();
}
Error OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
-
args = OS::get_singleton()->get_cmdline_args();
current_videomode = p_desired;
- main_loop = NULL;
+ main_loop = nullptr;
RasterizerDummy::make_current();
video_driver_index = p_video_driver; // unused in server platform, but should still be initialized
- visual_server = memnew(VisualServerRaster);
- visual_server->init();
+ rendering_server = memnew(RenderingServerRaster);
+ rendering_server->init();
AudioDriverManager::initialize(p_audio_driver);
@@ -96,13 +92,12 @@ Error OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int
}
void OS_Server::finalize() {
-
if (main_loop)
memdelete(main_loop);
- main_loop = NULL;
+ main_loop = nullptr;
- visual_server->finish();
- memdelete(visual_server);
+ rendering_server->finish();
+ memdelete(rendering_server);
memdelete(input);
@@ -116,22 +111,18 @@ void OS_Server::set_mouse_show(bool p_show) {
}
void OS_Server::set_mouse_grab(bool p_grab) {
-
grab = p_grab;
}
bool OS_Server::is_mouse_grab_enabled() const {
-
return grab;
}
int OS_Server::get_mouse_button_state() const {
-
return 0;
}
Point2 OS_Server::get_mouse_position() const {
-
return Point2();
}
@@ -142,12 +133,10 @@ void OS_Server::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
}
OS::VideoMode OS_Server::get_video_mode(int p_screen) const {
-
return current_videomode;
}
Size2 OS_Server::get_window_size() const {
-
return Vector2(current_videomode.width, current_videomode.height);
}
@@ -155,30 +144,25 @@ void OS_Server::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen)
}
MainLoop *OS_Server::get_main_loop() const {
-
return main_loop;
}
void OS_Server::delete_main_loop() {
-
if (main_loop)
memdelete(main_loop);
- main_loop = NULL;
+ main_loop = nullptr;
}
void OS_Server::set_main_loop(MainLoop *p_main_loop) {
-
main_loop = 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";
}
@@ -190,7 +174,6 @@ bool OS_Server::_check_internal_feature_support(const String &p_feature) {
}
void OS_Server::run() {
-
force_quit = false;
if (!main_loop)
@@ -199,7 +182,6 @@ void OS_Server::run() {
main_loop->init();
while (!force_quit) {
-
if (Main::iteration())
break;
};
@@ -208,7 +190,6 @@ void OS_Server::run() {
}
String OS_Server::get_config_path() const {
-
if (has_environment("XDG_CONFIG_HOME")) {
return get_environment("XDG_CONFIG_HOME");
} else if (has_environment("HOME")) {
@@ -219,7 +200,6 @@ String OS_Server::get_config_path() const {
}
String OS_Server::get_data_path() const {
-
if (has_environment("XDG_DATA_HOME")) {
return get_environment("XDG_DATA_HOME");
} else if (has_environment("HOME")) {
@@ -230,7 +210,6 @@ String OS_Server::get_data_path() const {
}
String OS_Server::get_cache_path() const {
-
if (has_environment("XDG_CACHE_HOME")) {
return get_environment("XDG_CACHE_HOME");
} else if (has_environment("HOME")) {
@@ -241,46 +220,37 @@ String OS_Server::get_cache_path() const {
}
String OS_Server::get_system_dir(SystemDir p_dir) const {
-
String xdgparam;
switch (p_dir) {
case SYSTEM_DIR_DESKTOP: {
-
xdgparam = "DESKTOP";
} break;
case SYSTEM_DIR_DCIM: {
-
xdgparam = "PICTURES";
} break;
case SYSTEM_DIR_DOCUMENTS: {
-
xdgparam = "DOCUMENTS";
} break;
case SYSTEM_DIR_DOWNLOADS: {
-
xdgparam = "DOWNLOAD";
} break;
case SYSTEM_DIR_MOVIES: {
-
xdgparam = "VIDEOS";
} break;
case SYSTEM_DIR_MUSIC: {
-
xdgparam = "MUSIC";
} break;
case SYSTEM_DIR_PICTURES: {
-
xdgparam = "PICTURES";
} break;
case SYSTEM_DIR_RINGTONES: {
-
xdgparam = "MUSIC";
} break;
@@ -289,7 +259,7 @@ String OS_Server::get_system_dir(SystemDir p_dir) const {
String pipe;
List<String> arg;
arg.push_back(xdgparam);
- Error err = const_cast<OS_Server *>(this)->execute("xdg-user-dir", arg, true, NULL, &pipe);
+ Error err = const_cast<OS_Server *>(this)->execute("xdg-user-dir", arg, true, nullptr, &pipe);
if (err != OK)
return ".";
return pipe.strip_edges();
@@ -304,7 +274,6 @@ bool OS_Server::is_disable_crash_handler() const {
}
OS_Server::OS_Server() {
-
//adriver here
grab = false;
};
diff --git a/platform/server/os_server.h b/platform/server/os_server.h
index 7584293722..06ea483fd4 100644
--- a/platform/server/os_server.h
+++ b/platform/server/os_server.h
@@ -31,24 +31,23 @@
#ifndef OS_SERVER_H
#define OS_SERVER_H
+#include "core/input/input.h"
#include "drivers/dummy/texture_loader_dummy.h"
#include "drivers/unix/os_unix.h"
-#include "main/input_default.h"
#ifdef __APPLE__
#include "platform/osx/crash_handler_osx.h"
#include "platform/osx/semaphore_osx.h"
#else
-#include "platform/x11/crash_handler_x11.h"
+#include "platform/x11/crash_handler_linuxbsd.h"
#endif
#include "servers/audio_server.h"
-#include "servers/visual/rasterizer.h"
-#include "servers/visual_server.h"
+#include "servers/rendering/rasterizer.h"
+#include "servers/rendering_server.h"
#undef CursorShape
class OS_Server : public OS_Unix {
-
- VisualServer *visual_server;
+ RenderingServer *rendering_server;
VideoMode current_videomode;
List<String> args;
MainLoop *main_loop;
diff --git a/platform/uwp/SCsub b/platform/uwp/SCsub
index 620d8c3c3a..4358b0eead 100644
--- a/platform/uwp/SCsub
+++ b/platform/uwp/SCsub
@@ -1,21 +1,21 @@
#!/usr/bin/env python
-Import('env')
+Import("env")
files = [
- 'thread_uwp.cpp',
- '#platform/windows/key_mapping_windows.cpp',
- '#platform/windows/windows_terminal_logger.cpp',
- 'joypad_uwp.cpp',
- 'context_egl_uwp.cpp',
- 'app.cpp',
- 'os_uwp.cpp',
+ "thread_uwp.cpp",
+ "#platform/windows/key_mapping_windows.cpp",
+ "#platform/windows/windows_terminal_logger.cpp",
+ "joypad_uwp.cpp",
+ "context_egl_uwp.cpp",
+ "app.cpp",
+ "os_uwp.cpp",
]
if "build_angle" in env and env["build_angle"]:
- cmd = env.AlwaysBuild(env.ANGLE('libANGLE.lib', None))
+ cmd = env.AlwaysBuild(env.ANGLE("libANGLE.lib", None))
-prog = env.add_program('#bin/godot', files)
+prog = env.add_program("#bin/godot", files)
if "build_angle" in env and env["build_angle"]:
env.Depends(prog, [cmd])
diff --git a/platform/uwp/app.cpp b/platform/uwp/app.cpp
index ccb4b43373..6090d13854 100644
--- a/platform/uwp/app.cpp
+++ b/platform/uwp/app.cpp
@@ -78,16 +78,6 @@ public:
return 0;
}
-App::App() :
- mWindowClosed(false),
- mWindowVisible(true),
- mWindowWidth(0),
- mWindowHeight(0),
- mEglDisplay(EGL_NO_DISPLAY),
- mEglContext(EGL_NO_CONTEXT),
- mEglSurface(EGL_NO_SURFACE) {
-}
-
// The first method called when the IFrameworkView is being created.
void App::Initialize(CoreApplicationView ^ applicationView) {
// Register event handlers for app lifecycle. This example includes Activated, so that we
@@ -156,7 +146,6 @@ void App::SetWindow(CoreWindow ^ p_window) {
}
static int _get_button(Windows::UI::Input::PointerPoint ^ pt) {
-
using namespace Windows::UI::Input;
#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
@@ -207,7 +196,6 @@ static bool _is_touch(Windows::UI::Input::PointerPoint ^ pointerPoint) {
}
static Windows::Foundation::Point _get_pixel_position(CoreWindow ^ window, Windows::Foundation::Point rawPosition, OS *os) {
-
Windows::Foundation::Point outputPosition;
// Compute coordinates normalized from 0..1.
@@ -247,17 +235,14 @@ static Windows::Foundation::Point _get_pixel_position(CoreWindow ^ window, Windo
};
static int _get_finger(uint32_t p_touch_id) {
-
return p_touch_id % 31; // for now
};
void App::pointer_event(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args, bool p_pressed, bool p_is_wheel) {
-
Windows::UI::Input::PointerPoint ^ point = args->CurrentPoint;
Windows::Foundation::Point pos = _get_pixel_position(window, point->Position, os);
int but = _get_button(point);
if (_is_touch(point)) {
-
Ref<InputEventScreenTouch> screen_touch;
screen_touch.instance();
screen_touch->set_device(0);
@@ -270,7 +255,6 @@ void App::pointer_event(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Cor
os->input_event(screen_touch);
} else {
-
Ref<InputEventMouseButton> mouse_button;
mouse_button.instance();
mouse_button->set_device(0);
@@ -301,22 +285,18 @@ void App::pointer_event(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Cor
};
void App::OnPointerPressed(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args) {
-
pointer_event(sender, args, true);
};
void App::OnPointerReleased(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args) {
-
pointer_event(sender, args, false);
};
void App::OnPointerWheelChanged(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args) {
-
pointer_event(sender, args, true, true);
}
void App::OnMouseModeChanged(Windows::System::Threading::Core::SignalNotifier ^ signalNotifier, bool timedOut) {
-
OS::MouseMode mode = os->get_mouse_mode();
SignalNotifier ^ notifier = mouseChangedNotifier;
@@ -325,12 +305,10 @@ void App::OnMouseModeChanged(Windows::System::Threading::Core::SignalNotifier ^
ref new DispatchedHandler(
[mode, notifier, this]() {
if (mode == OS::MOUSE_MODE_CAPTURED) {
-
this->MouseMovedToken = MouseDevice::GetForCurrentView()->MouseMoved +=
ref new TypedEventHandler<MouseDevice ^, MouseEventArgs ^>(this, &App::OnMouseMoved);
} else {
-
MouseDevice::GetForCurrentView()->MouseMoved -= MouseMovedToken;
}
@@ -341,12 +319,10 @@ void App::OnMouseModeChanged(Windows::System::Threading::Core::SignalNotifier ^
}
void App::OnPointerMoved(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args) {
-
Windows::UI::Input::PointerPoint ^ point = args->CurrentPoint;
Windows::Foundation::Point pos = _get_pixel_position(window, point->Position, os);
if (_is_touch(point)) {
-
Ref<InputEventScreenDrag> screen_drag;
screen_drag.instance();
screen_drag->set_device(0);
@@ -356,7 +332,6 @@ void App::OnPointerMoved(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Co
os->input_event(screen_drag);
} else {
-
// In case the mouse grabbed, MouseMoved will handle this
if (os->get_mouse_mode() == OS::MouseMode::MOUSE_MODE_CAPTURED)
return;
@@ -375,7 +350,6 @@ void App::OnPointerMoved(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Co
}
void App::OnMouseMoved(MouseDevice ^ mouse_device, MouseEventArgs ^ args) {
-
// In case the mouse isn't grabbed, PointerMoved will handle this
if (os->get_mouse_mode() != OS::MouseMode::MOUSE_MODE_CAPTURED)
return;
@@ -397,7 +371,6 @@ void App::OnMouseMoved(MouseDevice ^ mouse_device, MouseEventArgs ^ args) {
}
void App::key_event(Windows::UI::Core::CoreWindow ^ sender, bool p_pressed, Windows::UI::Core::KeyEventArgs ^ key_args, Windows::UI::Core::CharacterReceivedEventArgs ^ char_args) {
-
OS_UWP::KeyEvent ke;
ke.control = sender->GetAsyncKeyState(VirtualKey::Control) == CoreVirtualKeyStates::Down;
@@ -407,7 +380,6 @@ void App::key_event(Windows::UI::Core::CoreWindow ^ sender, bool p_pressed, Wind
ke.pressed = p_pressed;
if (key_args != nullptr) {
-
ke.type = OS_UWP::KeyEvent::MessageType::KEY_EVENT_MESSAGE;
ke.unicode = 0;
ke.keycode = KeyMappingWindows::get_keysym((unsigned int)key_args->VirtualKey);
@@ -415,7 +387,6 @@ void App::key_event(Windows::UI::Core::CoreWindow ^ sender, bool p_pressed, Wind
ke.echo = (!p_pressed && !key_args->KeyStatus.IsKeyReleased) || (p_pressed && key_args->KeyStatus.WasKeyDown);
} else {
-
ke.type = OS_UWP::KeyEvent::MessageType::CHAR_EVENT_MESSAGE;
ke.unicode = char_args->KeyCode;
ke.keycode = 0;
@@ -425,6 +396,7 @@ void App::key_event(Windows::UI::Core::CoreWindow ^ sender, bool p_pressed, Wind
os->queue_key_event(ke);
}
+
void App::OnKeyDown(CoreWindow ^ sender, KeyEventArgs ^ args) {
key_event(sender, true, args);
}
@@ -506,14 +478,12 @@ void App::UpdateWindowSize(Size size) {
}
char **App::get_command_line(unsigned int *out_argc) {
-
- static char *fail_cl[] = { "--path", "game", NULL };
+ static char *fail_cl[] = { "--path", "game", nullptr };
*out_argc = 2;
FILE *f = _wfopen(L"__cl__.cl", L"rb");
- if (f == NULL) {
-
+ if (f == nullptr) {
wprintf(L"Couldn't open command line file.\n");
return fail_cl;
}
@@ -535,7 +505,6 @@ char **App::get_command_line(unsigned int *out_argc) {
int argc = READ_LE_4(len);
for (int i = 0; i < argc; i++) {
-
r = fread(len, sizeof(uint8_t), 4, f);
if (r < 4) {
@@ -557,8 +526,7 @@ char **App::get_command_line(unsigned int *out_argc) {
arg[strlen] = '\0';
if (r == strlen) {
-
- int warg_size = MultiByteToWideChar(CP_UTF8, 0, arg, -1, NULL, 0);
+ int warg_size = MultiByteToWideChar(CP_UTF8, 0, arg, -1, nullptr, 0);
wchar_t *warg = new wchar_t[warg_size];
MultiByteToWideChar(CP_UTF8, 0, arg, -1, warg, warg_size);
@@ -566,7 +534,6 @@ char **App::get_command_line(unsigned int *out_argc) {
cl.Append(ref new Platform::String(warg, warg_size));
} else {
-
delete[] arg;
fclose(f);
wprintf(L"Error reading command.\n");
@@ -582,15 +549,14 @@ char **App::get_command_line(unsigned int *out_argc) {
char **ret = new char *[cl.Size + 1];
for (int i = 0; i < cl.Size; i++) {
-
- int arg_size = WideCharToMultiByte(CP_UTF8, 0, cl.GetAt(i)->Data(), -1, NULL, 0, NULL, NULL);
+ int arg_size = WideCharToMultiByte(CP_UTF8, 0, cl.GetAt(i)->Data(), -1, nullptr, 0, nullptr, nullptr);
char *arg = new char[arg_size];
- WideCharToMultiByte(CP_UTF8, 0, cl.GetAt(i)->Data(), -1, arg, arg_size, NULL, NULL);
+ WideCharToMultiByte(CP_UTF8, 0, cl.GetAt(i)->Data(), -1, arg, arg_size, nullptr, nullptr);
ret[i] = arg;
}
- ret[cl.Size] = NULL;
+ ret[cl.Size] = nullptr;
*out_argc = cl.Size;
return ret;
diff --git a/platform/uwp/app.h b/platform/uwp/app.h
index b7265ad086..5cffe378b1 100644
--- a/platform/uwp/app.h
+++ b/platform/uwp/app.h
@@ -45,7 +45,7 @@ namespace GodotUWP
ref class App sealed : public Windows::ApplicationModel::Core::IFrameworkView
{
public:
- App();
+ App() {}
// IFrameworkView Methods.
virtual void Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView);
@@ -92,14 +92,14 @@ namespace GodotUWP
char** get_command_line(unsigned int* out_argc);
- bool mWindowClosed;
- bool mWindowVisible;
- GLsizei mWindowWidth;
- GLsizei mWindowHeight;
+ bool mWindowClosed = false;
+ bool mWindowVisible = true;
+ GLsizei mWindowWidth = 0;
+ GLsizei mWindowHeight = 0;
- EGLDisplay mEglDisplay;
- EGLContext mEglContext;
- EGLSurface mEglSurface;
+ EGLDisplay mEglDisplay = EGL_NO_DISPLAY;
+ EGLContext mEglContext = EGL_NO_CONTEXT;
+ EGLSurface mEglSurface = EGL_NO_SURFACE;
CoreWindow^ window;
OS_UWP* os;
diff --git a/platform/uwp/context_egl_uwp.cpp b/platform/uwp/context_egl_uwp.cpp
index 7ac9489bb4..2da6c5897a 100644
--- a/platform/uwp/context_egl_uwp.cpp
+++ b/platform/uwp/context_egl_uwp.cpp
@@ -35,27 +35,22 @@
using Platform::Exception;
void ContextEGL_UWP::release_current() {
-
eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, mEglContext);
};
void ContextEGL_UWP::make_current() {
-
eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
};
int ContextEGL_UWP::get_window_width() {
-
return width;
};
int ContextEGL_UWP::get_window_height() {
-
return height;
};
void ContextEGL_UWP::reset() {
-
cleanup();
window = CoreWindow::GetForCurrentThread();
@@ -63,7 +58,6 @@ void ContextEGL_UWP::reset() {
};
void ContextEGL_UWP::swap_buffers() {
-
if (eglSwapBuffers(mEglDisplay, mEglSurface) != EGL_TRUE) {
cleanup();
@@ -75,7 +69,6 @@ void ContextEGL_UWP::swap_buffers() {
};
Error ContextEGL_UWP::initialize() {
-
EGLint configAttribList[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
@@ -115,7 +108,6 @@ Error ContextEGL_UWP::initialize() {
}
try {
-
const EGLint displayAttributes[] = {
/*EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, 9,
@@ -155,7 +147,7 @@ Error ContextEGL_UWP::initialize() {
throw Exception::CreateException(E_FAIL, L"Failed to initialize EGL");
}
- if (eglGetConfigs(display, NULL, 0, &numConfigs) == EGL_FALSE) {
+ if (eglGetConfigs(display, nullptr, 0, &numConfigs) == EGL_FALSE) {
throw Exception::CreateException(E_FAIL, L"Failed to get EGLConfig count");
}
@@ -191,7 +183,6 @@ Error ContextEGL_UWP::initialize() {
};
void ContextEGL_UWP::cleanup() {
-
if (mEglDisplay != EGL_NO_DISPLAY && mEglSurface != EGL_NO_SURFACE) {
eglDestroySurface(mEglDisplay, mEglSurface);
mEglSurface = EGL_NO_SURFACE;
@@ -216,6 +207,5 @@ ContextEGL_UWP::ContextEGL_UWP(CoreWindow ^ p_window, Driver p_driver) :
window(p_window) {}
ContextEGL_UWP::~ContextEGL_UWP() {
-
cleanup();
};
diff --git a/platform/uwp/context_egl_uwp.h b/platform/uwp/context_egl_uwp.h
index fa61cf50c6..6f333b8e6a 100644
--- a/platform/uwp/context_egl_uwp.h
+++ b/platform/uwp/context_egl_uwp.h
@@ -41,7 +41,6 @@
using namespace Windows::UI::Core;
class ContextEGL_UWP {
-
public:
enum Driver {
GLES_2_0,
diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py
index 000bd18e7d..669bfe6814 100644
--- a/platform/uwp/detect.py
+++ b/platform/uwp/detect.py
@@ -12,11 +12,11 @@ def get_name():
def can_build():
- if (os.name == "nt"):
+ if os.name == "nt":
# building natively on windows!
- if (os.getenv("VSINSTALLDIR")):
+ if os.getenv("VSINSTALLDIR"):
- if (os.getenv("ANGLE_SRC_PATH") is None):
+ if os.getenv("ANGLE_SRC_PATH") is None:
return False
return True
@@ -25,16 +25,16 @@ def can_build():
def get_opts():
return [
- ('msvc_version', 'MSVC version to use (ignored if the VCINSTALLDIR environment variable is set)', None),
+ ("msvc_version", "MSVC version to use (ignored if the VCINSTALLDIR environment variable is set)", None),
]
def get_flags():
return [
- ('tools', False),
- ('xaudio2', True),
- ('builtin_pcre2_with_jit', False),
+ ("tools", False),
+ ("xaudio2", True),
+ ("builtin_pcre2_with_jit", False),
]
@@ -42,45 +42,53 @@ def configure(env):
env.msvc = True
- if (env["bits"] != "default"):
+ if env["bits"] != "default":
print("Error: bits argument is disabled for MSVC")
- print("""
+ print(
+ """
Bits argument is not supported for MSVC compilation. Architecture depends on the Native/Cross Compile Tools Prompt/Developer Console
(or Visual Studio settings) that is being used to run SCons. As a consequence, bits argument is disabled. Run scons again without bits
argument (example: scons p=uwp) and SCons will attempt to detect what MSVC compiler will be executed and inform you.
- """)
+ """
+ )
sys.exit()
## Build type
- if (env["target"] == "release"):
- env.Append(CCFLAGS=['/O2', '/GL'])
- env.Append(CCFLAGS=['/MD'])
- env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS', '/LTCG'])
+ if env["target"] == "release":
+ env.Append(CCFLAGS=["/O2", "/GL"])
+ env.Append(CCFLAGS=["/MD"])
+ env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS", "/LTCG"])
- elif (env["target"] == "release_debug"):
- env.Append(CCFLAGS=['/O2', '/Zi'])
- env.Append(CCFLAGS=['/MD'])
- env.Append(CPPDEFINES=['DEBUG_ENABLED'])
- env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE'])
+ elif env["target"] == "release_debug":
+ env.Append(CCFLAGS=["/O2", "/Zi"])
+ env.Append(CCFLAGS=["/MD"])
+ env.Append(CPPDEFINES=["DEBUG_ENABLED"])
+ env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"])
- elif (env["target"] == "debug"):
- env.Append(CCFLAGS=['/Zi'])
- env.Append(CCFLAGS=['/MDd'])
- env.Append(CPPDEFINES=['DEBUG_ENABLED', 'DEBUG_MEMORY_ENABLED'])
- env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE'])
- env.Append(LINKFLAGS=['/DEBUG'])
+ elif env["target"] == "debug":
+ env.Append(CCFLAGS=["/Zi"])
+ env.Append(CCFLAGS=["/MDd"])
+ env.Append(CPPDEFINES=["DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED"])
+ env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"])
+ env.Append(LINKFLAGS=["/DEBUG"])
## Compiler configuration
- env['ENV'] = os.environ
- vc_base_path = os.environ['VCTOOLSINSTALLDIR'] if "VCTOOLSINSTALLDIR" in os.environ else os.environ['VCINSTALLDIR']
+ env["ENV"] = os.environ
+ vc_base_path = os.environ["VCTOOLSINSTALLDIR"] if "VCTOOLSINSTALLDIR" in os.environ else os.environ["VCINSTALLDIR"]
# ANGLE
angle_root = os.getenv("ANGLE_SRC_PATH")
- env.Prepend(CPPPATH=[angle_root + '/include'])
+ env.Prepend(CPPPATH=[angle_root + "/include"])
jobs = str(env.GetOption("num_jobs"))
- angle_build_cmd = "msbuild.exe " + angle_root + "/winrt/10/src/angle.sln /nologo /v:m /m:" + jobs + " /p:Configuration=Release /p:Platform="
+ angle_build_cmd = (
+ "msbuild.exe "
+ + angle_root
+ + "/winrt/10/src/angle.sln /nologo /v:m /m:"
+ + jobs
+ + " /p:Configuration=Release /p:Platform="
+ )
if os.path.isfile(str(os.getenv("ANGLE_SRC_PATH")) + "/winrt/10/src/angle.sln"):
env["build_angle"] = True
@@ -88,49 +96,51 @@ def configure(env):
## Architecture
arch = ""
- if str(os.getenv('Platform')).lower() == "arm":
+ if str(os.getenv("Platform")).lower() == "arm":
print("Compiled program architecture will be an ARM executable. (forcing bits=32).")
arch = "arm"
env["bits"] = "32"
- env.Append(LINKFLAGS=['/MACHINE:ARM'])
- env.Append(LIBPATH=[vc_base_path + 'lib/store/arm'])
+ env.Append(LINKFLAGS=["/MACHINE:ARM"])
+ env.Append(LIBPATH=[vc_base_path + "lib/store/arm"])
angle_build_cmd += "ARM"
- env.Append(LIBPATH=[angle_root + '/winrt/10/src/Release_ARM/lib'])
+ env.Append(LIBPATH=[angle_root + "/winrt/10/src/Release_ARM/lib"])
else:
- compiler_version_str = methods.detect_visual_c_compiler_version(env['ENV'])
+ compiler_version_str = methods.detect_visual_c_compiler_version(env["ENV"])
- if(compiler_version_str == "amd64" or compiler_version_str == "x86_amd64"):
+ if compiler_version_str == "amd64" or compiler_version_str == "x86_amd64":
env["bits"] = "64"
print("Compiled program architecture will be a x64 executable (forcing bits=64).")
- elif (compiler_version_str == "x86" or compiler_version_str == "amd64_x86"):
+ elif compiler_version_str == "x86" or compiler_version_str == "amd64_x86":
env["bits"] = "32"
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.")
+ 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."
+ )
env["bits"] = "32"
- if (env["bits"] == "32"):
+ if env["bits"] == "32":
arch = "x86"
angle_build_cmd += "Win32"
- env.Append(LINKFLAGS=['/MACHINE:X86'])
- env.Append(LIBPATH=[vc_base_path + 'lib/store'])
- env.Append(LIBPATH=[angle_root + '/winrt/10/src/Release_Win32/lib'])
+ env.Append(LINKFLAGS=["/MACHINE:X86"])
+ env.Append(LIBPATH=[vc_base_path + "lib/store"])
+ env.Append(LIBPATH=[angle_root + "/winrt/10/src/Release_Win32/lib"])
else:
arch = "x64"
angle_build_cmd += "x64"
- env.Append(LINKFLAGS=['/MACHINE:X64'])
- env.Append(LIBPATH=[os.environ['VCINSTALLDIR'] + 'lib/store/amd64'])
- env.Append(LIBPATH=[angle_root + '/winrt/10/src/Release_x64/lib'])
+ env.Append(LINKFLAGS=["/MACHINE:X64"])
+ env.Append(LIBPATH=[os.environ["VCINSTALLDIR"] + "lib/store/amd64"])
+ env.Append(LIBPATH=[angle_root + "/winrt/10/src/Release_x64/lib"])
env["PROGSUFFIX"] = "." + arch + env["PROGSUFFIX"]
env["OBJSUFFIX"] = "." + arch + env["OBJSUFFIX"]
@@ -138,39 +148,61 @@ def configure(env):
## Compile flags
- env.Prepend(CPPPATH=['#platform/uwp', '#drivers/windows'])
- env.Append(CPPDEFINES=['UWP_ENABLED', 'WINDOWS_ENABLED', 'TYPED_METHOD_BIND'])
- env.Append(CPPDEFINES=['GLES_ENABLED', 'GL_GLEXT_PROTOTYPES', 'EGL_EGLEXT_PROTOTYPES', 'ANGLE_ENABLED'])
- winver = "0x0602" # Windows 8 is the minimum target for UWP build
- env.Append(CPPDEFINES=[('WINVER', winver), ('_WIN32_WINNT', winver), 'WIN32'])
-
- env.Append(CPPDEFINES=['__WRL_NO_DEFAULT_LIB__', ('PNG_ABORT', 'abort')])
-
- env.Append(CPPFLAGS=['/AI', vc_base_path + 'lib/store/references'])
- 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())
- env.Append(CPPDEFINES=['_UNICODE', 'UNICODE', ('WINAPI_FAMILY', 'WINAPI_FAMILY_APP')])
- env.Append(CXXFLAGS=['/ZW'])
- env.Append(CCFLAGS=['/AI', vc_base_path + '\\vcpackages', '/AI', os.environ['WINDOWSSDKDIR'] + '\\References\\CommonConfiguration\\Neutral'])
+ env.Prepend(CPPPATH=["#platform/uwp", "#drivers/windows"])
+ env.Append(CPPDEFINES=["UWP_ENABLED", "WINDOWS_ENABLED", "TYPED_METHOD_BIND"])
+ env.Append(CPPDEFINES=["GLES_ENABLED", "GL_GLEXT_PROTOTYPES", "EGL_EGLEXT_PROTOTYPES", "ANGLE_ENABLED"])
+ winver = "0x0602" # Windows 8 is the minimum target for UWP build
+ env.Append(CPPDEFINES=[("WINVER", winver), ("_WIN32_WINNT", winver), "WIN32"])
+
+ env.Append(CPPDEFINES=["__WRL_NO_DEFAULT_LIB__", ("PNG_ABORT", "abort")])
+
+ env.Append(CPPFLAGS=["/AI", vc_base_path + "lib/store/references"])
+ 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()
+ )
+ env.Append(CPPDEFINES=["_UNICODE", "UNICODE", ("WINAPI_FAMILY", "WINAPI_FAMILY_APP")])
+ env.Append(CXXFLAGS=["/ZW"])
+ env.Append(
+ CCFLAGS=[
+ "/AI",
+ vc_base_path + "\\vcpackages",
+ "/AI",
+ os.environ["WINDOWSSDKDIR"] + "\\References\\CommonConfiguration\\Neutral",
+ ]
+ )
## Link flags
- env.Append(LINKFLAGS=['/MANIFEST:NO', '/NXCOMPAT', '/DYNAMICBASE', '/WINMD', '/APPCONTAINER', '/ERRORREPORT:PROMPT', '/NOLOGO', '/TLBID:1', '/NODEFAULTLIB:"kernel32.lib"', '/NODEFAULTLIB:"ole32.lib"'])
+ env.Append(
+ LINKFLAGS=[
+ "/MANIFEST:NO",
+ "/NXCOMPAT",
+ "/DYNAMICBASE",
+ "/WINMD",
+ "/APPCONTAINER",
+ "/ERRORREPORT:PROMPT",
+ "/NOLOGO",
+ "/TLBID:1",
+ '/NODEFAULTLIB:"kernel32.lib"',
+ '/NODEFAULTLIB:"ole32.lib"',
+ ]
+ )
LIBS = [
- 'WindowsApp',
- 'mincore',
- 'ws2_32',
- 'libANGLE',
- 'libEGL',
- 'libGLESv2',
- 'bcrypt',
+ "WindowsApp",
+ "mincore",
+ "ws2_32",
+ "libANGLE",
+ "libEGL",
+ "libGLESv2",
+ "bcrypt",
]
env.Append(LINKFLAGS=[p + ".lib" for p in LIBS])
# Incremental linking fix
- env['BUILDERS']['ProgramOriginal'] = env['BUILDERS']['Program']
- env['BUILDERS']['Program'] = methods.precious_program
+ env["BUILDERS"]["ProgramOriginal"] = env["BUILDERS"]["Program"]
+ env["BUILDERS"]["Program"] = methods.precious_program
- env.Append(BUILDERS={'ANGLE': env.Builder(action=angle_build_cmd)})
+ env.Append(BUILDERS={"ANGLE": env.Builder(action=angle_build_cmd)})
diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp
index 533293387d..db42908f89 100644
--- a/platform/uwp/export/export.cpp
+++ b/platform/uwp/export/export.cpp
@@ -54,7 +54,7 @@ static const char *uwp_capabilities[] = {
"internetClient",
"internetClientServer",
"privateNetworkClientServer",
- NULL
+ nullptr
};
static const char *uwp_uap_capabilities[] = {
"appointments",
@@ -71,7 +71,7 @@ static const char *uwp_uap_capabilities[] = {
"userAccountInformation",
"videosLibrary",
"voipCall",
- NULL
+ nullptr
};
static const char *uwp_device_capabilities[] = {
"bluetooth",
@@ -79,11 +79,10 @@ static const char *uwp_device_capabilities[] = {
"microphone",
"proximity",
"webcam",
- NULL
+ nullptr
};
class AppxPackager {
-
enum {
FILE_HEADER_MAGIC = 0x04034b50,
DATA_DESCRIPTOR_MAGIC = 0x08074b50,
@@ -107,29 +106,21 @@ class AppxPackager {
};
struct BlockHash {
-
String base64_hash;
size_t compressed_size;
};
struct FileMeta {
-
String name;
- int lfh_size;
- bool compressed;
- size_t compressed_size;
- size_t uncompressed_size;
+ int lfh_size = 0;
+ bool compressed = false;
+ size_t compressed_size = 0;
+ size_t uncompressed_size = 0;
Vector<BlockHash> hashes;
- uLong file_crc32;
- ZPOS64_T zip_offset;
-
- FileMeta() :
- lfh_size(0),
- compressed(false),
- compressed_size(0),
- uncompressed_size(0),
- file_crc32(0),
- zip_offset(0) {}
+ uLong file_crc32 = 0;
+ ZPOS64_T zip_offset = 0;
+
+ FileMeta() {}
};
String progress_task;
@@ -195,7 +186,6 @@ public:
///////////////////////////////////////////////////////////////////////////
String AppxPackager::hash_block(const uint8_t *p_block_data, size_t p_block_len) {
-
unsigned char hash[32];
char base64[45];
@@ -208,24 +198,22 @@ String AppxPackager::hash_block(const uint8_t *p_block_data, size_t p_block_len)
}
void AppxPackager::make_block_map(const String &p_path) {
-
FileAccess *tmp_file = FileAccess::open(p_path, FileAccess::WRITE);
tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
tmp_file->store_string("<BlockMap xmlns=\"http://schemas.microsoft.com/appx/2010/blockmap\" HashMethod=\"http://www.w3.org/2001/04/xmlenc#sha256\">");
for (int i = 0; i < file_metadata.size(); i++) {
-
FileMeta file = file_metadata[i];
tmp_file->store_string(
"<File Name=\"" + file.name.replace("/", "\\") + "\" Size=\"" + itos(file.uncompressed_size) + "\" LfhSize=\"" + itos(file.lfh_size) + "\">");
for (int j = 0; j < file.hashes.size(); j++) {
-
tmp_file->store_string("<Block Hash=\"" + file.hashes[j].base64_hash + "\" ");
- if (file.compressed)
+ if (file.compressed) {
tmp_file->store_string("Size=\"" + itos(file.hashes[j].compressed_size) + "\" ");
+ }
tmp_file->store_string("/>");
}
@@ -239,21 +227,20 @@ void AppxPackager::make_block_map(const String &p_path) {
}
String AppxPackager::content_type(String p_extension) {
-
- if (p_extension == "png")
+ if (p_extension == "png") {
return "image/png";
- else if (p_extension == "jpg")
+ } else if (p_extension == "jpg") {
return "image/jpg";
- else if (p_extension == "xml")
+ } else if (p_extension == "xml") {
return "application/xml";
- else if (p_extension == "exe" || p_extension == "dll")
+ } else if (p_extension == "exe" || p_extension == "dll") {
return "application/x-msdownload";
- else
+ } else {
return "application/octet-stream";
+ }
}
void AppxPackager::make_content_types(const String &p_path) {
-
FileAccess *tmp_file = FileAccess::open(p_path, FileAccess::WRITE);
tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
@@ -262,10 +249,11 @@ 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();
- if (types.has(ext)) continue;
+ if (types.has(ext)) {
+ continue;
+ }
types[ext] = content_type(ext);
@@ -288,7 +276,6 @@ void AppxPackager::make_content_types(const String &p_path) {
}
Vector<uint8_t> AppxPackager::make_file_header(FileMeta p_file_meta) {
-
Vector<uint8_t> buf;
buf.resize(BASE_FILE_HEADER_SIZE + p_file_meta.name.length());
@@ -331,7 +318,6 @@ Vector<uint8_t> AppxPackager::make_file_header(FileMeta p_file_meta) {
}
void AppxPackager::store_central_dir_header(const FileMeta &p_file, bool p_do_hash) {
-
Vector<uint8_t> &buf = central_dir_data;
int offs = buf.size();
buf.resize(buf.size() + BASE_CENTRAL_DIR_SIZE + p_file.name.length());
@@ -383,7 +369,6 @@ void AppxPackager::store_central_dir_header(const FileMeta &p_file, bool p_do_ha
}
Vector<uint8_t> AppxPackager::make_end_of_central_record() {
-
Vector<uint8_t> buf;
buf.resize(ZIP64_END_OF_CENTRAL_DIR_SIZE + 12 + END_OF_CENTRAL_DIR_SIZE); // Size plus magic
@@ -453,14 +438,12 @@ Vector<uint8_t> AppxPackager::make_end_of_central_record() {
}
void AppxPackager::init(FileAccess *p_fa) {
-
package = p_fa;
central_dir_offset = 0;
end_of_central_dir_offset = 0;
}
Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress) {
-
if (p_file_no >= 1 && p_total_files >= 1) {
if (EditorNode::progress_task_step(progress_task, "File: " + p_file_name, (p_file_no * 100) / p_total_files)) {
return ERR_SKIP;
@@ -478,13 +461,12 @@ Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t
// Data for compression
z_stream strm;
- FileAccess *strm_f = NULL;
+ FileAccess *strm_f = nullptr;
Vector<uint8_t> strm_in;
strm_in.resize(BLOCK_SIZE);
Vector<uint8_t> strm_out;
if (p_compress) {
-
strm.zalloc = zipio_alloc;
strm.zfree = zipio_free;
strm.opaque = &strm_f;
@@ -497,7 +479,6 @@ Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t
int step = 0;
while (p_len - step > 0) {
-
size_t block_size = (p_len - step) > BLOCK_SIZE ? (size_t)BLOCK_SIZE : (p_len - step);
for (uint64_t i = 0; i < block_size; i++) {
@@ -508,7 +489,6 @@ Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t
bh.base64_hash = hash_block(strm_in.ptr(), block_size);
if (p_compress) {
-
strm.avail_in = block_size;
strm.avail_out = strm_out.size();
strm.next_in = (uint8_t *)strm_in.ptr();
@@ -524,15 +504,17 @@ Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t
//package->store_buffer(strm_out.ptr(), strm.total_out - total_out_before);
int start = file_buffer.size();
file_buffer.resize(file_buffer.size() + bh.compressed_size);
- for (uint64_t i = 0; i < bh.compressed_size; i++)
+ for (uint64_t i = 0; i < bh.compressed_size; i++) {
file_buffer.write[start + i] = strm_out[i];
+ }
} else {
bh.compressed_size = block_size;
//package->store_buffer(strm_in.ptr(), block_size);
int start = file_buffer.size();
file_buffer.resize(file_buffer.size() + block_size);
- for (uint64_t i = 0; i < bh.compressed_size; i++)
+ for (uint64_t i = 0; i < bh.compressed_size; i++) {
file_buffer.write[start + i] = strm_in[i];
+ }
}
meta.hashes.push_back(bh);
@@ -541,7 +523,6 @@ Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t
}
if (p_compress) {
-
strm.avail_in = 0;
strm.avail_out = strm_out.size();
strm.next_in = (uint8_t *)strm_in.ptr();
@@ -554,14 +535,14 @@ Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t
//package->store_buffer(strm_out.ptr(), strm.total_out - total_out_before);
int start = file_buffer.size();
file_buffer.resize(file_buffer.size() + (strm.total_out - total_out_before));
- for (uint64_t i = 0; i < (strm.total_out - total_out_before); i++)
+ for (uint64_t i = 0; i < (strm.total_out - total_out_before); i++) {
file_buffer.write[start + i] = strm_out[i];
+ }
deflateEnd(&strm);
meta.compressed_size = strm.total_out;
} else {
-
meta.compressed_size = p_len;
}
@@ -584,7 +565,6 @@ Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t
}
void AppxPackager::finish() {
-
// Create and add block map file
EditorNode::progress_task_step("export", "Creating block map...", 4);
@@ -641,7 +621,7 @@ void AppxPackager::finish() {
package->close();
memdelete(package);
- package = NULL;
+ package = nullptr;
}
AppxPackager::AppxPackager() {}
@@ -651,7 +631,6 @@ AppxPackager::~AppxPackager() {}
////////////////////////////////////////////////////////////////////
class EditorExportPlatformUWP : public EditorExportPlatform {
-
GDCLASS(EditorExportPlatformUWP, EditorExportPlatform);
Ref<ImageTexture> logo;
@@ -663,19 +642,24 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
};
bool _valid_resource_name(const String &p_name) const {
-
- if (p_name.empty()) return false;
- if (p_name.ends_with(".")) return false;
+ if (p_name.empty()) {
+ return false;
+ }
+ if (p_name.ends_with(".")) {
+ return false;
+ }
static const char *invalid_names[] = {
"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7",
"COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
- NULL
+ nullptr
};
const char **t = invalid_names;
while (*t) {
- if (p_name == *t) return false;
+ if (p_name == *t) {
+ return false;
+ }
t++;
}
@@ -683,22 +667,33 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
}
bool _valid_guid(const String &p_guid) const {
-
Vector<String> parts = p_guid.split("-");
- if (parts.size() != 5) return false;
- if (parts[0].length() != 8) return false;
- for (int i = 1; i < 4; i++)
- if (parts[i].length() != 4) return false;
- if (parts[4].length() != 12) return false;
+ if (parts.size() != 5) {
+ return false;
+ }
+ if (parts[0].length() != 8) {
+ return false;
+ }
+ for (int i = 1; i < 4; i++) {
+ if (parts[i].length() != 4) {
+ return false;
+ }
+ }
+ if (parts[4].length() != 12) {
+ return false;
+ }
return true;
}
bool _valid_bgcolor(const String &p_color) const {
-
- if (p_color.empty()) return true;
- if (p_color.begins_with("#") && p_color.is_valid_html_color()) return true;
+ if (p_color.empty()) {
+ return true;
+ }
+ if (p_color.begins_with("#") && p_color.is_valid_html_color()) {
+ return true;
+ }
// Colors from https://msdn.microsoft.com/en-us/library/windows/apps/dn934817.aspx
static const char *valid_colors[] = {
@@ -726,21 +721,22 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
"snow", "springGreen", "steelBlue", "tan", "teal", "thistle",
"tomato", "transparent", "turquoise", "violet", "wheat", "white",
"whiteSmoke", "yellow", "yellowGreen",
- NULL
+ nullptr
};
const char **color = valid_colors;
while (*color) {
- if (p_color == *color) return true;
+ if (p_color == *color) {
+ return true;
+ }
color++;
}
return false;
}
- bool _valid_image(const StreamTexture *p_image, int p_width, int p_height) const {
-
+ bool _valid_image(const StreamTexture2D *p_image, int p_width, int p_height) const {
if (!p_image) {
return false;
}
@@ -752,21 +748,21 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
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())
+ if (w == p_image->get_width()) {
valid_w = true;
- if (h == p_image->get_height())
+ }
+ if (h == p_image->get_height()) {
valid_h = true;
+ }
}
return valid_w && valid_h;
}
Vector<uint8_t> _fix_manifest(const Ref<EditorExportPreset> &p_preset, const Vector<uint8_t> &p_template, bool p_give_internet) const {
-
String result = String::utf8((const char *)p_template.ptr(), p_template.size());
result = result.replace("$godot_version$", VERSION_FULL_NAME);
@@ -867,43 +863,44 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
Vector<uint8_t> r_ret;
r_ret.resize(result.length());
- for (int i = 0; i < result.length(); i++)
+ for (int i = 0; i < result.length(); i++) {
r_ret.write[i] = result.utf8().get(i);
+ }
return r_ret;
}
Vector<uint8_t> _get_image_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
-
Vector<uint8_t> data;
- StreamTexture *image = NULL;
+ StreamTexture2D *image = nullptr;
if (p_path.find("StoreLogo") != -1) {
- image = p_preset->get("images/store_logo").is_zero() ? NULL : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/store_logo")));
+ image = p_preset->get("images/store_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/store_logo")));
} else if (p_path.find("Square44x44Logo") != -1) {
- image = p_preset->get("images/square44x44_logo").is_zero() ? NULL : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/square44x44_logo")));
+ image = p_preset->get("images/square44x44_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square44x44_logo")));
} else if (p_path.find("Square71x71Logo") != -1) {
- image = p_preset->get("images/square71x71_logo").is_zero() ? NULL : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/square71x71_logo")));
+ image = p_preset->get("images/square71x71_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square71x71_logo")));
} else if (p_path.find("Square150x150Logo") != -1) {
- image = p_preset->get("images/square150x150_logo").is_zero() ? NULL : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/square150x150_logo")));
+ image = p_preset->get("images/square150x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square150x150_logo")));
} else if (p_path.find("Square310x310Logo") != -1) {
- image = p_preset->get("images/square310x310_logo").is_zero() ? NULL : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/square310x310_logo")));
+ image = p_preset->get("images/square310x310_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square310x310_logo")));
} else if (p_path.find("Wide310x150Logo") != -1) {
- image = p_preset->get("images/wide310x150_logo").is_zero() ? NULL : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/wide310x150_logo")));
+ image = p_preset->get("images/wide310x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/wide310x150_logo")));
} else if (p_path.find("SplashScreen") != -1) {
- image = p_preset->get("images/splash_screen").is_zero() ? NULL : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/splash_screen")));
+ image = p_preset->get("images/splash_screen").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/splash_screen")));
} else {
ERR_PRINT("Unable to load logo");
}
- if (!image) return data;
+ if (!image) {
+ return data;
+ }
String tmp_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("uwp_tmp_logo.png");
Error err = image->get_data()->save_png(tmp_path);
if (err != OK) {
-
String err_string = "Couldn't save temp logo file.";
EditorNode::add_io_error(err_string);
@@ -913,7 +910,6 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
FileAccess *f = FileAccess::open(tmp_path, FileAccess::READ, &err);
if (err != OK) {
-
String err_string = "Couldn't open temp logo file.";
// Cleanup generated file.
DirAccess::remove_file_or_error(tmp_path);
@@ -932,17 +928,16 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
}
static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) {
-
/* TODO: This was copied verbatim from Android export. It should be
- * refactored to the parent class and also be used for .zip export.
- */
+ * refactored to the parent class and also be used for .zip export.
+ */
/*
- * By not compressing files with little or not benefit in doing so,
- * a performance gain is expected at runtime. Moreover, if the APK is
- * zip-aligned, assets stored as they are can be efficiently read by
- * Android by memory-mapping them.
- */
+ * By not compressing files with little or not benefit in doing so,
+ * a performance gain is expected at runtime. Moreover, if the APK is
+ * zip-aligned, assets stored as they are can be efficiently read by
+ * Android by memory-mapping them.
+ */
// -- Unconditional uncompress to mimic AAPT plus some other
@@ -961,7 +956,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
".scn", // Binary scenes are usually already compressed
".stex", // Streamable textures are usually already compressed
// Trailer for easier processing
- NULL
+ nullptr
};
for (const char **ext = unconditional_compress_ext; *ext; ++ext) {
@@ -983,7 +978,6 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
}
static Error save_appx_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
-
AppxPackager *packager = (AppxPackager *)p_userdata;
String dst_path = p_path.replace_first("res://", "game/");
@@ -1054,13 +1048,13 @@ public:
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait_flipped"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "images/background_color"), "transparent"));
- r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/store_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant()));
- r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square44x44_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant()));
- r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square71x71_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant()));
- r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square150x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant()));
- r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square310x310_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant()));
- r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/wide310x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant()));
- r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/splash_screen", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant()));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/store_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant()));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square44x44_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant()));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square71x71_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant()));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square150x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant()));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square310x310_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant()));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/wide310x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant()));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/splash_screen", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant()));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square150x150"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_wide310x150"), false));
@@ -1090,7 +1084,6 @@ public:
}
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
-
String err;
bool valid = false;
@@ -1161,37 +1154,37 @@ public:
err += TTR("Invalid background color.") + "\n";
}
- if (!p_preset->get("images/store_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture>((Object *)p_preset->get("images/store_logo"))), 50, 50)) {
+ if (!p_preset->get("images/store_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/store_logo"))), 50, 50)) {
valid = false;
err += TTR("Invalid Store Logo image dimensions (should be 50x50).") + "\n";
}
- if (!p_preset->get("images/square44x44_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture>((Object *)p_preset->get("images/square44x44_logo"))), 44, 44)) {
+ if (!p_preset->get("images/square44x44_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square44x44_logo"))), 44, 44)) {
valid = false;
err += TTR("Invalid square 44x44 logo image dimensions (should be 44x44).") + "\n";
}
- if (!p_preset->get("images/square71x71_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture>((Object *)p_preset->get("images/square71x71_logo"))), 71, 71)) {
+ if (!p_preset->get("images/square71x71_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square71x71_logo"))), 71, 71)) {
valid = false;
err += TTR("Invalid square 71x71 logo image dimensions (should be 71x71).") + "\n";
}
- if (!p_preset->get("images/square150x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture>((Object *)p_preset->get("images/square150x150_logo"))), 150, 150)) {
+ if (!p_preset->get("images/square150x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square150x150_logo"))), 150, 150)) {
valid = false;
err += TTR("Invalid square 150x150 logo image dimensions (should be 150x150).") + "\n";
}
- if (!p_preset->get("images/square310x310_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture>((Object *)p_preset->get("images/square310x310_logo"))), 310, 310)) {
+ if (!p_preset->get("images/square310x310_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square310x310_logo"))), 310, 310)) {
valid = false;
err += TTR("Invalid square 310x310 logo image dimensions (should be 310x310).") + "\n";
}
- if (!p_preset->get("images/wide310x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture>((Object *)p_preset->get("images/wide310x150_logo"))), 310, 150)) {
+ if (!p_preset->get("images/wide310x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/wide310x150_logo"))), 310, 150)) {
valid = false;
err += TTR("Invalid wide 310x150 logo image dimensions (should be 310x150).") + "\n";
}
- if (!p_preset->get("images/splash_screen").is_zero() && !_valid_image((Object::cast_to<StreamTexture>((Object *)p_preset->get("images/splash_screen"))), 620, 300)) {
+ if (!p_preset->get("images/splash_screen").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/splash_screen"))), 620, 300)) {
valid = false;
err += TTR("Invalid splash screen image dimensions (should be 620x300).") + "\n";
}
@@ -1201,15 +1194,15 @@ public:
}
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) {
-
String src_appx;
EditorProgress ep("export", "Exporting for Windows Universal", 7, true);
- if (p_debug)
+ if (p_debug) {
src_appx = p_preset->get("custom_template/debug");
- else
+ } else {
src_appx = p_preset->get("custom_template/release");
+ }
src_appx = src_appx.strip_edges();
@@ -1251,7 +1244,7 @@ public:
AppxPackager packager;
packager.init(fa_pack);
- FileAccess *src_f = NULL;
+ FileAccess *src_f = nullptr;
zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
if (ep.step("Creating package...", 0)) {
@@ -1261,7 +1254,6 @@ public:
unzFile pkg = unzOpen2(src_appx.utf8().get_data(), &io);
if (!pkg) {
-
EditorNode::add_io_error("Could not find template appx to export:\n" + src_appx);
return ERR_FILE_NOT_FOUND;
}
@@ -1279,11 +1271,10 @@ public:
int template_file_no = 1;
while (ret == UNZ_OK) {
-
// get file name
unz_file_info info;
char fname[16834];
- ret = unzGetCurrentFileInfo(pkg, &info, fname, 16834, NULL, 0, NULL, 0);
+ ret = unzGetCurrentFileInfo(pkg, &info, fname, 16834, nullptr, 0, nullptr, 0);
String path = fname;
@@ -1297,11 +1288,12 @@ public:
bool do_read = true;
if (path.begins_with("Assets/")) {
-
path = path.replace(".scale-100", "");
data = _get_image_data(p_preset, path);
- if (data.size() > 0) do_read = false;
+ if (data.size() > 0) {
+ do_read = false;
+ }
}
//read
@@ -1313,7 +1305,6 @@ public:
}
if (path == "AppxManifest.xml") {
-
data = _fix_manifest(p_preset, data, p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG));
}
@@ -1356,7 +1347,6 @@ public:
encode_uint32(cl.size(), clf.ptrw());
for (int i = 0; i < cl.size(); i++) {
-
CharString txt = cl[i].utf8();
int base = clf.size();
clf.resize(base + 4 + txt.length());
@@ -1445,7 +1435,6 @@ public:
}
virtual void get_platform_features(List<String> *r_features) {
-
r_features->push_back("pc");
r_features->push_back("UWP");
}
@@ -1461,7 +1450,6 @@ public:
};
void register_uwp_exporter() {
-
#ifdef WINDOWS_ENABLED
EDITOR_DEF("export/uwp/signtool", "");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/uwp/signtool", PROPERTY_HINT_GLOBAL_FILE, "*.exe"));
diff --git a/platform/uwp/export/export.h b/platform/uwp/export/export.h
index ce03bc0aeb..1a1555d8ee 100644
--- a/platform/uwp/export/export.h
+++ b/platform/uwp/export/export.h
@@ -28,4 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef UWP_EXPORT_H
+#define UWP_EXPORT_H
+
void register_uwp_exporter();
+
+#endif // UWP_EXPORT_H
diff --git a/platform/uwp/joypad_uwp.cpp b/platform/uwp/joypad_uwp.cpp
index 90df6fe5d7..4fdfde9673 100644
--- a/platform/uwp/joypad_uwp.cpp
+++ b/platform/uwp/joypad_uwp.cpp
@@ -35,7 +35,6 @@ using namespace Windows::Gaming::Input;
using namespace Windows::Foundation;
void JoypadUWP::register_events() {
-
Gamepad::GamepadAdded +=
ref new EventHandler<Gamepad ^>(this, &JoypadUWP::OnGamepadAdded);
Gamepad::GamepadRemoved +=
@@ -43,32 +42,28 @@ void JoypadUWP::register_events() {
}
void JoypadUWP::process_controllers() {
-
for (int i = 0; i < MAX_CONTROLLERS; i++) {
-
ControllerDevice &joy = controllers[i];
- if (!joy.connected) break;
+ if (!joy.connected)
+ break;
switch (joy.type) {
-
case ControllerType::GAMEPAD_CONTROLLER: {
-
GamepadReading reading = ((Gamepad ^) joy.controller_reference)->GetCurrentReading();
int button_mask = (int)GamepadButtons::Menu;
for (int j = 0; j < 14; j++) {
-
input->joy_button(joy.id, j, (int)reading.Buttons & button_mask);
button_mask *= 2;
}
- input->joy_axis(joy.id, JOY_AXIS_0, axis_correct(reading.LeftThumbstickX));
- input->joy_axis(joy.id, JOY_AXIS_1, axis_correct(reading.LeftThumbstickY, true));
- input->joy_axis(joy.id, JOY_AXIS_2, axis_correct(reading.RightThumbstickX));
- input->joy_axis(joy.id, JOY_AXIS_3, axis_correct(reading.RightThumbstickY, true));
- input->joy_axis(joy.id, JOY_AXIS_4, axis_correct(reading.LeftTrigger, false, true));
- input->joy_axis(joy.id, JOY_AXIS_5, axis_correct(reading.RightTrigger, false, true));
+ input->joy_axis(joy.id, JOY_AXIS_LEFT_X, axis_correct(reading.LeftThumbstickX));
+ input->joy_axis(joy.id, JOY_AXIS_LEFT_Y, axis_correct(reading.LeftThumbstickY, true));
+ input->joy_axis(joy.id, JOY_AXIS_RIGHT_X, axis_correct(reading.RightThumbstickX));
+ input->joy_axis(joy.id, JOY_AXIS_RIGHT_Y, axis_correct(reading.RightThumbstickY, true));
+ input->joy_axis(joy.id, JOY_AXIS_TRIGGER_LEFT, axis_correct(reading.LeftTrigger, false, true));
+ input->joy_axis(joy.id, JOY_AXIS_TRIGGER_RIGHT, axis_correct(reading.RightTrigger, false, true));
uint64_t timestamp = input->get_joy_vibration_timestamp(joy.id);
if (timestamp > joy.ff_timestamp) {
@@ -92,24 +87,20 @@ void JoypadUWP::process_controllers() {
}
JoypadUWP::JoypadUWP() {
-
for (int i = 0; i < MAX_CONTROLLERS; i++)
controllers[i].id = i;
}
JoypadUWP::JoypadUWP(InputDefault *p_input) {
-
input = p_input;
JoypadUWP();
}
void JoypadUWP::OnGamepadAdded(Platform::Object ^ sender, Windows::Gaming::Input::Gamepad ^ value) {
-
short idx = -1;
for (int i = 0; i < MAX_CONTROLLERS; i++) {
-
if (!controllers[i].connected) {
idx = i;
break;
@@ -127,11 +118,9 @@ void JoypadUWP::OnGamepadAdded(Platform::Object ^ sender, Windows::Gaming::Input
}
void JoypadUWP::OnGamepadRemoved(Platform::Object ^ sender, Windows::Gaming::Input::Gamepad ^ value) {
-
short idx = -1;
for (int i = 0; i < MAX_CONTROLLERS; i++) {
-
if (controllers[i].controller_reference == value) {
idx = i;
break;
@@ -146,7 +135,6 @@ void JoypadUWP::OnGamepadRemoved(Platform::Object ^ sender, Windows::Gaming::Inp
}
InputDefault::JoyAxis JoypadUWP::axis_correct(double p_val, bool p_negate, bool p_trigger) const {
-
InputDefault::JoyAxis jx;
jx.min = p_trigger ? 0 : -1;
diff --git a/platform/uwp/joypad_uwp.h b/platform/uwp/joypad_uwp.h
index f2a721f3dd..13f246a438 100644
--- a/platform/uwp/joypad_uwp.h
+++ b/platform/uwp/joypad_uwp.h
@@ -31,10 +31,9 @@
#ifndef JOYPAD_UWP_H
#define JOYPAD_UWP_H
-#include "main/input_default.h"
+#include "core/input/input.h"
ref class JoypadUWP sealed {
-
/** clang-format breaks this, it does not understand this token. */
/* clang-format off */
internal:
@@ -57,7 +56,6 @@ private:
};
struct ControllerDevice {
-
Windows::Gaming::Input::IGameController ^ controller_reference;
int id;
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index 4ddb5463d0..7bd67d3726 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -45,8 +45,8 @@
#include "main/main.h"
#include "platform/windows/windows_terminal_logger.h"
#include "servers/audio_server.h"
-#include "servers/visual/visual_server_raster.h"
-#include "servers/visual/visual_server_wrap_mt.h"
+#include "servers/rendering/rendering_server_raster.h"
+#include "servers/rendering/rendering_server_wrap_mt.h"
#include "thread_uwp.h"
#include <ppltasks.h>
@@ -82,7 +82,6 @@ int OS_UWP::get_current_video_driver() const {
}
void OS_UWP::set_window_size(const Size2 p_size) {
-
Windows::Foundation::Size new_size;
new_size.Width = p_size.width;
new_size.Height = p_size.height;
@@ -90,14 +89,12 @@ void OS_UWP::set_window_size(const Size2 p_size) {
ApplicationView ^ view = ApplicationView::GetForCurrentView();
if (view->TryResizeView(new_size)) {
-
video_mode.width = p_size.width;
video_mode.height = p_size.height;
}
}
void OS_UWP::set_window_fullscreen(bool p_enabled) {
-
ApplicationView ^ view = ApplicationView::GetForCurrentView();
video_mode.fullscreen = view->IsFullScreenMode;
@@ -106,24 +103,21 @@ void OS_UWP::set_window_fullscreen(bool p_enabled) {
return;
if (p_enabled) {
-
video_mode.fullscreen = view->TryEnterFullScreenMode();
} else {
-
view->ExitFullScreenMode();
video_mode.fullscreen = false;
}
}
bool OS_UWP::is_window_fullscreen() const {
-
return ApplicationView::GetForCurrentView()->IsFullScreenMode;
}
void OS_UWP::set_keep_screen_on(bool p_enabled) {
-
- if (is_keep_screen_on() == p_enabled) return;
+ if (is_keep_screen_on() == p_enabled)
+ return;
if (p_enabled)
display_request->RequestActive();
@@ -134,7 +128,6 @@ void OS_UWP::set_keep_screen_on(bool p_enabled) {
}
void OS_UWP::initialize_core() {
-
last_button_state = 0;
//RedirectIOToConsole();
@@ -165,7 +158,6 @@ void OS_UWP::initialize_core() {
}
bool OS_UWP::can_draw() const {
-
return !minimized;
};
@@ -174,13 +166,11 @@ void OS_UWP::set_window(Windows::UI::Core::CoreWindow ^ p_window) {
}
void OS_UWP::screen_size_changed() {
-
gl_context->reset();
};
Error OS_UWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
-
- main_loop = NULL;
+ main_loop = nullptr;
outside = true;
// FIXME: Hardcoded for now, add Vulkan support.
@@ -193,7 +183,7 @@ Error OS_UWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
if (gl_context->initialize() != OK) {
memdelete(gl_context);
- gl_context = NULL;
+ gl_context = nullptr;
gl_initialization_error = true;
}
@@ -230,11 +220,9 @@ Error OS_UWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
if (p_desired.fullscreen != view->IsFullScreenMode) {
if (p_desired.fullscreen) {
-
vm.fullscreen = view->TryEnterFullScreenMode();
} else {
-
view->ExitFullScreenMode();
vm.fullscreen = false;
}
@@ -247,20 +235,19 @@ Error OS_UWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
view->PreferredLaunchViewSize = desired;
if (view->TryResizeView(desired)) {
-
vm.width = view->VisibleBounds.Width;
vm.height = view->VisibleBounds.Height;
}
set_video_mode(vm);
- visual_server = memnew(VisualServerRaster);
+ rendering_server = memnew(RenderingServerRaster);
// FIXME: Reimplement threaded rendering
if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
- visual_server = memnew(VisualServerWrapMT(visual_server, false));
+ rendering_server = memnew(RenderingServerWrapMT(rendering_server, false));
}
- visual_server->init();
+ rendering_server->init();
input = memnew(InputDefault);
@@ -308,7 +295,6 @@ 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()));
@@ -317,7 +303,6 @@ void OS_UWP::set_clipboard(const String &p_text) {
};
String OS_UWP::get_clipboard() const {
-
if (managed_object->clipboard != nullptr)
return managed_object->clipboard->Data();
else
@@ -325,32 +310,28 @@ String OS_UWP::get_clipboard() const {
};
void OS_UWP::input_event(const Ref<InputEvent> &p_event) {
-
input->parse_input_event(p_event);
};
void OS_UWP::delete_main_loop() {
-
if (main_loop)
memdelete(main_loop);
- main_loop = NULL;
+ main_loop = nullptr;
}
void OS_UWP::set_main_loop(MainLoop *p_main_loop) {
-
input->set_main_loop(p_main_loop);
main_loop = p_main_loop;
}
void OS_UWP::finalize() {
-
if (main_loop)
memdelete(main_loop);
- main_loop = NULL;
+ main_loop = nullptr;
- visual_server->finish();
- memdelete(visual_server);
+ rendering_server->finish();
+ memdelete(rendering_server);
#ifdef OPENGL_ENABLED
if (gl_context)
memdelete(gl_context);
@@ -362,12 +343,10 @@ void OS_UWP::finalize() {
}
void OS_UWP::finalize_core() {
-
NetSocketPosix::cleanup();
}
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());
@@ -383,21 +362,17 @@ void OS_UWP::alert(const String &p_alert, const String &p_title) {
}
void OS_UWP::ManagedType::alert_close(IUICommand ^ command) {
-
alert_close_handle = false;
}
void OS_UWP::ManagedType::on_clipboard_changed(Platform::Object ^ sender, Platform::Object ^ ev) {
-
update_clipboard();
}
void OS_UWP::ManagedType::update_clipboard() {
-
DataPackageView ^ data = Clipboard::GetContent();
if (data->Contains(StandardDataFormats::Text)) {
-
create_task(data->GetTextAsync()).then([this](Platform::String ^ clipboard_content) {
this->clipboard = clipboard_content;
});
@@ -405,7 +380,6 @@ void OS_UWP::ManagedType::update_clipboard() {
}
void OS_UWP::ManagedType::on_accelerometer_reading_changed(Accelerometer ^ sender, AccelerometerReadingChangedEventArgs ^ args) {
-
AccelerometerReading ^ reading = args->Reading;
os->input->set_accelerometer(Vector3(
@@ -415,7 +389,6 @@ void OS_UWP::ManagedType::on_accelerometer_reading_changed(Accelerometer ^ sende
}
void OS_UWP::ManagedType::on_magnetometer_reading_changed(Magnetometer ^ sender, MagnetometerReadingChangedEventArgs ^ args) {
-
MagnetometerReading ^ reading = args->Reading;
os->input->set_magnetometer(Vector3(
@@ -425,7 +398,6 @@ void OS_UWP::ManagedType::on_magnetometer_reading_changed(Magnetometer ^ sender,
}
void OS_UWP::ManagedType::on_gyroscope_reading_changed(Gyrometer ^ sender, GyrometerReadingChangedEventArgs ^ args) {
-
GyrometerReading ^ reading = args->Reading;
os->input->set_magnetometer(Vector3(
@@ -435,22 +407,17 @@ void OS_UWP::ManagedType::on_gyroscope_reading_changed(Gyrometer ^ sender, Gyrom
}
void OS_UWP::set_mouse_mode(MouseMode p_mode) {
-
if (p_mode == MouseMode::MOUSE_MODE_CAPTURED) {
-
CoreWindow::GetForCurrentThread()->SetPointerCapture();
} else {
-
CoreWindow::GetForCurrentThread()->ReleasePointerCapture();
}
if (p_mode == MouseMode::MOUSE_MODE_CAPTURED || p_mode == MouseMode::MOUSE_MODE_HIDDEN) {
-
CoreWindow::GetForCurrentThread()->PointerCursor = nullptr;
} else {
-
CoreWindow::GetForCurrentThread()->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0);
}
@@ -460,17 +427,14 @@ void OS_UWP::set_mouse_mode(MouseMode p_mode) {
}
OS_UWP::MouseMode OS_UWP::get_mouse_mode() const {
-
return mouse_mode;
}
Point2 OS_UWP::get_mouse_position() const {
-
return Point2(old_x, old_y);
}
int OS_UWP::get_mouse_button_state() const {
-
return last_button_state;
}
@@ -478,23 +442,21 @@ void OS_UWP::set_window_title(const String &p_title) {
}
void OS_UWP::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
-
video_mode = p_video_mode;
}
-OS::VideoMode OS_UWP::get_video_mode(int p_screen) const {
+OS::VideoMode OS_UWP::get_video_mode(int p_screen) const {
return video_mode;
}
+
void OS_UWP::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
}
String OS_UWP::get_name() const {
-
return "UWP";
}
OS::Date OS_UWP::get_date(bool utc) const {
-
SYSTEMTIME systemtime;
if (utc)
GetSystemTime(&systemtime);
@@ -509,8 +471,8 @@ OS::Date OS_UWP::get_date(bool utc) const {
date.dst = false;
return date;
}
-OS::Time OS_UWP::get_time(bool utc) const {
+OS::Time OS_UWP::get_time(bool utc) const {
SYSTEMTIME systemtime;
if (utc)
GetSystemTime(&systemtime);
@@ -544,7 +506,6 @@ OS::TimeZoneInfo OS_UWP::get_time_zone_info() const {
}
uint64_t OS_UWP::get_unix_time() const {
-
FILETIME ft;
SYSTEMTIME st;
GetSystemTime(&st);
@@ -566,14 +527,13 @@ uint64_t OS_UWP::get_unix_time() const {
};
void OS_UWP::delay_usec(uint32_t p_usec) const {
-
int msec = p_usec < 1000 ? 1 : p_usec / 1000;
// no Sleep()
WaitForSingleObjectEx(GetCurrentThread(), msec, false);
}
-uint64_t OS_UWP::get_ticks_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
@@ -587,15 +547,12 @@ uint64_t OS_UWP::get_ticks_usec() const {
}
void OS_UWP::process_events() {
-
joypad->process_controllers();
process_key_events();
}
void OS_UWP::process_key_events() {
-
for (int i = 0; i < key_event_pos; i++) {
-
KeyEvent &kev = key_event_buffer[i];
Ref<InputEventKey> key_event;
@@ -618,7 +575,6 @@ void OS_UWP::queue_key_event(KeyEvent &p_event) {
// This merges Char events with the previous Key event, so
// the unicode can be retrieved without sending duplicate events.
if (p_event.type == KeyEvent::MessageType::CHAR_EVENT_MESSAGE && key_event_pos > 0) {
-
KeyEvent &old = key_event_buffer[key_event_pos - 1];
ERR_FAIL_COND(old.type != KeyEvent::MessageType::KEY_EVENT_MESSAGE);
@@ -632,7 +588,6 @@ void OS_UWP::queue_key_event(KeyEvent &p_event) {
}
void OS_UWP::set_cursor_shape(CursorShape p_shape) {
-
ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
if (cursor_shape == p_shape)
@@ -664,7 +619,6 @@ void OS_UWP::set_cursor_shape(CursorShape p_shape) {
}
OS::CursorShape OS_UWP::get_cursor_shape() const {
-
return cursor_shape;
}
@@ -673,22 +627,18 @@ void OS_UWP::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c
}
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) {
-
return FAILED;
};
Error OS_UWP::kill(const ProcessID &p_pid) {
-
return FAILED;
};
Error OS_UWP::set_cwd(const String &p_cwd) {
-
return FAILED;
}
String OS_UWP::get_executable_path() const {
-
return "";
}
@@ -696,22 +646,18 @@ void OS_UWP::set_icon(const Ref<Image> &p_icon) {
}
bool OS_UWP::has_environment(const String &p_var) const {
-
return false;
};
String OS_UWP::get_environment(const String &p_var) const {
-
return "";
};
bool OS_UWP::set_environment(const String &p_var, const String &p_value) const {
-
return false;
}
String OS_UWP::get_stdin_string(bool p_block) {
-
return String();
}
@@ -719,12 +665,10 @@ void OS_UWP::move_window_to_foreground() {
}
Error OS_UWP::shell_open(String p_uri) {
-
return FAILED;
}
String OS_UWP::get_locale() const {
-
#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP // this should work on phone 8.1, but it doesn't
return "en";
#else
@@ -734,48 +678,40 @@ String OS_UWP::get_locale() const {
}
void OS_UWP::release_rendering_thread() {
-
gl_context->release_current();
}
void OS_UWP::make_rendering_thread() {
-
gl_context->make_current();
}
void OS_UWP::swap_buffers() {
-
gl_context->swap_buffers();
}
bool OS_UWP::has_touchscreen_ui_hint() const {
-
TouchCapabilities ^ tc = ref new TouchCapabilities();
return tc->TouchPresent != 0 || UIViewSettings::GetForCurrentView()->UserInteractionMode == UserInteractionMode::Touch;
}
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) {
-
+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) {
InputPane ^ pane = InputPane::GetForCurrentView();
pane->TryShow();
}
void OS_UWP::hide_virtual_keyboard() {
-
InputPane ^ pane = InputPane::GetForCurrentView();
pane->TryHide();
}
static String format_error_message(DWORD id) {
-
- LPWSTR messageBuffer = NULL;
+ LPWSTR messageBuffer = nullptr;
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL);
+ nullptr, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr);
String msg = "Error " + itos(id) + ": " + String(messageBuffer, size);
@@ -785,7 +721,6 @@ 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);
ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + full_path + ", error: " + format_error_message(GetLastError()) + ".");
@@ -812,7 +747,6 @@ Error OS_UWP::get_dynamic_library_symbol_handle(void *p_library_handle, const St
}
void OS_UWP::run() {
-
if (!main_loop)
return;
@@ -824,9 +758,9 @@ void OS_UWP::run() {
uint64_t frame = 0;
while (!force_quit) {
-
CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
- if (managed_object->alert_close_handle) continue;
+ if (managed_object->alert_close_handle)
+ continue;
process_events(); // get rid of pending events
if (Main::iteration())
break;
@@ -836,12 +770,10 @@ void OS_UWP::run() {
}
MainLoop *OS_UWP::get_main_loop() const {
-
return main_loop;
}
String OS_UWP::get_user_data_dir() const {
-
Windows::Storage::StorageFolder ^ data_folder = Windows::Storage::ApplicationData::Current->LocalFolder;
return String(data_folder->Path->Data()).replace("\\", "/");
@@ -852,7 +784,6 @@ bool OS_UWP::_check_internal_feature_support(const String &p_feature) {
}
OS_UWP::OS_UWP() {
-
key_event_pos = 0;
force_quit = false;
alt_mem = false;
@@ -869,14 +800,14 @@ OS_UWP::OS_UWP() {
stdo = fopen("stdout.txt", "wb");
#endif
- gl_context = NULL;
+ gl_context = nullptr;
display_request = ref new Windows::System::Display::DisplayRequest();
managed_object = ref new ManagedType;
managed_object->os = this;
- mouse_mode_changed = CreateEvent(NULL, TRUE, FALSE, L"os_mouse_mode_changed");
+ mouse_mode_changed = CreateEvent(nullptr, TRUE, FALSE, L"os_mouse_mode_changed");
AudioDriverManager::add_driver(&audio_driver);
diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h
index ac6e0f3dd5..95359c68b0 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -32,16 +32,15 @@
#define OS_UWP_H
#include "context_egl_uwp.h"
+#include "core/input/input.h"
#include "core/math/transform_2d.h"
-#include "core/os/input.h"
#include "core/os/os.h"
#include "core/ustring.h"
#include "drivers/xaudio2/audio_driver_xaudio2.h"
#include "joypad_uwp.h"
-#include "main/input_default.h"
#include "servers/audio_server.h"
-#include "servers/visual/rasterizer.h"
-#include "servers/visual_server.h"
+#include "servers/rendering/rasterizer.h"
+#include "servers/rendering_server.h"
#include <fcntl.h>
#include <io.h>
@@ -49,10 +48,8 @@
#include <windows.h>
class OS_UWP : public OS {
-
public:
struct KeyEvent {
-
enum MessageType {
KEY_EVENT_MESSAGE,
CHAR_EVENT_MESSAGE
@@ -89,7 +86,7 @@ private:
bool outside;
int old_x, old_y;
Point2i center;
- VisualServer *visual_server;
+ RenderingServer *rendering_server;
int pressrc;
ContextEGL_UWP *gl_context;
@@ -203,7 +200,7 @@ public:
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 = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL);
+ 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 bool has_environment(const String &p_var) const;
@@ -237,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);
+ 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 Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);
diff --git a/platform/uwp/thread_uwp.cpp b/platform/uwp/thread_uwp.cpp
index 9dc20a74e8..8e7bb144be 100644
--- a/platform/uwp/thread_uwp.cpp
+++ b/platform/uwp/thread_uwp.cpp
@@ -33,7 +33,6 @@
#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);
@@ -43,18 +42,15 @@ Thread *ThreadUWP::create_func_uwp(ThreadCreateCallback p_callback, void *p_user
};
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());
};
@@ -63,11 +59,3 @@ void ThreadUWP::make_default() {
get_thread_id_func = get_thread_id_func_uwp;
wait_to_finish_func = wait_to_finish_func_uwp;
};
-
-ThreadUWP::ThreadUWP(){
-
-};
-
-ThreadUWP::~ThreadUWP(){
-
-};
diff --git a/platform/uwp/thread_uwp.h b/platform/uwp/thread_uwp.h
index a2d367ae2f..9b2a2590a8 100644
--- a/platform/uwp/thread_uwp.h
+++ b/platform/uwp/thread_uwp.h
@@ -38,23 +38,22 @@
#include <thread>
class ThreadUWP : public Thread {
-
std::thread thread;
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);
- ThreadUWP();
+ ThreadUWP() {}
public:
virtual ID get_id() const;
static void make_default();
- ~ThreadUWP();
+ ~ThreadUWP() {}
};
-#endif
+#endif // UWP_ENABLED
-#endif
+#endif // THREAD_UWP_H
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 8e94c7b35d..daffe59f34 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-Import('env')
+Import("env")
import os
from platform_methods import run_in_subprocess
@@ -10,21 +10,22 @@ common_win = [
"godot_windows.cpp",
"crash_handler_windows.cpp",
"os_windows.cpp",
+ "display_server_windows.cpp",
"key_mapping_windows.cpp",
"joypad_windows.cpp",
"windows_terminal_logger.cpp",
"vulkan_context_win.cpp",
- "context_gl_windows.cpp"
+ "context_gl_windows.cpp",
]
-res_file = 'godot_res.rc'
+res_file = "godot_res.rc"
res_target = "godot_res" + env["OBJSUFFIX"]
res_obj = env.RES(res_target, res_file)
-prog = env.add_program('#bin/godot', common_win + res_obj, PROGSUFFIX=env["PROGSUFFIX"])
+prog = env.add_program("#bin/godot", common_win + res_obj, PROGSUFFIX=env["PROGSUFFIX"])
# Microsoft Visual Studio Project Generation
-if env['vsproj']:
+if env["vsproj"]:
env.vs_srcs = env.vs_srcs + ["platform/windows/" + res_file]
env.vs_srcs = env.vs_srcs + ["platform/windows/godot.natvis"]
for x in common_win:
diff --git a/platform/windows/context_gl_windows.cpp b/platform/windows/context_gl_windows.cpp
index ad62e3a306..1c32639a38 100644
--- a/platform/windows/context_gl_windows.cpp
+++ b/platform/windows/context_gl_windows.cpp
@@ -51,27 +51,22 @@
typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *);
void ContextGL_Windows::release_current() {
-
- wglMakeCurrent(hDC, NULL);
+ wglMakeCurrent(hDC, nullptr);
}
void ContextGL_Windows::make_current() {
-
wglMakeCurrent(hDC, hRC);
}
int ContextGL_Windows::get_window_width() {
-
return OS::get_singleton()->get_video_mode().width;
}
int ContextGL_Windows::get_window_height() {
-
return OS::get_singleton()->get_video_mode().height;
}
bool ContextGL_Windows::should_vsync_via_compositor() {
-
if (OS::get_singleton()->is_window_fullscreen() || !OS::get_singleton()->is_vsync_via_compositor_enabled()) {
return false;
}
@@ -88,7 +83,6 @@ bool ContextGL_Windows::should_vsync_via_compositor() {
}
void ContextGL_Windows::swap_buffers() {
-
SwapBuffers(hDC);
if (use_vsync) {
@@ -108,7 +102,6 @@ void ContextGL_Windows::swap_buffers() {
}
void ContextGL_Windows::set_use_vsync(bool p_use) {
-
vsync_via_compositor = p_use && should_vsync_via_compositor();
if (wglSwapIntervalEXT) {
@@ -120,14 +113,12 @@ void ContextGL_Windows::set_use_vsync(bool p_use) {
}
bool ContextGL_Windows::is_using_vsync() const {
-
return use_vsync;
}
#define _WGL_CONTEXT_DEBUG_BIT_ARB 0x0001
Error ContextGL_Windows::initialize() {
-
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
1,
@@ -175,7 +166,6 @@ Error ContextGL_Windows::initialize() {
wglMakeCurrent(hDC, hRC);
if (opengl_3_context) {
-
int attribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3, //we want a 3.3 context
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
@@ -185,10 +175,10 @@ Error ContextGL_Windows::initialize() {
0
}; //zero indicates the end of the array
- PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; //pointer to the method
+ PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = nullptr; //pointer to the method
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
- if (wglCreateContextAttribsARB == NULL) //OpenGL 3.0 is not supported
+ if (wglCreateContextAttribsARB == nullptr) //OpenGL 3.0 is not supported
{
wglDeleteContext(hRC);
return ERR_CANT_CREATE;
@@ -199,7 +189,7 @@ Error ContextGL_Windows::initialize() {
wglDeleteContext(hRC);
return ERR_CANT_CREATE; // Return false
}
- wglMakeCurrent(hDC, NULL);
+ wglMakeCurrent(hDC, nullptr);
wglDeleteContext(hRC);
hRC = new_hRC;
@@ -217,7 +207,6 @@ Error ContextGL_Windows::initialize() {
}
ContextGL_Windows::ContextGL_Windows(HWND hwnd, bool p_opengl_3_context) {
-
opengl_3_context = p_opengl_3_context;
hWnd = hwnd;
use_vsync = false;
diff --git a/platform/windows/context_gl_windows.h b/platform/windows/context_gl_windows.h
index 280c5a1e3c..046e3437ea 100644
--- a/platform/windows/context_gl_windows.h
+++ b/platform/windows/context_gl_windows.h
@@ -44,7 +44,6 @@ typedef bool(APIENTRY *PFNWGLSWAPINTERVALEXTPROC)(int interval);
typedef int(APIENTRY *PFNWGLGETSWAPINTERVALEXTPROC)(void);
class ContextGL_Windows {
-
HDC hDC;
HGLRC hRC;
unsigned int pixel_format;
diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp
index 6145751e00..996d9722f5 100644
--- a/platform/windows/crash_handler_windows.cpp
+++ b/platform/windows/crash_handler_windows.cpp
@@ -38,11 +38,13 @@
// Backtrace code code based on: https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app
-#include <psapi.h>
#include <algorithm>
#include <iterator>
+#include <string>
#include <vector>
+#include <psapi.h>
+
#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "dbghelp.lib")
@@ -121,7 +123,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
DWORD cbNeeded;
std::vector<HMODULE> module_handles(1);
- if (OS::get_singleton() == NULL || OS::get_singleton()->is_disable_crash_handler() || IsDebuggerPresent()) {
+ if (OS::get_singleton() == nullptr || OS::get_singleton()->is_disable_crash_handler() || IsDebuggerPresent()) {
return EXCEPTION_CONTINUE_SEARCH;
}
@@ -131,7 +133,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
// Load the symbols:
- if (!SymInitialize(process, NULL, false))
+ if (!SymInitialize(process, nullptr, false))
return EXCEPTION_CONTINUE_SEARCH;
SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
@@ -193,7 +195,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
n++;
}
- if (!StackWalk64(image_type, process, hThread, &frame, context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
+ if (!StackWalk64(image_type, process, hThread, &frame, context, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr))
break;
} while (frame.AddrReturn.Offset != 0 && n < 256);
diff --git a/platform/windows/crash_handler_windows.h b/platform/windows/crash_handler_windows.h
index adc548073c..66a4cac296 100644
--- a/platform/windows/crash_handler_windows.h
+++ b/platform/windows/crash_handler_windows.h
@@ -41,7 +41,6 @@ extern DWORD CrashHandlerException(EXCEPTION_POINTERS *ep);
#endif
class CrashHandler {
-
bool disabled;
public:
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index cc859c0339..9f79e92dcb 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -14,10 +14,10 @@ def get_name():
def can_build():
- if (os.name == "nt"):
+ if os.name == "nt":
# Building natively on Windows
# If VCINSTALLDIR is set in the OS environ, use traditional Godot logic to set up MSVC
- if (os.getenv("VCINSTALLDIR")): # MSVC, manual setup
+ if os.getenv("VCINSTALLDIR"): # MSVC, manual setup
return True
# Otherwise, let SCons find MSVC if installed, or else Mingw.
@@ -26,18 +26,18 @@ def can_build():
# null compiler.
return True
- if (os.name == "posix"):
+ if os.name == "posix":
# Cross-compiling with MinGW-w64 (old MinGW32 is not supported)
mingw32 = "i686-w64-mingw32-"
mingw64 = "x86_64-w64-mingw32-"
- if (os.getenv("MINGW32_PREFIX")):
+ if os.getenv("MINGW32_PREFIX"):
mingw32 = os.getenv("MINGW32_PREFIX")
- if (os.getenv("MINGW64_PREFIX")):
+ if os.getenv("MINGW64_PREFIX"):
mingw64 = os.getenv("MINGW64_PREFIX")
test = "gcc --version > /dev/null 2>&1"
- if (os.system(mingw64 + test) == 0 or os.system(mingw32 + test) == 0):
+ if os.system(mingw64 + test) == 0 or os.system(mingw32 + test) == 0:
return True
return False
@@ -48,47 +48,47 @@ def get_opts():
mingw32 = ""
mingw64 = ""
- if (os.name == "posix"):
+ if os.name == "posix":
mingw32 = "i686-w64-mingw32-"
mingw64 = "x86_64-w64-mingw32-"
- if (os.getenv("MINGW32_PREFIX")):
+ if os.getenv("MINGW32_PREFIX"):
mingw32 = os.getenv("MINGW32_PREFIX")
- if (os.getenv("MINGW64_PREFIX")):
+ if os.getenv("MINGW64_PREFIX"):
mingw64 = os.getenv("MINGW64_PREFIX")
return [
- ('mingw_prefix_32', 'MinGW prefix (Win32)', mingw32),
- ('mingw_prefix_64', 'MinGW prefix (Win64)', mingw64),
+ ("mingw_prefix_32", "MinGW prefix (Win32)", mingw32),
+ ("mingw_prefix_64", "MinGW prefix (Win64)", mingw64),
# Targeted Windows version: 7 (and later), minimum supported version
# 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('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_llvm', 'Use the LLVM compiler', False),
- BoolVariable('use_thinlto', 'Use ThinLTO', False),
+ ("target_win_version", "Targeted Windows version, >= 0x0601 (Windows 7)", "0x0601"),
+ EnumVariable("debug_symbols", "Add debugging symbols to release builds", "yes", ("yes", "no", "full")),
+ 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_llvm", "Use the LLVM compiler", False),
+ BoolVariable("use_thinlto", "Use ThinLTO", False),
]
def get_flags():
- return [
- ]
+ return []
def build_res_file(target, source, env):
- if (env["bits"] == "32"):
- cmdbase = env['mingw_prefix_32']
+ if env["bits"] == "32":
+ cmdbase = env["mingw_prefix_32"]
else:
- cmdbase = env['mingw_prefix_64']
- cmdbase = cmdbase + 'windres --include-dir . '
+ cmdbase = env["mingw_prefix_64"]
+ cmdbase = cmdbase + "windres --include-dir . "
import subprocess
+
for x in range(len(source)):
- cmd = cmdbase + '-i ' + str(source[x]) + ' -o ' + str(target[x])
+ cmd = cmdbase + "-i " + str(source[x]) + " -o " + str(target[x])
try:
out = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE).communicate()
if len(out[1]):
@@ -100,12 +100,14 @@ def build_res_file(target, source, env):
def setup_msvc_manual(env):
"""Set up env to use MSVC manually, using VCINSTALLDIR"""
- if (env["bits"] != "default"):
- print("""
+ if env["bits"] != "default":
+ print(
+ """
Bits argument is not supported for MSVC compilation. Architecture depends on the Native/Cross Compile Tools Prompt/Developer Console
(or Visual Studio settings) that is being used to run SCons. As a consequence, bits argument is disabled. Run scons again without bits
argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler will be executed and inform you.
- """)
+ """
+ )
raise SCons.Errors.UserError("Bits argument should not be used when using VCINSTALLDIR")
# Force bits arg
@@ -114,18 +116,21 @@ def setup_msvc_manual(env):
env["x86_libtheora_opt_vc"] = True
# find compiler manually
- compiler_version_str = methods.detect_visual_c_compiler_version(env['ENV'])
+ compiler_version_str = methods.detect_visual_c_compiler_version(env["ENV"])
print("Found MSVC compiler: " + compiler_version_str)
# If building for 64bit architecture, disable assembly optimisations for 32 bit builds (theora as of writing)... vc compiler for 64bit can not compile _asm
- if(compiler_version_str == "amd64" or compiler_version_str == "x86_amd64"):
+ if compiler_version_str == "amd64" or compiler_version_str == "x86_amd64":
env["bits"] = "64"
env["x86_libtheora_opt_vc"] = False
print("Compiled program architecture will be a 64 bit executable (forcing bits=64).")
- elif (compiler_version_str == "x86" or compiler_version_str == "amd64_x86"):
+ elif compiler_version_str == "x86" or compiler_version_str == "amd64_x86":
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.")
+ 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."
+ )
+
def setup_msvc_auto(env):
"""Set up MSVC using SCons's auto-detection logic"""
@@ -138,103 +143,127 @@ def setup_msvc_auto(env):
# (Ideally we'd decide on the tool config before configuring any
# environment, and just set the env up once, but this function runs
# on an existing env so this is the simplest way.)
- env['MSVC_SETUP_RUN'] = False # Need to set this to re-run the tool
- env['MSVS_VERSION'] = None
- env['MSVC_VERSION'] = None
- env['TARGET_ARCH'] = None
- if env['bits'] != 'default':
- env['TARGET_ARCH'] = {'32': 'x86', '64': 'x86_64'}[env['bits']]
- if env.has_key('msvc_version'):
- env['MSVC_VERSION'] = env['msvc_version']
- env.Tool('msvc')
- env.Tool('mssdk') # we want the MS SDK
+ env["MSVC_SETUP_RUN"] = False # Need to set this to re-run the tool
+ env["MSVS_VERSION"] = None
+ env["MSVC_VERSION"] = None
+ env["TARGET_ARCH"] = None
+ if env["bits"] != "default":
+ env["TARGET_ARCH"] = {"32": "x86", "64": "x86_64"}[env["bits"]]
+ if env.has_key("msvc_version"):
+ env["MSVC_VERSION"] = env["msvc_version"]
+ env.Tool("msvc")
+ env.Tool("mssdk") # we want the MS SDK
# Note: actual compiler version can be found in env['MSVC_VERSION'], e.g. "14.1" for VS2015
# Get actual target arch into bits (it may be "default" at this point):
- if env['TARGET_ARCH'] in ('amd64', 'x86_64'):
- env['bits'] = '64'
+ if env["TARGET_ARCH"] in ("amd64", "x86_64"):
+ env["bits"] = "64"
else:
- env['bits'] = '32'
- print("Found MSVC version %s, arch %s, bits=%s" % (env['MSVC_VERSION'], env['TARGET_ARCH'], env['bits']))
- if env['TARGET_ARCH'] in ('amd64', 'x86_64'):
+ env["bits"] = "32"
+ print("Found MSVC version %s, arch %s, bits=%s" % (env["MSVC_VERSION"], env["TARGET_ARCH"], env["bits"]))
+ if env["TARGET_ARCH"] in ("amd64", "x86_64"):
env["x86_libtheora_opt_vc"] = False
+
def setup_mingw(env):
"""Set up env for use with mingw"""
# Nothing to do here
print("Using MinGW")
pass
+
def configure_msvc(env, manual_msvc_config):
"""Configure env to work with MSVC"""
# Build type
- 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'])
-
- elif (env["target"] == "release_debug"):
- if (env["optimize"] == "speed"): #optimize for speed (default)
- env.Append(CCFLAGS=['/O2'])
- 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.Append(LINKFLAGS=['/DEBUG'])
-
- if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes"):
- env.AppendUnique(CCFLAGS=['/Z7'])
- env.AppendUnique(LINKFLAGS=['/DEBUG'])
+ 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"])
+
+ elif env["target"] == "release_debug":
+ if env["optimize"] == "speed": # optimize for speed (default)
+ env.Append(CCFLAGS=["/O2"])
+ 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.Append(LINKFLAGS=["/DEBUG"])
+
+ if env["debug_symbols"] == "full" or env["debug_symbols"] == "yes":
+ env.AppendUnique(CCFLAGS=["/Z7"])
+ env.AppendUnique(LINKFLAGS=["/DEBUG"])
## 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'])
- env.AppendUnique(CXXFLAGS=['/TP']) # assume all sources are C++
- if manual_msvc_config: # should be automatic if SCons found it
+ env.AppendUnique(CCFLAGS=["/MT", "/Gd", "/GR", "/nologo"])
+ if int(env["MSVC_VERSION"].split(".")[0]) >= 14: # vs2015 and later
+ 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:
env.Prepend(CPPPATH=[os.getenv("WindowsSdkDir") + "/Include"])
else:
print("Missing environment variable: WindowsSdkDir")
- env.AppendUnique(CPPDEFINES = ['WINDOWS_ENABLED',
- 'WASAPI_ENABLED', 'WINMIDI_ENABLED',
- 'TYPED_METHOD_BIND',
- 'WIN32', 'MSVC',
- 'WINVER=%s' % env["target_win_version"],
- '_WIN32_WINNT=%s' % env["target_win_version"]])
- env.AppendUnique(CPPDEFINES=['NOMINMAX']) # disable bogus min/max WinDef.h macros
+ env.AppendUnique(
+ CPPDEFINES=[
+ "WINDOWS_ENABLED",
+ "WASAPI_ENABLED",
+ "WINMIDI_ENABLED",
+ "TYPED_METHOD_BIND",
+ "WIN32",
+ "MSVC",
+ "WINVER=%s" % env["target_win_version"],
+ "_WIN32_WINNT=%s" % env["target_win_version"],
+ ]
+ )
+ env.AppendUnique(CPPDEFINES=["NOMINMAX"]) # disable bogus min/max WinDef.h macros
if env["bits"] == "64":
- env.AppendUnique(CPPDEFINES=['_WIN64'])
+ env.AppendUnique(CPPDEFINES=["_WIN64"])
## Libs
- LIBS = ['winmm', 'dsound', 'kernel32', 'ole32', 'oleaut32',
- 'user32', 'gdi32', 'IPHLPAPI', 'Shlwapi', 'wsock32', 'Ws2_32',
- 'shell32', 'advapi32', 'dinput8', 'dxguid', 'imm32', 'bcrypt', 'Avrt',
- 'dwmapi']
+ LIBS = [
+ "winmm",
+ "dsound",
+ "kernel32",
+ "ole32",
+ "oleaut32",
+ "user32",
+ "gdi32",
+ "IPHLPAPI",
+ "Shlwapi",
+ "wsock32",
+ "Ws2_32",
+ "shell32",
+ "advapi32",
+ "dinput8",
+ "dxguid",
+ "imm32",
+ "bcrypt",
+ "Avrt",
+ "dwmapi",
+ ]
- env.AppendUnique(CPPDEFINES=['VULKAN_ENABLED'])
- if not env['builtin_vulkan']:
- LIBS += ['vulkan']
+ env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED"])
+ if not env["builtin_vulkan"]:
+ LIBS += ["vulkan"]
else:
- LIBS += ['cfgmgr32']
+ LIBS += ["cfgmgr32"]
- #env.AppendUnique(CPPDEFINES = ['OPENGL_ENABLED'])
- LIBS += ['opengl32']
+ # env.AppendUnique(CPPDEFINES = ['OPENGL_ENABLED'])
+ LIBS += ["opengl32"]
env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS])
@@ -246,23 +275,24 @@ def configure_msvc(env, manual_msvc_config):
## LTO
- if (env["use_lto"]):
- env.AppendUnique(CCFLAGS=['/GL'])
- env.AppendUnique(ARFLAGS=['/LTCG'])
+ if env["use_lto"]:
+ env.AppendUnique(CCFLAGS=["/GL"])
+ env.AppendUnique(ARFLAGS=["/LTCG"])
if env["progress"]:
- env.AppendUnique(LINKFLAGS=['/LTCG:STATUS'])
+ env.AppendUnique(LINKFLAGS=["/LTCG:STATUS"])
else:
- env.AppendUnique(LINKFLAGS=['/LTCG'])
+ env.AppendUnique(LINKFLAGS=["/LTCG"])
if manual_msvc_config:
env.Prepend(CPPPATH=[p for p in os.getenv("INCLUDE").split(";")])
env.Append(LIBPATH=[p for p in os.getenv("LIB").split(";")])
# Incremental linking fix
- env['BUILDERS']['ProgramOriginal'] = env['BUILDERS']['Program']
- env['BUILDERS']['Program'] = methods.precious_program
+ env["BUILDERS"]["ProgramOriginal"] = env["BUILDERS"]["Program"]
+ env["BUILDERS"]["Program"] = methods.precious_program
+
+ env.AppendUnique(LINKFLAGS=["/STACK:" + str(STACK_SIZE)])
- env.AppendUnique(LINKFLAGS=['/STACK:' + str(STACK_SIZE)])
def configure_mingw(env):
# Workaround for MinGW. See:
@@ -271,125 +301,148 @@ def configure_mingw(env):
## Build type
- if (env["target"] == "release"):
- env.Append(CCFLAGS=['-msse2'])
+ if env["target"] == "release":
+ env.Append(CCFLAGS=["-msse2"])
- if (env["optimize"] == "speed"): #optimize for speed (default)
- if (env["bits"] == "64"):
- env.Append(CCFLAGS=['-O3'])
+ if env["optimize"] == "speed": # optimize for speed (default)
+ if env["bits"] == "64":
+ env.Append(CCFLAGS=["-O3"])
else:
- env.Append(CCFLAGS=['-O2'])
- 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"):
- 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"):
- env.Prepend(CCFLAGS=['-g2'])
- if (env["optimize"] == "speed"): #optimize for speed (default)
- env.Append(CCFLAGS=['-O2'])
- else: #optimize for size
- env.Prepend(CCFLAGS=['-Os'])
-
- elif (env["target"] == "debug"):
- env.Append(CCFLAGS=['-g3'])
- env.Append(CPPDEFINES=['DEBUG_ENABLED', 'DEBUG_MEMORY_ENABLED'])
+ env.Append(CCFLAGS=["-O2"])
+ 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":
+ 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":
+ env.Prepend(CCFLAGS=["-g2"])
+ if env["optimize"] == "speed": # optimize for speed (default)
+ env.Append(CCFLAGS=["-O2"])
+ else: # optimize for size
+ env.Prepend(CCFLAGS=["-Os"])
+
+ elif env["target"] == "debug":
+ env.Append(CCFLAGS=["-g3"])
+ env.Append(CPPDEFINES=["DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED"])
## Compiler configuration
if os.name != "nt":
env["PROGSUFFIX"] = env["PROGSUFFIX"] + ".exe" # for linux cross-compilation
- if (env["bits"] == "default"):
- if (os.name == "nt"):
+ if env["bits"] == "default":
+ if os.name == "nt":
env["bits"] = "64" if "PROGRAMFILES(X86)" in os.environ else "32"
- else: # default to 64-bit on Linux
+ else: # default to 64-bit on Linux
env["bits"] = "64"
mingw_prefix = ""
- if (env["bits"] == "32"):
- env.Append(LINKFLAGS=['-static'])
- env.Append(LINKFLAGS=['-static-libgcc'])
- env.Append(LINKFLAGS=['-static-libstdc++'])
+ if env["bits"] == "32":
+ 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'])
+ env.Append(LINKFLAGS=["-static"])
mingw_prefix = env["mingw_prefix_64"]
- if env['use_llvm']:
+ if env["use_llvm"]:
env["CC"] = mingw_prefix + "clang"
- env['AS'] = mingw_prefix + "as"
+ env["AS"] = mingw_prefix + "as"
env["CXX"] = mingw_prefix + "clang++"
- env['AR'] = mingw_prefix + "ar"
- env['RANLIB'] = mingw_prefix + "ranlib"
+ 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['AR'] = mingw_prefix + "gcc-ar"
- env['RANLIB'] = mingw_prefix + "gcc-ranlib"
- env['LINK'] = mingw_prefix + "g++"
+ env["AS"] = mingw_prefix + "as"
+ env["CXX"] = mingw_prefix + "g++"
+ 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']:
- if not env['use_llvm'] and env.GetOption("num_jobs") > 1:
- env.Append(CCFLAGS=['-flto'])
- env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))])
+ if env["use_lto"]:
+ if not env["use_llvm"] and env.GetOption("num_jobs") > 1:
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto=" + str(env.GetOption("num_jobs"))])
else:
- if env['use_thinlto']:
- env.Append(CCFLAGS=['-flto=thin'])
- env.Append(LINKFLAGS=['-flto=thin'])
+ if env["use_thinlto"]:
+ env.Append(CCFLAGS=["-flto=thin"])
+ env.Append(LINKFLAGS=["-flto=thin"])
else:
- env.Append(CCFLAGS=['-flto'])
- env.Append(LINKFLAGS=['-flto'])
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto"])
- env.Append(LINKFLAGS=['-Wl,--stack,' + str(STACK_SIZE)])
+ env.Append(LINKFLAGS=["-Wl,--stack," + str(STACK_SIZE)])
## Compile flags
- env.Append(CCFLAGS=['-mwindows'])
-
- env.Append(CPPDEFINES=['WINDOWS_ENABLED', 'WASAPI_ENABLED', 'WINMIDI_ENABLED'])
- env.Append(CPPDEFINES=[('WINVER', env['target_win_version']), ('_WIN32_WINNT', env['target_win_version'])])
- env.Append(LIBS=['mingw32', 'dsound', 'ole32', 'd3d9', 'winmm', 'gdi32', 'iphlpapi', 'shlwapi', 'wsock32', 'ws2_32', 'kernel32', 'oleaut32', 'dinput8', 'dxguid', 'ksuser', 'imm32', 'bcrypt', 'avrt', 'uuid', 'dwmapi'])
-
- env.Append(CPPDEFINES=['VULKAN_ENABLED'])
- if not env['builtin_vulkan']:
- env.Append(LIBS=['vulkan'])
+ env.Append(CCFLAGS=["-mwindows"])
+
+ env.Append(CPPDEFINES=["WINDOWS_ENABLED", "WASAPI_ENABLED", "WINMIDI_ENABLED"])
+ env.Append(CPPDEFINES=[("WINVER", env["target_win_version"]), ("_WIN32_WINNT", env["target_win_version"])])
+ env.Append(
+ LIBS=[
+ "mingw32",
+ "dsound",
+ "ole32",
+ "d3d9",
+ "winmm",
+ "gdi32",
+ "iphlpapi",
+ "shlwapi",
+ "wsock32",
+ "ws2_32",
+ "kernel32",
+ "oleaut32",
+ "dinput8",
+ "dxguid",
+ "ksuser",
+ "imm32",
+ "bcrypt",
+ "avrt",
+ "uuid",
+ "dwmapi",
+ ]
+ )
+
+ env.Append(CPPDEFINES=["VULKAN_ENABLED"])
+ if not env["builtin_vulkan"]:
+ env.Append(LIBS=["vulkan"])
else:
- env.Append(LIBS=['cfgmgr32'])
+ env.Append(LIBS=["cfgmgr32"])
## TODO !!! Reenable when OpenGLES Rendering Device is implemented !!!
- #env.Append(CPPDEFINES=['OPENGL_ENABLED'])
- env.Append(LIBS=['opengl32'])
+ # env.Append(CPPDEFINES=['OPENGL_ENABLED'])
+ env.Append(LIBS=["opengl32"])
- env.Append(CPPDEFINES=['MINGW_ENABLED', ('MINGW_HAS_SECURE_API', 1)])
+ env.Append(CPPDEFINES=["MINGW_ENABLED", ("MINGW_HAS_SECURE_API", 1)])
# resrc
- env.Append(BUILDERS={'RES': env.Builder(action=build_res_file, suffix='.o', src_suffix='.rc')})
+ env.Append(BUILDERS={"RES": env.Builder(action=build_res_file, suffix=".o", src_suffix=".rc")})
+
def configure(env):
# At this point the env has been set up with basic tools/compilers.
- env.Prepend(CPPPATH=['#platform/windows'])
+ env.Prepend(CPPPATH=["#platform/windows"])
- print("Configuring for Windows: target=%s, bits=%s" % (env['target'], env['bits']))
+ print("Configuring for Windows: target=%s, bits=%s" % (env["target"], env["bits"]))
- if (os.name == "nt"):
- env['ENV'] = os.environ # this makes build less repeatable, but simplifies some things
- env['ENV']['TMP'] = os.environ['TMP']
+ if os.name == "nt":
+ env["ENV"] = os.environ # this makes build less repeatable, but simplifies some things
+ env["ENV"]["TMP"] = os.environ["TMP"]
# First figure out which compiler, version, and target arch we're using
if os.getenv("VCINSTALLDIR") and not env["use_mingw"]:
@@ -397,7 +450,7 @@ def configure(env):
setup_msvc_manual(env)
env.msvc = True
manual_msvc_config = True
- elif env.get('MSVC_VERSION', '') and not env["use_mingw"]:
+ elif env.get("MSVC_VERSION", "") and not env["use_mingw"]:
setup_msvc_auto(env)
env.msvc = True
manual_msvc_config = False
@@ -409,5 +462,5 @@ def configure(env):
if env.msvc:
configure_msvc(env, manual_msvc_config)
- else: # MinGW
+ else: # MinGW
configure_mingw(env)
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
new file mode 100644
index 0000000000..9d8344fa7e
--- /dev/null
+++ b/platform/windows/display_server_windows.cpp
@@ -0,0 +1,3136 @@
+/*************************************************************************/
+/* display_server_windows.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 "display_server_windows.h"
+#include "core/io/marshalls.h"
+#include "main/main.h"
+#include "os_windows.h"
+#include "scene/resources/texture.h"
+
+#include <avrt.h>
+
+#ifdef DEBUG_ENABLED
+static String format_error_message(DWORD id) {
+ LPWSTR messageBuffer = nullptr;
+ 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);
+
+ LocalFree(messageBuffer);
+
+ return msg;
+}
+#endif // DEBUG_ENABLED
+
+bool DisplayServerWindows::has_feature(Feature p_feature) const {
+ switch (p_feature) {
+ case FEATURE_SUBWINDOWS:
+ case FEATURE_TOUCHSCREEN:
+ case FEATURE_MOUSE:
+ case FEATURE_MOUSE_WARP:
+ case FEATURE_CLIPBOARD:
+ case FEATURE_CURSOR_SHAPE:
+ case FEATURE_CUSTOM_CURSOR_SHAPE:
+ case FEATURE_CONSOLE_WINDOW:
+ case FEATURE_IME:
+ case FEATURE_WINDOW_TRANSPARENCY:
+ case FEATURE_HIDPI:
+ case FEATURE_ICON:
+ case FEATURE_NATIVE_ICON:
+ case FEATURE_SWAP_BUFFERS:
+ case FEATURE_KEEP_SCREEN_ON:
+ return true;
+ default:
+ return false;
+ }
+}
+
+String DisplayServerWindows::get_name() const {
+ return "Windows";
+}
+
+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);
+}
+
+void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
+ if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED) {
+ WindowData &wd = windows[MAIN_WINDOW_ID];
+
+ RECT clipRect;
+ GetClientRect(wd.hWnd, &clipRect);
+ ClientToScreen(wd.hWnd, (POINT *)&clipRect.left);
+ ClientToScreen(wd.hWnd, (POINT *)&clipRect.right);
+ ClipCursor(&clipRect);
+ if (p_mode == MOUSE_MODE_CAPTURED) {
+ center = window_get_size() / 2;
+ POINT pos = { (int)center.x, (int)center.y };
+ ClientToScreen(wd.hWnd, &pos);
+ SetCursorPos(pos.x, pos.y);
+ SetCapture(wd.hWnd);
+ }
+ } else {
+ ReleaseCapture();
+ ClipCursor(nullptr);
+ }
+
+ if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_HIDDEN) {
+ hCursor = SetCursor(nullptr);
+ } else {
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ cursor_set_shape(c);
+ }
+}
+
+void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) {
+ _THREAD_SAFE_METHOD_
+
+ if (mouse_mode == p_mode)
+ return;
+
+ _set_mouse_mode_impl(p_mode);
+
+ mouse_mode = p_mode;
+}
+
+DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const {
+ return mouse_mode;
+}
+
+void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) {
+ _THREAD_SAFE_METHOD_
+
+ if (!windows.has(last_focused_window)) {
+ return; //no window focused?
+ }
+
+ if (mouse_mode == MOUSE_MODE_CAPTURED) {
+ old_x = p_to.x;
+ old_y = p_to.y;
+ } else {
+ POINT p;
+ p.x = p_to.x;
+ p.y = p_to.y;
+ ClientToScreen(windows[last_focused_window].hWnd, &p);
+
+ SetCursorPos(p.x, p.y);
+ }
+}
+
+Point2i DisplayServerWindows::mouse_get_position() const {
+ POINT p;
+ GetCursorPos(&p);
+ return Point2i(p.x, p.y);
+ //return Point2(old_x, old_y);
+}
+
+int DisplayServerWindows::mouse_get_button_state() const {
+ return last_button_state;
+}
+
+void DisplayServerWindows::clipboard_set(const String &p_text) {
+ _THREAD_SAFE_METHOD_
+
+ if (!windows.has(last_focused_window)) {
+ return; //no window focused?
+ }
+
+ // 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");
+
+ 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));
+ 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));
+ GlobalUnlock(mem);
+
+ SetClipboardData(CF_UNICODETEXT, mem);
+
+ // set the CF_TEXT version (not needed?)
+ CharString utf8 = text.utf8();
+ mem = GlobalAlloc(GMEM_MOVEABLE, utf8.length() + 1);
+ ERR_FAIL_COND_MSG(mem == nullptr, "Unable to allocate memory for clipboard contents.");
+
+ LPTSTR ptr = (LPTSTR)GlobalLock(mem);
+ memcpy(ptr, utf8.get_data(), utf8.length());
+ ptr[utf8.length()] = 0;
+ GlobalUnlock(mem);
+
+ SetClipboardData(CF_TEXT, mem);
+
+ CloseClipboard();
+}
+
+String DisplayServerWindows::clipboard_get() const {
+ _THREAD_SAFE_METHOD_
+
+ if (!windows.has(last_focused_window)) {
+ return String(); //no window focused?
+ }
+
+ String ret;
+ if (!OpenClipboard(windows[last_focused_window].hWnd)) {
+ ERR_FAIL_V_MSG("", "Unable to open clipboard.");
+ };
+
+ if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
+ HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
+ if (mem != nullptr) {
+ LPWSTR ptr = (LPWSTR)GlobalLock(mem);
+ if (ptr != nullptr) {
+ ret = String((CharType *)ptr);
+ GlobalUnlock(mem);
+ };
+ };
+
+ } else if (IsClipboardFormatAvailable(CF_TEXT)) {
+ HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
+ if (mem != nullptr) {
+ LPTSTR ptr = (LPTSTR)GlobalLock(mem);
+ if (ptr != nullptr) {
+ ret.parse_utf8((const char *)ptr);
+ GlobalUnlock(mem);
+ };
+ };
+ };
+
+ CloseClipboard();
+
+ return ret;
+}
+
+typedef struct {
+ int count;
+ int screen;
+ HMONITOR monitor;
+} EnumScreenData;
+
+static BOOL CALLBACK _MonitorEnumProcScreen(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+ EnumScreenData *data = (EnumScreenData *)dwData;
+ if (data->monitor == hMonitor) {
+ data->screen = data->count;
+ }
+
+ data->count++;
+ return TRUE;
+}
+
+static BOOL CALLBACK _MonitorEnumProcCount(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+ int *data = (int *)dwData;
+ (*data)++;
+ return TRUE;
+}
+
+int DisplayServerWindows::get_screen_count() const {
+ _THREAD_SAFE_METHOD_
+
+ int data = 0;
+ EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcCount, (LPARAM)&data);
+ return data;
+}
+
+typedef struct {
+ int count;
+ int screen;
+ Point2 pos;
+} EnumPosData;
+
+static BOOL CALLBACK _MonitorEnumProcPos(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+ EnumPosData *data = (EnumPosData *)dwData;
+ if (data->count == data->screen) {
+ data->pos.x = lprcMonitor->left;
+ data->pos.y = lprcMonitor->top;
+ }
+
+ data->count++;
+ return TRUE;
+}
+
+Point2i DisplayServerWindows::screen_get_position(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ EnumPosData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, Point2() };
+ EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcPos, (LPARAM)&data);
+ return data.pos;
+}
+
+typedef struct {
+ int count;
+ int screen;
+ Size2 size;
+} EnumSizeData;
+
+typedef struct {
+ int count;
+ int screen;
+ Rect2i rect;
+} EnumRectData;
+
+static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+ EnumSizeData *data = (EnumSizeData *)dwData;
+ if (data->count == data->screen) {
+ data->size.x = lprcMonitor->right - lprcMonitor->left;
+ data->size.y = lprcMonitor->bottom - lprcMonitor->top;
+ }
+
+ data->count++;
+ return TRUE;
+}
+
+Size2i DisplayServerWindows::screen_get_size(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ EnumSizeData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, Size2() };
+ EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcSize, (LPARAM)&data);
+ return data.size;
+}
+
+static BOOL CALLBACK _MonitorEnumProcUsableSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+ EnumRectData *data = (EnumRectData *)dwData;
+ if (data->count == data->screen) {
+ MONITORINFO minfo;
+ zeromem(&minfo, sizeof(MONITORINFO));
+ minfo.cbSize = sizeof(MONITORINFO);
+ GetMonitorInfoA(hMonitor, &minfo);
+
+ data->rect.position.x = minfo.rcWork.left;
+ data->rect.position.y = minfo.rcWork.top;
+ data->rect.size.x = minfo.rcWork.right - minfo.rcWork.left;
+ data->rect.size.y = minfo.rcWork.bottom - minfo.rcWork.top;
+ }
+
+ data->count++;
+ return TRUE;
+}
+
+Rect2i DisplayServerWindows::screen_get_usable_rect(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ EnumRectData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, Rect2i() };
+ EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcUsableSize, (LPARAM)&data);
+ return data.rect;
+}
+
+typedef struct {
+ int count;
+ int screen;
+ int dpi;
+} EnumDpiData;
+
+enum _MonitorDpiType {
+ MDT_Effective_DPI = 0,
+ MDT_Angular_DPI = 1,
+ MDT_Raw_DPI = 2,
+ MDT_Default = MDT_Effective_DPI
+};
+
+static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Default) {
+ int dpiX = 96, dpiY = 96;
+
+ static HMODULE Shcore = nullptr;
+ typedef HRESULT(WINAPI * GetDPIForMonitor_t)(HMONITOR hmonitor, _MonitorDpiType dpiType, UINT * dpiX, UINT * dpiY);
+ static GetDPIForMonitor_t getDPIForMonitor = nullptr;
+
+ if (Shcore == nullptr) {
+ Shcore = LoadLibraryW(L"Shcore.dll");
+ getDPIForMonitor = Shcore ? (GetDPIForMonitor_t)GetProcAddress(Shcore, "GetDpiForMonitor") : nullptr;
+
+ if ((Shcore == nullptr) || (getDPIForMonitor == nullptr)) {
+ if (Shcore)
+ FreeLibrary(Shcore);
+ Shcore = (HMODULE)INVALID_HANDLE_VALUE;
+ }
+ }
+
+ UINT x = 0, y = 0;
+ HRESULT hr = E_FAIL;
+ if (hmon && (Shcore != (HMODULE)INVALID_HANDLE_VALUE)) {
+ hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y);
+ if (SUCCEEDED(hr) && (x > 0) && (y > 0)) {
+ dpiX = (int)x;
+ dpiY = (int)y;
+ }
+ } else {
+ static int overallX = 0, overallY = 0;
+ if (overallX <= 0 || overallY <= 0) {
+ HDC hdc = GetDC(nullptr);
+ if (hdc) {
+ overallX = GetDeviceCaps(hdc, LOGPIXELSX);
+ overallY = GetDeviceCaps(hdc, LOGPIXELSY);
+ ReleaseDC(nullptr, hdc);
+ }
+ }
+ if (overallX > 0 && overallY > 0) {
+ dpiX = overallX;
+ dpiY = overallY;
+ }
+ }
+
+ return (dpiX + dpiY) / 2;
+}
+
+static BOOL CALLBACK _MonitorEnumProcDpi(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+ EnumDpiData *data = (EnumDpiData *)dwData;
+ if (data->count == data->screen) {
+ data->dpi = QueryDpiForMonitor(hMonitor);
+ }
+
+ data->count++;
+ return TRUE;
+}
+
+int DisplayServerWindows::screen_get_dpi(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ EnumDpiData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, 72 };
+ EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcDpi, (LPARAM)&data);
+ return data.dpi;
+}
+
+bool DisplayServerWindows::screen_is_touchscreen(int p_screen) const {
+#ifndef _MSC_VER
+#warning touchscreen not working
+#endif
+ return false;
+}
+
+void DisplayServerWindows::screen_set_orientation(ScreenOrientation p_orientation, int p_screen) {
+}
+
+DisplayServer::ScreenOrientation DisplayServerWindows::screen_get_orientation(int p_screen) const {
+ return SCREEN_LANDSCAPE;
+}
+
+void DisplayServerWindows::screen_set_keep_on(bool p_enable) {
+}
+
+bool DisplayServerWindows::screen_is_kept_on() const {
+ return false;
+}
+
+Vector<DisplayServer::WindowID> DisplayServerWindows::get_window_list() const {
+ _THREAD_SAFE_METHOD_
+
+ Vector<DisplayServer::WindowID> ret;
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+
+DisplayServer::WindowID DisplayServerWindows::get_window_at_screen_position(const Point2i &p_position) const {
+ POINT p;
+ p.x = p_position.x;
+ p.y = p_position.y;
+ HWND hwnd = WindowFromPoint(p);
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (E->get().hWnd == hwnd) {
+ return E->key();
+ }
+ }
+
+ return INVALID_WINDOW_ID;
+}
+
+DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+ _THREAD_SAFE_METHOD_
+
+ WindowID window_id = _create_window(p_mode, p_flags, p_rect);
+
+ WindowData &wd = windows[window_id];
+
+ if (p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT) {
+ wd.resizable = false;
+ }
+ if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) {
+ wd.borderless = true;
+ }
+ if (p_flags & WINDOW_FLAG_ALWAYS_ON_TOP_BIT && p_mode != WINDOW_MODE_FULLSCREEN) {
+ wd.always_on_top = true;
+ }
+ if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
+ wd.no_focus = true;
+ }
+
+ _update_window_style(window_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)) {
+ SetForegroundWindow(wd.hWnd); // Slightly Higher Priority
+ SetFocus(wd.hWnd); // Sets Keyboard Focus To
+ }
+
+ return window_id;
+}
+
+void DisplayServerWindows::delete_sub_window(WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window cannot be deleted.");
+
+ WindowData &wd = windows[p_window];
+
+ while (wd.transient_children.size()) {
+ window_set_transient(wd.transient_children.front()->get(), INVALID_WINDOW_ID);
+ }
+
+ if (wd.transient_parent != INVALID_WINDOW_ID) {
+ window_set_transient(p_window, INVALID_WINDOW_ID);
+ }
+
+#ifdef VULKAN_ENABLED
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_destroy(p_window);
+ }
+#endif
+
+ if ((OS::get_singleton()->get_current_tablet_driver() == "wintab") && wintab_available && windows[p_window].wtctx) {
+ wintab_WTClose(windows[p_window].wtctx);
+ windows[p_window].wtctx = 0;
+ }
+ DestroyWindow(windows[p_window].hWnd);
+ windows.erase(p_window);
+}
+
+void DisplayServerWindows::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].instance_id = p_instance;
+}
+
+ObjectID DisplayServerWindows::window_get_attached_instance_id(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), ObjectID());
+ return windows[p_window].instance_id;
+}
+
+void DisplayServerWindows::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].rect_changed_callback = p_callable;
+}
+
+void DisplayServerWindows::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].event_callback = p_callable;
+}
+
+void DisplayServerWindows::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].input_event_callback = p_callable;
+}
+
+void DisplayServerWindows::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].input_text_callback = p_callable;
+}
+
+void DisplayServerWindows::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].drop_files_callback = p_callable;
+}
+
+void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ SetWindowTextW(windows[p_window].hWnd, p_title.c_str());
+}
+
+int DisplayServerWindows::window_get_current_screen(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), -1);
+
+ EnumScreenData data = { 0, 0, MonitorFromWindow(windows[p_window].hWnd, MONITOR_DEFAULTTONEAREST) };
+ EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcScreen, (LPARAM)&data);
+ return data.screen;
+}
+
+void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ ERR_FAIL_INDEX(p_screen, get_screen_count());
+
+ Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));
+ window_set_position(ofs + screen_get_position(p_screen), p_window);
+}
+
+Point2i DisplayServerWindows::window_get_position(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Point2i());
+ const WindowData &wd = windows[p_window];
+
+ if (wd.minimized) {
+ return wd.last_pos;
+ }
+
+ POINT point;
+ point.x = 0;
+ point.y = 0;
+
+ ClientToScreen(wd.hWnd, &point);
+
+ return Point2i(point.x, point.y);
+
+#if 0
+ //do not use this method, as it includes windows decorations
+ RECT r;
+ GetWindowRect(wd.hWnd, &r);
+ return Point2(r.left, r.top);
+#endif
+}
+
+void DisplayServerWindows::_update_real_mouse_position(WindowID p_window) {
+ POINT mouse_pos;
+ if (GetCursorPos(&mouse_pos) && ScreenToClient(windows[p_window].hWnd, &mouse_pos)) {
+ if (mouse_pos.x > 0 && mouse_pos.y > 0 && mouse_pos.x <= windows[p_window].width && mouse_pos.y <= windows[p_window].height) {
+ old_x = mouse_pos.x;
+ old_y = mouse_pos.y;
+ old_invalid = false;
+ Input::get_singleton()->set_mouse_position(Point2i(mouse_pos.x, mouse_pos.y));
+ }
+ }
+}
+
+void DisplayServerWindows::window_set_position(const Point2i &p_position, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if (wd.fullscreen)
+ return;
+#if 0
+ //wrong needs to account properly for decorations
+ RECT r;
+ GetWindowRect(wd.hWnd, &r);
+ MoveWindow(wd.hWnd, p_position.x, p_position.y, r.right - r.left, r.bottom - r.top, TRUE);
+#else
+
+ RECT rc;
+ rc.left = p_position.x;
+ rc.right = p_position.x + wd.width;
+ rc.bottom = p_position.y + wd.height;
+ rc.top = p_position.y;
+
+ const DWORD style = GetWindowLongPtr(wd.hWnd, GWL_STYLE);
+ const DWORD exStyle = GetWindowLongPtr(wd.hWnd, GWL_EXSTYLE);
+
+ AdjustWindowRectEx(&rc, style, false, exStyle);
+ MoveWindow(wd.hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
+#endif
+ // Don't let the mouse leave the window when moved
+ if (mouse_mode == MOUSE_MODE_CONFINED) {
+ RECT rect;
+ GetClientRect(wd.hWnd, &rect);
+ ClientToScreen(wd.hWnd, (POINT *)&rect.left);
+ ClientToScreen(wd.hWnd, (POINT *)&rect.right);
+ ClipCursor(&rect);
+ }
+
+ wd.last_pos = p_position;
+ _update_real_mouse_position(p_window);
+}
+
+void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_parent) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(p_window == p_parent);
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd_window = windows[p_window];
+
+ ERR_FAIL_COND(wd_window.transient_parent == p_parent);
+
+ ERR_FAIL_COND_MSG(wd_window.always_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));
+
+ WindowData &wd_parent = windows[wd_window.transient_parent];
+
+ wd_window.transient_parent = INVALID_WINDOW_ID;
+ wd_parent.transient_children.erase(p_window);
+
+ SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr);
+ } 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");
+ WindowData &wd_parent = windows[p_parent];
+
+ wd_window.transient_parent = p_parent;
+ wd_parent.transient_children.insert(p_window);
+
+ SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd);
+ }
+}
+
+void DisplayServerWindows::window_set_max_size(const Size2i p_size, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if ((p_size != Size2()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {
+ ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
+ return;
+ }
+ wd.max_size = p_size;
+}
+
+Size2i DisplayServerWindows::window_get_max_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+ return wd.max_size;
+}
+
+void DisplayServerWindows::window_set_min_size(const Size2i p_size, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if ((p_size != Size2()) && (wd.max_size != Size2()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {
+ ERR_PRINT("Minimum window size can't be larger than maximum window size!");
+ return;
+ }
+ wd.min_size = p_size;
+}
+
+Size2i DisplayServerWindows::window_get_min_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+ return wd.min_size;
+}
+
+void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ int w = p_size.width;
+ int h = p_size.height;
+
+ wd.width = w;
+ wd.height = h;
+
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_resize(p_window, w, h);
+ }
+#endif
+
+ if (wd.fullscreen) {
+ return;
+ }
+
+ RECT rect;
+ GetWindowRect(wd.hWnd, &rect);
+
+ if (!wd.borderless) {
+ RECT crect;
+ GetClientRect(wd.hWnd, &crect);
+
+ w += (rect.right - rect.left) - (crect.right - crect.left);
+ h += (rect.bottom - rect.top) - (crect.bottom - crect.top);
+ }
+
+ MoveWindow(wd.hWnd, rect.left, rect.top, w, h, TRUE);
+
+ // Don't let the mouse leave the window when resizing to a smaller resolution
+ if (mouse_mode == MOUSE_MODE_CONFINED) {
+ RECT crect;
+ GetClientRect(wd.hWnd, &crect);
+ ClientToScreen(wd.hWnd, (POINT *)&crect.left);
+ ClientToScreen(wd.hWnd, (POINT *)&crect.right);
+ ClipCursor(&crect);
+ }
+}
+
+Size2i DisplayServerWindows::window_get_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+
+ if (wd.minimized) {
+ return Size2(wd.width, wd.height);
+ }
+
+ RECT r;
+ if (GetClientRect(wd.hWnd, &r)) { // Only area inside of window border
+ return Size2(r.right - r.left, r.bottom - r.top);
+ }
+ return Size2();
+}
+
+Size2i DisplayServerWindows::window_get_real_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+
+ RECT r;
+ if (GetWindowRect(wd.hWnd, &r)) { // Includes area of the window border
+ return Size2(r.right - r.left, r.bottom - r.top);
+ }
+ return Size2();
+}
+
+void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscreen, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) {
+ r_style = 0;
+ r_style_ex = WS_EX_WINDOWEDGE;
+ if (p_main_window) {
+ r_style_ex |= WS_EX_APPWINDOW;
+ }
+
+ if (p_fullscreen || p_borderless) {
+ r_style |= WS_POPUP;
+ //if (p_borderless) {
+ // r_style_ex |= WS_EX_TOOLWINDOW;
+ //}
+ } else {
+ if (p_resizable) {
+ if (p_maximized) {
+ r_style = WS_OVERLAPPEDWINDOW | WS_MAXIMIZE;
+ } else {
+ r_style = WS_OVERLAPPEDWINDOW;
+ }
+ } else {
+ r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;
+ }
+ }
+
+ if (p_no_activate_focus) {
+ r_style_ex |= WS_EX_TOPMOST | WS_EX_NOACTIVATE;
+ }
+ r_style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
+}
+
+void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repaint, bool p_maximized) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ DWORD style = 0;
+ DWORD style_ex = 0;
+
+ _get_window_style(p_window == MAIN_WINDOW_ID, wd.fullscreen, wd.borderless, wd.resizable, wd.maximized, wd.no_focus, style, style_ex);
+
+ SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);
+ SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex);
+
+ SetWindowPos(wd.hWnd, wd.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | (wd.no_focus ? SWP_NOACTIVATE : 0));
+
+ if (p_repaint) {
+ RECT rect;
+ GetWindowRect(wd.hWnd, &rect);
+ MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
+ }
+}
+
+void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if (wd.fullscreen && p_mode != WINDOW_MODE_FULLSCREEN) {
+ RECT rect;
+
+ wd.fullscreen = false;
+
+ if (wd.pre_fs_valid) {
+ rect = wd.pre_fs_rect;
+ } else {
+ rect.left = 0;
+ rect.right = wd.width;
+ rect.top = 0;
+ rect.bottom = wd.height;
+ }
+
+ _update_window_style(p_window, false, wd.was_maximized);
+
+ MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
+
+ wd.pre_fs_valid = true;
+ }
+
+ if (p_mode == WINDOW_MODE_MAXIMIZED) {
+ ShowWindow(wd.hWnd, SW_MAXIMIZE);
+ wd.maximized = true;
+ 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;
+ wd.minimized = true;
+ }
+
+ if (p_mode == WINDOW_MODE_FULLSCREEN && !wd.fullscreen) {
+ if (wd.minimized) {
+ ShowWindow(wd.hWnd, SW_RESTORE);
+ }
+ wd.was_maximized = wd.maximized;
+
+ if (wd.pre_fs_valid) {
+ GetWindowRect(wd.hWnd, &wd.pre_fs_rect);
+ }
+
+ int cs = window_get_current_screen(p_window);
+ Point2 pos = screen_get_position(cs);
+ Size2 size = screen_get_size(cs);
+
+ wd.fullscreen = true;
+ wd.maximized = false;
+ wd.minimized = false;
+
+ _update_window_style(false);
+
+ MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE);
+ }
+}
+
+DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED);
+ const WindowData &wd = windows[p_window];
+
+ if (wd.fullscreen) {
+ return WINDOW_MODE_FULLSCREEN;
+ } else if (wd.minimized) {
+ return WINDOW_MODE_MINIMIZED;
+ } else if (wd.maximized) {
+ return WINDOW_MODE_MAXIMIZED;
+ } else {
+ return WINDOW_MODE_WINDOWED;
+ }
+}
+
+bool DisplayServerWindows::window_is_maximize_allowed(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), false);
+
+ // FIXME: Implement this, or confirm that it should always be true.
+
+ return true; //no idea
+}
+
+void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ switch (p_flag) {
+ case WINDOW_FLAG_RESIZE_DISABLED: {
+ wd.resizable = !p_enabled;
+ _update_window_style(p_window);
+ } break;
+ case WINDOW_FLAG_BORDERLESS: {
+ wd.borderless = p_enabled;
+ _update_window_style(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");
+ wd.always_on_top = p_enabled;
+ _update_window_style(p_window);
+ } break;
+ case WINDOW_FLAG_TRANSPARENT: {
+ // FIXME: Implement.
+ } break;
+ case WINDOW_FLAG_NO_FOCUS: {
+ wd.no_focus = p_enabled;
+ _update_window_style(p_window);
+ } break;
+ case WINDOW_FLAG_MAX:
+ break;
+ }
+}
+
+bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), false);
+ const WindowData &wd = windows[p_window];
+ switch (p_flag) {
+ case WINDOW_FLAG_RESIZE_DISABLED: {
+ return !wd.resizable;
+ } break;
+ case WINDOW_FLAG_BORDERLESS: {
+ return wd.borderless;
+ } break;
+ case WINDOW_FLAG_ALWAYS_ON_TOP: {
+ return wd.always_on_top;
+ } break;
+ case WINDOW_FLAG_TRANSPARENT: {
+ // FIXME: Implement.
+ } break;
+ case WINDOW_FLAG_NO_FOCUS: {
+ return wd.no_focus;
+ } break;
+ case WINDOW_FLAG_MAX:
+ break;
+ }
+
+ return false;
+}
+
+void DisplayServerWindows::window_request_attention(WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ FLASHWINFO info;
+ info.cbSize = sizeof(FLASHWINFO);
+ info.hwnd = wd.hWnd;
+ info.dwFlags = FLASHW_TRAY;
+ info.dwTimeout = 0;
+ info.uCount = 2;
+ FlashWindowEx(&info);
+}
+
+void DisplayServerWindows::window_move_to_foreground(WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ SetForegroundWindow(wd.hWnd);
+}
+
+bool DisplayServerWindows::window_can_draw(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), false);
+ const WindowData &wd = windows[p_window];
+ return wd.minimized;
+}
+
+bool DisplayServerWindows::can_any_window_draw() const {
+ _THREAD_SAFE_METHOD_
+
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (!E->get().minimized) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void DisplayServerWindows::window_set_ime_active(const bool p_active, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if (p_active) {
+ ImmAssociateContext(wd.hWnd, wd.im_himc);
+
+ window_set_ime_position(wd.im_position, p_window);
+ } else {
+ ImmAssociateContext(wd.hWnd, (HIMC)0);
+ }
+}
+
+void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ wd.im_position = p_pos;
+
+ HIMC himc = ImmGetContext(wd.hWnd);
+ if (himc == (HIMC)0)
+ return;
+
+ COMPOSITIONFORM cps;
+ cps.dwStyle = CFS_FORCE_POSITION;
+ cps.ptCurrentPos.x = wd.im_position.x;
+ cps.ptCurrentPos.y = wd.im_position.y;
+ ImmSetCompositionWindow(himc, &cps);
+ ImmReleaseContext(wd.hWnd, himc);
+}
+
+void DisplayServerWindows::console_set_visible(bool p_enabled) {
+ _THREAD_SAFE_METHOD_
+
+ if (console_visible == p_enabled)
+ return;
+ ShowWindow(GetConsoleWindow(), p_enabled ? SW_SHOW : SW_HIDE);
+ console_visible = p_enabled;
+}
+
+bool DisplayServerWindows::is_console_visible() const {
+ return console_visible;
+}
+
+void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
+
+ if (cursor_shape == p_shape)
+ return;
+
+ if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
+ cursor_shape = p_shape;
+ return;
+ }
+
+ static const LPCTSTR win_cursors[CURSOR_MAX] = {
+ IDC_ARROW,
+ IDC_IBEAM,
+ IDC_HAND, //finger
+ IDC_CROSS,
+ IDC_WAIT,
+ IDC_APPSTARTING,
+ IDC_ARROW,
+ IDC_ARROW,
+ IDC_NO,
+ IDC_SIZENS,
+ IDC_SIZEWE,
+ IDC_SIZENESW,
+ IDC_SIZENWSE,
+ IDC_SIZEALL,
+ IDC_SIZENS,
+ IDC_SIZEWE,
+ IDC_HELP
+ };
+
+ if (cursors[p_shape] != nullptr) {
+ SetCursor(cursors[p_shape]);
+ } else {
+ SetCursor(LoadCursor(hInstance, win_cursors[p_shape]));
+ }
+
+ cursor_shape = p_shape;
+}
+
+DisplayServer::CursorShape DisplayServerWindows::cursor_get_shape() const {
+ return cursor_shape;
+}
+
+void DisplayServerWindows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap) {
+ // Get the system display DC
+ HDC hDC = GetDC(nullptr);
+
+ // Create helper DC
+ HDC hMainDC = CreateCompatibleDC(hDC);
+ HDC hAndMaskDC = CreateCompatibleDC(hDC);
+ HDC hXorMaskDC = CreateCompatibleDC(hDC);
+
+ // Get the dimensions of the source bitmap
+ BITMAP bm;
+ GetObject(hSourceBitmap, sizeof(BITMAP), &bm);
+
+ // Create the mask bitmaps
+ hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color
+ hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color
+
+ // Release the system display DC
+ ReleaseDC(nullptr, hDC);
+
+ // Select the bitmaps to helper DC
+ HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap);
+ HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap);
+ HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap);
+
+ // Assign the monochrome AND mask bitmap pixels so that a pixels of the source bitmap
+ // with 'clrTransparent' will be white pixels of the monochrome bitmap
+ SetBkColor(hMainDC, clrTransparent);
+ BitBlt(hAndMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCCOPY);
+
+ // Assign the color XOR mask bitmap pixels so that a pixels of the source bitmap
+ // with 'clrTransparent' will be black and rest the pixels same as corresponding
+ // pixels of the source bitmap
+ SetBkColor(hXorMaskDC, RGB(0, 0, 0));
+ SetTextColor(hXorMaskDC, RGB(255, 255, 255));
+ BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hAndMaskDC, 0, 0, SRCCOPY);
+ BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCAND);
+
+ // Deselect bitmaps from the helper DC
+ SelectObject(hMainDC, hOldMainBitmap);
+ SelectObject(hAndMaskDC, hOldAndMaskBitmap);
+ SelectObject(hXorMaskDC, hOldXorMaskBitmap);
+
+ // Delete the helper DC
+ DeleteDC(hXorMaskDC);
+ DeleteDC(hAndMaskDC);
+ DeleteDC(hMainDC);
+}
+
+void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
+ _THREAD_SAFE_METHOD_
+
+ 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;
+ Size2 texture_size;
+ Rect2 atlas_rect;
+
+ if (texture.is_valid()) {
+ image = texture->get_data();
+ }
+
+ if (!image.is_valid() && atlas_texture.is_valid()) {
+ texture = atlas_texture->get_atlas();
+
+ atlas_rect.size.width = texture->get_width();
+ atlas_rect.size.height = texture->get_height();
+ atlas_rect.position.x = atlas_texture->get_region().position.x;
+ atlas_rect.position.y = atlas_texture->get_region().position.y;
+
+ texture_size.width = atlas_texture->get_region().size.x;
+ texture_size.height = atlas_texture->get_region().size.y;
+ } else if (image.is_valid()) {
+ texture_size.width = texture->get_width();
+ texture_size.height = texture->get_height();
+ }
+
+ ERR_FAIL_COND(!texture.is_valid());
+ ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
+ ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
+ ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
+
+ image = texture->get_data();
+
+ ERR_FAIL_COND(!image.is_valid());
+
+ UINT image_size = texture_size.width * texture_size.height;
+
+ // Create the BITMAP with alpha channel
+ COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size);
+
+ for (UINT index = 0; index < image_size; index++) {
+ int row_index = floor(index / texture_size.width) + atlas_rect.position.y;
+ int column_index = (index % int(texture_size.width)) + atlas_rect.position.x;
+
+ if (atlas_texture.is_valid()) {
+ column_index = MIN(column_index, atlas_rect.size.width - 1);
+ row_index = MIN(row_index, atlas_rect.size.height - 1);
+ }
+
+ *(buffer + index) = image->get_pixel(column_index, row_index).to_argb32();
+ }
+
+ // Using 4 channels, so 4 * 8 bits
+ HBITMAP bitmap = CreateBitmap(texture_size.width, texture_size.height, 1, 4 * 8, buffer);
+ COLORREF clrTransparent = -1;
+
+ // Create the AND and XOR masks for the bitmap
+ HBITMAP hAndMask = nullptr;
+ HBITMAP hXorMask = nullptr;
+
+ GetMaskBitmaps(bitmap, clrTransparent, hAndMask, hXorMask);
+
+ if (nullptr == hAndMask || nullptr == hXorMask) {
+ memfree(buffer);
+ DeleteObject(bitmap);
+ return;
+ }
+
+ // Finally, create the icon
+ ICONINFO iconinfo;
+ iconinfo.fIcon = FALSE;
+ iconinfo.xHotspot = p_hotspot.x;
+ iconinfo.yHotspot = p_hotspot.y;
+ iconinfo.hbmMask = hAndMask;
+ iconinfo.hbmColor = hXorMask;
+
+ if (cursors[p_shape])
+ DestroyIcon(cursors[p_shape]);
+
+ cursors[p_shape] = CreateIconIndirect(&iconinfo);
+
+ Vector<Variant> params;
+ params.push_back(p_cursor);
+ params.push_back(p_hotspot);
+ cursors_cache.insert(p_shape, params);
+
+ if (p_shape == cursor_shape) {
+ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
+ SetCursor(cursors[p_shape]);
+ }
+ }
+
+ if (hAndMask != nullptr) {
+ DeleteObject(hAndMask);
+ }
+
+ if (hXorMask != nullptr) {
+ DeleteObject(hXorMask);
+ }
+
+ memfree(buffer);
+ DeleteObject(bitmap);
+ } else {
+ // Reset to default system cursor
+ if (cursors[p_shape]) {
+ DestroyIcon(cursors[p_shape]);
+ cursors[p_shape] = nullptr;
+ }
+
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ cursor_set_shape(c);
+
+ cursors_cache.erase(p_shape);
+ }
+}
+
+bool DisplayServerWindows::get_swap_ok_cancel() {
+ return true;
+}
+
+void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) {
+ _THREAD_SAFE_METHOD_
+
+ AllowSetForegroundWindow(pid);
+}
+
+DisplayServer::LatinKeyboardVariant DisplayServerWindows::get_latin_keyboard_variant() const {
+ _THREAD_SAFE_METHOD_
+
+ 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
+ };
+
+ char name[KL_NAMELENGTH + 1];
+ name[0] = 0;
+ GetKeyboardLayoutNameA(name);
+
+ unsigned long hex = strtoul(name, nullptr, 16);
+
+ int i = 0;
+ while (azerty[i] != 0) {
+ if (azerty[i] == hex)
+ return LATIN_KEYBOARD_AZERTY;
+ i++;
+ }
+
+ i = 0;
+ while (qwertz[i] != 0) {
+ if (qwertz[i] == hex)
+ return LATIN_KEYBOARD_QWERTZ;
+ i++;
+ }
+
+ i = 0;
+ while (dvorak[i] != 0) {
+ if (dvorak[i] == hex)
+ return LATIN_KEYBOARD_DVORAK;
+ i++;
+ }
+
+ return LATIN_KEYBOARD_QWERTY;
+}
+
+void DisplayServerWindows::process_events() {
+ _THREAD_SAFE_METHOD_
+
+ MSG msg;
+
+ if (!drop_events) {
+ joypad->process_joypads();
+ }
+
+ while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+
+ if (!drop_events) {
+ _process_key_events();
+ Input::get_singleton()->flush_accumulated_events();
+ }
+}
+
+void DisplayServerWindows::force_process_and_drop_events() {
+ _THREAD_SAFE_METHOD_
+
+ drop_events = true;
+ process_events();
+ drop_events = false;
+}
+
+void DisplayServerWindows::release_rendering_thread() {
+}
+
+void DisplayServerWindows::make_rendering_thread() {
+}
+
+void DisplayServerWindows::swap_buffers() {
+}
+
+void DisplayServerWindows::set_native_icon(const String &p_filename) {
+ _THREAD_SAFE_METHOD_
+
+ FileAccess *f = FileAccess::open(p_filename, FileAccess::READ);
+ ERR_FAIL_COND_MSG(!f, "Cannot open file with icon '" + p_filename + "'.");
+
+ ICONDIR *icon_dir = (ICONDIR *)memalloc(sizeof(ICONDIR));
+ int pos = 0;
+
+ icon_dir->idReserved = f->get_32();
+ pos += sizeof(WORD);
+ f->seek(pos);
+
+ icon_dir->idType = f->get_32();
+ pos += sizeof(WORD);
+ f->seek(pos);
+
+ ERR_FAIL_COND_MSG(icon_dir->idType != 1, "Invalid icon file format!");
+
+ icon_dir->idCount = f->get_32();
+ pos += sizeof(WORD);
+ f->seek(pos);
+
+ icon_dir = (ICONDIR *)memrealloc(icon_dir, 3 * sizeof(WORD) + icon_dir->idCount * sizeof(ICONDIRENTRY));
+ f->get_buffer((uint8_t *)&icon_dir->idEntries[0], icon_dir->idCount * sizeof(ICONDIRENTRY));
+
+ int small_icon_index = -1; // Select 16x16 with largest color count
+ int small_icon_cc = 0;
+ int big_icon_index = -1; // Select largest
+ int big_icon_width = 16;
+ int big_icon_cc = 0;
+
+ for (int i = 0; i < icon_dir->idCount; i++) {
+ int colors = (icon_dir->idEntries[i].bColorCount == 0) ? 32768 : icon_dir->idEntries[i].bColorCount;
+ int width = (icon_dir->idEntries[i].bWidth == 0) ? 256 : icon_dir->idEntries[i].bWidth;
+ if (width == 16) {
+ if (colors >= small_icon_cc) {
+ small_icon_index = i;
+ small_icon_cc = colors;
+ }
+ }
+ if (width >= big_icon_width) {
+ if (colors >= big_icon_cc) {
+ big_icon_index = i;
+ big_icon_width = width;
+ big_icon_cc = colors;
+ }
+ }
+ }
+
+ ERR_FAIL_COND_MSG(big_icon_index == -1, "No valid icons found!");
+
+ if (small_icon_index == -1) {
+ WARN_PRINT("No small icon found, reusing " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon!");
+ small_icon_index = big_icon_index;
+ small_icon_cc = big_icon_cc;
+ }
+
+ // Read the big icon
+ DWORD bytecount_big = icon_dir->idEntries[big_icon_index].dwBytesInRes;
+ Vector<uint8_t> data_big;
+ data_big.resize(bytecount_big);
+ pos = icon_dir->idEntries[big_icon_index].dwImageOffset;
+ f->seek(pos);
+ f->get_buffer((uint8_t *)&data_big.write[0], bytecount_big);
+ HICON icon_big = CreateIconFromResource((PBYTE)&data_big.write[0], bytecount_big, TRUE, 0x00030000);
+ ERR_FAIL_COND_MSG(!icon_big, "Could not create " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
+
+ // Read the small icon
+ DWORD bytecount_small = icon_dir->idEntries[small_icon_index].dwBytesInRes;
+ Vector<uint8_t> data_small;
+ data_small.resize(bytecount_small);
+ pos = icon_dir->idEntries[small_icon_index].dwImageOffset;
+ f->seek(pos);
+ f->get_buffer((uint8_t *)&data_small.write[0], bytecount_small);
+ HICON icon_small = CreateIconFromResource((PBYTE)&data_small.write[0], bytecount_small, TRUE, 0x00030000);
+ ERR_FAIL_COND_MSG(!icon_small, "Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
+
+ // Online tradition says to be sure last error is cleared and set the small icon first
+ int err = 0;
+ SetLastError(err);
+
+ SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_small);
+ err = GetLastError();
+ ERR_FAIL_COND_MSG(err, "Error setting ICON_SMALL: " + format_error_message(err) + ".");
+
+ SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);
+ err = GetLastError();
+ ERR_FAIL_COND_MSG(err, "Error setting ICON_BIG: " + format_error_message(err) + ".");
+
+ memdelete(f);
+ memdelete(icon_dir);
+}
+
+void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!p_icon.is_valid());
+ Ref<Image> icon = p_icon->duplicate();
+ if (icon->get_format() != Image::FORMAT_RGBA8)
+ icon->convert(Image::FORMAT_RGBA8);
+ int w = icon->get_width();
+ int h = icon->get_height();
+
+ /* Create temporary bitmap buffer */
+ int icon_len = 40 + h * w * 4;
+ Vector<BYTE> v;
+ v.resize(icon_len);
+ BYTE *icon_bmp = v.ptrw();
+
+ encode_uint32(40, &icon_bmp[0]);
+ encode_uint32(w, &icon_bmp[4]);
+ encode_uint32(h * 2, &icon_bmp[8]);
+ encode_uint16(1, &icon_bmp[12]);
+ encode_uint16(32, &icon_bmp[14]);
+ encode_uint32(BI_RGB, &icon_bmp[16]);
+ encode_uint32(w * h * 4, &icon_bmp[20]);
+ encode_uint32(0, &icon_bmp[24]);
+ encode_uint32(0, &icon_bmp[28]);
+ encode_uint32(0, &icon_bmp[32]);
+ encode_uint32(0, &icon_bmp[36]);
+
+ uint8_t *wr = &icon_bmp[40];
+ const uint8_t *r = icon->get_data().ptr();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];
+ uint8_t *wpx = &wr[(i * w + j) * 4];
+ wpx[0] = rpx[2];
+ wpx[1] = rpx[1];
+ wpx[2] = rpx[0];
+ wpx[3] = rpx[3];
+ }
+ }
+
+ HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
+
+ /* Set the icon for the window */
+ SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon);
+
+ /* Set the icon in the task manager (should we do this?) */
+ SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon);
+}
+
+void DisplayServerWindows::vsync_set_use_via_compositor(bool p_enable) {
+}
+
+bool DisplayServerWindows::vsync_is_using_via_compositor() const {
+ return false;
+}
+
+void DisplayServerWindows::set_context(Context p_context) {
+}
+
+#define MI_WP_SIGNATURE 0xFF515700
+#define SIGNATURE_MASK 0xFFFFFF00
+// Keeping the name suggested by Microsoft, but this macro really answers:
+// Is this mouse event emulated from touch or pen input?
+#define IsPenEvent(dw) (((dw)&SIGNATURE_MASK) == MI_WP_SIGNATURE)
+// This one tells whether the event comes from touchscreen (and not from pen)
+#define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw)&0x80))
+
+void DisplayServerWindows::_touch_event(WindowID p_window, bool p_pressed, float p_x, float p_y, int idx) {
+ // Defensive
+ if (touch_state.has(idx) == p_pressed)
+ return;
+
+ if (p_pressed) {
+ touch_state.insert(idx, Vector2(p_x, p_y));
+ } else {
+ touch_state.erase(idx);
+ }
+
+ Ref<InputEventScreenTouch> event;
+ event.instance();
+ event->set_index(idx);
+ event->set_window_id(p_window);
+ event->set_pressed(p_pressed);
+ event->set_position(Vector2(p_x, p_y));
+
+ Input::get_singleton()->accumulate_input_event(event);
+}
+
+void DisplayServerWindows::_drag_event(WindowID p_window, float p_x, float p_y, int idx) {
+ Map<int, Vector2>::Element *curr = touch_state.find(idx);
+ // Defensive
+ if (!curr)
+ return;
+
+ if (curr->get() == Vector2(p_x, p_y))
+ return;
+
+ Ref<InputEventScreenDrag> event;
+ event.instance();
+ event->set_window_id(p_window);
+ event->set_index(idx);
+ event->set_position(Vector2(p_x, p_y));
+ event->set_relative(Vector2(p_x, p_y) - curr->get());
+
+ Input::get_singleton()->accumulate_input_event(event);
+
+ curr->get() = Vector2(p_x, p_y);
+}
+
+void DisplayServerWindows::_send_window_event(const WindowData &wd, WindowEvent p_event) {
+ if (!wd.event_callback.is_null()) {
+ Variant event = int(p_event);
+ Variant *eventp = &event;
+ Variant ret;
+ Callable::CallError ce;
+ wd.event_callback.call((const Variant **)&eventp, 1, ret, ce);
+ }
+}
+
+void DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> &p_event) {
+ ((DisplayServerWindows *)(get_singleton()))->_dispatch_input_event(p_event);
+}
+
+void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) {
+ _THREAD_SAFE_METHOD_
+ if (in_dispatch_input_event) {
+ return;
+ }
+
+ in_dispatch_input_event = true;
+ Variant ev = p_event;
+ Variant *evp = &ev;
+ Variant ret;
+ Callable::CallError ce;
+
+ 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()));
+ Callable callable = windows[event_from_window->get_window_id()].input_event_callback;
+ if (callable.is_null()) {
+ in_dispatch_input_event = false;
+ return;
+ }
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ } else {
+ //send to all windows
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ Callable callable = E->get().input_event_callback;
+ if (callable.is_null()) {
+ continue;
+ }
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ }
+ }
+
+ in_dispatch_input_event = false;
+}
+
+LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ if (drop_events) {
+ if (user_proc) {
+ return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
+ } else {
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+ }
+ };
+
+ WindowID window_id = INVALID_WINDOW_ID;
+
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (E->get().hWnd == hWnd) {
+ window_id = E->key();
+ break;
+ }
+ }
+
+ switch (uMsg) // Check For Windows Messages
+ {
+ case WM_SETFOCUS: {
+ windows[window_id].window_has_focus = true;
+ last_focused_window = window_id;
+
+ // Restore mouse mode
+ _set_mouse_mode_impl(mouse_mode);
+
+ break;
+ }
+ case WM_KILLFOCUS: {
+ windows[window_id].window_has_focus = false;
+ last_focused_window = window_id;
+
+ // Release capture unconditionally because it can be set due to dragging, in addition to captured mode
+ ReleaseCapture();
+
+ // Release every touch to avoid sticky points
+ for (Map<int, Vector2>::Element *E = touch_state.front(); E; E = E->next()) {
+ _touch_event(window_id, false, E->get().x, E->get().y, E->key());
+ }
+ touch_state.clear();
+
+ 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;
+ };
+
+ 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));
+ }
+
+ return 0; // Return To The Message Loop
+ }
+ case WM_GETMINMAXINFO: {
+ if (windows[window_id].resizable && !windows[window_id].fullscreen) {
+ Size2 decor = window_get_size(window_id) - window_get_real_size(window_id); // Size of window decorations
+ MINMAXINFO *min_max_info = (MINMAXINFO *)lParam;
+ if (windows[window_id].min_size != Size2()) {
+ min_max_info->ptMinTrackSize.x = windows[window_id].min_size.x + decor.x;
+ min_max_info->ptMinTrackSize.y = windows[window_id].min_size.y + decor.y;
+ }
+ if (windows[window_id].max_size != Size2()) {
+ min_max_info->ptMaxTrackSize.x = windows[window_id].max_size.x + decor.x;
+ min_max_info->ptMaxTrackSize.y = windows[window_id].max_size.y + decor.y;
+ }
+ return 0;
+ } else {
+ break;
+ }
+ }
+ case WM_PAINT:
+
+ Main::force_redraw();
+ break;
+
+ case WM_SYSCOMMAND: // Intercept System Commands
+ {
+ switch (wParam) // Check System Calls
+ {
+ case SC_SCREENSAVE: // Screensaver Trying To Start?
+ case SC_MONITORPOWER: // Monitor Trying To Enter Powersave?
+ return 0; // Prevent From Happening
+ case SC_KEYMENU:
+ if ((lParam >> 16) <= 0)
+ return 0;
+ }
+ break; // Exit
+ }
+
+ case WM_CLOSE: // Did We Receive A Close Message?
+ {
+ _send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);
+
+ return 0; // Jump Back
+ }
+ case WM_MOUSELEAVE: {
+ old_invalid = true;
+ outside = true;
+
+ _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
+
+ } break;
+ case WM_INPUT: {
+ if (mouse_mode != MOUSE_MODE_CAPTURED || !use_raw_input) {
+ break;
+ }
+
+ UINT dwSize;
+
+ GetRawInputData((HRAWINPUT)lParam, RID_INPUT, nullptr, &dwSize, sizeof(RAWINPUTHEADER));
+ LPBYTE lpb = new BYTE[dwSize];
+ if (lpb == nullptr) {
+ return 0;
+ }
+
+ if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize)
+ OutputDebugString(TEXT("GetRawInputData does not return correct size !\n"));
+
+ RAWINPUT *raw = (RAWINPUT *)lpb;
+
+ if (raw->header.dwType == RIM_TYPEMOUSE) {
+ Ref<InputEventMouseMotion> mm;
+ mm.instance();
+
+ mm->set_window_id(window_id);
+ mm->set_control(control_mem);
+ mm->set_shift(shift_mem);
+ mm->set_alt(alt_mem);
+
+ mm->set_pressure((raw->data.mouse.ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN) ? 1.0f : 0.0f);
+
+ mm->set_button_mask(last_button_state);
+
+ Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);
+
+ // centering just so it works as before
+ POINT pos = { (int)c.x, (int)c.y };
+ ClientToScreen(windows[window_id].hWnd, &pos);
+ SetCursorPos(pos.x, pos.y);
+
+ mm->set_position(c);
+ mm->set_global_position(c);
+ Input::get_singleton()->set_mouse_position(c);
+ mm->set_speed(Vector2(0, 0));
+
+ if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) {
+ mm->set_relative(Vector2(raw->data.mouse.lLastX, raw->data.mouse.lLastY));
+
+ } else if (raw->data.mouse.usFlags == MOUSE_MOVE_ABSOLUTE) {
+ int nScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ int nScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
+ int nScreenLeft = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ int nScreenTop = GetSystemMetrics(SM_YVIRTUALSCREEN);
+
+ Vector2 abs_pos(
+ (double(raw->data.mouse.lLastX) - 65536.0 / (nScreenWidth)) * nScreenWidth / 65536.0 + nScreenLeft,
+ (double(raw->data.mouse.lLastY) - 65536.0 / (nScreenHeight)) * nScreenHeight / 65536.0 + nScreenTop);
+
+ POINT coords; //client coords
+ coords.x = abs_pos.x;
+ coords.y = abs_pos.y;
+
+ ScreenToClient(hWnd, &coords);
+
+ mm->set_relative(Vector2(coords.x - old_x, coords.y - old_y));
+ old_x = coords.x;
+ old_y = coords.y;
+
+ /*Input.mi.dx = (int)((((double)(pos.x)-nScreenLeft) * 65536) / nScreenWidth + 65536 / (nScreenWidth));
+ Input.mi.dy = (int)((((double)(pos.y)-nScreenTop) * 65536) / nScreenHeight + 65536 / (nScreenHeight));
+ */
+ }
+
+ if (windows[window_id].window_has_focus && mm->get_relative() != Vector2())
+ Input::get_singleton()->accumulate_input_event(mm);
+ }
+ delete[] lpb;
+ } break;
+ case WT_CSRCHANGE:
+ case WT_PROXIMITY: {
+ if ((OS::get_singleton()->get_current_tablet_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);
+ windows[window_id].max_pressure = int(pressure.axMax);
+ }
+ AXIS orientation[3];
+ if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {
+ windows[window_id].tilt_supported = orientation[0].axResolution && orientation[1].axResolution;
+ }
+ return 0;
+ }
+ } break;
+ case WT_PACKET: {
+ if ((OS::get_singleton()->get_current_tablet_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);
+ windows[window_id].last_pressure = pressure;
+ windows[window_id].last_pressure_update = 0;
+
+ double azim = (packet.pkOrientation.orAzimuth / 10.0f) * (Math_PI / 180);
+ double alt = Math::tan((Math::abs(packet.pkOrientation.orAltitude / 10.0f)) * (Math_PI / 180));
+
+ if (windows[window_id].tilt_supported) {
+ windows[window_id].last_tilt = Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt));
+ } else {
+ windows[window_id].last_tilt = Vector2();
+ }
+
+ POINT coords;
+ GetCursorPos(&coords);
+ ScreenToClient(windows[window_id].hWnd, &coords);
+
+ // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
+ if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED)
+ break;
+
+ 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_alt(alt_mem);
+
+ mm->set_pressure(windows[window_id].last_pressure);
+ mm->set_tilt(windows[window_id].last_tilt);
+
+ mm->set_button_mask(last_button_state);
+
+ mm->set_position(Vector2(coords.x, coords.y));
+ mm->set_global_position(Vector2(coords.x, coords.y));
+
+ if (mouse_mode == MOUSE_MODE_CAPTURED) {
+ Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);
+ old_x = c.x;
+ old_y = c.y;
+
+ if (mm->get_position() == c) {
+ center = c;
+ return 0;
+ }
+
+ Point2i ncenter = mm->get_position();
+ center = ncenter;
+ POINT pos = { (int)c.x, (int)c.y };
+ ClientToScreen(windows[window_id].hWnd, &pos);
+ SetCursorPos(pos.x, pos.y);
+ }
+
+ Input::get_singleton()->set_mouse_position(mm->get_position());
+ mm->set_speed(Input::get_singleton()->get_last_mouse_speed());
+
+ if (old_invalid) {
+ old_x = mm->get_position().x;
+ old_y = mm->get_position().y;
+ old_invalid = false;
+ }
+
+ mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
+ old_x = mm->get_position().x;
+ old_y = mm->get_position().y;
+ if (windows[window_id].window_has_focus)
+ Input::get_singleton()->accumulate_input_event(mm);
+ }
+ return 0;
+ }
+ } break;
+ case WM_POINTERENTER: {
+ if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
+ break;
+ }
+
+ if ((OS::get_singleton()->get_current_tablet_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) {
+ 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;
+ }
+
+ POINTER_PEN_INFO pen_info;
+ if (!win8p_GetPointerPenInfo(pointer_id, &pen_info)) {
+ break;
+ }
+
+ if (Input::get_singleton()->is_emulating_mouse_from_touch()) {
+ // Universal translation enabled; ignore OS translation
+ LPARAM extra = GetMessageExtraInfo();
+ if (IsTouchEvent(extra)) {
+ break;
+ }
+ }
+
+ if (outside) {
+ //mouse enter
+
+ if (mouse_mode != MOUSE_MODE_CAPTURED) {
+ _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
+ }
+
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ cursor_set_shape(c);
+ outside = false;
+
+ //Once-Off notification, must call again....
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = hWnd;
+ tme.dwHoverTime = HOVER_DEFAULT;
+ TrackMouseEvent(&tme);
+ }
+
+ // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
+ if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED)
+ break;
+
+ Ref<InputEventMouseMotion> mm;
+ mm.instance();
+
+ mm->set_window_id(window_id);
+ if (pen_info.penMask & PEN_MASK_PRESSURE) {
+ mm->set_pressure((float)pen_info.pressure / 1024);
+ } else {
+ mm->set_pressure((HIWORD(wParam) & POINTER_MESSAGE_FLAG_FIRSTBUTTON) ? 1.0f : 0.0f);
+ }
+ if ((pen_info.penMask & PEN_MASK_TILT_X) && (pen_info.penMask & PEN_MASK_TILT_Y)) {
+ 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_alt(alt_mem);
+
+ mm->set_button_mask(last_button_state);
+
+ POINT coords; //client coords
+ coords.x = GET_X_LPARAM(lParam);
+ coords.y = GET_Y_LPARAM(lParam);
+
+ ScreenToClient(windows[window_id].hWnd, &coords);
+
+ mm->set_position(Vector2(coords.x, coords.y));
+ mm->set_global_position(Vector2(coords.x, coords.y));
+
+ if (mouse_mode == MOUSE_MODE_CAPTURED) {
+ Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);
+ old_x = c.x;
+ old_y = c.y;
+
+ if (mm->get_position() == c) {
+ center = c;
+ return 0;
+ }
+
+ Point2i ncenter = mm->get_position();
+ center = ncenter;
+ POINT pos = { (int)c.x, (int)c.y };
+ ClientToScreen(hWnd, &pos);
+ SetCursorPos(pos.x, pos.y);
+ }
+
+ Input::get_singleton()->set_mouse_position(mm->get_position());
+ mm->set_speed(Input::get_singleton()->get_last_mouse_speed());
+
+ if (old_invalid) {
+ old_x = mm->get_position().x;
+ old_y = mm->get_position().y;
+ old_invalid = false;
+ }
+
+ mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
+ old_x = mm->get_position().x;
+ old_y = mm->get_position().y;
+ if (windows[window_id].window_has_focus) {
+ 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;
+ }
+
+ if (Input::get_singleton()->is_emulating_mouse_from_touch()) {
+ // Universal translation enabled; ignore OS translation
+ LPARAM extra = GetMessageExtraInfo();
+ if (IsTouchEvent(extra)) {
+ break;
+ }
+ }
+
+ if (outside) {
+ //mouse enter
+
+ if (mouse_mode != MOUSE_MODE_CAPTURED) {
+ _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
+ }
+
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ cursor_set_shape(c);
+ outside = false;
+
+ //Once-Off notification, must call again....
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = hWnd;
+ tme.dwHoverTime = HOVER_DEFAULT;
+ TrackMouseEvent(&tme);
+ }
+
+ // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
+ if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED)
+ break;
+
+ Ref<InputEventMouseMotion> mm;
+ mm.instance();
+ mm->set_window_id(window_id);
+ mm->set_control((wParam & MK_CONTROL) != 0);
+ 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) {
+ // 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++;
+ } else {
+ windows[window_id].last_tilt = Vector2();
+ windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f;
+ }
+ } else {
+ windows[window_id].last_tilt = Vector2();
+ windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f;
+ }
+
+ mm->set_pressure(windows[window_id].last_pressure);
+ mm->set_tilt(windows[window_id].last_tilt);
+
+ mm->set_button_mask(last_button_state);
+
+ mm->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
+ mm->set_global_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
+
+ if (mouse_mode == MOUSE_MODE_CAPTURED) {
+ Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);
+ old_x = c.x;
+ old_y = c.y;
+
+ if (mm->get_position() == c) {
+ center = c;
+ return 0;
+ }
+
+ Point2i ncenter = mm->get_position();
+ center = ncenter;
+ POINT pos = { (int)c.x, (int)c.y };
+ ClientToScreen(windows[window_id].hWnd, &pos);
+ SetCursorPos(pos.x, pos.y);
+ }
+
+ Input::get_singleton()->set_mouse_position(mm->get_position());
+ mm->set_speed(Input::get_singleton()->get_last_mouse_speed());
+
+ if (old_invalid) {
+ old_x = mm->get_position().x;
+ old_y = mm->get_position().y;
+ old_invalid = false;
+ }
+
+ mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
+ old_x = mm->get_position().x;
+ old_y = mm->get_position().y;
+ if (windows[window_id].window_has_focus)
+ Input::get_singleton()->accumulate_input_event(mm);
+
+ } break;
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ if (Input::get_singleton()->is_emulating_mouse_from_touch()) {
+ // Universal translation enabled; ignore OS translations for left button
+ LPARAM extra = GetMessageExtraInfo();
+ if (IsTouchEvent(extra)) {
+ break;
+ }
+ }
+ [[fallthrough]];
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP:
+ case WM_MOUSEWHEEL:
+ case WM_MOUSEHWHEEL:
+ case WM_LBUTTONDBLCLK:
+ case WM_MBUTTONDBLCLK:
+ case WM_RBUTTONDBLCLK:
+ case WM_XBUTTONDBLCLK:
+ case WM_XBUTTONDOWN:
+ case WM_XBUTTONUP: {
+ Ref<InputEventMouseButton> mb;
+ mb.instance();
+ mb->set_window_id(window_id);
+
+ switch (uMsg) {
+ case WM_LBUTTONDOWN: {
+ mb->set_pressed(true);
+ mb->set_button_index(1);
+ } break;
+ case WM_LBUTTONUP: {
+ mb->set_pressed(false);
+ mb->set_button_index(1);
+ } break;
+ case WM_MBUTTONDOWN: {
+ mb->set_pressed(true);
+ mb->set_button_index(3);
+ } break;
+ case WM_MBUTTONUP: {
+ mb->set_pressed(false);
+ mb->set_button_index(3);
+ } break;
+ case WM_RBUTTONDOWN: {
+ mb->set_pressed(true);
+ mb->set_button_index(2);
+ } break;
+ case WM_RBUTTONUP: {
+ mb->set_pressed(false);
+ mb->set_button_index(2);
+ } break;
+ case WM_LBUTTONDBLCLK: {
+ mb->set_pressed(true);
+ mb->set_button_index(1);
+ mb->set_doubleclick(true);
+ } break;
+ case WM_RBUTTONDBLCLK: {
+ mb->set_pressed(true);
+ mb->set_button_index(2);
+ mb->set_doubleclick(true);
+ } break;
+ case WM_MBUTTONDBLCLK: {
+ mb->set_pressed(true);
+ mb->set_button_index(3);
+ mb->set_doubleclick(true);
+ } break;
+ case WM_MOUSEWHEEL: {
+ mb->set_pressed(true);
+ int motion = (short)HIWORD(wParam);
+ if (!motion)
+ return 0;
+
+ if (motion > 0)
+ mb->set_button_index(BUTTON_WHEEL_UP);
+ else
+ mb->set_button_index(BUTTON_WHEEL_DOWN);
+
+ } break;
+ case WM_MOUSEHWHEEL: {
+ mb->set_pressed(true);
+ int motion = (short)HIWORD(wParam);
+ if (!motion)
+ return 0;
+
+ if (motion < 0) {
+ mb->set_button_index(BUTTON_WHEEL_LEFT);
+ mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA));
+ } else {
+ mb->set_button_index(BUTTON_WHEEL_RIGHT);
+ mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA));
+ }
+ } break;
+ case WM_XBUTTONDOWN: {
+ mb->set_pressed(true);
+ if (HIWORD(wParam) == XBUTTON1)
+ mb->set_button_index(BUTTON_XBUTTON1);
+ else
+ mb->set_button_index(BUTTON_XBUTTON2);
+ } break;
+ case WM_XBUTTONUP: {
+ mb->set_pressed(false);
+ if (HIWORD(wParam) == XBUTTON1)
+ mb->set_button_index(BUTTON_XBUTTON1);
+ else
+ mb->set_button_index(BUTTON_XBUTTON2);
+ } break;
+ case WM_XBUTTONDBLCLK: {
+ mb->set_pressed(true);
+ if (HIWORD(wParam) == XBUTTON1)
+ mb->set_button_index(BUTTON_XBUTTON1);
+ else
+ mb->set_button_index(BUTTON_XBUTTON2);
+ mb->set_doubleclick(true);
+ } break;
+ default: {
+ return 0;
+ }
+ }
+
+ mb->set_control((wParam & MK_CONTROL) != 0);
+ mb->set_shift((wParam & MK_SHIFT) != 0);
+ mb->set_alt(alt_mem);
+ //mb->get_alt()=(wParam&MK_MENU)!=0;
+ if (mb->is_pressed())
+ last_button_state |= (1 << (mb->get_button_index() - 1));
+ else
+ last_button_state &= ~(1 << (mb->get_button_index() - 1));
+ mb->set_button_mask(last_button_state);
+
+ mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
+
+ if (mouse_mode == MOUSE_MODE_CAPTURED && !use_raw_input) {
+ mb->set_position(Vector2(old_x, old_y));
+ }
+
+ if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) {
+ if (mb->is_pressed()) {
+ if (++pressrc > 0 && mouse_mode != MOUSE_MODE_CAPTURED)
+ SetCapture(hWnd);
+ } else {
+ if (--pressrc <= 0) {
+ if (mouse_mode != MOUSE_MODE_CAPTURED) {
+ ReleaseCapture();
+ }
+ pressrc = 0;
+ }
+ }
+ } else {
+ // for reasons unknown to mankind, wheel comes in screen coordinates
+ POINT coords;
+ coords.x = mb->get_position().x;
+ coords.y = mb->get_position().y;
+
+ ScreenToClient(hWnd, &coords);
+
+ mb->set_position(Vector2(coords.x, coords.y));
+ }
+
+ mb->set_global_position(mb->get_position());
+
+ Input::get_singleton()->accumulate_input_event(mb);
+ if (mb->is_pressed() && mb->get_button_index() > 3 && mb->get_button_index() < 8) {
+ //send release for mouse wheel
+ Ref<InputEventMouseButton> mbd = mb->duplicate();
+ mbd->set_window_id(window_id);
+ last_button_state &= ~(1 << (mbd->get_button_index() - 1));
+ mbd->set_button_mask(last_button_state);
+ mbd->set_pressed(false);
+ Input::get_singleton()->accumulate_input_event(mbd);
+ }
+
+ } break;
+
+ case WM_MOVE: {
+ if (!IsIconic(windows[window_id].hWnd)) {
+ int x = int16_t(LOWORD(lParam));
+ int y = int16_t(HIWORD(lParam));
+ windows[window_id].last_pos = Point2(x, y);
+
+ if (!windows[window_id].rect_changed_callback.is_null()) {
+ Variant size = Rect2i(windows[window_id].last_pos.x, windows[window_id].last_pos.y, windows[window_id].width, windows[window_id].height);
+ Variant *sizep = &size;
+ Variant ret;
+ Callable::CallError ce;
+ windows[window_id].rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce);
+ }
+ }
+ } break;
+
+ case WM_SIZE: {
+ // Ignore size when a SIZE_MINIMIZED event is triggered
+ if (wParam != SIZE_MINIMIZED) {
+ int window_w = LOWORD(lParam);
+ int window_h = HIWORD(lParam);
+ if (window_w > 0 && window_h > 0 && !windows[window_id].preserve_window_size) {
+ windows[window_id].width = window_w;
+ windows[window_id].height = window_h;
+
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_resize(window_id, windows[window_id].width, windows[window_id].height);
+ }
+#endif
+
+ } else {
+ windows[window_id].preserve_window_size = false;
+ window_set_size(Size2(windows[window_id].width, windows[window_id].height), window_id);
+ }
+ }
+
+ if (!windows[window_id].rect_changed_callback.is_null()) {
+ Variant size = Rect2i(windows[window_id].last_pos.x, windows[window_id].last_pos.y, windows[window_id].width, windows[window_id].height);
+ Variant *sizep = &size;
+ Variant ret;
+ Callable::CallError ce;
+ windows[window_id].rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce);
+ }
+
+ if (wParam == SIZE_MAXIMIZED) {
+ windows[window_id].maximized = true;
+ windows[window_id].minimized = false;
+ } else if (wParam == SIZE_MINIMIZED) {
+ windows[window_id].maximized = false;
+ windows[window_id].minimized = true;
+ } else if (wParam == SIZE_RESTORED) {
+ windows[window_id].maximized = false;
+ windows[window_id].minimized = false;
+ }
+#if 0
+ if (is_layered_allowed() && layered_window) {
+ DeleteObject(hBitmap);
+
+ RECT r;
+ GetWindowRect(hWnd, &r);
+ dib_size = Size2i(r.right - r.left, r.bottom - r.top);
+
+ BITMAPINFO bmi;
+ ZeroMemory(&bmi, sizeof(BITMAPINFO));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = dib_size.x;
+ bmi.bmiHeader.biHeight = dib_size.y;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4;
+ hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, nullptr, 0x0);
+ SelectObject(hDC_dib, hBitmap);
+
+ ZeroMemory(dib_data, dib_size.x * dib_size.y * 4);
+ }
+#endif
+ //return 0; // Jump Back
+ } break;
+
+ case WM_ENTERSIZEMOVE: {
+ Input::get_singleton()->release_pressed_events();
+ 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);
+ } break;
+ case WM_TIMER: {
+ if (wParam == move_timer_id) {
+ _process_key_events();
+ if (!Main::is_iterating()) {
+ Main::iteration();
+ }
+ }
+ } break;
+
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ case WM_KEYUP:
+ case WM_KEYDOWN: {
+ if (wParam == VK_SHIFT)
+ shift_mem = uMsg == WM_KEYDOWN;
+ if (wParam == VK_CONTROL)
+ control_mem = uMsg == WM_KEYDOWN;
+ if (wParam == VK_MENU) {
+ alt_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN);
+ if (lParam & (1 << 24))
+ gr_mem = alt_mem;
+ }
+
+ if (mouse_mode == MOUSE_MODE_CAPTURED) {
+ // When SetCapture is used, ALT+F4 hotkey is ignored by Windows, so handle it ourselves
+ if (wParam == VK_F4 && alt_mem && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)) {
+ _send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);
+ }
+ }
+ /*
+ if (wParam==VK_WIN) TODO wtf is this?
+ meta_mem=uMsg==WM_KEYDOWN;
+ */
+ [[fallthrough]];
+ }
+ case WM_CHAR: {
+ ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);
+
+ // Make sure we don't include modifiers for the modifier key itself.
+ KeyEvent ke;
+ ke.shift = (wParam != VK_SHIFT) ? shift_mem : false;
+ ke.alt = (!(wParam == VK_MENU && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN))) ? alt_mem : false;
+ ke.control = (wParam != VK_CONTROL) ? control_mem : false;
+ ke.meta = meta_mem;
+ ke.uMsg = uMsg;
+ ke.window_id = window_id;
+
+ if (ke.uMsg == WM_SYSKEYDOWN)
+ ke.uMsg = WM_KEYDOWN;
+ if (ke.uMsg == WM_SYSKEYUP)
+ ke.uMsg = WM_KEYUP;
+
+ ke.wParam = wParam;
+ ke.lParam = lParam;
+ key_event_buffer[key_event_pos++] = ke;
+
+ } break;
+ case WM_INPUTLANGCHANGEREQUEST: {
+ // FIXME: Do something?
+ } break;
+
+ case WM_TOUCH: {
+ BOOL bHandled = FALSE;
+ UINT cInputs = LOWORD(wParam);
+ PTOUCHINPUT pInputs = memnew_arr(TOUCHINPUT, cInputs);
+ if (pInputs) {
+ if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))) {
+ for (UINT i = 0; i < cInputs; i++) {
+ TOUCHINPUT ti = pInputs[i];
+ POINT touch_pos = {
+ TOUCH_COORD_TO_PIXEL(ti.x),
+ TOUCH_COORD_TO_PIXEL(ti.y),
+ };
+ ScreenToClient(hWnd, &touch_pos);
+ //do something with each touch input entry
+ if (ti.dwFlags & TOUCHEVENTF_MOVE) {
+ _drag_event(window_id, touch_pos.x, touch_pos.y, ti.dwID);
+ } else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) {
+ _touch_event(window_id, ti.dwFlags & TOUCHEVENTF_DOWN, touch_pos.x, touch_pos.y, ti.dwID);
+ };
+ }
+ bHandled = TRUE;
+ } else {
+ /* handle the error here */
+ }
+ memdelete_arr(pInputs);
+ } else {
+ /* handle the error here, probably out of memory */
+ }
+ if (bHandled) {
+ CloseTouchInputHandle((HTOUCHINPUT)lParam);
+ return 0;
+ };
+
+ } break;
+
+ case WM_DEVICECHANGE: {
+ joypad->probe_joypads();
+ } break;
+ case WM_SETCURSOR: {
+ 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)
+ hCursor = SetCursor(nullptr);
+ else
+ SetCursor(nullptr);
+ } else {
+ if (hCursor != nullptr) {
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ cursor_set_shape(c);
+ hCursor = nullptr;
+ }
+ }
+ }
+
+ } break;
+ case WM_DROPFILES: {
+ HDROP hDropInfo = (HDROP)wParam;
+ const int buffsize = 4096;
+ wchar_t buf[buffsize];
+
+ int fcount = DragQueryFileW(hDropInfo, 0xFFFFFFFF, nullptr, 0);
+
+ Vector<String> files;
+
+ for (int i = 0; i < fcount; i++) {
+ DragQueryFileW(hDropInfo, i, buf, buffsize);
+ String file = buf;
+ files.push_back(file);
+ }
+
+ if (files.size() && !windows[window_id].drop_files_callback.is_null()) {
+ Variant v = files;
+ Variant *vp = &v;
+ Variant ret;
+ Callable::CallError ce;
+ windows[window_id].drop_files_callback.call((const Variant **)&vp, 1, ret, ce);
+ }
+
+ } break;
+
+ default: {
+ if (user_proc) {
+ return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
+ };
+ };
+ }
+
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+}
+
+LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());
+ if (ds_win)
+ return ds_win->WndProc(hWnd, uMsg, wParam, lParam);
+ else
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+}
+
+void DisplayServerWindows::_process_key_events() {
+ for (int i = 0; i < key_event_pos; i++) {
+ KeyEvent &ke = key_event_buffer[i];
+ switch (ke.uMsg) {
+ 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))) {
+ Ref<InputEventKey> k;
+ k.instance();
+
+ k->set_window_id(ke.window_id);
+ k->set_shift(ke.shift);
+ k->set_alt(ke.alt);
+ k->set_control(ke.control);
+ k->set_metakey(ke.meta);
+ 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);
+ if (k->get_unicode() && gr_mem) {
+ k->set_alt(false);
+ k->set_control(false);
+ }
+
+ if (k->get_unicode() < 32)
+ k->set_unicode(0);
+
+ Input::get_singleton()->accumulate_input_event(k);
+ }
+
+ //do nothing
+ } break;
+ case WM_KEYUP:
+ case WM_KEYDOWN: {
+ Ref<InputEventKey> k;
+ k.instance();
+
+ k->set_window_id(ke.window_id);
+ k->set_shift(ke.shift);
+ k->set_alt(ke.alt);
+ k->set_control(ke.control);
+ k->set_metakey(ke.meta);
+
+ k->set_pressed(ke.uMsg == WM_KEYDOWN);
+
+ if ((ke.lParam & (1 << 24)) && (ke.wParam == VK_RETURN)) {
+ // Special case for Numpad Enter key
+ k->set_keycode(KEY_KP_ENTER);
+ } else {
+ k->set_keycode(KeyMappingWindows::get_keysym(ke.wParam));
+ }
+
+ 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);
+ }
+ if (k->get_unicode() && gr_mem) {
+ k->set_alt(false);
+ k->set_control(false);
+ }
+
+ if (k->get_unicode() < 32)
+ k->set_unicode(0);
+
+ k->set_echo((ke.uMsg == WM_KEYDOWN && (ke.lParam & (1 << 30))));
+
+ Input::get_singleton()->accumulate_input_event(k);
+
+ } break;
+ }
+ }
+
+ key_event_pos = 0;
+}
+
+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);
+ wd.wtctx = 0;
+ }
+ if ((p_new_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;
+ wd.wtlc.lcMoveMask = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;
+ wd.wtlc.lcPktMode = 0;
+ wd.wtlc.lcOutOrgX = 0;
+ wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX;
+ wd.wtlc.lcOutOrgY = 0;
+ wd.wtlc.lcOutExtY = -wd.wtlc.lcInExtY;
+ wd.wtctx = wintab_WTOpen(wd.hWnd, &wd.wtlc, false);
+ if (wd.wtctx) {
+ wintab_WTEnable(wd.wtctx, true);
+ AXIS pressure;
+ if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {
+ wd.min_pressure = int(pressure.axMin);
+ wd.max_pressure = int(pressure.axMax);
+ }
+ AXIS orientation[3];
+ if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {
+ wd.tilt_supported = orientation[0].axResolution && orientation[1].axResolution;
+ }
+ wintab_WTEnable(wd.wtctx, true);
+ } else {
+ print_verbose("WinTab context creation failed.");
+ }
+ }
+ }
+}
+
+DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+ DWORD dwExStyle;
+ DWORD dwStyle;
+
+ _get_window_style(window_id_counter == MAIN_WINDOW_ID, p_mode == WINDOW_MODE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT), dwStyle, dwExStyle);
+
+ RECT WindowRect;
+
+ WindowRect.left = p_rect.position.x;
+ WindowRect.right = p_rect.position.x + p_rect.size.x;
+ WindowRect.top = p_rect.position.y;
+ WindowRect.bottom = p_rect.position.y + p_rect.size.y;
+
+ AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
+
+ WindowID id = window_id_counter;
+ {
+ WindowData wd;
+
+ wd.hWnd = CreateWindowExW(
+ dwExStyle,
+ L"Engine", L"",
+ dwStyle,
+ // (GetSystemMetrics(SM_CXSCREEN) - WindowRect.right) / 2,
+ // (GetSystemMetrics(SM_CYSCREEN) - WindowRect.bottom) / 2,
+ WindowRect.left,
+ WindowRect.top,
+ WindowRect.right - WindowRect.left,
+ WindowRect.bottom - WindowRect.top,
+ nullptr, nullptr, hInstance, nullptr);
+ if (!wd.hWnd) {
+ MessageBoxW(nullptr, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);
+ return INVALID_WINDOW_ID;
+ }
+#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);
+ }
+ }
+#endif
+
+ RegisterTouchWindow(wd.hWnd, 0);
+
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = wd.hWnd;
+ tme.dwHoverTime = HOVER_DEFAULT;
+ TrackMouseEvent(&tme);
+
+ DragAcceptFiles(wd.hWnd, true);
+
+ if ((OS::get_singleton()->get_current_tablet_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;
+ wd.wtlc.lcMoveMask = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;
+ wd.wtlc.lcPktMode = 0;
+ wd.wtlc.lcOutOrgX = 0;
+ wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX;
+ wd.wtlc.lcOutOrgY = 0;
+ wd.wtlc.lcOutExtY = -wd.wtlc.lcInExtY;
+ wd.wtctx = wintab_WTOpen(wd.hWnd, &wd.wtlc, false);
+ if (wd.wtctx) {
+ wintab_WTEnable(wd.wtctx, true);
+ AXIS pressure;
+ if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {
+ wd.min_pressure = int(pressure.axMin);
+ wd.max_pressure = int(pressure.axMax);
+ }
+ AXIS orientation[3];
+ if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {
+ wd.tilt_supported = orientation[0].axResolution && orientation[1].axResolution;
+ }
+ } else {
+ print_verbose("WinTab context creation failed.");
+ }
+ } else {
+ wd.wtctx = 0;
+ }
+
+ wd.last_pressure = 0;
+ wd.last_pressure_update = 0;
+ wd.last_tilt = Vector2();
+
+ // IME
+ wd.im_himc = ImmGetContext(wd.hWnd);
+ ImmReleaseContext(wd.hWnd, wd.im_himc);
+
+ wd.im_position = Vector2();
+ wd.last_pos = p_rect.position;
+ wd.width = p_rect.size.width;
+ wd.height = p_rect.size.height;
+
+ windows[id] = wd;
+
+ window_id_counter++;
+ }
+
+ return id;
+}
+
+// WinTab API
+bool DisplayServerWindows::wintab_available = false;
+WTOpenPtr DisplayServerWindows::wintab_WTOpen = nullptr;
+WTClosePtr DisplayServerWindows::wintab_WTClose = nullptr;
+WTInfoPtr DisplayServerWindows::wintab_WTInfo = nullptr;
+WTPacketPtr DisplayServerWindows::wintab_WTPacket = nullptr;
+WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr;
+
+// Windows Ink API
+bool DisplayServerWindows::winink_available = false;
+GetPointerTypePtr DisplayServerWindows::win8p_GetPointerType = nullptr;
+GetPointerPenInfoPtr DisplayServerWindows::win8p_GetPointerPenInfo = nullptr;
+
+typedef enum _SHC_PROCESS_DPI_AWARENESS {
+ SHC_PROCESS_DPI_UNAWARE = 0,
+ SHC_PROCESS_SYSTEM_DPI_AWARE = 1,
+ SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2
+} SHC_PROCESS_DPI_AWARENESS;
+
+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;
+
+ alt_mem = false;
+ gr_mem = false;
+ shift_mem = false;
+ control_mem = false;
+ meta_mem = false;
+ console_visible = IsWindowVisible(GetConsoleWindow());
+ hInstance = ((OS_Windows *)OS::get_singleton())->get_hinstance();
+
+ pressrc = 0;
+ old_invalid = true;
+ mouse_mode = MOUSE_MODE_VISIBLE;
+
+ outside = true;
+
+ if (OS::get_singleton()->is_hidpi_allowed()) {
+ HMODULE Shcore = LoadLibraryW(L"Shcore.dll");
+
+ if (Shcore != nullptr) {
+ typedef HRESULT(WINAPI * SetProcessDpiAwareness_t)(SHC_PROCESS_DPI_AWARENESS);
+
+ SetProcessDpiAwareness_t SetProcessDpiAwareness = (SetProcessDpiAwareness_t)GetProcAddress(Shcore, "SetProcessDpiAwareness");
+
+ if (SetProcessDpiAwareness) {
+ SetProcessDpiAwareness(SHC_PROCESS_SYSTEM_DPI_AWARE);
+ }
+ }
+ }
+
+ memset(&wc, 0, sizeof(WNDCLASSEXW));
+ wc.cbSize = sizeof(WNDCLASSEXW);
+ wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
+ wc.lpfnWndProc = (WNDPROC)::WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ //wc.hInstance = hInstance;
+ wc.hInstance = hInstance ? hInstance : GetModuleHandle(nullptr);
+ wc.hIcon = LoadIcon(nullptr, IDI_WINLOGO);
+ wc.hCursor = nullptr; //LoadCursor(nullptr, IDC_ARROW);
+ wc.hbrBackground = nullptr;
+ wc.lpszMenuName = nullptr;
+ wc.lpszClassName = L"Engine";
+
+ if (!RegisterClassExW(&wc)) {
+ MessageBox(nullptr, "Failed To Register The Window Class.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
+ r_error = ERR_UNAVAILABLE;
+ return;
+ }
+
+ use_raw_input = true;
+
+ RAWINPUTDEVICE Rid[1];
+
+ Rid[0].usUsagePage = 0x01;
+ Rid[0].usUsage = 0x02;
+ Rid[0].dwFlags = 0;
+ Rid[0].hwndTarget = 0;
+
+ if (RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) {
+ //registration failed.
+ use_raw_input = false;
+ }
+
+ rendering_driver = "vulkan";
+
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ context_vulkan = memnew(VulkanContextWindows);
+ if (context_vulkan->initialize() != OK) {
+ memdelete(context_vulkan);
+ context_vulkan = nullptr;
+ r_error = ERR_UNAVAILABLE;
+ return;
+ }
+ }
+#endif
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver_index == VIDEO_DRIVER_GLES2) {
+ context_gles2 = memnew(ContextGL_Windows(hWnd, false));
+
+ if (context_gles2->initialize() != OK) {
+ memdelete(context_gles2);
+ context_gles2 = nullptr;
+ ERR_FAIL_V(ERR_UNAVAILABLE);
+ }
+
+ context_gles2->set_use_vsync(video_mode.use_vsync);
+ set_vsync_via_compositor(video_mode.vsync_via_compositor);
+
+ if (RasterizerGLES2::is_viable() == OK) {
+ RasterizerGLES2::register_config();
+ RasterizerGLES2::make_current();
+ } else {
+ memdelete(context_gles2);
+ context_gles2 = nullptr;
+ ERR_FAIL_V(ERR_UNAVAILABLE);
+ }
+ }
+#endif
+ 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));
+ 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
+
+#if defined(VULKAN_ENABLED)
+
+ if (rendering_driver == "vulkan") {
+ rendering_device_vulkan = memnew(RenderingDeviceVulkan);
+ rendering_device_vulkan->initialize(context_vulkan);
+
+ RasterizerRD::make_current();
+ }
+#endif
+
+ move_timer_id = 1;
+
+ //set_ime_active(false);
+
+ if (!OS::get_singleton()->is_in_low_processor_usage_mode()) {
+ //SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
+ SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
+ DWORD index = 0;
+ HANDLE handle = AvSetMmThreadCharacteristics("Games", &index);
+ if (handle)
+ AvSetMmThreadPriority(handle, AVRT_PRIORITY_CRITICAL);
+
+ // This is needed to make sure that background work does not starve the main thread.
+ // This is only setting priority of this thread, not the whole process.
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+ }
+
+ cursor_shape = CURSOR_ARROW;
+
+ _update_real_mouse_position(MAIN_WINDOW_ID);
+
+ joypad = new JoypadWindows(&windows[MAIN_WINDOW_ID].hWnd);
+
+ r_error = OK;
+
+ ((OS_Windows *)OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd);
+ Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
+}
+
+Vector<String> DisplayServerWindows::get_rendering_drivers_func() {
+ Vector<String> drivers;
+
+#ifdef VULKAN_ENABLED
+ drivers.push_back("vulkan");
+#endif
+#ifdef OPENGL_ENABLED
+ drivers.push_back("opengl");
+#endif
+
+ return drivers;
+}
+
+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));
+}
+
+void DisplayServerWindows::register_windows_driver() {
+ register_create_function("windows", create_func, get_rendering_drivers_func);
+}
+
+DisplayServerWindows::~DisplayServerWindows() {
+ delete joypad;
+ touch_state.clear();
+
+ cursors_cache.clear();
+
+ if (user_proc) {
+ SetWindowLongPtr(windows[MAIN_WINDOW_ID].hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);
+ };
+
+ if (windows.has(MAIN_WINDOW_ID)) {
+#ifdef VULKAN_ENABLED
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_destroy(MAIN_WINDOW_ID);
+ }
+#endif
+ if (wintab_available && windows[MAIN_WINDOW_ID].wtctx) {
+ wintab_WTClose(windows[MAIN_WINDOW_ID].wtctx);
+ windows[MAIN_WINDOW_ID].wtctx = 0;
+ }
+ 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
new file mode 100644
index 0000000000..caf8598dc2
--- /dev/null
+++ b/platform/windows/display_server_windows.h
@@ -0,0 +1,552 @@
+/*************************************************************************/
+/* display_server_windows.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 DISPLAY_SERVER_WINDOWS_H
+#define DISPLAY_SERVER_WINDOWS_H
+
+#include "servers/display_server.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 "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_server.h"
+
+#ifdef XAUDIO2_ENABLED
+#include "drivers/xaudio2/audio_driver_xaudio2.h"
+#endif
+
+#if defined(OPENGL_ENABLED)
+#include "context_gl_windows.h"
+#endif
+
+#if defined(VULKAN_ENABLED)
+#include "drivers/vulkan/rendering_device_vulkan.h"
+#include "platform/windows/vulkan_context_win.h"
+#endif
+
+#include <fcntl.h>
+#include <io.h>
+#include <stdio.h>
+#include <windows.h>
+#include <windowsx.h>
+
+// WinTab API
+#define WT_PACKET 0x7FF0
+#define WT_PROXIMITY 0x7FF5
+#define WT_INFOCHANGE 0x7FF6
+#define WT_CSRCHANGE 0x7FF7
+
+#define WTI_DEFSYSCTX 4
+#define WTI_DEVICES 100
+#define DVC_NPRESSURE 15
+#define DVC_TPRESSURE 16
+#define DVC_ORIENTATION 17
+#define DVC_ROTATION 18
+
+#define CXO_MESSAGES 0x0004
+#define PK_NORMAL_PRESSURE 0x0400
+#define PK_TANGENT_PRESSURE 0x0800
+#define PK_ORIENTATION 0x1000
+
+typedef struct tagLOGCONTEXTW {
+ WCHAR lcName[40];
+ UINT lcOptions;
+ UINT lcStatus;
+ UINT lcLocks;
+ UINT lcMsgBase;
+ UINT lcDevice;
+ UINT lcPktRate;
+ DWORD lcPktData;
+ DWORD lcPktMode;
+ DWORD lcMoveMask;
+ DWORD lcBtnDnMask;
+ DWORD lcBtnUpMask;
+ LONG lcInOrgX;
+ LONG lcInOrgY;
+ LONG lcInOrgZ;
+ LONG lcInExtX;
+ LONG lcInExtY;
+ LONG lcInExtZ;
+ LONG lcOutOrgX;
+ LONG lcOutOrgY;
+ LONG lcOutOrgZ;
+ LONG lcOutExtX;
+ LONG lcOutExtY;
+ LONG lcOutExtZ;
+ DWORD lcSensX;
+ DWORD lcSensY;
+ DWORD lcSensZ;
+ BOOL lcSysMode;
+ int lcSysOrgX;
+ int lcSysOrgY;
+ int lcSysExtX;
+ int lcSysExtY;
+ DWORD lcSysSensX;
+ DWORD lcSysSensY;
+} LOGCONTEXTW;
+
+typedef struct tagAXIS {
+ LONG axMin;
+ LONG axMax;
+ UINT axUnits;
+ DWORD axResolution;
+} AXIS;
+
+typedef struct tagORIENTATION {
+ int orAzimuth;
+ int orAltitude;
+ int orTwist;
+} ORIENTATION;
+
+typedef struct tagPACKET {
+ int pkNormalPressure;
+ int pkTangentPressure;
+ ORIENTATION pkOrientation;
+} PACKET;
+
+typedef HANDLE(WINAPI *WTOpenPtr)(HWND p_window, LOGCONTEXTW *p_ctx, BOOL p_enable);
+typedef BOOL(WINAPI *WTClosePtr)(HANDLE p_ctx);
+typedef UINT(WINAPI *WTInfoPtr)(UINT p_category, UINT p_index, LPVOID p_output);
+typedef BOOL(WINAPI *WTPacketPtr)(HANDLE p_ctx, UINT p_param, LPVOID p_packets);
+typedef BOOL(WINAPI *WTEnablePtr)(HANDLE p_ctx, BOOL p_enable);
+
+// Windows Ink API
+#ifndef POINTER_STRUCTURES
+
+#define POINTER_STRUCTURES
+
+typedef DWORD POINTER_INPUT_TYPE;
+typedef UINT32 POINTER_FLAGS;
+typedef UINT32 PEN_FLAGS;
+typedef UINT32 PEN_MASK;
+
+#ifndef PEN_MASK_PRESSURE
+#define PEN_MASK_PRESSURE 0x00000001
+#endif
+
+#ifndef PEN_MASK_TILT_X
+#define PEN_MASK_TILT_X 0x00000004
+#endif
+
+#ifndef PEN_MASK_TILT_Y
+#define PEN_MASK_TILT_Y 0x00000008
+#endif
+
+#ifndef POINTER_MESSAGE_FLAG_FIRSTBUTTON
+#define POINTER_MESSAGE_FLAG_FIRSTBUTTON 0x00000010
+#endif
+
+enum tagPOINTER_INPUT_TYPE {
+ PT_POINTER = 0x00000001,
+ PT_TOUCH = 0x00000002,
+ PT_PEN = 0x00000003,
+ PT_MOUSE = 0x00000004,
+ PT_TOUCHPAD = 0x00000005
+};
+
+typedef enum tagPOINTER_BUTTON_CHANGE_TYPE {
+ POINTER_CHANGE_NONE,
+ POINTER_CHANGE_FIRSTBUTTON_DOWN,
+ POINTER_CHANGE_FIRSTBUTTON_UP,
+ POINTER_CHANGE_SECONDBUTTON_DOWN,
+ POINTER_CHANGE_SECONDBUTTON_UP,
+ POINTER_CHANGE_THIRDBUTTON_DOWN,
+ POINTER_CHANGE_THIRDBUTTON_UP,
+ POINTER_CHANGE_FOURTHBUTTON_DOWN,
+ POINTER_CHANGE_FOURTHBUTTON_UP,
+ POINTER_CHANGE_FIFTHBUTTON_DOWN,
+ POINTER_CHANGE_FIFTHBUTTON_UP,
+} POINTER_BUTTON_CHANGE_TYPE;
+
+typedef struct tagPOINTER_INFO {
+ POINTER_INPUT_TYPE pointerType;
+ UINT32 pointerId;
+ UINT32 frameId;
+ POINTER_FLAGS pointerFlags;
+ HANDLE sourceDevice;
+ HWND hwndTarget;
+ POINT ptPixelLocation;
+ POINT ptHimetricLocation;
+ POINT ptPixelLocationRaw;
+ POINT ptHimetricLocationRaw;
+ DWORD dwTime;
+ UINT32 historyCount;
+ INT32 InputData;
+ DWORD dwKeyStates;
+ UINT64 PerformanceCount;
+ POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
+} POINTER_INFO;
+
+typedef struct tagPOINTER_PEN_INFO {
+ POINTER_INFO pointerInfo;
+ PEN_FLAGS penFlags;
+ PEN_MASK penMask;
+ UINT32 pressure;
+ UINT32 rotation;
+ INT32 tiltX;
+ INT32 tiltY;
+} POINTER_PEN_INFO;
+
+#endif //POINTER_STRUCTURES
+
+#ifndef WM_POINTERUPDATE
+#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);
+
+typedef struct {
+ BYTE bWidth; // Width, in pixels, of the image
+ BYTE bHeight; // Height, in pixels, of the image
+ BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
+ BYTE bReserved; // Reserved ( must be 0)
+ WORD wPlanes; // Color Planes
+ WORD wBitCount; // Bits per pixel
+ DWORD dwBytesInRes; // How many bytes in this resource?
+ DWORD dwImageOffset; // Where in the file is this image?
+} ICONDIRENTRY, *LPICONDIRENTRY;
+
+typedef struct {
+ WORD idReserved; // Reserved (must be 0)
+ WORD idType; // Resource Type (1 for icons)
+ WORD idCount; // How many images?
+ ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em)
+} ICONDIR, *LPICONDIR;
+
+class DisplayServerWindows : public DisplayServer {
+ //No need to register, it's platform-specific and nothing is added
+ //GDCLASS(DisplayServerWindows, DisplayServer)
+
+ _THREAD_SAFE_CLASS_
+
+public:
+ // WinTab API
+ static bool wintab_available;
+ static WTOpenPtr wintab_WTOpen;
+ static WTClosePtr wintab_WTClose;
+ static WTInfoPtr wintab_WTInfo;
+ static WTPacketPtr wintab_WTPacket;
+ static WTEnablePtr wintab_WTEnable;
+
+ // Windows Ink API
+ static bool winink_available;
+ static GetPointerTypePtr win8p_GetPointerType;
+ static GetPointerPenInfoPtr win8p_GetPointerPenInfo;
+
+ void _update_tablet_ctx(const String &p_old_driver, const String &p_new_driver);
+
+private:
+ void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap);
+
+ enum {
+ KEY_EVENT_BUFFER_SIZE = 512
+ };
+
+ struct KeyEvent {
+ WindowID window_id;
+ bool alt, shift, control, meta;
+ UINT uMsg;
+ WPARAM wParam;
+ LPARAM lParam;
+ };
+
+ KeyEvent key_event_buffer[KEY_EVENT_BUFFER_SIZE];
+ int key_event_pos;
+
+ bool old_invalid;
+ bool outside;
+ int old_x, old_y;
+ Point2i center;
+
+#if defined(OPENGL_ENABLED)
+ ContextGL_Windows *context_gles2;
+#endif
+
+#if defined(VULKAN_ENABLED)
+ VulkanContextWindows *context_vulkan;
+ RenderingDeviceVulkan *rendering_device_vulkan;
+#endif
+
+ Map<int, Vector2> touch_state;
+
+ int pressrc;
+ HINSTANCE hInstance; // Holds The Instance Of The Application
+ String rendering_driver;
+
+ struct WindowData {
+ HWND hWnd;
+ //layered window
+
+ bool preserve_window_size = false;
+ bool pre_fs_valid = false;
+ RECT pre_fs_rect;
+ bool maximized = false;
+ bool minimized = false;
+ bool fullscreen = false;
+ bool borderless = false;
+ bool resizable = true;
+ bool window_focused = false;
+ bool was_maximized = false;
+ bool always_on_top = false;
+ bool no_focus = false;
+ bool window_has_focus = false;
+
+ HANDLE wtctx;
+ LOGCONTEXTW wtlc;
+ int min_pressure;
+ int max_pressure;
+ bool tilt_supported;
+ bool block_mm = false;
+
+ int last_pressure_update;
+ float last_pressure;
+ Vector2 last_tilt;
+
+ HBITMAP hBitmap; //DIB section for layered window
+ uint8_t *dib_data = nullptr;
+ Size2 dib_size;
+ HDC hDC_dib;
+ Size2 min_size;
+ Size2 max_size;
+ int width = 0, height = 0;
+
+ Size2 window_rect;
+ Point2 last_pos;
+
+ ObjectID instance_id;
+
+ // IME
+ HIMC im_himc;
+ Vector2 im_position;
+
+ bool layered_window = false;
+
+ Callable rect_changed_callback;
+ Callable event_callback;
+ Callable input_event_callback;
+ Callable input_text_callback;
+ Callable drop_files_callback;
+
+ WindowID transient_parent = INVALID_WINDOW_ID;
+ Set<WindowID> transient_children;
+ };
+
+ JoypadWindows *joypad;
+
+ WindowID _create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect);
+ WindowID window_id_counter = MAIN_WINDOW_ID;
+ Map<WindowID, WindowData> windows;
+
+ WindowID last_focused_window = INVALID_WINDOW_ID;
+
+ uint32_t move_timer_id;
+
+ HCURSOR hCursor;
+
+ WNDPROC user_proc = nullptr;
+
+ void _send_window_event(const WindowData &wd, WindowEvent p_event);
+ void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex);
+
+ MouseMode mouse_mode;
+ bool alt_mem = false;
+ bool gr_mem = false;
+ bool shift_mem = false;
+ bool control_mem = false;
+ bool meta_mem = false;
+ uint32_t last_button_state = 0;
+ bool use_raw_input = false;
+ bool drop_events = false;
+ bool in_dispatch_input_event = false;
+ bool console_visible = false;
+
+ WNDCLASSEXW wc;
+
+ HCURSOR cursors[CURSOR_MAX] = { nullptr };
+ CursorShape cursor_shape;
+ 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_real_mouse_position(WindowID p_window);
+
+ void _set_mouse_mode_impl(MouseMode p_mode);
+
+ void _process_key_events();
+
+ static void _dispatch_input_events(const Ref<InputEvent> &p_event);
+ void _dispatch_input_event(const Ref<InputEvent> &p_event);
+
+public:
+ LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ virtual bool has_feature(Feature p_feature) const;
+ virtual String get_name() const;
+
+ virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
+
+ virtual void mouse_set_mode(MouseMode p_mode);
+ virtual MouseMode mouse_get_mode() const;
+
+ virtual void mouse_warp_to_position(const Point2i &p_to);
+ virtual Point2i mouse_get_position() const;
+ virtual int mouse_get_button_state() const;
+
+ virtual void clipboard_set(const String &p_text);
+ virtual String clipboard_get() const;
+
+ 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;
+ virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+
+ virtual void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW);
+ ScreenOrientation screen_get_orientation(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+
+ virtual void screen_set_keep_on(bool p_enable); //disable screensaver
+ virtual bool screen_is_kept_on() const;
+
+ 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 delete_sub_window(WindowID p_window);
+
+ virtual WindowID get_window_at_screen_position(const Point2i &p_position) const;
+
+ 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_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_title(const String &p_title, 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);
+
+ 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 void window_set_transient(WindowID p_window, WindowID p_parent);
+
+ 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_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_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; //wtf is this? should probable use proper name
+
+ 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 bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ 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_request_attention(WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual bool can_any_window_draw() const;
+
+ 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 console_set_visible(bool p_enabled);
+ virtual bool is_console_visible() const;
+
+ 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 bool get_swap_ok_cancel();
+
+ virtual void enable_for_stealing_focus(OS::ProcessID pid);
+
+ virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
+
+ virtual void process_events();
+
+ virtual void force_process_and_drop_events();
+
+ virtual void release_rendering_thread();
+ virtual void make_rendering_thread();
+ virtual void swap_buffers();
+
+ virtual void set_native_icon(const String &p_filename);
+ virtual void set_icon(const Ref<Image> &p_icon);
+
+ virtual void vsync_set_use_via_compositor(bool p_enable);
+ virtual bool vsync_is_using_via_compositor() const;
+
+ virtual void set_context(Context p_context);
+
+ 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_windows_driver();
+
+ DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ ~DisplayServerWindows();
+};
+
+#endif // DISPLAY_SERVER_WINDOWS_H
diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp
index 78a3fc8f79..c2436e8b64 100644
--- a/platform/windows/export/export.cpp
+++ b/platform/windows/export/export.cpp
@@ -38,7 +38,6 @@
static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size);
class EditorExportPlatformWindows : public EditorExportPlatformPC {
-
void _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path);
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
@@ -315,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, NULL, &str, NULL, true);
+ Error err = OS::get_singleton()->execute(signtool_path, args, true, nullptr, &str, nullptr, true);
ERR_FAIL_COND_V(err != OK, err);
print_line("codesign (" + p_path + "): " + str);
@@ -331,7 +330,6 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
}
void register_windows_exporter() {
-
EDITOR_DEF("export/windows/rcedit", "");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/rcedit", PROPERTY_HINT_GLOBAL_FILE, "*.exe"));
#ifdef WINDOWS_ENABLED
@@ -366,7 +364,6 @@ void register_windows_exporter() {
}
static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) {
-
// Patch the header of the "pck" section in the PE file so that it corresponds to the embedded data
FileAccess *f = FileAccess::open(p_path, FileAccess::READ_WRITE);
@@ -408,7 +405,6 @@ static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start,
bool found = false;
for (int i = 0; i < num_sections; ++i) {
-
int64_t section_header_pos = section_table_pos + i * 40;
f->seek(section_header_pos);
diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp
index dcc12b7649..910059a9fc 100644
--- a/platform/windows/godot_windows.cpp
+++ b/platform/windows/godot_windows.cpp
@@ -121,23 +121,22 @@ CommandLineToArgvA(
i++;
}
_argv[j] = '\0';
- argv[argc] = NULL;
+ argv[argc] = nullptr;
(*_argc) = argc;
return argv;
}
char *wc_to_utf8(const wchar_t *wc) {
- int ulen = WideCharToMultiByte(CP_UTF8, 0, wc, -1, NULL, 0, NULL, NULL);
+ int ulen = WideCharToMultiByte(CP_UTF8, 0, wc, -1, nullptr, 0, nullptr, nullptr);
char *ubuf = new char[ulen + 1];
- WideCharToMultiByte(CP_UTF8, 0, wc, -1, ubuf, ulen, NULL, NULL);
+ WideCharToMultiByte(CP_UTF8, 0, wc, -1, ubuf, ulen, nullptr, nullptr);
ubuf[ulen] = 0;
return ubuf;
}
int widechar_main(int argc, wchar_t **argv) {
-
- OS_Windows os(NULL);
+ OS_Windows os(nullptr);
setlocale(LC_CTYPE, "");
@@ -176,7 +175,7 @@ int _main() {
wc_argv = CommandLineToArgvW(GetCommandLineW(), &argc);
- if (NULL == wc_argv) {
+ if (nullptr == wc_argv) {
wprintf(L"CommandLineToArgvW failed\n");
return 0;
}
@@ -202,9 +201,9 @@ int main(int _argc, char **_argv) {
#endif
}
-HINSTANCE godot_hinstance = NULL;
+HINSTANCE godot_hinstance = nullptr;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
godot_hinstance = hInstance;
- return main(0, NULL);
+ return main(0, nullptr);
}
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index 49432435b9..0cff12ca8c 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -45,6 +45,7 @@
DWORD WINAPI _xinput_get_state(DWORD dwUserIndex, XINPUT_STATE *pState) {
return ERROR_DEVICE_NOT_CONNECTED;
}
+
DWORD WINAPI _xinput_set_state(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration) {
return ERROR_DEVICE_NOT_CONNECTED;
}
@@ -52,15 +53,14 @@ DWORD WINAPI _xinput_set_state(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration)
JoypadWindows::JoypadWindows() {
}
-JoypadWindows::JoypadWindows(InputDefault *_input, HWND *hwnd) {
-
- input = _input;
+JoypadWindows::JoypadWindows(HWND *hwnd) {
+ input = Input::get_singleton();
hWnd = hwnd;
joypad_count = 0;
- dinput = NULL;
- xinput_dll = NULL;
- xinput_get_state = NULL;
- xinput_set_state = NULL;
+ dinput = nullptr;
+ xinput_dll = nullptr;
+ xinput_get_state = nullptr;
+ xinput_set_state = nullptr;
load_xinput();
@@ -68,7 +68,7 @@ JoypadWindows::JoypadWindows(InputDefault *_input, HWND *hwnd) {
attached_joypads[i] = false;
HRESULT result;
- result = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, NULL);
+ result = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr);
if (FAILED(result)) {
printf("failed init DINPUT: %ld\n", result);
}
@@ -76,18 +76,14 @@ JoypadWindows::JoypadWindows(InputDefault *_input, HWND *hwnd) {
}
JoypadWindows::~JoypadWindows() {
-
close_joypad();
dinput->Release();
unload_xinput();
}
bool JoypadWindows::have_device(const GUID &p_guid) {
-
for (int i = 0; i < JOYPADS_MAX; i++) {
-
if (d_joypads[i].guid == p_guid) {
-
d_joypads[i].confirmed = true;
return true;
}
@@ -97,7 +93,6 @@ bool JoypadWindows::have_device(const GUID &p_guid) {
// adapted from SDL2, works a lot better than the MSDN version
bool JoypadWindows::is_xinput_device(const GUID *p_guid) {
-
static GUID IID_ValveStreamingGamepad = { MAKELONG(0x28DE, 0x11FF), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
static GUID IID_X360WiredGamepad = { MAKELONG(0x045E, 0x02A1), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
static GUID IID_X360WirelessGamepad = { MAKELONG(0x045E, 0x028E), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
@@ -105,21 +100,21 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) {
if (p_guid == &IID_ValveStreamingGamepad || p_guid == &IID_X360WiredGamepad || p_guid == &IID_X360WirelessGamepad)
return true;
- PRAWINPUTDEVICELIST dev_list = NULL;
+ PRAWINPUTDEVICELIST dev_list = nullptr;
unsigned int dev_list_count = 0;
- if (GetRawInputDeviceList(NULL, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) {
+ if (GetRawInputDeviceList(nullptr, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) {
return false;
}
dev_list = (PRAWINPUTDEVICELIST)malloc(sizeof(RAWINPUTDEVICELIST) * dev_list_count);
- if (!dev_list) return false;
+ if (!dev_list)
+ return false;
if (GetRawInputDeviceList(dev_list, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) {
free(dev_list);
return false;
}
for (unsigned int i = 0; i < dev_list_count; i++) {
-
RID_DEVICE_INFO rdi;
char dev_name[128];
UINT rdiSize = sizeof(rdi);
@@ -130,8 +125,7 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) {
(GetRawInputDeviceInfoA(dev_list[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != (UINT)-1) &&
(MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == (LONG)p_guid->Data1) &&
(GetRawInputDeviceInfoA(dev_list[i].hDevice, RIDI_DEVICENAME, &dev_name, &nameSize) != (UINT)-1) &&
- (strstr(dev_name, "IG_") != NULL)) {
-
+ (strstr(dev_name, "IG_") != nullptr)) {
free(dev_list);
return true;
}
@@ -141,7 +135,6 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) {
}
bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
-
HRESULT hr;
int num = input->get_unused_joy_id();
@@ -157,7 +150,7 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
return false;
}
- hr = dinput->CreateDevice(instance->guidInstance, &joy->di_joy, NULL);
+ hr = dinput->CreateDevice(instance->guidInstance, &joy->di_joy, nullptr);
if (FAILED(hr)) {
return false;
@@ -188,9 +181,7 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
}
void JoypadWindows::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_joy_id) {
-
if (ob->dwType & DIDFT_AXIS) {
-
HRESULT res;
DIPROPRANGE prop_range;
DIPROPDWORD dilong;
@@ -239,7 +230,6 @@ void JoypadWindows::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_
}
BOOL CALLBACK JoypadWindows::enumCallback(const DIDEVICEINSTANCE *p_instance, void *p_context) {
-
JoypadWindows *self = (JoypadWindows *)p_context;
if (self->is_xinput_device(&p_instance->guidProduct)) {
return DIENUM_CONTINUE;
@@ -249,7 +239,6 @@ BOOL CALLBACK JoypadWindows::enumCallback(const DIDEVICEINSTANCE *p_instance, vo
}
BOOL CALLBACK JoypadWindows::objectsCallback(const DIDEVICEOBJECTINSTANCE *instance, void *context) {
-
JoypadWindows *self = (JoypadWindows *)context;
self->setup_joypad_object(instance, self->id_to_change);
@@ -257,17 +246,15 @@ BOOL CALLBACK JoypadWindows::objectsCallback(const DIDEVICEOBJECTINSTANCE *insta
}
void JoypadWindows::close_joypad(int id) {
-
if (id == -1) {
-
for (int i = 0; i < JOYPADS_MAX; i++) {
-
close_joypad(i);
}
return;
}
- if (!d_joypads[id].attached) return;
+ if (!d_joypads[id].attached)
+ return;
d_joypads[id].di_joy->Unacquire();
d_joypads[id].di_joy->Release();
@@ -279,18 +266,14 @@ void JoypadWindows::close_joypad(int id) {
}
void JoypadWindows::probe_joypads() {
-
DWORD dwResult;
for (DWORD i = 0; i < XUSER_MAX_COUNT; i++) {
-
ZeroMemory(&x_joypads[i].state, sizeof(XINPUT_STATE));
dwResult = xinput_get_state(i, &x_joypads[i].state);
if (dwResult == ERROR_SUCCESS) {
-
int id = input->get_unused_joy_id();
if (id != -1 && !x_joypads[i].attached) {
-
x_joypads[i].attached = true;
x_joypads[i].id = id;
x_joypads[i].ff_timestamp = 0;
@@ -300,7 +283,6 @@ void JoypadWindows::probe_joypads() {
input->joy_connection_changed(id, true, "XInput Gamepad", "__XINPUT_DEVICE__");
}
} else if (x_joypads[i].attached) {
-
x_joypads[i].attached = false;
attached_joypads[x_joypads[i].id] = false;
input->joy_connection_changed(x_joypads[i].id, false, "");
@@ -308,27 +290,22 @@ void JoypadWindows::probe_joypads() {
}
for (int i = 0; i < joypad_count; i++) {
-
d_joypads[i].confirmed = false;
}
dinput->EnumDevices(DI8DEVCLASS_GAMECTRL, enumCallback, this, DIEDFL_ATTACHEDONLY);
for (int i = 0; i < joypad_count; i++) {
-
if (!d_joypads[i].confirmed) {
-
close_joypad(i);
}
}
}
void JoypadWindows::process_joypads() {
-
HRESULT hr;
for (int i = 0; i < XUSER_MAX_COUNT; i++) {
-
xinput_gamepad &joy = x_joypads[i];
if (!joy.attached) {
continue;
@@ -337,20 +314,18 @@ void JoypadWindows::process_joypads() {
xinput_get_state(i, &joy.state);
if (joy.state.dwPacketNumber != joy.last_packet) {
-
int button_mask = XINPUT_GAMEPAD_DPAD_UP;
for (int j = 0; j <= 16; j++) {
-
input->joy_button(joy.id, j, joy.state.Gamepad.wButtons & button_mask);
button_mask = button_mask * 2;
}
- input->joy_axis(joy.id, JOY_AXIS_0, axis_correct(joy.state.Gamepad.sThumbLX, true));
- input->joy_axis(joy.id, JOY_AXIS_1, axis_correct(joy.state.Gamepad.sThumbLY, true, false, true));
- input->joy_axis(joy.id, JOY_AXIS_2, axis_correct(joy.state.Gamepad.sThumbRX, true));
- input->joy_axis(joy.id, JOY_AXIS_3, axis_correct(joy.state.Gamepad.sThumbRY, true, false, true));
- input->joy_axis(joy.id, JOY_AXIS_4, axis_correct(joy.state.Gamepad.bLeftTrigger, true, true));
- input->joy_axis(joy.id, JOY_AXIS_5, axis_correct(joy.state.Gamepad.bRightTrigger, true, true));
+ input->joy_axis(joy.id, JOY_AXIS_LEFT_X, axis_correct(joy.state.Gamepad.sThumbLX, true));
+ input->joy_axis(joy.id, JOY_AXIS_LEFT_Y, axis_correct(joy.state.Gamepad.sThumbLY, true, false, true));
+ input->joy_axis(joy.id, JOY_AXIS_RIGHT_X, axis_correct(joy.state.Gamepad.sThumbRX, true));
+ input->joy_axis(joy.id, JOY_AXIS_RIGHT_Y, axis_correct(joy.state.Gamepad.sThumbRY, true, false, true));
+ input->joy_axis(joy.id, JOY_AXIS_TRIGGER_LEFT, axis_correct(joy.state.Gamepad.bLeftTrigger, true, true));
+ input->joy_axis(joy.id, JOY_AXIS_TRIGGER_RIGHT, axis_correct(joy.state.Gamepad.bRightTrigger, true, true));
joy.last_packet = joy.state.dwPacketNumber;
}
uint64_t timestamp = input->get_joy_vibration_timestamp(joy.id);
@@ -370,7 +345,6 @@ void JoypadWindows::process_joypads() {
}
for (int i = 0; i < JOYPADS_MAX; i++) {
-
dinput_gamepad *joy = &d_joypads[i];
if (!joy->attached)
@@ -391,18 +365,13 @@ void JoypadWindows::process_joypads() {
post_hat(joy->id, js.rgdwPOV[0]);
for (int j = 0; j < 128; j++) {
-
if (js.rgbButtons[j] & 0x80) {
-
if (!joy->last_buttons[j]) {
-
input->joy_button(joy->id, j, true);
joy->last_buttons[j] = true;
}
} else {
-
if (joy->last_buttons[j]) {
-
input->joy_button(joy->id, j, false);
joy->last_buttons[j] = false;
}
@@ -415,7 +384,6 @@ void JoypadWindows::process_joypads() {
int values[] = { js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz };
for (int j = 0; j < joy->joy_axis.size(); j++) {
-
for (int k = 0; k < count; k++) {
if (joy->joy_axis[j] == axes[k]) {
input->joy_axis(joy->id, j, axis_correct(values[k]));
@@ -428,7 +396,6 @@ void JoypadWindows::process_joypads() {
}
void JoypadWindows::post_hat(int p_device, DWORD p_dpad) {
-
int dpad_val = 0;
// Should be -1 when centered, but according to docs:
@@ -436,53 +403,43 @@ void JoypadWindows::post_hat(int p_device, DWORD p_dpad) {
// BOOL POVCentered = (LOWORD(dwPOV) == 0xFFFF);"
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416628(v%3Dvs.85)#remarks
if (LOWORD(p_dpad) == 0xFFFF) {
- dpad_val = InputDefault::HAT_MASK_CENTER;
+ dpad_val = Input::HAT_MASK_CENTER;
}
if (p_dpad == 0) {
-
- dpad_val = InputDefault::HAT_MASK_UP;
+ dpad_val = Input::HAT_MASK_UP;
} else if (p_dpad == 4500) {
-
- dpad_val = (InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_RIGHT);
+ dpad_val = (Input::HAT_MASK_UP | Input::HAT_MASK_RIGHT);
} else if (p_dpad == 9000) {
-
- dpad_val = InputDefault::HAT_MASK_RIGHT;
+ dpad_val = Input::HAT_MASK_RIGHT;
} else if (p_dpad == 13500) {
-
- dpad_val = (InputDefault::HAT_MASK_RIGHT | InputDefault::HAT_MASK_DOWN);
+ dpad_val = (Input::HAT_MASK_RIGHT | Input::HAT_MASK_DOWN);
} else if (p_dpad == 18000) {
-
- dpad_val = InputDefault::HAT_MASK_DOWN;
+ dpad_val = Input::HAT_MASK_DOWN;
} else if (p_dpad == 22500) {
-
- dpad_val = (InputDefault::HAT_MASK_DOWN | InputDefault::HAT_MASK_LEFT);
+ dpad_val = (Input::HAT_MASK_DOWN | Input::HAT_MASK_LEFT);
} else if (p_dpad == 27000) {
-
- dpad_val = InputDefault::HAT_MASK_LEFT;
+ dpad_val = Input::HAT_MASK_LEFT;
} else if (p_dpad == 31500) {
-
- dpad_val = (InputDefault::HAT_MASK_LEFT | InputDefault::HAT_MASK_UP);
+ dpad_val = (Input::HAT_MASK_LEFT | Input::HAT_MASK_UP);
}
input->joy_hat(p_device, dpad_val);
};
-InputDefault::JoyAxis JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const {
-
- InputDefault::JoyAxis jx;
+Input::JoyAxis JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const {
+ Input::JoyAxis jx;
if (Math::abs(p_val) < MIN_JOY_AXIS) {
jx.min = p_trigger ? 0 : -1;
jx.value = 0.0f;
return jx;
}
if (p_xinput) {
-
if (p_trigger) {
jx.min = 0;
jx.value = (float)p_val / MAX_TRIGGER;
@@ -532,7 +489,6 @@ void JoypadWindows::joypad_vibration_stop_xinput(int p_device, uint64_t p_timest
}
void JoypadWindows::load_xinput() {
-
xinput_get_state = &_xinput_get_state;
xinput_set_state = &_xinput_set_state;
xinput_dll = LoadLibrary("XInput1_4.dll");
@@ -559,9 +515,7 @@ void JoypadWindows::load_xinput() {
}
void JoypadWindows::unload_xinput() {
-
if (xinput_dll) {
-
FreeLibrary((HMODULE)xinput_dll);
}
}
diff --git a/platform/windows/joypad_windows.h b/platform/windows/joypad_windows.h
index ab85bc60ac..6c06b3f6f0 100644
--- a/platform/windows/joypad_windows.h
+++ b/platform/windows/joypad_windows.h
@@ -39,9 +39,9 @@
#ifndef SAFE_RELEASE // when Windows Media Device M? is not present
#define SAFE_RELEASE(x) \
- if (x != NULL) { \
+ if (x != nullptr) { \
x->Release(); \
- x = NULL; \
+ x = nullptr; \
}
#endif
@@ -52,7 +52,7 @@
class JoypadWindows {
public:
JoypadWindows();
- JoypadWindows(InputDefault *_input, HWND *hwnd);
+ JoypadWindows(HWND *hwnd);
~JoypadWindows();
void probe_joypads();
@@ -70,7 +70,6 @@ private:
};
struct dinput_gamepad {
-
int id;
bool attached;
bool confirmed;
@@ -93,7 +92,6 @@ private:
};
struct xinput_gamepad {
-
int id;
bool attached;
bool vibrating;
@@ -117,7 +115,7 @@ private:
HWND *hWnd;
HANDLE xinput_dll;
LPDIRECTINPUT8 dinput;
- InputDefault *input;
+ Input *input;
int id_to_change;
int joypad_count;
@@ -141,7 +139,7 @@ private:
void joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
void joypad_vibration_stop_xinput(int p_device, uint64_t p_timestamp);
- InputDefault::JoyAxis axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const;
+ Input::JoyAxis axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const;
XInputGetState_t xinput_get_state;
XInputSetState_t xinput_set_state;
};
diff --git a/platform/windows/key_mapping_windows.cpp b/platform/windows/key_mapping_windows.cpp
index da63e92622..d8d0b13068 100644
--- a/platform/windows/key_mapping_windows.cpp
+++ b/platform/windows/key_mapping_windows.cpp
@@ -33,7 +33,6 @@
#include <stdio.h>
struct _WinTranslatePair {
-
unsigned int keysym;
unsigned int keycode;
};
@@ -130,7 +129,7 @@ static _WinTranslatePair _vk_to_keycode[] = {
{ KEY_MASK_META, VK_LWIN }, //(0x5B)
{ KEY_MASK_META, VK_RWIN }, //(0x5C)
- //VK_APPS (0x5D)
+ { KEY_MENU, VK_APPS }, //(0x5D)
{ KEY_STANDBY, VK_SLEEP }, //(0x5F)
{ KEY_KP_0, VK_NUMPAD0 }, //(0x60)
{ KEY_KP_1, VK_NUMPAD1 }, //(0x61)
@@ -337,9 +336,7 @@ static _WinTranslatePair _scancode_to_keycode[] = {
};
unsigned int KeyMappingWindows::get_keysym(unsigned int p_code) {
-
for (int i = 0; _vk_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
-
if (_vk_to_keycode[i].keycode == p_code) {
//printf("outcode: %x\n",_vk_to_keycode[i].keysym);
@@ -353,7 +350,6 @@ unsigned int KeyMappingWindows::get_keysym(unsigned int p_code) {
unsigned int KeyMappingWindows::get_scansym(unsigned int p_code, bool p_extended) {
unsigned int keycode = KEY_UNKNOWN;
for (int i = 0; _scancode_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
-
if (_scancode_to_keycode[i].keycode == p_code) {
keycode = _scancode_to_keycode[i].keysym;
break;
@@ -415,3 +411,16 @@ unsigned int KeyMappingWindows::get_scansym(unsigned int p_code, bool p_extended
return keycode;
}
+
+bool KeyMappingWindows::is_extended_key(unsigned int p_code) {
+ return p_code == VK_INSERT ||
+ p_code == VK_DELETE ||
+ p_code == VK_HOME ||
+ p_code == VK_END ||
+ p_code == VK_PRIOR ||
+ p_code == VK_NEXT ||
+ p_code == VK_LEFT ||
+ p_code == VK_UP ||
+ p_code == VK_RIGHT ||
+ p_code == VK_DOWN;
+}
diff --git a/platform/windows/key_mapping_windows.h b/platform/windows/key_mapping_windows.h
index 3361ad397f..f64f1feb9f 100644
--- a/platform/windows/key_mapping_windows.h
+++ b/platform/windows/key_mapping_windows.h
@@ -38,12 +38,12 @@
#include <winuser.h>
class KeyMappingWindows {
-
- KeyMappingWindows(){};
+ KeyMappingWindows() {}
public:
static unsigned int get_keysym(unsigned int p_code);
static unsigned int get_scansym(unsigned int p_code, bool p_extended);
+ static bool is_extended_key(unsigned int p_code);
};
#endif // KEY_MAPPING_WINDOWS_H
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 041a5bffa6..f11888b26c 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -37,15 +37,6 @@
#include "core/debugger/script_debugger.h"
#include "core/io/marshalls.h"
#include "core/version_generated.gen.h"
-
-#if defined(OPENGL_ENABLED)
-#include "drivers/gles2/rasterizer_gles2.h"
-#endif
-
-#if defined(VULKAN_ENABLED)
-#include "servers/visual/rasterizer_rd/rasterizer_rd.h"
-#endif
-
#include "drivers/windows/dir_access_windows.h"
#include "drivers/windows/file_access_windows.h"
#include "drivers/windows/rw_lock_windows.h"
@@ -53,9 +44,10 @@
#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/visual/visual_server_raster.h"
-#include "servers/visual/visual_server_wrap_mt.h"
+#include "servers/rendering/rendering_server_raster.h"
+#include "servers/rendering/rendering_server_wrap_mt.h"
#include "windows_terminal_logger.h"
#include <avrt.h>
@@ -86,36 +78,11 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
#define GetProcAddress (void *)GetProcAddress
#endif
-typedef struct {
- int count;
- int screen;
- Size2 size;
-} EnumSizeData;
-
-typedef struct {
- int count;
- int screen;
- Point2 pos;
-} EnumPosData;
-
-static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
-
- EnumSizeData *data = (EnumSizeData *)dwData;
- if (data->count == data->screen) {
- data->size.x = lprcMonitor->right - lprcMonitor->left;
- data->size.y = lprcMonitor->bottom - lprcMonitor->top;
- }
-
- data->count++;
- return TRUE;
-}
-
#ifdef DEBUG_ENABLED
static String format_error_message(DWORD id) {
-
- LPWSTR messageBuffer = NULL;
+ LPWSTR messageBuffer = nullptr;
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL);
+ nullptr, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr);
String msg = "Error " + itos(id) + ": " + String(messageBuffer, size);
@@ -125,10 +92,7 @@ static String format_error_message(DWORD id) {
}
#endif // DEBUG_ENABLED
-extern HINSTANCE godot_hinstance;
-
void RedirectIOToConsole() {
-
int hConHandle;
intptr_t lStdHandle;
@@ -163,7 +127,7 @@ void RedirectIOToConsole() {
*stdout = *fp;
- setvbuf(stdout, NULL, _IONBF, 0);
+ setvbuf(stdout, nullptr, _IONBF, 0);
// redirect unbuffered STDIN to the console
@@ -175,7 +139,7 @@ void RedirectIOToConsole() {
*stdin = *fp;
- setvbuf(stdin, NULL, _IONBF, 0);
+ setvbuf(stdin, nullptr, _IONBF, 0);
// redirect unbuffered STDERR to the console
@@ -187,7 +151,7 @@ void RedirectIOToConsole() {
*stderr = *fp;
- setvbuf(stderr, NULL, _IONBF, 0);
+ setvbuf(stderr, nullptr, _IONBF, 0);
// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
@@ -208,24 +172,14 @@ BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) {
}
}
-GetPointerTypePtr OS_Windows::win8p_GetPointerType = NULL;
-GetPointerPenInfoPtr OS_Windows::win8p_GetPointerPenInfo = NULL;
-
void OS_Windows::initialize_debugging() {
-
SetConsoleCtrlHandler(HandlerRoutine, TRUE);
}
-void OS_Windows::initialize_core() {
-
+void OS_Windows::initialize() {
crash_handler.initialize();
- last_button_state = 0;
-
//RedirectIOToConsole();
- maximized = false;
- minimized = false;
- borderless = false;
ThreadWindows::make_default();
RWLockWindows::make_default();
@@ -255,1386 +209,20 @@ void OS_Windows::initialize_core() {
process_map = memnew((Map<ProcessID, ProcessInfo>));
IP_Unix::make_default();
-
- cursor_shape = CURSOR_ARROW;
+ main_loop = nullptr;
}
-bool OS_Windows::can_draw() const {
-
- return !minimized;
-};
-
-#define MI_WP_SIGNATURE 0xFF515700
-#define SIGNATURE_MASK 0xFFFFFF00
-// Keeping the name suggested by Microsoft, but this macro really answers:
-// Is this mouse event emulated from touch or pen input?
-#define IsPenEvent(dw) (((dw)&SIGNATURE_MASK) == MI_WP_SIGNATURE)
-// This one tells whether the event comes from touchscreen (and not from pen)
-#define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw)&0x80))
-
-void OS_Windows::_touch_event(bool p_pressed, float p_x, float p_y, int idx) {
-
- // Defensive
- if (touch_state.has(idx) == p_pressed)
- return;
-
- if (p_pressed) {
- touch_state.insert(idx, Vector2(p_x, p_y));
- } else {
- touch_state.erase(idx);
- }
-
- Ref<InputEventScreenTouch> event;
- event.instance();
- event->set_index(idx);
- event->set_pressed(p_pressed);
- event->set_position(Vector2(p_x, p_y));
-
- if (main_loop) {
- input->accumulate_input_event(event);
- }
-};
-
-void OS_Windows::_drag_event(float p_x, float p_y, int idx) {
-
- Map<int, Vector2>::Element *curr = touch_state.find(idx);
- // Defensive
- if (!curr)
- return;
-
- if (curr->get() == Vector2(p_x, p_y))
- return;
-
- Ref<InputEventScreenDrag> event;
- event.instance();
- event->set_index(idx);
- event->set_position(Vector2(p_x, p_y));
- event->set_relative(Vector2(p_x, p_y) - curr->get());
-
- if (main_loop)
- input->accumulate_input_event(event);
-
- curr->get() = Vector2(p_x, p_y);
-};
-
-LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
-
- if (drop_events) {
-
- if (user_proc) {
-
- return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
- } else {
- return DefWindowProcW(hWnd, uMsg, wParam, lParam);
- }
- };
-
- switch (uMsg) // Check For Windows Messages
- {
- case WM_SETFOCUS: {
- window_has_focus = true;
-
- // Restore mouse mode
- _set_mouse_mode_impl(mouse_mode);
-
- break;
- }
- case WM_KILLFOCUS: {
- window_has_focus = false;
-
- // Release capture unconditionally because it can be set due to dragging, in addition to captured mode
- ReleaseCapture();
-
- // Release every touch to avoid sticky points
- for (Map<int, Vector2>::Element *E = touch_state.front(); E; E = E->next()) {
- _touch_event(false, E->get().x, E->get().y, E->key());
- }
- touch_state.clear();
-
- break;
- }
- case WM_ACTIVATE: // Watch For Window Activate Message
- {
- minimized = HIWORD(wParam) != 0;
- if (!main_loop) {
- return 0;
- };
- if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) {
-
- main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
- window_focused = true;
- alt_mem = false;
- control_mem = false;
- shift_mem = false;
- } else { // WM_INACTIVE
- input->release_pressed_events();
- main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
- window_focused = false;
- alt_mem = false;
- };
-
- return 0; // Return To The Message Loop
- }
- case WM_GETMINMAXINFO: {
- if (video_mode.resizable && !video_mode.fullscreen) {
- Size2 decor = get_real_window_size() - get_window_size(); // Size of window decorations
- MINMAXINFO *min_max_info = (MINMAXINFO *)lParam;
- if (min_size != Size2()) {
- min_max_info->ptMinTrackSize.x = min_size.x + decor.x;
- min_max_info->ptMinTrackSize.y = min_size.y + decor.y;
- }
- if (max_size != Size2()) {
- min_max_info->ptMaxTrackSize.x = max_size.x + decor.x;
- min_max_info->ptMaxTrackSize.y = max_size.y + decor.y;
- }
- return 0;
- } else {
- break;
- }
- }
- case WM_PAINT:
-
- Main::force_redraw();
- break;
-
- case WM_SYSCOMMAND: // Intercept System Commands
- {
- switch (wParam) // Check System Calls
- {
- case SC_SCREENSAVE: // Screensaver Trying To Start?
- case SC_MONITORPOWER: // Monitor Trying To Enter Powersave?
- return 0; // Prevent From Happening
- case SC_KEYMENU:
- if ((lParam >> 16) <= 0)
- return 0;
- }
- break; // Exit
- }
-
- case WM_CLOSE: // Did We Receive A Close Message?
- {
- if (main_loop)
- main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
- //force_quit=true;
- return 0; // Jump Back
- }
- case WM_MOUSELEAVE: {
-
- old_invalid = true;
- outside = true;
- if (main_loop && mouse_mode != MOUSE_MODE_CAPTURED)
- main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
-
- } break;
- case WM_INPUT: {
- if (mouse_mode != MOUSE_MODE_CAPTURED || !use_raw_input) {
- break;
- }
-
- UINT dwSize;
-
- GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
- LPBYTE lpb = new BYTE[dwSize];
- if (lpb == NULL) {
- return 0;
- }
-
- if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize)
- OutputDebugString(TEXT("GetRawInputData does not return correct size !\n"));
-
- RAWINPUT *raw = (RAWINPUT *)lpb;
-
- if (raw->header.dwType == RIM_TYPEMOUSE) {
- Ref<InputEventMouseMotion> mm;
- mm.instance();
-
- mm->set_control(control_mem);
- mm->set_shift(shift_mem);
- mm->set_alt(alt_mem);
-
- mm->set_button_mask(last_button_state);
-
- Point2i c(video_mode.width / 2, video_mode.height / 2);
-
- // centering just so it works as before
- POINT pos = { (int)c.x, (int)c.y };
- ClientToScreen(hWnd, &pos);
- SetCursorPos(pos.x, pos.y);
-
- mm->set_position(c);
- mm->set_global_position(c);
- input->set_mouse_position(c);
- mm->set_speed(Vector2(0, 0));
-
- if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) {
- mm->set_relative(Vector2(raw->data.mouse.lLastX, raw->data.mouse.lLastY));
-
- } else if (raw->data.mouse.usFlags == MOUSE_MOVE_ABSOLUTE) {
-
- int nScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
- int nScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
- int nScreenLeft = GetSystemMetrics(SM_XVIRTUALSCREEN);
- int nScreenTop = GetSystemMetrics(SM_YVIRTUALSCREEN);
-
- Vector2 abs_pos(
- (double(raw->data.mouse.lLastX) - 65536.0 / (nScreenWidth)) * nScreenWidth / 65536.0 + nScreenLeft,
- (double(raw->data.mouse.lLastY) - 65536.0 / (nScreenHeight)) * nScreenHeight / 65536.0 + nScreenTop);
-
- POINT coords; //client coords
- coords.x = abs_pos.x;
- coords.y = abs_pos.y;
-
- ScreenToClient(hWnd, &coords);
-
- mm->set_relative(Vector2(coords.x - old_x, coords.y - old_y));
- old_x = coords.x;
- old_y = coords.y;
-
- /*Input.mi.dx = (int)((((double)(pos.x)-nScreenLeft) * 65536) / nScreenWidth + 65536 / (nScreenWidth));
- Input.mi.dy = (int)((((double)(pos.y)-nScreenTop) * 65536) / nScreenHeight + 65536 / (nScreenHeight));
- */
- }
-
- if (window_has_focus && main_loop && mm->get_relative() != Vector2())
- input->accumulate_input_event(mm);
- }
- delete[] lpb;
- } break;
- case WM_POINTERUPDATE: {
- if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
- break;
- }
-
- if (!win8p_GetPointerType || !win8p_GetPointerPenInfo) {
- 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;
- }
-
- POINTER_PEN_INFO pen_info;
- if (!win8p_GetPointerPenInfo(pointer_id, &pen_info)) {
- break;
- }
-
- if (input->is_emulating_mouse_from_touch()) {
- // Universal translation enabled; ignore OS translation
- LPARAM extra = GetMessageExtraInfo();
- if (IsTouchEvent(extra)) {
- break;
- }
- }
-
- if (outside) {
- //mouse enter
-
- if (main_loop && mouse_mode != MOUSE_MODE_CAPTURED)
- main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
-
- CursorShape c = cursor_shape;
- cursor_shape = CURSOR_MAX;
- set_cursor_shape(c);
- outside = false;
-
- //Once-Off notification, must call again....
- TRACKMOUSEEVENT tme;
- tme.cbSize = sizeof(TRACKMOUSEEVENT);
- tme.dwFlags = TME_LEAVE;
- tme.hwndTrack = hWnd;
- tme.dwHoverTime = HOVER_DEFAULT;
- TrackMouseEvent(&tme);
- }
-
- // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
- if (!window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED)
- break;
-
- Ref<InputEventMouseMotion> mm;
- mm.instance();
-
- mm->set_pressure(pen_info.pressure ? (float)pen_info.pressure / 1024 : 0);
- mm->set_tilt(Vector2(pen_info.tiltX ? (float)pen_info.tiltX / 90 : 0, pen_info.tiltY ? (float)pen_info.tiltY / 90 : 0));
-
- mm->set_control((wParam & MK_CONTROL) != 0);
- mm->set_shift((wParam & MK_SHIFT) != 0);
- mm->set_alt(alt_mem);
-
- mm->set_button_mask(last_button_state);
-
- POINT coords; //client coords
- coords.x = GET_X_LPARAM(lParam);
- coords.y = GET_Y_LPARAM(lParam);
-
- ScreenToClient(hWnd, &coords);
-
- mm->set_position(Vector2(coords.x, coords.y));
- mm->set_global_position(Vector2(coords.x, coords.y));
-
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
-
- Point2i c(video_mode.width / 2, video_mode.height / 2);
- old_x = c.x;
- old_y = c.y;
-
- if (mm->get_position() == c) {
- center = c;
- return 0;
- }
-
- Point2i ncenter = mm->get_position();
- center = ncenter;
- POINT pos = { (int)c.x, (int)c.y };
- ClientToScreen(hWnd, &pos);
- SetCursorPos(pos.x, pos.y);
- }
-
- input->set_mouse_position(mm->get_position());
- mm->set_speed(input->get_last_mouse_speed());
-
- if (old_invalid) {
-
- old_x = mm->get_position().x;
- old_y = mm->get_position().y;
- old_invalid = false;
- }
-
- mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
- old_x = mm->get_position().x;
- old_y = mm->get_position().y;
- if (window_has_focus && main_loop)
- input->parse_input_event(mm);
-
- return 0; //Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event
- } break;
- case WM_MOUSEMOVE: {
- if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
- break;
- }
-
- if (input->is_emulating_mouse_from_touch()) {
- // Universal translation enabled; ignore OS translation
- LPARAM extra = GetMessageExtraInfo();
- if (IsTouchEvent(extra)) {
- break;
- }
- }
-
- if (outside) {
- //mouse enter
-
- if (main_loop && mouse_mode != MOUSE_MODE_CAPTURED)
- main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
-
- CursorShape c = cursor_shape;
- cursor_shape = CURSOR_MAX;
- set_cursor_shape(c);
- outside = false;
-
- //Once-Off notification, must call again....
- TRACKMOUSEEVENT tme;
- tme.cbSize = sizeof(TRACKMOUSEEVENT);
- tme.dwFlags = TME_LEAVE;
- tme.hwndTrack = hWnd;
- tme.dwHoverTime = HOVER_DEFAULT;
- TrackMouseEvent(&tme);
- }
-
- // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
- if (!window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED)
- break;
-
- Ref<InputEventMouseMotion> mm;
- mm.instance();
-
- mm->set_control((wParam & MK_CONTROL) != 0);
- mm->set_shift((wParam & MK_SHIFT) != 0);
- mm->set_alt(alt_mem);
-
- mm->set_button_mask(last_button_state);
-
- mm->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
- mm->set_global_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
-
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
-
- Point2i c(video_mode.width / 2, video_mode.height / 2);
- old_x = c.x;
- old_y = c.y;
-
- if (mm->get_position() == c) {
- center = c;
- return 0;
- }
-
- Point2i ncenter = mm->get_position();
- center = ncenter;
- POINT pos = { (int)c.x, (int)c.y };
- ClientToScreen(hWnd, &pos);
- SetCursorPos(pos.x, pos.y);
- }
-
- input->set_mouse_position(mm->get_position());
- mm->set_speed(input->get_last_mouse_speed());
-
- if (old_invalid) {
-
- old_x = mm->get_position().x;
- old_y = mm->get_position().y;
- old_invalid = false;
- }
-
- mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
- old_x = mm->get_position().x;
- old_y = mm->get_position().y;
- if (window_has_focus && main_loop)
- input->accumulate_input_event(mm);
-
- } break;
- case WM_LBUTTONDOWN:
- case WM_LBUTTONUP:
- if (input->is_emulating_mouse_from_touch()) {
- // Universal translation enabled; ignore OS translations for left button
- LPARAM extra = GetMessageExtraInfo();
- if (IsTouchEvent(extra)) {
- break;
- }
- }
- [[fallthrough]];
- case WM_MBUTTONDOWN:
- case WM_MBUTTONUP:
- case WM_RBUTTONDOWN:
- case WM_RBUTTONUP:
- case WM_MOUSEWHEEL:
- case WM_MOUSEHWHEEL:
- case WM_LBUTTONDBLCLK:
- case WM_MBUTTONDBLCLK:
- case WM_RBUTTONDBLCLK:
- case WM_XBUTTONDBLCLK:
- case WM_XBUTTONDOWN:
- case WM_XBUTTONUP: {
-
- Ref<InputEventMouseButton> mb;
- mb.instance();
-
- switch (uMsg) {
- case WM_LBUTTONDOWN: {
- mb->set_pressed(true);
- mb->set_button_index(1);
- } break;
- case WM_LBUTTONUP: {
- mb->set_pressed(false);
- mb->set_button_index(1);
- } break;
- case WM_MBUTTONDOWN: {
- mb->set_pressed(true);
- mb->set_button_index(3);
- } break;
- case WM_MBUTTONUP: {
- mb->set_pressed(false);
- mb->set_button_index(3);
- } break;
- case WM_RBUTTONDOWN: {
- mb->set_pressed(true);
- mb->set_button_index(2);
- } break;
- case WM_RBUTTONUP: {
- mb->set_pressed(false);
- mb->set_button_index(2);
- } break;
- case WM_LBUTTONDBLCLK: {
- mb->set_pressed(true);
- mb->set_button_index(1);
- mb->set_doubleclick(true);
- } break;
- case WM_RBUTTONDBLCLK: {
- mb->set_pressed(true);
- mb->set_button_index(2);
- mb->set_doubleclick(true);
- } break;
- case WM_MBUTTONDBLCLK: {
- mb->set_pressed(true);
- mb->set_button_index(3);
- mb->set_doubleclick(true);
- } break;
- case WM_MOUSEWHEEL: {
-
- mb->set_pressed(true);
- int motion = (short)HIWORD(wParam);
- if (!motion)
- return 0;
-
- if (motion > 0)
- mb->set_button_index(BUTTON_WHEEL_UP);
- else
- mb->set_button_index(BUTTON_WHEEL_DOWN);
-
- } break;
- case WM_MOUSEHWHEEL: {
-
- mb->set_pressed(true);
- int motion = (short)HIWORD(wParam);
- if (!motion)
- return 0;
-
- if (motion < 0) {
- mb->set_button_index(BUTTON_WHEEL_LEFT);
- mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA));
- } else {
- mb->set_button_index(BUTTON_WHEEL_RIGHT);
- mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA));
- }
- } break;
- case WM_XBUTTONDOWN: {
-
- mb->set_pressed(true);
- if (HIWORD(wParam) == XBUTTON1)
- mb->set_button_index(BUTTON_XBUTTON1);
- else
- mb->set_button_index(BUTTON_XBUTTON2);
- } break;
- case WM_XBUTTONUP: {
-
- mb->set_pressed(false);
- if (HIWORD(wParam) == XBUTTON1)
- mb->set_button_index(BUTTON_XBUTTON1);
- else
- mb->set_button_index(BUTTON_XBUTTON2);
- } break;
- case WM_XBUTTONDBLCLK: {
-
- mb->set_pressed(true);
- if (HIWORD(wParam) == XBUTTON1)
- mb->set_button_index(BUTTON_XBUTTON1);
- else
- mb->set_button_index(BUTTON_XBUTTON2);
- mb->set_doubleclick(true);
- } break;
- default: {
- return 0;
- }
- }
-
- mb->set_control((wParam & MK_CONTROL) != 0);
- mb->set_shift((wParam & MK_SHIFT) != 0);
- mb->set_alt(alt_mem);
- //mb->get_alt()=(wParam&MK_MENU)!=0;
- if (mb->is_pressed())
- last_button_state |= (1 << (mb->get_button_index() - 1));
- else
- last_button_state &= ~(1 << (mb->get_button_index() - 1));
- mb->set_button_mask(last_button_state);
-
- mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
-
- if (mouse_mode == MOUSE_MODE_CAPTURED && !use_raw_input) {
-
- mb->set_position(Vector2(old_x, old_y));
- }
-
- if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) {
- if (mb->is_pressed()) {
-
- if (++pressrc > 0 && mouse_mode != MOUSE_MODE_CAPTURED)
- SetCapture(hWnd);
- } else {
-
- if (--pressrc <= 0) {
- if (mouse_mode != MOUSE_MODE_CAPTURED) {
- ReleaseCapture();
- }
- pressrc = 0;
- }
- }
- } else {
- // for reasons unknown to mankind, wheel comes in screen coordinates
- POINT coords;
- coords.x = mb->get_position().x;
- coords.y = mb->get_position().y;
-
- ScreenToClient(hWnd, &coords);
-
- mb->set_position(Vector2(coords.x, coords.y));
- }
-
- mb->set_global_position(mb->get_position());
-
- if (main_loop) {
- input->accumulate_input_event(mb);
- if (mb->is_pressed() && mb->get_button_index() > 3 && mb->get_button_index() < 8) {
- //send release for mouse wheel
- Ref<InputEventMouseButton> mbd = mb->duplicate();
- last_button_state &= ~(1 << (mbd->get_button_index() - 1));
- mbd->set_button_mask(last_button_state);
- mbd->set_pressed(false);
- input->accumulate_input_event(mbd);
- }
- }
- } break;
-
- case WM_MOVE: {
- if (!IsIconic(hWnd)) {
- int x = LOWORD(lParam);
- int y = HIWORD(lParam);
- last_pos = Point2(x, y);
- }
- } break;
-
- case WM_SIZE: {
- // Ignore size when a SIZE_MINIMIZED event is triggered
- if (wParam != SIZE_MINIMIZED) {
- int window_w = LOWORD(lParam);
- int window_h = HIWORD(lParam);
- if (window_w > 0 && window_h > 0 && !preserve_window_size) {
- video_mode.width = window_w;
- video_mode.height = window_h;
- } else {
- preserve_window_size = false;
- set_window_size(Size2(video_mode.width, video_mode.height));
- }
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
- context_vulkan->window_resize(0, video_mode.width, video_mode.height);
- }
-#endif
- }
-
- if (wParam == SIZE_MAXIMIZED) {
- maximized = true;
- minimized = false;
- } else if (wParam == SIZE_MINIMIZED) {
- maximized = false;
- minimized = true;
- } else if (wParam == SIZE_RESTORED) {
- maximized = false;
- minimized = false;
- }
- if (is_layered_allowed() && layered_window) {
- DeleteObject(hBitmap);
-
- RECT r;
- GetWindowRect(hWnd, &r);
- dib_size = Size2i(r.right - r.left, r.bottom - r.top);
-
- BITMAPINFO bmi;
- ZeroMemory(&bmi, sizeof(BITMAPINFO));
- bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- bmi.bmiHeader.biWidth = dib_size.x;
- bmi.bmiHeader.biHeight = dib_size.y;
- bmi.bmiHeader.biPlanes = 1;
- bmi.bmiHeader.biBitCount = 32;
- bmi.bmiHeader.biCompression = BI_RGB;
- bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4;
- hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, NULL, 0x0);
- SelectObject(hDC_dib, hBitmap);
-
- ZeroMemory(dib_data, dib_size.x * dib_size.y * 4);
- }
- //return 0; // Jump Back
- } break;
-
- case WM_ENTERSIZEMOVE: {
- input->release_pressed_events();
- move_timer_id = SetTimer(hWnd, 1, USER_TIMER_MINIMUM, (TIMERPROC)NULL);
- } break;
- case WM_EXITSIZEMOVE: {
- KillTimer(hWnd, move_timer_id);
- } break;
- case WM_TIMER: {
- if (wParam == move_timer_id) {
- process_key_events();
- if (!Main::is_iterating()) {
- Main::iteration();
- }
- }
- } break;
-
- case WM_SYSKEYDOWN:
- case WM_SYSKEYUP:
- case WM_KEYUP:
- case WM_KEYDOWN: {
-
- if (wParam == VK_SHIFT)
- shift_mem = uMsg == WM_KEYDOWN;
- if (wParam == VK_CONTROL)
- control_mem = uMsg == WM_KEYDOWN;
- if (wParam == VK_MENU) {
- alt_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN);
- if (lParam & (1 << 24))
- gr_mem = alt_mem;
- }
-
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
- // When SetCapture is used, ALT+F4 hotkey is ignored by Windows, so handle it ourselves
- if (wParam == VK_F4 && alt_mem && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)) {
- if (main_loop)
- main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
- }
- }
- /*
- if (wParam==VK_WIN) TODO wtf is this?
- meta_mem=uMsg==WM_KEYDOWN;
- */
- [[fallthrough]];
- }
- case WM_CHAR: {
-
- ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);
-
- // Make sure we don't include modifiers for the modifier key itself.
- KeyEvent ke;
- ke.shift = (wParam != VK_SHIFT) ? shift_mem : false;
- ke.alt = (!(wParam == VK_MENU && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN))) ? alt_mem : false;
- ke.control = (wParam != VK_CONTROL) ? control_mem : false;
- ke.meta = meta_mem;
- ke.uMsg = uMsg;
-
- if (ke.uMsg == WM_SYSKEYDOWN)
- ke.uMsg = WM_KEYDOWN;
- if (ke.uMsg == WM_SYSKEYUP)
- ke.uMsg = WM_KEYUP;
-
- ke.wParam = wParam;
- ke.lParam = lParam;
- key_event_buffer[key_event_pos++] = ke;
-
- } break;
- case WM_INPUTLANGCHANGEREQUEST: {
-
- // FIXME: Do something?
- } break;
-
- case WM_TOUCH: {
-
- BOOL bHandled = FALSE;
- UINT cInputs = LOWORD(wParam);
- PTOUCHINPUT pInputs = memnew_arr(TOUCHINPUT, cInputs);
- if (pInputs) {
- if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))) {
- for (UINT i = 0; i < cInputs; i++) {
- TOUCHINPUT ti = pInputs[i];
- POINT touch_pos = {
- TOUCH_COORD_TO_PIXEL(ti.x),
- TOUCH_COORD_TO_PIXEL(ti.y),
- };
- ScreenToClient(hWnd, &touch_pos);
- //do something with each touch input entry
- if (ti.dwFlags & TOUCHEVENTF_MOVE) {
-
- _drag_event(touch_pos.x, touch_pos.y, ti.dwID);
- } else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) {
-
- _touch_event(ti.dwFlags & TOUCHEVENTF_DOWN, touch_pos.x, touch_pos.y, ti.dwID);
- };
- }
- bHandled = TRUE;
- } else {
- /* handle the error here */
- }
- memdelete_arr(pInputs);
- } else {
- /* handle the error here, probably out of memory */
- }
- if (bHandled) {
- CloseTouchInputHandle((HTOUCHINPUT)lParam);
- return 0;
- };
-
- } break;
-
- case WM_DEVICECHANGE: {
-
- joypad->probe_joypads();
- } break;
- case WM_SETCURSOR: {
- if (LOWORD(lParam) == HTCLIENT) {
- if (window_has_focus && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED)) {
- //Hide the cursor
- if (hCursor == NULL)
- hCursor = SetCursor(NULL);
- else
- SetCursor(NULL);
- } else {
- if (hCursor != NULL) {
- CursorShape c = cursor_shape;
- cursor_shape = CURSOR_MAX;
- set_cursor_shape(c);
- hCursor = NULL;
- }
- }
- }
-
- } break;
- case WM_DROPFILES: {
-
- HDROP hDropInfo = (HDROP)wParam;
- const int buffsize = 4096;
- wchar_t buf[buffsize];
-
- int fcount = DragQueryFileW(hDropInfo, 0xFFFFFFFF, NULL, 0);
-
- Vector<String> files;
-
- for (int i = 0; i < fcount; i++) {
-
- DragQueryFileW(hDropInfo, i, buf, buffsize);
- String file = buf;
- files.push_back(file);
- }
-
- if (files.size() && main_loop) {
- main_loop->drop_files(files, 0);
- }
-
- } break;
-
- default: {
-
- if (user_proc) {
-
- return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
- };
- };
- }
-
- return DefWindowProcW(hWnd, uMsg, wParam, lParam);
-}
-
-LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
-
- OS_Windows *os_win = static_cast<OS_Windows *>(OS::get_singleton());
- if (os_win)
- return os_win->WndProc(hWnd, uMsg, wParam, lParam);
- else
- return DefWindowProcW(hWnd, uMsg, wParam, lParam);
-}
-
-void OS_Windows::process_key_events() {
-
- for (int i = 0; i < key_event_pos; i++) {
-
- KeyEvent &ke = key_event_buffer[i];
- switch (ke.uMsg) {
-
- case WM_CHAR: {
- if ((i == 0 && ke.uMsg == WM_CHAR) || (i > 0 && key_event_buffer[i - 1].uMsg == WM_CHAR)) {
- Ref<InputEventKey> k;
- k.instance();
-
- k->set_shift(ke.shift);
- k->set_alt(ke.alt);
- k->set_control(ke.control);
- k->set_metakey(ke.meta);
- 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);
- if (k->get_unicode() && gr_mem) {
- k->set_alt(false);
- k->set_control(false);
- }
-
- if (k->get_unicode() < 32)
- k->set_unicode(0);
-
- input->accumulate_input_event(k);
- }
-
- //do nothing
- } break;
- case WM_KEYUP:
- case WM_KEYDOWN: {
-
- Ref<InputEventKey> k;
- k.instance();
-
- k->set_shift(ke.shift);
- k->set_alt(ke.alt);
- k->set_control(ke.control);
- k->set_metakey(ke.meta);
-
- k->set_pressed(ke.uMsg == WM_KEYDOWN);
-
- if ((ke.lParam & (1 << 24)) && (ke.wParam == VK_RETURN)) {
- // Special case for Numpad Enter key
- k->set_keycode(KEY_KP_ENTER);
- } else {
- k->set_keycode(KeyMappingWindows::get_keysym(ke.wParam));
- }
-
- 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);
- }
- if (k->get_unicode() && gr_mem) {
- k->set_alt(false);
- k->set_control(false);
- }
-
- if (k->get_unicode() < 32)
- k->set_unicode(0);
-
- k->set_echo((ke.uMsg == WM_KEYDOWN && (ke.lParam & (1 << 30))));
-
- input->accumulate_input_event(k);
-
- } break;
- }
- }
-
- key_event_pos = 0;
-}
-
-enum _MonitorDpiType {
- MDT_Effective_DPI = 0,
- MDT_Angular_DPI = 1,
- MDT_Raw_DPI = 2,
- MDT_Default = MDT_Effective_DPI
-};
-
-static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Default) {
-
- int dpiX = 96, dpiY = 96;
-
- static HMODULE Shcore = NULL;
- typedef HRESULT(WINAPI * GetDPIForMonitor_t)(HMONITOR hmonitor, _MonitorDpiType dpiType, UINT * dpiX, UINT * dpiY);
- static GetDPIForMonitor_t getDPIForMonitor = NULL;
-
- if (Shcore == NULL) {
- Shcore = LoadLibraryW(L"Shcore.dll");
- getDPIForMonitor = Shcore ? (GetDPIForMonitor_t)GetProcAddress(Shcore, "GetDpiForMonitor") : NULL;
-
- if ((Shcore == NULL) || (getDPIForMonitor == NULL)) {
- if (Shcore)
- FreeLibrary(Shcore);
- Shcore = (HMODULE)INVALID_HANDLE_VALUE;
- }
- }
-
- UINT x = 0, y = 0;
- HRESULT hr = E_FAIL;
- if (hmon && (Shcore != (HMODULE)INVALID_HANDLE_VALUE)) {
- hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y);
- if (SUCCEEDED(hr) && (x > 0) && (y > 0)) {
-
- dpiX = (int)x;
- dpiY = (int)y;
- }
- } else {
- static int overallX = 0, overallY = 0;
- if (overallX <= 0 || overallY <= 0) {
- HDC hdc = GetDC(NULL);
- if (hdc) {
- overallX = GetDeviceCaps(hdc, LOGPIXELSX);
- overallY = GetDeviceCaps(hdc, LOGPIXELSY);
- ReleaseDC(NULL, hdc);
- }
- }
- if (overallX > 0 && overallY > 0) {
- dpiX = overallX;
- dpiY = overallY;
- }
- }
-
- return (dpiX + dpiY) / 2;
-}
-
-typedef enum _SHC_PROCESS_DPI_AWARENESS {
- SHC_PROCESS_DPI_UNAWARE = 0,
- SHC_PROCESS_SYSTEM_DPI_AWARE = 1,
- SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2
-} SHC_PROCESS_DPI_AWARENESS;
-
-int OS_Windows::get_current_video_driver() const {
- return video_driver_index;
-}
-
-Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
-
- main_loop = NULL;
- outside = true;
- window_has_focus = true;
- WNDCLASSEXW wc;
-
- if (is_hidpi_allowed()) {
- HMODULE Shcore = LoadLibraryW(L"Shcore.dll");
-
- if (Shcore != NULL) {
- typedef HRESULT(WINAPI * SetProcessDpiAwareness_t)(SHC_PROCESS_DPI_AWARENESS);
-
- SetProcessDpiAwareness_t SetProcessDpiAwareness = (SetProcessDpiAwareness_t)GetProcAddress(Shcore, "SetProcessDpiAwareness");
-
- if (SetProcessDpiAwareness) {
- SetProcessDpiAwareness(SHC_PROCESS_SYSTEM_DPI_AWARE);
- }
- }
- }
-
- video_mode = p_desired;
- //printf("**************** desired %s, mode %s\n", p_desired.fullscreen?"true":"false", video_mode.fullscreen?"true":"false");
- RECT WindowRect;
-
- WindowRect.left = 0;
- WindowRect.right = video_mode.width;
- WindowRect.top = 0;
- WindowRect.bottom = video_mode.height;
-
- memset(&wc, 0, sizeof(WNDCLASSEXW));
- wc.cbSize = sizeof(WNDCLASSEXW);
- wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
- wc.lpfnWndProc = (WNDPROC)::WndProc;
- wc.cbClsExtra = 0;
- wc.cbWndExtra = 0;
- //wc.hInstance = hInstance;
- wc.hInstance = godot_hinstance ? godot_hinstance : GetModuleHandle(NULL);
- wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
- wc.hCursor = NULL; //LoadCursor(NULL, IDC_ARROW);
- wc.hbrBackground = NULL;
- wc.lpszMenuName = NULL;
- wc.lpszClassName = L"Engine";
-
- if (!RegisterClassExW(&wc)) {
- MessageBox(NULL, "Failed To Register The Window Class.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
- return ERR_UNAVAILABLE;
- }
-
- use_raw_input = true;
-
- RAWINPUTDEVICE Rid[1];
-
- Rid[0].usUsagePage = 0x01;
- Rid[0].usUsage = 0x02;
- Rid[0].dwFlags = 0;
- Rid[0].hwndTarget = 0;
-
- if (RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) {
- //registration failed.
- use_raw_input = false;
- }
-
- pre_fs_valid = true;
- if (video_mode.fullscreen) {
-
- /* this returns DPI unaware size, commenting
- DEVMODE current;
- memset(&current, 0, sizeof(current));
- EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &current);
-
- WindowRect.right = current.dmPelsWidth;
- WindowRect.bottom = current.dmPelsHeight;
-
- */
-
- EnumSizeData data = { 0, 0, Size2() };
- EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcSize, (LPARAM)&data);
-
- WindowRect.right = data.size.width;
- WindowRect.bottom = data.size.height;
-
- /* DEVMODE dmScreenSettings;
- memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
- dmScreenSettings.dmSize=sizeof(dmScreenSettings);
- dmScreenSettings.dmPelsWidth = video_mode.width;
- dmScreenSettings.dmPelsHeight = video_mode.height;
- dmScreenSettings.dmBitsPerPel = current.dmBitsPerPel;
- dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
-
- LONG err = ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN);
- if (err!=DISP_CHANGE_SUCCESSFUL) {
-
- video_mode.fullscreen=false;
- }*/
- pre_fs_valid = false;
- }
-
- DWORD dwExStyle;
- DWORD dwStyle;
-
- if (video_mode.fullscreen || video_mode.borderless_window) {
-
- dwExStyle = WS_EX_APPWINDOW;
- dwStyle = WS_POPUP;
-
- } else {
- dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
- dwStyle = WS_OVERLAPPEDWINDOW;
- if (!video_mode.resizable) {
- dwStyle &= ~WS_THICKFRAME;
- dwStyle &= ~WS_MAXIMIZEBOX;
- }
- }
-
- AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
-
- char *windowid;
-#ifdef MINGW_ENABLED
- windowid = getenv("GODOT_WINDOWID");
-#else
- size_t len;
- _dupenv_s(&windowid, &len, "GODOT_WINDOWID");
-#endif
-
- if (windowid) {
-
-// strtoull on mingw
-#ifdef MINGW_ENABLED
- hWnd = (HWND)strtoull(windowid, NULL, 0);
-#else
- hWnd = (HWND)_strtoui64(windowid, NULL, 0);
-#endif
- free(windowid);
- SetLastError(0);
- user_proc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC);
- SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)(WNDPROC)::WndProc);
- DWORD le = GetLastError();
- if (user_proc == 0 && le != 0) {
-
- printf("Error setting WNDPROC: %li\n", le);
- };
- GetWindowLongPtr(hWnd, GWLP_WNDPROC);
-
- RECT rect;
- if (!GetClientRect(hWnd, &rect)) {
- MessageBoxW(NULL, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);
- return ERR_UNAVAILABLE;
- };
- video_mode.width = rect.right;
- video_mode.height = rect.bottom;
- video_mode.fullscreen = false;
- } else {
-
- hWnd = CreateWindowExW(
- dwExStyle,
- L"Engine", L"",
- dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
- (GetSystemMetrics(SM_CXSCREEN) - WindowRect.right) / 2,
- (GetSystemMetrics(SM_CYSCREEN) - WindowRect.bottom) / 2,
- WindowRect.right - WindowRect.left,
- WindowRect.bottom - WindowRect.top,
- NULL, NULL, hInstance, NULL);
- if (!hWnd) {
- MessageBoxW(NULL, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);
- return ERR_UNAVAILABLE;
- }
- };
-
- if (video_mode.always_on_top) {
- SetWindowPos(hWnd, video_mode.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
- }
-
- //!!!!!!!!!!!!!!!!!!!!!!!!!!
- //TODO - do Vulkan and GLES2 support checks, driver selection and fallback
- video_driver_index = p_video_driver;
- print_verbose("Driver: " + String(get_video_driver_name(video_driver_index)) + " [" + itos(video_driver_index) + "]");
- //!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- // Init context and rendering device
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
-
- context_gles2 = memnew(ContextGL_Windows(hWnd, false));
-
- if (context_gles2->initialize() != OK) {
- memdelete(context_gles2);
- context_gles2 = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
-
- context_gles2->set_use_vsync(video_mode.use_vsync);
- set_vsync_via_compositor(video_mode.vsync_via_compositor);
-
- if (RasterizerGLES2::is_viable() == OK) {
- RasterizerGLES2::register_config();
- RasterizerGLES2::make_current();
- } else {
- memdelete(context_gles2);
- context_gles2 = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
-
- context_vulkan = memnew(VulkanContextWindows);
- if (context_vulkan->initialize() != OK) {
- memdelete(context_vulkan);
- context_vulkan = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
- if (context_vulkan->window_create(hWnd, hInstance, get_video_mode().width, get_video_mode().height) == -1) {
- memdelete(context_vulkan);
- context_vulkan = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
-
- //temporary
- rendering_device_vulkan = memnew(RenderingDeviceVulkan);
- rendering_device_vulkan->initialize(context_vulkan);
-
- RasterizerRD::make_current();
- }
-#endif
-
- 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();
-
- input = memnew(InputDefault);
- joypad = memnew(JoypadWindows(input, &hWnd));
-
- AudioDriverManager::initialize(p_audio_driver);
-
- TRACKMOUSEEVENT tme;
- tme.cbSize = sizeof(TRACKMOUSEEVENT);
- tme.dwFlags = TME_LEAVE;
- tme.hwndTrack = hWnd;
- tme.dwHoverTime = HOVER_DEFAULT;
- TrackMouseEvent(&tme);
-
- RegisterTouchWindow(hWnd, 0);
-
- _ensure_user_data_dir();
-
- DragAcceptFiles(hWnd, true);
-
- move_timer_id = 1;
-
- if (!is_no_window_mode_enabled()) {
- ShowWindow(hWnd, SW_SHOW); // Show The Window
- SetForegroundWindow(hWnd); // Slightly Higher Priority
- SetFocus(hWnd); // Sets Keyboard Focus To
- }
-
- if (p_desired.layered) {
- set_window_per_pixel_transparency_enabled(true);
- }
-
- // IME
- im_himc = ImmGetContext(hWnd);
- ImmReleaseContext(hWnd, im_himc);
-
- im_position = Vector2();
-
- set_ime_active(false);
-
- if (!OS::get_singleton()->is_in_low_processor_usage_mode()) {
- //SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
- SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
- DWORD index = 0;
- HANDLE handle = AvSetMmThreadCharacteristics("Games", &index);
- if (handle)
- AvSetMmThreadPriority(handle, AVRT_PRIORITY_CRITICAL);
-
- // This is needed to make sure that background work does not starve the main thread.
- // This is only setting priority of this thread, not the whole process.
- SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
- }
-
- update_real_mouse_position();
-
- return OK;
-}
-
-void OS_Windows::set_clipboard(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");
-
- if (!OpenClipboard(hWnd)) {
- ERR_FAIL_MSG("Unable to open clipboard.");
- }
- EmptyClipboard();
-
- HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (text.length() + 1) * sizeof(CharType));
- ERR_FAIL_COND_MSG(mem == NULL, "Unable to allocate memory for clipboard contents.");
-
- LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem);
- memcpy(lptstrCopy, text.c_str(), (text.length() + 1) * sizeof(CharType));
- GlobalUnlock(mem);
-
- SetClipboardData(CF_UNICODETEXT, mem);
-
- // set the CF_TEXT version (not needed?)
- CharString utf8 = text.utf8();
- mem = GlobalAlloc(GMEM_MOVEABLE, utf8.length() + 1);
- ERR_FAIL_COND_MSG(mem == NULL, "Unable to allocate memory for clipboard contents.");
-
- LPTSTR ptr = (LPTSTR)GlobalLock(mem);
- memcpy(ptr, utf8.get_data(), utf8.length());
- ptr[utf8.length()] = 0;
- GlobalUnlock(mem);
-
- SetClipboardData(CF_TEXT, mem);
-
- CloseClipboard();
-};
-
-String OS_Windows::get_clipboard() const {
-
- String ret;
- if (!OpenClipboard(hWnd)) {
- ERR_FAIL_V_MSG("", "Unable to open clipboard.");
- };
-
- if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
-
- HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
- if (mem != NULL) {
-
- LPWSTR ptr = (LPWSTR)GlobalLock(mem);
- if (ptr != NULL) {
-
- ret = String((CharType *)ptr);
- GlobalUnlock(mem);
- };
- };
-
- } else if (IsClipboardFormatAvailable(CF_TEXT)) {
-
- HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
- if (mem != NULL) {
-
- LPTSTR ptr = (LPTSTR)GlobalLock(mem);
- if (ptr != NULL) {
-
- ret.parse_utf8((const char *)ptr);
- GlobalUnlock(mem);
- };
- };
- };
-
- CloseClipboard();
-
- return ret;
-};
-
void OS_Windows::delete_main_loop() {
-
if (main_loop)
memdelete(main_loop);
- main_loop = NULL;
+ main_loop = nullptr;
}
void OS_Windows::set_main_loop(MainLoop *p_main_loop) {
-
- input->set_main_loop(p_main_loop);
main_loop = p_main_loop;
}
void OS_Windows::finalize() {
-
#ifdef WINMIDI_ENABLED
driver_midi.close();
#endif
@@ -1642,621 +230,17 @@ void OS_Windows::finalize() {
if (main_loop)
memdelete(main_loop);
- main_loop = NULL;
-
- memdelete(joypad);
- memdelete(input);
- touch_state.clear();
-
- cursors_cache.clear();
- visual_server->finish();
- memdelete(visual_server);
-
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
-
- if (context_gles2)
- memdelete(context_gles2);
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_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(hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);
- };
+ main_loop = nullptr;
}
void OS_Windows::finalize_core() {
-
timeEndPeriod(1);
memdelete(process_map);
NetSocketPosix::cleanup();
}
-void OS_Windows::alert(const String &p_alert, const String &p_title) {
-
- if (!is_no_window_mode_enabled())
- MessageBoxW(NULL, p_alert.c_str(), p_title.c_str(), MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
- else
- print_line("ALERT: " + p_alert);
-}
-
-void OS_Windows::set_mouse_mode(MouseMode p_mode) {
-
- if (mouse_mode == p_mode)
- return;
-
- _set_mouse_mode_impl(p_mode);
-
- mouse_mode = p_mode;
-}
-
-void OS_Windows::_set_mouse_mode_impl(MouseMode p_mode) {
-
- if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED) {
- RECT clipRect;
- GetClientRect(hWnd, &clipRect);
- ClientToScreen(hWnd, (POINT *)&clipRect.left);
- ClientToScreen(hWnd, (POINT *)&clipRect.right);
- ClipCursor(&clipRect);
- if (p_mode == MOUSE_MODE_CAPTURED) {
- center = Point2i(video_mode.width / 2, video_mode.height / 2);
- POINT pos = { (int)center.x, (int)center.y };
- ClientToScreen(hWnd, &pos);
- SetCursorPos(pos.x, pos.y);
- SetCapture(hWnd);
- }
- } else {
- ReleaseCapture();
- ClipCursor(NULL);
- }
-
- if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_HIDDEN) {
- hCursor = SetCursor(NULL);
- } else {
- CursorShape c = cursor_shape;
- cursor_shape = CURSOR_MAX;
- set_cursor_shape(c);
- }
-}
-OS_Windows::MouseMode OS_Windows::get_mouse_mode() const {
-
- return mouse_mode;
-}
-
-void OS_Windows::warp_mouse_position(const Point2 &p_to) {
-
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
-
- old_x = p_to.x;
- old_y = p_to.y;
- } else {
-
- POINT p;
- p.x = p_to.x;
- p.y = p_to.y;
- ClientToScreen(hWnd, &p);
-
- SetCursorPos(p.x, p.y);
- }
-}
-
-Point2 OS_Windows::get_mouse_position() const {
-
- return Point2(old_x, old_y);
-}
-
-void OS_Windows::update_real_mouse_position() {
-
- POINT mouse_pos;
- if (GetCursorPos(&mouse_pos) && ScreenToClient(hWnd, &mouse_pos)) {
- if (mouse_pos.x > 0 && mouse_pos.y > 0 && mouse_pos.x <= video_mode.width && mouse_pos.y <= video_mode.height) {
- old_x = mouse_pos.x;
- old_y = mouse_pos.y;
- old_invalid = false;
- input->set_mouse_position(Point2i(mouse_pos.x, mouse_pos.y));
- }
- }
-}
-
-int OS_Windows::get_mouse_button_state() const {
-
- return last_button_state;
-}
-
-void OS_Windows::set_window_title(const String &p_title) {
-
- SetWindowTextW(hWnd, p_title.c_str());
-}
-
-void OS_Windows::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
-}
-
-OS::VideoMode OS_Windows::get_video_mode(int p_screen) const {
-
- return video_mode;
-}
-void OS_Windows::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
-}
-
-static BOOL CALLBACK _MonitorEnumProcCount(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
-
- int *data = (int *)dwData;
- (*data)++;
- return TRUE;
-}
-
-int OS_Windows::get_screen_count() const {
-
- int data = 0;
- EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcCount, (LPARAM)&data);
- return data;
-}
-
-typedef struct {
- int count;
- int screen;
- HMONITOR monitor;
-} EnumScreenData;
-
-static BOOL CALLBACK _MonitorEnumProcScreen(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
-
- EnumScreenData *data = (EnumScreenData *)dwData;
- if (data->monitor == hMonitor) {
- data->screen = data->count;
- }
-
- data->count++;
- return TRUE;
-}
-
-int OS_Windows::get_current_screen() const {
-
- EnumScreenData data = { 0, 0, MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST) };
- EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcScreen, (LPARAM)&data);
- return data.screen;
-}
-
-void OS_Windows::set_current_screen(int p_screen) {
-
- Vector2 ofs = get_window_position() - get_screen_position(get_current_screen());
- set_window_position(ofs + get_screen_position(p_screen));
-}
-
-static BOOL CALLBACK _MonitorEnumProcPos(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
-
- EnumPosData *data = (EnumPosData *)dwData;
- if (data->count == data->screen) {
- data->pos.x = lprcMonitor->left;
- data->pos.y = lprcMonitor->top;
- }
-
- data->count++;
- return TRUE;
-}
-
-Point2 OS_Windows::get_screen_position(int p_screen) const {
-
- EnumPosData data = { 0, p_screen == -1 ? get_current_screen() : p_screen, Point2() };
- EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcPos, (LPARAM)&data);
- return data.pos;
-}
-
-Size2 OS_Windows::get_screen_size(int p_screen) const {
-
- EnumSizeData data = { 0, p_screen == -1 ? get_current_screen() : p_screen, Size2() };
- EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcSize, (LPARAM)&data);
- return data.size;
-}
-
-typedef struct {
- int count;
- int screen;
- int dpi;
-} EnumDpiData;
-
-static BOOL CALLBACK _MonitorEnumProcDpi(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
-
- EnumDpiData *data = (EnumDpiData *)dwData;
- if (data->count == data->screen) {
- data->dpi = QueryDpiForMonitor(hMonitor);
- }
-
- data->count++;
- return TRUE;
-}
-
-int OS_Windows::get_screen_dpi(int p_screen) const {
-
- EnumDpiData data = { 0, p_screen == -1 ? get_current_screen() : p_screen, 72 };
- EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcDpi, (LPARAM)&data);
- return data.dpi;
-}
-
-Point2 OS_Windows::get_window_position() const {
-
- if (minimized) {
- return last_pos;
- }
-
- RECT r;
- GetWindowRect(hWnd, &r);
- return Point2(r.left, r.top);
-}
-
-void OS_Windows::set_window_position(const Point2 &p_position) {
-
- if (video_mode.fullscreen) return;
- RECT r;
- GetWindowRect(hWnd, &r);
- MoveWindow(hWnd, p_position.x, p_position.y, r.right - r.left, r.bottom - r.top, TRUE);
-
- // Don't let the mouse leave the window when moved
- if (mouse_mode == MOUSE_MODE_CONFINED) {
- RECT rect;
- GetClientRect(hWnd, &rect);
- ClientToScreen(hWnd, (POINT *)&rect.left);
- ClientToScreen(hWnd, (POINT *)&rect.right);
- ClipCursor(&rect);
- }
-
- last_pos = p_position;
- update_real_mouse_position();
-}
-
-Size2 OS_Windows::get_window_size() const {
-
- if (minimized) {
- return Size2(video_mode.width, video_mode.height);
- }
-
- RECT r;
- if (GetClientRect(hWnd, &r)) { // Only area inside of window border
- return Size2(r.right - r.left, r.bottom - r.top);
- }
- return Size2();
-}
-
-Size2 OS_Windows::get_max_window_size() const {
- return max_size;
-}
-
-Size2 OS_Windows::get_min_window_size() const {
- return min_size;
-}
-
-void OS_Windows::set_min_window_size(const Size2 p_size) {
-
- if ((p_size != Size2()) && (max_size != Size2()) && ((p_size.x > max_size.x) || (p_size.y > max_size.y))) {
- ERR_PRINT("Minimum window size can't be larger than maximum window size!");
- return;
- }
- min_size = p_size;
-}
-
-void OS_Windows::set_max_window_size(const Size2 p_size) {
-
- if ((p_size != Size2()) && ((p_size.x < min_size.x) || (p_size.y < min_size.y))) {
- ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
- return;
- }
- max_size = p_size;
-}
-
-Size2 OS_Windows::get_real_window_size() const {
-
- RECT r;
- if (GetWindowRect(hWnd, &r)) { // Includes area of the window border
- return Size2(r.right - r.left, r.bottom - r.top);
- }
- return Size2();
-}
-
-void OS_Windows::set_window_size(const Size2 p_size) {
-
- int w = p_size.width;
- int h = p_size.height;
-
- video_mode.width = w;
- video_mode.height = h;
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
- context_vulkan->window_resize(0, video_mode.width, video_mode.height);
- }
-#endif
-
- if (video_mode.fullscreen) {
- return;
- }
-
- RECT rect;
- GetWindowRect(hWnd, &rect);
-
- if (!video_mode.borderless_window) {
- RECT crect;
- GetClientRect(hWnd, &crect);
-
- w += (rect.right - rect.left) - (crect.right - crect.left);
- h += (rect.bottom - rect.top) - (crect.bottom - crect.top);
- }
-
- MoveWindow(hWnd, rect.left, rect.top, w, h, TRUE);
-
- // Don't let the mouse leave the window when resizing to a smaller resolution
- if (mouse_mode == MOUSE_MODE_CONFINED) {
- RECT crect;
- GetClientRect(hWnd, &crect);
- ClientToScreen(hWnd, (POINT *)&crect.left);
- ClientToScreen(hWnd, (POINT *)&crect.right);
- ClipCursor(&crect);
- }
-}
-void OS_Windows::set_window_fullscreen(bool p_enabled) {
-
- if (video_mode.fullscreen == p_enabled)
- return;
-
- if (layered_window)
- set_window_per_pixel_transparency_enabled(false);
-
- if (p_enabled) {
-
- was_maximized = maximized;
-
- if (pre_fs_valid) {
- GetWindowRect(hWnd, &pre_fs_rect);
- }
-
- int cs = get_current_screen();
- Point2 pos = get_screen_position(cs);
- Size2 size = get_screen_size(cs);
-
- video_mode.fullscreen = true;
-
- _update_window_style(false);
-
- MoveWindow(hWnd, pos.x, pos.y, size.width, size.height, TRUE);
-
- } else {
-
- RECT rect;
-
- video_mode.fullscreen = false;
-
- if (pre_fs_valid) {
- rect = pre_fs_rect;
- } else {
- rect.left = 0;
- rect.right = video_mode.width;
- rect.top = 0;
- rect.bottom = video_mode.height;
- }
-
- _update_window_style(false, was_maximized);
-
- MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
-
- pre_fs_valid = true;
- }
-}
-bool OS_Windows::is_window_fullscreen() const {
-
- return video_mode.fullscreen;
-}
-void OS_Windows::set_window_resizable(bool p_enabled) {
-
- if (video_mode.resizable == p_enabled)
- return;
-
- video_mode.resizable = p_enabled;
-
- _update_window_style();
-}
-bool OS_Windows::is_window_resizable() const {
-
- return video_mode.resizable;
-}
-void OS_Windows::set_window_minimized(bool p_enabled) {
-
- if (p_enabled) {
- maximized = false;
- minimized = true;
- ShowWindow(hWnd, SW_MINIMIZE);
- } else {
- ShowWindow(hWnd, SW_RESTORE);
- maximized = false;
- minimized = false;
- }
-}
-bool OS_Windows::is_window_minimized() const {
-
- return minimized;
-}
-void OS_Windows::set_window_maximized(bool p_enabled) {
-
- if (p_enabled) {
- maximized = true;
- minimized = false;
- ShowWindow(hWnd, SW_MAXIMIZE);
- } else {
- ShowWindow(hWnd, SW_RESTORE);
- maximized = false;
- minimized = false;
- }
-}
-bool OS_Windows::is_window_maximized() const {
-
- return maximized;
-}
-
-void OS_Windows::set_window_always_on_top(bool p_enabled) {
- if (video_mode.always_on_top == p_enabled)
- return;
-
- video_mode.always_on_top = p_enabled;
-
- _update_window_style();
-}
-
-bool OS_Windows::is_window_always_on_top() const {
- return video_mode.always_on_top;
-}
-
-bool OS_Windows::is_window_focused() const {
-
- return window_focused;
-}
-
-void OS_Windows::set_console_visible(bool p_enabled) {
- if (console_visible == p_enabled)
- return;
- ShowWindow(GetConsoleWindow(), p_enabled ? SW_SHOW : SW_HIDE);
- console_visible = p_enabled;
-}
-
-bool OS_Windows::is_console_visible() const {
- return console_visible;
-}
-
-bool OS_Windows::get_window_per_pixel_transparency_enabled() const {
-
- if (!is_layered_allowed()) return false;
- return layered_window;
-}
-
-void OS_Windows::set_window_per_pixel_transparency_enabled(bool p_enabled) {
-
- if (!is_layered_allowed()) return;
- if (layered_window != p_enabled) {
- if (p_enabled) {
- set_borderless_window(true);
- //enable per-pixel alpha
- hDC_dib = CreateCompatibleDC(GetDC(hWnd));
-
- SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
-
- RECT r;
- GetWindowRect(hWnd, &r);
- dib_size = Size2(r.right - r.left, r.bottom - r.top);
-
- BITMAPINFO bmi;
- ZeroMemory(&bmi, sizeof(BITMAPINFO));
- bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- bmi.bmiHeader.biWidth = dib_size.x;
- bmi.bmiHeader.biHeight = dib_size.y;
- bmi.bmiHeader.biPlanes = 1;
- bmi.bmiHeader.biBitCount = 32;
- bmi.bmiHeader.biCompression = BI_RGB;
- bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4;
- hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, NULL, 0x0);
- SelectObject(hDC_dib, hBitmap);
-
- ZeroMemory(dib_data, dib_size.x * dib_size.y * 4);
-
- layered_window = true;
- } else {
- //disable per-pixel alpha
- layered_window = false;
-
- SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
-
- //cleanup
- DeleteObject(hBitmap);
- DeleteDC(hDC_dib);
- }
- }
-}
-
-uint8_t *OS_Windows::get_layered_buffer_data() {
-
- return (is_layered_allowed() && layered_window) ? dib_data : NULL;
-}
-
-Size2 OS_Windows::get_layered_buffer_size() {
-
- return (is_layered_allowed() && layered_window) ? dib_size : Size2();
-}
-
-void OS_Windows::swap_layered_buffer() {
-
- if (is_layered_allowed() && layered_window) {
-
- //premultiply alpha
- for (int y = 0; y < dib_size.y; y++) {
- for (int x = 0; x < dib_size.x; x++) {
- float alpha = (float)dib_data[y * (int)dib_size.x * 4 + x * 4 + 3] / (float)0xFF;
- dib_data[y * (int)dib_size.x * 4 + x * 4 + 0] *= alpha;
- dib_data[y * (int)dib_size.x * 4 + x * 4 + 1] *= alpha;
- dib_data[y * (int)dib_size.x * 4 + x * 4 + 2] *= alpha;
- }
- }
- //swap layered window buffer
- POINT ptSrc = { 0, 0 };
- SIZE sizeWnd = { (long)dib_size.x, (long)dib_size.y };
- BLENDFUNCTION bf;
- bf.BlendOp = AC_SRC_OVER;
- bf.BlendFlags = 0;
- bf.AlphaFormat = AC_SRC_ALPHA;
- bf.SourceConstantAlpha = 0xFF;
- UpdateLayeredWindow(hWnd, NULL, NULL, &sizeWnd, hDC_dib, &ptSrc, 0, &bf, ULW_ALPHA);
- }
-}
-
-void OS_Windows::set_borderless_window(bool p_borderless) {
- if (video_mode.borderless_window == p_borderless)
- return;
-
- if (!p_borderless && layered_window)
- set_window_per_pixel_transparency_enabled(false);
-
- video_mode.borderless_window = p_borderless;
-
- preserve_window_size = true;
- _update_window_style();
-}
-
-bool OS_Windows::get_borderless_window() {
- return video_mode.borderless_window;
-}
-
-void OS_Windows::_update_window_style(bool p_repaint, bool p_maximized) {
- if (video_mode.fullscreen || video_mode.borderless_window) {
- SetWindowLongPtr(hWnd, GWL_STYLE, WS_SYSMENU | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE);
- } else {
- if (video_mode.resizable) {
- if (p_maximized) {
- SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_MAXIMIZE);
- } else {
- SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
- }
- } else {
- SetWindowLongPtr(hWnd, GWL_STYLE, WS_CAPTION | WS_MINIMIZEBOX | WS_POPUPWINDOW | WS_VISIBLE);
- }
- }
-
- SetWindowPos(hWnd, video_mode.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
-
- if (p_repaint) {
- RECT rect;
- GetWindowRect(hWnd, &rect);
- MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
- }
-}
-
Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
-
String path = p_path;
if (!FileAccess::exists(path)) {
@@ -2270,14 +254,14 @@ Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_han
PAddDllDirectory add_dll_directory = (PAddDllDirectory)GetProcAddress(GetModuleHandle("kernel32.dll"), "AddDllDirectory");
PRemoveDllDirectory remove_dll_directory = (PRemoveDllDirectory)GetProcAddress(GetModuleHandle("kernel32.dll"), "RemoveDllDirectory");
- bool has_dll_directory_api = ((add_dll_directory != NULL) && (remove_dll_directory != NULL));
- DLL_DIRECTORY_COOKIE cookie = NULL;
+ bool has_dll_directory_api = ((add_dll_directory != nullptr) && (remove_dll_directory != nullptr));
+ 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());
}
- p_library_handle = (void *)LoadLibraryExW(path.c_str(), NULL, (p_also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0);
+ p_library_handle = (void *)LoadLibraryExW(path.c_str(), 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) {
@@ -2306,24 +290,11 @@ Error OS_Windows::get_dynamic_library_symbol_handle(void *p_library_handle, cons
return OK;
}
-void OS_Windows::request_attention() {
-
- FLASHWINFO info;
- info.cbSize = sizeof(FLASHWINFO);
- info.hwnd = hWnd;
- info.dwFlags = FLASHW_TRAY;
- info.dwTimeout = 0;
- info.uCount = 2;
- FlashWindowEx(&info);
-}
-
String OS_Windows::get_name() const {
-
return "Windows";
}
OS::Date OS_Windows::get_date(bool utc) const {
-
SYSTEMTIME systemtime;
if (utc)
GetSystemTime(&systemtime);
@@ -2338,8 +309,8 @@ OS::Date OS_Windows::get_date(bool utc) const {
date.dst = false;
return date;
}
-OS::Time OS_Windows::get_time(bool utc) const {
+OS::Time OS_Windows::get_time(bool utc) const {
SYSTEMTIME systemtime;
if (utc)
GetSystemTime(&systemtime);
@@ -2373,7 +344,6 @@ OS::TimeZoneInfo OS_Windows::get_time_zone_info() const {
}
uint64_t OS_Windows::get_unix_time() const {
-
FILETIME ft;
SYSTEMTIME st;
GetSystemTime(&st);
@@ -2405,12 +375,10 @@ uint64_t OS_Windows::get_unix_time() const {
};
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;
@@ -2427,14 +395,13 @@ uint64_t OS_Windows::get_system_time_msecs() const {
}
void OS_Windows::delay_usec(uint32_t p_usec) const {
-
if (p_usec < 1000)
Sleep(1);
else
Sleep(p_usec / 1000);
}
-uint64_t OS_Windows::get_ticks_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
@@ -2448,278 +415,34 @@ uint64_t OS_Windows::get_ticks_usec() const {
return time;
}
-void OS_Windows::process_events() {
-
- MSG msg;
-
- if (!drop_events) {
- joypad->process_joypads();
- }
-
- while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
-
- TranslateMessage(&msg);
- DispatchMessageW(&msg);
- }
-
- if (!drop_events) {
- process_key_events();
- input->flush_accumulated_events();
- }
-}
-
-void OS_Windows::set_cursor_shape(CursorShape p_shape) {
-
- ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
-
- if (cursor_shape == p_shape)
- return;
-
- if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
- cursor_shape = p_shape;
- return;
- }
-
- static const LPCTSTR win_cursors[CURSOR_MAX] = {
- IDC_ARROW,
- IDC_IBEAM,
- IDC_HAND, //finger
- IDC_CROSS,
- IDC_WAIT,
- IDC_APPSTARTING,
- IDC_ARROW,
- IDC_ARROW,
- IDC_NO,
- IDC_SIZENS,
- IDC_SIZEWE,
- IDC_SIZENESW,
- IDC_SIZENWSE,
- IDC_SIZEALL,
- IDC_SIZENS,
- IDC_SIZEWE,
- IDC_HELP
- };
-
- if (cursors[p_shape] != NULL) {
- SetCursor(cursors[p_shape]);
- } else {
- SetCursor(LoadCursor(hInstance, win_cursors[p_shape]));
- }
-
- cursor_shape = p_shape;
-}
-
-OS::CursorShape OS_Windows::get_cursor_shape() const {
-
- return cursor_shape;
-}
-
-void OS_Windows::set_custom_mouse_cursor(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) {
- set_cursor_shape(p_shape);
- return;
- }
-
- cursors_cache.erase(p_shape);
- }
-
- Ref<Texture2D> texture = p_cursor;
- Ref<AtlasTexture> atlas_texture = p_cursor;
- Ref<Image> image;
- Size2 texture_size;
- Rect2 atlas_rect;
-
- if (texture.is_valid()) {
- image = texture->get_data();
- }
-
- if (!image.is_valid() && atlas_texture.is_valid()) {
- texture = atlas_texture->get_atlas();
-
- atlas_rect.size.width = texture->get_width();
- atlas_rect.size.height = texture->get_height();
- atlas_rect.position.x = atlas_texture->get_region().position.x;
- atlas_rect.position.y = atlas_texture->get_region().position.y;
-
- texture_size.width = atlas_texture->get_region().size.x;
- texture_size.height = atlas_texture->get_region().size.y;
- } else if (image.is_valid()) {
- texture_size.width = texture->get_width();
- texture_size.height = texture->get_height();
- }
-
- ERR_FAIL_COND(!texture.is_valid());
- ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
- ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
- ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
-
- image = texture->get_data();
-
- ERR_FAIL_COND(!image.is_valid());
-
- UINT image_size = texture_size.width * texture_size.height;
-
- // Create the BITMAP with alpha channel
- COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size);
-
- for (UINT index = 0; index < image_size; index++) {
- int row_index = floor(index / texture_size.width) + atlas_rect.position.y;
- int column_index = (index % int(texture_size.width)) + atlas_rect.position.x;
-
- if (atlas_texture.is_valid()) {
- column_index = MIN(column_index, atlas_rect.size.width - 1);
- row_index = MIN(row_index, atlas_rect.size.height - 1);
- }
-
- *(buffer + index) = image->get_pixel(column_index, row_index).to_argb32();
- }
-
- // Using 4 channels, so 4 * 8 bits
- HBITMAP bitmap = CreateBitmap(texture_size.width, texture_size.height, 1, 4 * 8, buffer);
- COLORREF clrTransparent = -1;
-
- // Create the AND and XOR masks for the bitmap
- HBITMAP hAndMask = NULL;
- HBITMAP hXorMask = NULL;
-
- GetMaskBitmaps(bitmap, clrTransparent, hAndMask, hXorMask);
-
- if (NULL == hAndMask || NULL == hXorMask) {
- memfree(buffer);
- DeleteObject(bitmap);
- return;
- }
-
- // Finally, create the icon
- ICONINFO iconinfo;
- iconinfo.fIcon = FALSE;
- iconinfo.xHotspot = p_hotspot.x;
- iconinfo.yHotspot = p_hotspot.y;
- iconinfo.hbmMask = hAndMask;
- iconinfo.hbmColor = hXorMask;
-
- if (cursors[p_shape])
- DestroyIcon(cursors[p_shape]);
-
- cursors[p_shape] = CreateIconIndirect(&iconinfo);
-
- Vector<Variant> params;
- params.push_back(p_cursor);
- params.push_back(p_hotspot);
- cursors_cache.insert(p_shape, params);
-
- if (p_shape == cursor_shape) {
- if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
- SetCursor(cursors[p_shape]);
- }
- }
-
- if (hAndMask != NULL) {
- DeleteObject(hAndMask);
- }
-
- if (hXorMask != NULL) {
- DeleteObject(hXorMask);
- }
-
- memfree(buffer);
- DeleteObject(bitmap);
- } else {
- // Reset to default system cursor
- if (cursors[p_shape]) {
- DestroyIcon(cursors[p_shape]);
- cursors[p_shape] = NULL;
+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];
+ if (c == ' ' || c == '&' || c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '^' || c == '=' || c == ';' || c == '!' || c == '\'' || c == '+' || c == ',' || c == '`' || c == '~') {
+ return "\"" + p_text + "\"";
}
-
- CursorShape c = cursor_shape;
- cursor_shape = CURSOR_MAX;
- set_cursor_shape(c);
-
- cursors_cache.erase(p_shape);
}
-}
-
-void OS_Windows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap) {
-
- // Get the system display DC
- HDC hDC = GetDC(NULL);
-
- // Create helper DC
- HDC hMainDC = CreateCompatibleDC(hDC);
- HDC hAndMaskDC = CreateCompatibleDC(hDC);
- HDC hXorMaskDC = CreateCompatibleDC(hDC);
-
- // Get the dimensions of the source bitmap
- BITMAP bm;
- GetObject(hSourceBitmap, sizeof(BITMAP), &bm);
-
- // Create the mask bitmaps
- hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color
- hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color
-
- // Release the system display DC
- ReleaseDC(NULL, hDC);
-
- // Select the bitmaps to helper DC
- HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap);
- HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap);
- HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap);
-
- // Assign the monochrome AND mask bitmap pixels so that a pixels of the source bitmap
- // with 'clrTransparent' will be white pixels of the monochrome bitmap
- SetBkColor(hMainDC, clrTransparent);
- BitBlt(hAndMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCCOPY);
-
- // Assign the color XOR mask bitmap pixels so that a pixels of the source bitmap
- // with 'clrTransparent' will be black and rest the pixels same as corresponding
- // pixels of the source bitmap
- SetBkColor(hXorMaskDC, RGB(0, 0, 0));
- SetTextColor(hXorMaskDC, RGB(255, 255, 255));
- BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hAndMaskDC, 0, 0, SRCCOPY);
- BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCAND);
-
- // Deselect bitmaps from the helper DC
- SelectObject(hMainDC, hOldMainBitmap);
- SelectObject(hAndMaskDC, hOldAndMaskBitmap);
- SelectObject(hXorMaskDC, hOldXorMaskBitmap);
-
- // Delete the helper DC
- DeleteDC(hXorMaskDC);
- DeleteDC(hAndMaskDC);
- DeleteDC(hMainDC);
+ 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;
- argss = "\"\"" + p_path + "\"";
-
+ String argss = _quote_command_line_argument(p_path);
for (const List<String>::Element *E = p_arguments.front(); E; E = E->next()) {
-
- argss += " \"" + E->get() + "\"";
+ argss += " " + _quote_command_line_argument(E->get());
}
- argss += "\"";
-
if (read_stderr) {
argss += " 2>&1"; // Read stderr too
}
+ // 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);
char buf[65535];
while (fgets(buf, 65535, f)) {
-
if (p_pipe_mutex) {
p_pipe_mutex->lock();
}
@@ -2730,20 +453,19 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments,
}
int rv = _pclose(f);
- if (r_exitcode)
+ if (r_exitcode) {
*r_exitcode = rv;
+ }
return OK;
}
- String cmdline = "\"" + p_path + "\"";
+ String cmdline = _quote_command_line_argument(p_path);
const List<String>::Element *I = p_arguments.front();
while (I) {
-
- cmdline += " \"" + I->get() + "\"";
-
+ cmdline += " " + _quote_command_line_argument(I->get());
I = I->next();
- };
+ }
ProcessInfo pi;
ZeroMemory(&pi.si, sizeof(pi.si));
@@ -2751,34 +473,34 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments,
ZeroMemory(&pi.pi, sizeof(pi.pi));
LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si;
- Vector<CharType> modstr; //windows wants to change this no idea why
+ Vector<CharType> modstr; // Windows wants to change this no idea why.
modstr.resize(cmdline.size());
- for (int i = 0; i < cmdline.size(); i++)
+ for (int i = 0; i < cmdline.size(); i++) {
modstr.write[i] = cmdline[i];
- int ret = CreateProcessW(NULL, modstr.ptrw(), NULL, NULL, 0, NORMAL_PRIORITY_CLASS & CREATE_NO_WINDOW, NULL, NULL, si_w, &pi.pi);
+ }
+
+ 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);
if (p_blocking) {
-
DWORD ret2 = WaitForSingleObject(pi.pi.hProcess, INFINITE);
- if (r_exitcode)
+ if (r_exitcode) {
*r_exitcode = ret2;
+ }
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);
- };
+ }
return OK;
};
Error OS_Windows::kill(const ProcessID &p_pid) {
-
ERR_FAIL_COND_V(!process_map->has(p_pid), FAILED);
const PROCESS_INFORMATION pi = (*process_map)[p_pid].pi;
@@ -2797,7 +519,6 @@ int OS_Windows::get_process_id() const {
}
Error OS_Windows::set_cwd(const String &p_cwd) {
-
if (_wchdir(p_cwd.c_str()) != 0)
return ERR_CANT_OPEN;
@@ -2805,174 +526,26 @@ Error OS_Windows::set_cwd(const String &p_cwd) {
}
String OS_Windows::get_executable_path() const {
-
wchar_t bufname[4096];
- GetModuleFileNameW(NULL, bufname, 4096);
+ GetModuleFileNameW(nullptr, bufname, 4096);
String s = bufname;
return s;
}
-void OS_Windows::set_native_icon(const String &p_filename) {
-
- FileAccess *f = FileAccess::open(p_filename, FileAccess::READ);
- ERR_FAIL_COND_MSG(!f, "Cannot open file with icon '" + p_filename + "'.");
-
- ICONDIR *icon_dir = (ICONDIR *)memalloc(sizeof(ICONDIR));
- int pos = 0;
-
- icon_dir->idReserved = f->get_32();
- pos += sizeof(WORD);
- f->seek(pos);
-
- icon_dir->idType = f->get_32();
- pos += sizeof(WORD);
- f->seek(pos);
-
- ERR_FAIL_COND_MSG(icon_dir->idType != 1, "Invalid icon file format!");
-
- icon_dir->idCount = f->get_32();
- pos += sizeof(WORD);
- f->seek(pos);
-
- icon_dir = (ICONDIR *)memrealloc(icon_dir, 3 * sizeof(WORD) + icon_dir->idCount * sizeof(ICONDIRENTRY));
- f->get_buffer((uint8_t *)&icon_dir->idEntries[0], icon_dir->idCount * sizeof(ICONDIRENTRY));
-
- int small_icon_index = -1; // Select 16x16 with largest color count
- int small_icon_cc = 0;
- int big_icon_index = -1; // Select largest
- int big_icon_width = 16;
- int big_icon_cc = 0;
-
- for (int i = 0; i < icon_dir->idCount; i++) {
- int colors = (icon_dir->idEntries[i].bColorCount == 0) ? 32768 : icon_dir->idEntries[i].bColorCount;
- int width = (icon_dir->idEntries[i].bWidth == 0) ? 256 : icon_dir->idEntries[i].bWidth;
- if (width == 16) {
- if (colors >= small_icon_cc) {
- small_icon_index = i;
- small_icon_cc = colors;
- }
- }
- if (width >= big_icon_width) {
- if (colors >= big_icon_cc) {
- big_icon_index = i;
- big_icon_width = width;
- big_icon_cc = colors;
- }
- }
- }
-
- ERR_FAIL_COND_MSG(big_icon_index == -1, "No valid icons found!");
-
- if (small_icon_index == -1) {
- WARN_PRINT("No small icon found, reusing " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon!");
- small_icon_index = big_icon_index;
- small_icon_cc = big_icon_cc;
- }
-
- // Read the big icon
- DWORD bytecount_big = icon_dir->idEntries[big_icon_index].dwBytesInRes;
- Vector<uint8_t> data_big;
- data_big.resize(bytecount_big);
- pos = icon_dir->idEntries[big_icon_index].dwImageOffset;
- f->seek(pos);
- f->get_buffer((uint8_t *)&data_big.write[0], bytecount_big);
- HICON icon_big = CreateIconFromResource((PBYTE)&data_big.write[0], bytecount_big, TRUE, 0x00030000);
- ERR_FAIL_COND_MSG(!icon_big, "Could not create " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
-
- // Read the small icon
- DWORD bytecount_small = icon_dir->idEntries[small_icon_index].dwBytesInRes;
- Vector<uint8_t> data_small;
- data_small.resize(bytecount_small);
- pos = icon_dir->idEntries[small_icon_index].dwImageOffset;
- f->seek(pos);
- f->get_buffer((uint8_t *)&data_small.write[0], bytecount_small);
- HICON icon_small = CreateIconFromResource((PBYTE)&data_small.write[0], bytecount_small, TRUE, 0x00030000);
- ERR_FAIL_COND_MSG(!icon_small, "Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
-
- // Online tradition says to be sure last error is cleared and set the small icon first
- int err = 0;
- SetLastError(err);
-
- SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_small);
- err = GetLastError();
- ERR_FAIL_COND_MSG(err, "Error setting ICON_SMALL: " + format_error_message(err) + ".");
-
- SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);
- err = GetLastError();
- ERR_FAIL_COND_MSG(err, "Error setting ICON_BIG: " + format_error_message(err) + ".");
-
- memdelete(f);
- memdelete(icon_dir);
-}
-
-void OS_Windows::set_icon(const Ref<Image> &p_icon) {
-
- ERR_FAIL_COND(!p_icon.is_valid());
- Ref<Image> icon = p_icon->duplicate();
- if (icon->get_format() != Image::FORMAT_RGBA8)
- icon->convert(Image::FORMAT_RGBA8);
- int w = icon->get_width();
- int h = icon->get_height();
-
- /* Create temporary bitmap buffer */
- int icon_len = 40 + h * w * 4;
- Vector<BYTE> v;
- v.resize(icon_len);
- BYTE *icon_bmp = v.ptrw();
-
- encode_uint32(40, &icon_bmp[0]);
- encode_uint32(w, &icon_bmp[4]);
- encode_uint32(h * 2, &icon_bmp[8]);
- encode_uint16(1, &icon_bmp[12]);
- encode_uint16(32, &icon_bmp[14]);
- encode_uint32(BI_RGB, &icon_bmp[16]);
- encode_uint32(w * h * 4, &icon_bmp[20]);
- encode_uint32(0, &icon_bmp[24]);
- encode_uint32(0, &icon_bmp[28]);
- encode_uint32(0, &icon_bmp[32]);
- encode_uint32(0, &icon_bmp[36]);
-
- uint8_t *wr = &icon_bmp[40];
- const uint8_t *r = icon->get_data().ptr();
-
- for (int i = 0; i < h; i++) {
-
- for (int j = 0; j < w; j++) {
-
- const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];
- uint8_t *wpx = &wr[(i * w + j) * 4];
- wpx[0] = rpx[2];
- wpx[1] = rpx[1];
- wpx[2] = rpx[0];
- wpx[3] = rpx[3];
- }
- }
-
- HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
-
- /* Set the icon for the window */
- SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon);
-
- /* Set the icon in the task manager (should we do this?) */
- SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon);
-}
-
bool OS_Windows::has_environment(const String &p_var) const {
-
#ifdef MINGW_ENABLED
- return _wgetenv(p_var.c_str()) != NULL;
+ return _wgetenv(p_var.c_str()) != nullptr;
#else
wchar_t *env;
size_t len;
_wdupenv_s(&env, &len, p_var.c_str());
- const bool has_env = env != NULL;
+ const bool has_env = env != nullptr;
free(env);
return has_env;
#endif
};
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);
if (wlen > 0) {
@@ -2982,12 +555,10 @@ String OS_Windows::get_environment(const String &p_var) const {
}
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());
}
String OS_Windows::get_stdin_string(bool p_block) {
-
if (p_block) {
char buff[1024];
return fgets(buff, 1024, stdin);
@@ -2996,24 +567,12 @@ String OS_Windows::get_stdin_string(bool p_block) {
return String();
}
-void OS_Windows::enable_for_stealing_focus(ProcessID pid) {
-
- AllowSetForegroundWindow(pid);
-}
-
-void OS_Windows::move_window_to_foreground() {
-
- SetForegroundWindow(hWnd);
-}
-
Error OS_Windows::shell_open(String p_uri) {
-
- ShellExecuteW(NULL, NULL, p_uri.c_str(), NULL, NULL, SW_SHOWNORMAL);
+ ShellExecuteW(nullptr, nullptr, p_uri.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
return OK;
}
String OS_Windows::get_locale() const {
-
const _WinLocale *wl = &_win_locales[0];
LANGID langid = GetUserDefaultUILanguage();
@@ -3022,7 +581,6 @@ String OS_Windows::get_locale() const {
int sublang = langid & ~((1 << 9) - 1);
while (wl->locale) {
-
if (wl->main_lang == lang && wl->sublang == SUBLANG_NEUTRAL)
neutral = wl->locale;
@@ -3068,111 +626,14 @@ int OS_Windows::get_processor_count() const {
return sysinfo.dwNumberOfProcessors;
}
-OS::LatinKeyboardVariant OS_Windows::get_latin_keyboard_variant() const {
-
- 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
- };
-
- char name[KL_NAMELENGTH + 1];
- name[0] = 0;
- GetKeyboardLayoutNameA(name);
-
- unsigned long hex = strtoul(name, NULL, 16);
-
- int i = 0;
- while (azerty[i] != 0) {
- if (azerty[i] == hex) return LATIN_KEYBOARD_AZERTY;
- i++;
- }
-
- i = 0;
- while (qwertz[i] != 0) {
- if (qwertz[i] == hex) return LATIN_KEYBOARD_QWERTZ;
- i++;
- }
-
- i = 0;
- while (dvorak[i] != 0) {
- if (dvorak[i] == hex) return LATIN_KEYBOARD_DVORAK;
- i++;
- }
-
- return LATIN_KEYBOARD_QWERTY;
-}
-
-void OS_Windows::release_rendering_thread() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->release_current();
- }
-#endif
-}
-
-void OS_Windows::make_rendering_thread() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->make_current();
- }
-#endif
-}
-
-void OS_Windows::swap_buffers() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->swap_buffers();
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
- context_vulkan->swap_buffers();
- }
-#endif
-}
-
-void OS_Windows::force_process_input() {
- process_events(); // get rid of pending events
-}
-
void OS_Windows::run() {
-
if (!main_loop)
return;
main_loop->init();
while (!force_quit) {
-
- process_events(); // get rid of pending events
+ DisplayServer::get_singleton()->process_events(); // get rid of pending events
if (Main::iteration())
break;
};
@@ -3181,12 +642,10 @@ void OS_Windows::run() {
}
MainLoop *OS_Windows::get_main_loop() const {
-
return main_loop;
}
String OS_Windows::get_config_path() const {
-
if (has_environment("XDG_CONFIG_HOME")) { // unlikely, but after all why not?
return get_environment("XDG_CONFIG_HOME");
} else if (has_environment("APPDATA")) {
@@ -3197,7 +656,6 @@ String OS_Windows::get_config_path() const {
}
String OS_Windows::get_data_path() const {
-
if (has_environment("XDG_DATA_HOME")) {
return get_environment("XDG_DATA_HOME");
} else {
@@ -3206,7 +664,6 @@ String OS_Windows::get_data_path() const {
}
String OS_Windows::get_cache_path() const {
-
if (has_environment("XDG_CACHE_HOME")) {
return get_environment("XDG_CACHE_HOME");
} else if (has_environment("TEMP")) {
@@ -3218,12 +675,10 @@ String OS_Windows::get_cache_path() const {
// Get properly capitalized engine name for system paths
String OS_Windows::get_godot_dir_name() const {
-
return String(VERSION_SHORT_NAME).capitalize();
}
String OS_Windows::get_system_dir(SystemDir p_dir) const {
-
KNOWNFOLDERID id;
switch (p_dir) {
@@ -3254,7 +709,7 @@ String OS_Windows::get_system_dir(SystemDir p_dir) const {
}
PWSTR szPath;
- HRESULT res = SHGetKnownFolderPath(id, 0, NULL, &szPath);
+ HRESULT res = SHGetKnownFolderPath(id, 0, nullptr, &szPath);
ERR_FAIL_COND_V(res != S_OK, String());
String path = String(szPath);
CoTaskMemFree(szPath);
@@ -3262,7 +717,6 @@ String OS_Windows::get_system_dir(SystemDir p_dir) const {
}
String OS_Windows::get_user_data_dir() const {
-
String appname = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name"));
if (appname != "") {
bool use_custom_dir = ProjectSettings::get_singleton()->get("application/config/use_custom_user_dir");
@@ -3281,58 +735,12 @@ 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);
}
-void OS_Windows::set_ime_active(const bool p_active) {
-
- if (p_active) {
- ImmAssociateContext(hWnd, im_himc);
-
- set_ime_position(im_position);
- } else {
- ImmAssociateContext(hWnd, (HIMC)0);
- }
-}
-
-void OS_Windows::set_ime_position(const Point2 &p_pos) {
-
- im_position = p_pos;
-
- HIMC himc = ImmGetContext(hWnd);
- if (himc == (HIMC)0)
- return;
-
- COMPOSITIONFORM cps;
- cps.dwStyle = CFS_FORCE_POSITION;
- cps.ptCurrentPos.x = im_position.x;
- cps.ptCurrentPos.y = im_position.y;
- ImmSetCompositionWindow(himc, &cps);
- ImmReleaseContext(hWnd, himc);
-}
-
-bool OS_Windows::is_joy_known(int p_device) {
- return input->is_joy_mapped(p_device);
-}
-
-String OS_Windows::get_joy_guid(int p_device) const {
- return input->get_joy_guid_remapped(p_device);
-}
-
-void OS_Windows::_set_use_vsync(bool p_enable) {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- if (context_gles2)
- context_gles2->set_use_vsync(p_enable);
- }
-#endif
-}
-
bool OS_Windows::_check_internal_feature_support(const String &p_feature) {
-
return p_feature == "pc";
}
@@ -3344,27 +752,20 @@ bool OS_Windows::is_disable_crash_handler() const {
return crash_handler.is_disabled();
}
-void OS_Windows::process_and_drop_events() {
-
- drop_events = true;
- process_events();
- drop_events = false;
-}
-
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;
- sf.hwnd = hWnd;
+ sf.hwnd = main_window;
sf.wFunc = FO_DELETE;
sf.pFrom = from;
- sf.pTo = NULL;
+ sf.pTo = nullptr;
sf.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION;
sf.fAnyOperationsAborted = FALSE;
- sf.hNameMappings = NULL;
- sf.lpszProgressTitle = NULL;
+ sf.hNameMappings = nullptr;
+ sf.lpszProgressTitle = nullptr;
int ret = SHFileOperationW(&sf);
delete[] from;
@@ -3377,38 +778,75 @@ 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];
+ }
+}
+
+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");
- drop_events = false;
- key_event_pos = 0;
- layered_window = false;
- hBitmap = NULL;
- force_quit = false;
- alt_mem = false;
- gr_mem = false;
- shift_mem = false;
- control_mem = false;
- meta_mem = false;
- minimized = false;
- was_maximized = false;
- window_focused = true;
- console_visible = IsWindowVisible(GetConsoleWindow());
-
- //Note: Functions for pen input, available on Windows 8+
+ 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) {
- win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType");
- win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo");
+ 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");
+ }
+
+ force_quit = false;
+
hInstance = _hInstance;
- pressrc = 0;
- old_invalid = true;
- mouse_mode = MOUSE_MODE_VISIBLE;
#ifdef STDOUT_FILE
stdo = fopen("stdout.txt", "wb");
#endif
- user_proc = NULL;
#ifdef WASAPI_ENABLED
AudioDriverManager::add_driver(&driver_wasapi);
@@ -3417,16 +855,14 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) {
AudioDriverManager::add_driver(&driver_xaudio2);
#endif
+ DisplayServerWindows::register_windows_driver();
+
Vector<Logger *> loggers;
loggers.push_back(memnew(WindowsTerminalLogger));
_set_logger(memnew(CompositeLogger(loggers)));
}
OS_Windows::~OS_Windows() {
- if (is_layered_allowed() && layered_window) {
- DeleteObject(hBitmap);
- DeleteDC(hDC_dib);
- }
#ifdef STDOUT_FILE
fclose(stdo);
#endif
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 8506aa7b20..11e3533bfd 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -31,7 +31,7 @@
#ifndef OS_WINDOWS_H
#define OS_WINDOWS_H
-#include "core/os/input.h"
+#include "core/input/input.h"
#include "core/os/os.h"
#include "core/project_settings.h"
#include "crash_handler_windows.h"
@@ -39,10 +39,9 @@
#include "drivers/wasapi/audio_driver_wasapi.h"
#include "drivers/winmidi/midi_driver_winmidi.h"
#include "key_mapping_windows.h"
-#include "main/input_default.h"
#include "servers/audio_server.h"
-#include "servers/visual/rasterizer.h"
-#include "servers/visual_server.h"
+#include "servers/rendering/rasterizer.h"
+#include "servers/rendering_server.h"
#ifdef XAUDIO2_ENABLED
#include "drivers/xaudio2/audio_driver_xaudio2.h"
#endif
@@ -62,183 +61,21 @@
#include <windows.h>
#include <windowsx.h>
-#ifndef POINTER_STRUCTURES
-
-#define POINTER_STRUCTURES
-
-typedef DWORD POINTER_INPUT_TYPE;
-typedef UINT32 POINTER_FLAGS;
-typedef UINT32 PEN_FLAGS;
-typedef UINT32 PEN_MASK;
-
-enum tagPOINTER_INPUT_TYPE {
- PT_POINTER = 0x00000001,
- PT_TOUCH = 0x00000002,
- PT_PEN = 0x00000003,
- PT_MOUSE = 0x00000004,
- PT_TOUCHPAD = 0x00000005
-};
-
-typedef enum tagPOINTER_BUTTON_CHANGE_TYPE {
- POINTER_CHANGE_NONE,
- POINTER_CHANGE_FIRSTBUTTON_DOWN,
- POINTER_CHANGE_FIRSTBUTTON_UP,
- POINTER_CHANGE_SECONDBUTTON_DOWN,
- POINTER_CHANGE_SECONDBUTTON_UP,
- POINTER_CHANGE_THIRDBUTTON_DOWN,
- POINTER_CHANGE_THIRDBUTTON_UP,
- POINTER_CHANGE_FOURTHBUTTON_DOWN,
- POINTER_CHANGE_FOURTHBUTTON_UP,
- POINTER_CHANGE_FIFTHBUTTON_DOWN,
- POINTER_CHANGE_FIFTHBUTTON_UP,
-} POINTER_BUTTON_CHANGE_TYPE;
-
-typedef struct tagPOINTER_INFO {
- POINTER_INPUT_TYPE pointerType;
- UINT32 pointerId;
- UINT32 frameId;
- POINTER_FLAGS pointerFlags;
- HANDLE sourceDevice;
- HWND hwndTarget;
- POINT ptPixelLocation;
- POINT ptHimetricLocation;
- POINT ptPixelLocationRaw;
- POINT ptHimetricLocationRaw;
- DWORD dwTime;
- UINT32 historyCount;
- INT32 InputData;
- DWORD dwKeyStates;
- UINT64 PerformanceCount;
- POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
-} POINTER_INFO;
-
-typedef struct tagPOINTER_PEN_INFO {
- POINTER_INFO pointerInfo;
- PEN_FLAGS penFlags;
- PEN_MASK penMask;
- UINT32 pressure;
- UINT32 rotation;
- INT32 tiltX;
- INT32 tiltY;
-} POINTER_PEN_INFO;
-
-#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);
-
-typedef struct {
- BYTE bWidth; // Width, in pixels, of the image
- BYTE bHeight; // Height, in pixels, of the image
- BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
- BYTE bReserved; // Reserved ( must be 0)
- WORD wPlanes; // Color Planes
- WORD wBitCount; // Bits per pixel
- DWORD dwBytesInRes; // How many bytes in this resource?
- DWORD dwImageOffset; // Where in the file is this image?
-} ICONDIRENTRY, *LPICONDIRENTRY;
-
-typedef struct {
- WORD idReserved; // Reserved (must be 0)
- WORD idType; // Resource Type (1 for icons)
- WORD idCount; // How many images?
- ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em)
-} ICONDIR, *LPICONDIR;
-
class JoypadWindows;
class OS_Windows : public OS {
-
- static GetPointerTypePtr win8p_GetPointerType;
- static GetPointerPenInfoPtr win8p_GetPointerPenInfo;
-
- enum {
- KEY_EVENT_BUFFER_SIZE = 512
- };
-
#ifdef STDOUT_FILE
FILE *stdo;
#endif
- struct KeyEvent {
-
- bool alt, shift, control, meta;
- UINT uMsg;
- WPARAM wParam;
- LPARAM lParam;
- };
-
- KeyEvent key_event_buffer[KEY_EVENT_BUFFER_SIZE];
- int key_event_pos;
-
uint64_t ticks_start;
uint64_t ticks_per_second;
- bool old_invalid;
- bool outside;
- int old_x, old_y;
- Point2i center;
-
-#if defined(OPENGL_ENABLED)
- ContextGL_Windows *context_gles2;
-#endif
-
-#if defined(VULKAN_ENABLED)
- VulkanContextWindows *context_vulkan;
- RenderingDeviceVulkan *rendering_device_vulkan;
-#endif
-
- VisualServer *visual_server;
- int pressrc;
- HINSTANCE hInstance; // Holds The Instance Of The Application
- HWND hWnd;
- Point2 last_pos;
-
- HBITMAP hBitmap; //DIB section for layered window
- uint8_t *dib_data;
- Size2 dib_size;
- HDC hDC_dib;
- bool layered_window;
-
- uint32_t move_timer_id;
-
- HCURSOR hCursor;
-
- Size2 min_size;
- Size2 max_size;
-
- Size2 window_rect;
- VideoMode video_mode;
- bool preserve_window_size = false;
-
+ HINSTANCE hInstance;
MainLoop *main_loop;
- WNDPROC user_proc;
-
- // IME
- HIMC im_himc;
- Vector2 im_position;
-
- MouseMode mouse_mode;
- bool alt_mem;
- bool gr_mem;
- bool shift_mem;
- bool control_mem;
- bool meta_mem;
- bool force_quit;
- bool window_has_focus;
- uint32_t last_button_state;
- bool use_raw_input;
- bool drop_events;
-
- HCURSOR cursors[CURSOR_MAX] = { NULL };
- CursorShape cursor_shape;
- Map<CursorShape, Vector<Variant>> cursors_cache;
-
- InputDefault *input;
- JoypadWindows *joypad;
- Map<int, Vector2> touch_state;
+ String tablet_driver;
+ Vector<String> tablet_drivers;
- int video_driver_index;
#ifdef WASAPI_ENABLED
AudioDriverWASAPI driver_wasapi;
#endif
@@ -251,105 +88,29 @@ class OS_Windows : public OS {
CrashHandler crash_handler;
- void _drag_event(float p_x, float p_y, int idx);
- void _touch_event(bool p_pressed, float p_x, float p_y, int idx);
-
- void _update_window_style(bool p_repaint = true, bool p_maximized = false);
-
- void _set_mouse_mode_impl(MouseMode p_mode);
+ bool force_quit;
+ HWND main_window;
// functions used by main to initialize/deinitialize the OS
protected:
- 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 initialize();
virtual void set_main_loop(MainLoop *p_main_loop);
virtual void delete_main_loop();
virtual void finalize();
virtual void finalize_core();
+ virtual String get_stdin_string(bool p_block);
- void process_events();
- void process_key_events();
+ String _quote_command_line_argument(const String &p_text) const;
struct ProcessInfo {
-
STARTUPINFO si;
PROCESS_INFORMATION pi;
};
Map<ProcessID, ProcessInfo> *process_map;
- bool pre_fs_valid;
- RECT pre_fs_rect;
- bool maximized;
- bool minimized;
- bool borderless;
- bool window_focused;
- bool console_visible;
- bool was_maximized;
-
public:
- LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
-
- virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
- String get_stdin_string(bool p_block);
-
- void set_mouse_mode(MouseMode p_mode);
- MouseMode get_mouse_mode() const;
-
- virtual void warp_mouse_position(const Point2 &p_to);
- virtual Point2 get_mouse_position() const;
- void update_real_mouse_position();
- virtual int get_mouse_button_state() const;
- virtual void set_window_title(const String &p_title);
-
- 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 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 int get_screen_dpi(int p_screen = -1) const;
-
- virtual Point2 get_window_position() const;
- virtual void set_window_position(const Point2 &p_position);
- virtual Size2 get_window_size() const;
- virtual Size2 get_real_window_size() const;
- virtual Size2 get_max_window_size() const;
- virtual Size2 get_min_window_size() const;
- virtual void set_min_window_size(const Size2 p_size);
- virtual void set_max_window_size(const Size2 p_size);
- virtual void set_window_size(const Size2 p_size);
- 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_window_always_on_top(bool p_enabled);
- virtual bool is_window_always_on_top() const;
- virtual bool is_window_focused() const;
- virtual void set_console_visible(bool p_enabled);
- virtual bool is_console_visible() const;
- virtual void request_attention();
-
- virtual void set_borderless_window(bool p_borderless);
- virtual bool get_borderless_window();
-
- virtual bool get_window_per_pixel_transparency_enabled() const;
- virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
-
- virtual uint8_t *get_layered_buffer_data();
- virtual Size2 get_layered_buffer_size();
- virtual void swap_layered_buffer();
-
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);
@@ -358,6 +119,13 @@ public:
virtual String get_name() const;
+ 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 void initialize_joypads() {}
+
virtual Date get_date(bool utc) const;
virtual Time get_time(bool utc) const;
virtual TimeZoneInfo get_time_zone_info() const;
@@ -365,13 +133,12 @@ public:
virtual uint64_t get_system_time_secs() const;
virtual uint64_t get_system_time_msecs() 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 = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL);
+ 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;
@@ -379,28 +146,12 @@ public:
virtual String get_environment(const String &p_var) const;
virtual bool set_environment(const String &p_var, const String &p_value) const;
- virtual void set_clipboard(const String &p_text);
- virtual String get_clipboard() const;
-
- void set_cursor_shape(CursorShape p_shape);
- CursorShape get_cursor_shape() const;
- virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
- void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap);
-
- void set_native_icon(const String &p_filename);
- void set_icon(const Ref<Image> &p_icon);
-
virtual String get_executable_path() const;
virtual String get_locale() const;
virtual int get_processor_count() const;
- virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
-
- virtual void enable_for_stealing_focus(ProcessID pid);
- virtual void move_window_to_foreground();
-
virtual String get_config_path() const;
virtual String get_data_path() const;
virtual String get_cache_path() const;
@@ -411,37 +162,21 @@ public:
virtual String get_unique_id() const;
- virtual void set_ime_active(const bool p_active);
- virtual void set_ime_position(const Point2 &p_pos);
-
- virtual void release_rendering_thread();
- virtual void make_rendering_thread();
- virtual void swap_buffers();
-
virtual Error shell_open(String p_uri);
void run();
- virtual bool get_swap_ok_cancel() { return true; }
-
- virtual bool is_joy_known(int p_device);
- virtual String get_joy_guid(int p_device) const;
-
- virtual void _set_use_vsync(bool p_enable);
- //virtual bool is_vsync_enabled() const;
-
virtual bool _check_internal_feature_support(const String &p_feature);
void disable_crash_handler();
bool is_disable_crash_handler() const;
virtual void initialize_debugging();
- void force_process_input();
-
virtual Error move_to_trash(const String &p_path);
- virtual void process_and_drop_events();
+ void set_main_window(HWND p_main_window) { main_window = p_main_window; }
+ HINSTANCE get_hinstance() { return hInstance; }
OS_Windows(HINSTANCE _hInstance);
~OS_Windows();
};
diff --git a/platform/windows/platform_windows_builders.py b/platform/windows/platform_windows_builders.py
index a1ad3b8b50..22e33b51b4 100644
--- a/platform/windows/platform_windows_builders.py
+++ b/platform/windows/platform_windows_builders.py
@@ -9,14 +9,14 @@ from platform_methods import subprocess_main
def make_debug_mingw(target, source, env):
mingw_prefix = ""
- if (env["bits"] == "32"):
+ if env["bits"] == "32":
mingw_prefix = env["mingw_prefix_32"]
else:
mingw_prefix = env["mingw_prefix_64"]
- os.system(mingw_prefix + 'objcopy --only-keep-debug {0} {0}.debugsymbols'.format(target[0]))
- os.system(mingw_prefix + 'strip --strip-debug --strip-unneeded {0}'.format(target[0]))
- os.system(mingw_prefix + 'objcopy --add-gnu-debuglink={0}.debugsymbols {0}'.format(target[0]))
+ os.system(mingw_prefix + "objcopy --only-keep-debug {0} {0}.debugsymbols".format(target[0]))
+ os.system(mingw_prefix + "strip --strip-debug --strip-unneeded {0}".format(target[0]))
+ os.system(mingw_prefix + "objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(target[0]))
-if __name__ == '__main__':
+if __name__ == "__main__":
subprocess_main(globals())
diff --git a/platform/windows/vulkan_context_win.cpp b/platform/windows/vulkan_context_win.cpp
index 20e1b46682..2c63281c49 100644
--- a/platform/windows/vulkan_context_win.cpp
+++ b/platform/windows/vulkan_context_win.cpp
@@ -35,19 +35,18 @@ const char *VulkanContextWindows::_get_platform_surface_extension() const {
return VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
}
-int VulkanContextWindows::window_create(HWND p_window, HINSTANCE p_instance, int p_width, int p_height) {
-
+int VulkanContextWindows::window_create(DisplayServer::WindowID p_window_id, HWND p_window, HINSTANCE p_instance, int p_width, int p_height) {
VkWin32SurfaceCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
- createInfo.pNext = NULL;
+ createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.hinstance = p_instance;
createInfo.hwnd = p_window;
VkSurfaceKHR surface;
- VkResult err = vkCreateWin32SurfaceKHR(_get_instance(), &createInfo, NULL, &surface);
+ VkResult err = vkCreateWin32SurfaceKHR(_get_instance(), &createInfo, nullptr, &surface);
ERR_FAIL_COND_V(err, -1);
- return _window_create(surface, p_width, p_height);
+ return _window_create(p_window_id, surface, p_width, p_height);
}
VulkanContextWindows::VulkanContextWindows() {
diff --git a/platform/windows/vulkan_context_win.h b/platform/windows/vulkan_context_win.h
index 1289f2a299..6e80db0286 100644
--- a/platform/windows/vulkan_context_win.h
+++ b/platform/windows/vulkan_context_win.h
@@ -35,11 +35,10 @@
#include <windows.h>
class VulkanContextWindows : public VulkanContext {
-
virtual const char *_get_platform_surface_extension() const;
public:
- int window_create(HWND p_window, HINSTANCE p_instance, int p_width, int p_height);
+ int window_create(DisplayServer::WindowID p_window_id, HWND p_window, HINSTANCE p_instance, int p_width, int p_height);
VulkanContextWindows();
~VulkanContextWindows();
diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp
index 520b654b94..0938b65b04 100644
--- a/platform/windows/windows_terminal_logger.cpp
+++ b/platform/windows/windows_terminal_logger.cpp
@@ -49,7 +49,7 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er
len = BUFFER_SIZE; // Output is too big, will be truncated
buf[len] = 0;
- int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, len, NULL, 0);
+ int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, len, nullptr, 0);
if (wlen < 0)
return;
@@ -81,7 +81,6 @@ void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file
StdLogger::log_error(p_function, p_file, p_line, p_code, p_rationale, p_type);
#ifndef UWP_ENABLED
} else {
-
CONSOLE_SCREEN_BUFFER_INFO sbi; //original
GetConsoleScreenBufferInfo(hCon, &sbi);
@@ -89,20 +88,36 @@ void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file
uint32_t basecol = 0;
switch (p_type) {
- case ERR_ERROR: basecol = FOREGROUND_RED; break;
- case ERR_WARNING: basecol = FOREGROUND_RED | FOREGROUND_GREEN; break;
- case ERR_SCRIPT: basecol = FOREGROUND_RED | FOREGROUND_BLUE; break;
- case ERR_SHADER: basecol = FOREGROUND_GREEN | FOREGROUND_BLUE; break;
+ case ERR_ERROR:
+ basecol = FOREGROUND_RED;
+ break;
+ case ERR_WARNING:
+ basecol = FOREGROUND_RED | FOREGROUND_GREEN;
+ break;
+ case ERR_SCRIPT:
+ basecol = FOREGROUND_RED | FOREGROUND_BLUE;
+ break;
+ case ERR_SHADER:
+ basecol = FOREGROUND_GREEN | FOREGROUND_BLUE;
+ break;
}
basecol |= current_bg;
SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY);
switch (p_type) {
- case ERR_ERROR: logf("ERROR:"); break;
- case ERR_WARNING: logf("WARNING:"); break;
- case ERR_SCRIPT: logf("SCRIPT ERROR:"); break;
- case ERR_SHADER: logf("SHADER ERROR:"); break;
+ case ERR_ERROR:
+ logf("ERROR:");
+ break;
+ case ERR_WARNING:
+ logf("WARNING:");
+ break;
+ case ERR_SCRIPT:
+ logf("SCRIPT ERROR:");
+ break;
+ case ERR_SHADER:
+ logf("SHADER ERROR:");
+ break;
}
SetConsoleTextAttribute(hCon, basecol);
@@ -115,10 +130,18 @@ void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file
// `FOREGROUND_INTENSITY` alone results in gray text.
SetConsoleTextAttribute(hCon, FOREGROUND_INTENSITY);
switch (p_type) {
- case ERR_ERROR: logf(" at: "); break;
- case ERR_WARNING: logf(" at: "); break;
- case ERR_SCRIPT: logf(" at: "); break;
- case ERR_SHADER: logf(" at: "); break;
+ case ERR_ERROR:
+ logf(" at: ");
+ break;
+ case ERR_WARNING:
+ logf(" at: ");
+ break;
+ case ERR_SCRIPT:
+ logf(" at: ");
+ break;
+ case ERR_SHADER:
+ logf(" at: ");
+ break;
}
if (p_rationale && p_rationale[0]) {
diff --git a/platform/x11/SCsub b/platform/x11/SCsub
deleted file mode 100644
index 2268e4cc3d..0000000000
--- a/platform/x11/SCsub
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env python
-
-Import('env')
-
-from platform_methods import run_in_subprocess
-import platform_x11_builders
-
-common_x11 = [
- "context_gl_x11.cpp",
- "vulkan_context_x11.cpp",
- "crash_handler_x11.cpp",
- "os_x11.cpp",
- "key_mapping_x11.cpp",
- "joypad_linux.cpp",
- "detect_prime.cpp"
-]
-
-prog = env.add_program('#bin/godot', ['godot_x11.cpp'] + common_x11)
-
-if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]:
- env.AddPostAction(prog, run_in_subprocess(platform_x11_builders.make_debug_x11))
diff --git a/platform/x11/detect.py b/platform/x11/detect.py
deleted file mode 100644
index 9a9ab86068..0000000000
--- a/platform/x11/detect.py
+++ /dev/null
@@ -1,368 +0,0 @@
-import os
-import platform
-import sys
-
-
-def is_active():
- return True
-
-
-def get_name():
- return "X11"
-
-
-def can_build():
-
- if (os.name != "posix" or sys.platform == "darwin"):
- return False
-
- # Check the minimal dependencies
- x11_error = os.system("pkg-config --version > /dev/null")
- if (x11_error):
- return False
-
- x11_error = os.system("pkg-config x11 --modversion > /dev/null ")
- if (x11_error):
- return False
-
- x11_error = os.system("pkg-config xcursor --modversion > /dev/null ")
- if (x11_error):
- print("xcursor not found.. x11 disabled.")
- return False
-
- x11_error = os.system("pkg-config xinerama --modversion > /dev/null ")
- if (x11_error):
- print("xinerama 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.")
- return False
-
- x11_error = os.system("pkg-config xrender --modversion > /dev/null ")
- if (x11_error):
- print("xrender not found.. x11 disabled.")
- return False
-
- x11_error = os.system("pkg-config xi --modversion > /dev/null ")
- if (x11_error):
- print("xi not found.. Aborting.")
- return False
-
- return True
-
-def get_opts():
- from SCons.Variables import BoolVariable, EnumVariable
-
- return [
- 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_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('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),
- ]
-
-
-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'])
- else: #optimize for size
- env.Prepend(CCFLAGS=['-Os'])
-
- if (env["debug_symbols"] == "yes"):
- env.Prepend(CCFLAGS=['-g1'])
- if (env["debug_symbols"] == "full"):
- env.Prepend(CCFLAGS=['-g2'])
-
- elif (env["target"] == "release_debug"):
- if (env["optimize"] == "speed"): #optimize for speed (default)
- env.Prepend(CCFLAGS=['-O2'])
- 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"):
- env.Prepend(CCFLAGS=['-g2'])
-
- elif (env["target"] == "debug"):
- env.Prepend(CCFLAGS=['-g3'])
- env.Prepend(CPPDEFINES=['DEBUG_ENABLED', 'DEBUG_MEMORY_ENABLED'])
- env.Append(LINKFLAGS=['-rdynamic'])
-
- ## Architecture
-
- is64 = sys.maxsize > 2**32
- if (env["bits"] == "default"):
- env["bits"] = "64" if is64 else "32"
-
- ## Compiler configuration
-
- if 'CXX' in env and 'clang' in os.path.basename(env['CXX']):
- # Convenience check to enforce the use_llvm overrides when CXX is clang(++)
- env['use_llvm'] = True
-
- if env['use_llvm']:
- 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']:
- if env['use_llvm']:
- env.Append(LINKFLAGS=['-fuse-ld=lld'])
- if env['use_thinlto']:
- # A convenience so you don't need to write use_lto too when using SCons
- env['use_lto'] = True
- else:
- print("Using LLD with GCC is not supported yet, try compiling with 'use_llvm=yes'.")
- sys.exit(255)
-
- if env['use_coverage']:
- env.Append(CCFLAGS=['-ftest-coverage', '-fprofile-arcs'])
- env.Append(LINKFLAGS=['-ftest-coverage', '-fprofile-arcs'])
-
- if env['use_ubsan'] or env['use_asan'] or env['use_lsan'] or env['use_tsan']:
- env.extra_suffix += "s"
-
- 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_tsan']:
- env.Append(CCFLAGS=['-fsanitize=thread'])
- env.Append(LINKFLAGS=['-fsanitize=thread'])
-
- if env['use_lto']:
- if not env['use_llvm'] and env.GetOption("num_jobs") > 1:
- env.Append(CCFLAGS=['-flto'])
- env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))])
- else:
- if env['use_lld'] and env['use_thinlto']:
- env.Append(CCFLAGS=['-flto=thin'])
- env.Append(LINKFLAGS=['-flto=thin'])
- else:
- env.Append(CCFLAGS=['-flto'])
- env.Append(LINKFLAGS=['-flto'])
-
- if not env['use_llvm']:
- env['RANLIB'] = 'gcc-ranlib'
- env['AR'] = 'gcc-ar'
-
- env.Append(CCFLAGS=['-pipe'])
- env.Append(LINKFLAGS=['-pipe'])
-
- # -fpie and -no-pie is supported on GCC 6+ and Clang 4+, both below our
- # minimal requirements.
- env.Append(CCFLAGS=['-fpie'])
- env.Append(LINKFLAGS=['-no-pie'])
-
- ## Dependencies
-
- 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 xrandr --cflags --libs')
- env.ParseConfig('pkg-config xrender --cflags --libs')
- env.ParseConfig('pkg-config xi --cflags --libs')
-
- if (env['touch']):
- env.Append(CPPDEFINES=['TOUCH_ENABLED'])
-
- # FIXME: Check for existence of the libs before parsing their flags with pkg-config
-
- # 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.89
- import subprocess
- bullet_version = subprocess.check_output(['pkg-config', 'bullet', '--modversion']).strip()
- if str(bullet_version) < "2.89":
- # 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.89"))
- sys.exit(255)
- env.ParseConfig('pkg-config bullet --cflags --libs')
-
- if False: # not env['builtin_assimp']:
- # FIXME: Add min version check
- env.ParseConfig('pkg-config assimp --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')
- else:
- 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_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 not env['builtin_libwebp']:
- env.ParseConfig('pkg-config libwebp --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_wslay']:
- env.ParseConfig('pkg-config libwslay --cflags --libs')
-
- if not env['builtin_miniupnpc']:
- # No pkgconfig file so far, hardcode default paths.
- env.Prepend(CPPPATH=["/usr/include/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
-
- if (os.system("pkg-config --exists alsa") == 0): # 0 means found
- print("Enabling ALSA")
- 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")
-
- if env['pulseaudio']:
- 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')
- 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")
-
- # Linkflags below this line should typically stay the last ones
- if not env['builtin_zlib']:
- env.ParseConfig('pkg-config zlib --cflags --libs')
-
- env.Prepend(CPPPATH=['#platform/x11'])
- env.Append(CPPDEFINES=['X11_ENABLED', 'UNIX_ENABLED'])
-
- env.Append(CPPDEFINES=['VULKAN_ENABLED'])
- if not env['builtin_vulkan']:
- env.ParseConfig('pkg-config vulkan --cflags --libs')
- if not env['builtin_glslang']:
- # No pkgconfig file for glslang so far
- env.Append(LIBS=['glslang', 'SPIRV'])
-
- #env.Append(CPPDEFINES=['OPENGL_ENABLED'])
- env.Append(LIBS=['GL'])
-
- env.Append(LIBS=['pthread'])
-
- if (platform.system() == "Linux"):
- env.Append(LIBS=['dl'])
-
- if (platform.system().find("BSD") >= 0):
- env["execinfo"] = True
-
- if env["execinfo"]:
- env.Append(LIBS=['execinfo'])
-
- if not env['tools']:
- import subprocess
- import re
- linker_version_str = subprocess.check_output([env.subst(env["LINK"]), '-Wl,--version']).decode("utf-8")
- gnu_ld_version = re.search('^GNU ld [^$]*(\d+\.\d+)$', linker_version_str, re.MULTILINE)
- if not gnu_ld_version:
- print("Warning: Creating template binaries enabled for PCK embedding is currently only supported with GNU ld")
- else:
- if float(gnu_ld_version.group(1)) >= 2.30:
- env.Append(LINKFLAGS=['-T', 'platform/x11/pck_embed.ld'])
- else:
- env.Append(LINKFLAGS=['-T', 'platform/x11/pck_embed.legacy.ld'])
-
- ## Cross-compilation
-
- if (is64 and env["bits"] == "32"):
- env.Append(CCFLAGS=['-m32'])
- env.Append(LINKFLAGS=['-m32', '-L/usr/lib/i386-linux-gnu'])
- elif (not is64 and env["bits"] == "64"):
- env.Append(CCFLAGS=['-m64'])
- env.Append(LINKFLAGS=['-m64', '-L/usr/lib/i686-linux-gnu'])
-
- # Link those statically for portability
- if env['use_static_cpp']:
- env.Append(LINKFLAGS=['-static-libgcc', '-static-libstdc++'])
diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h
deleted file mode 100644
index 997a6cc053..0000000000
--- a/platform/x11/os_x11.h
+++ /dev/null
@@ -1,339 +0,0 @@
-/*************************************************************************/
-/* os_x11.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_X11_H
-#define OS_X11_H
-
-#include "core/os/input.h"
-#include "crash_handler_x11.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 "main/input_default.h"
-#include "servers/audio_server.h"
-#include "servers/visual/rasterizer.h"
-#include "servers/visual_server.h"
-
-#if defined(OPENGL_ENABLED)
-#include "context_gl_x11.h"
-#endif
-
-#if defined(VULKAN_ENABLED)
-#include "drivers/vulkan/rendering_device_vulkan.h"
-#include "platform/x11/vulkan_context_x11.h"
-#endif
-
-#include <X11/Xcursor/Xcursor.h>
-#include <X11/Xlib.h>
-#include <X11/extensions/XInput2.h>
-#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;
-} xrr_monitor_info;
-
-#undef CursorShape
-
-class OS_X11 : public OS_Unix {
-
- Atom wm_delete;
- Atom xdnd_enter;
- Atom xdnd_position;
- Atom xdnd_status;
- Atom xdnd_action_copy;
- Atom xdnd_drop;
- Atom xdnd_finished;
- Atom xdnd_selection;
- Atom requested;
-
- int xdnd_version;
-
-#if defined(OPENGL_ENABLED)
- ContextGL_X11 *context_gles2;
-#endif
-#if defined(VULKAN_ENABLED)
- VulkanContextX11 *context_vulkan;
- RenderingDeviceVulkan *rendering_device_vulkan;
-#endif
-
- //Rasterizer *rasterizer;
- VisualServer *visual_server;
- VideoMode current_videomode;
- List<String> args;
- Window x11_window;
- Window xdnd_source_window;
- MainLoop *main_loop;
- ::Display *x11_display;
- char *xmbstring;
- int xmblen;
- unsigned long last_timestamp;
- ::Time last_keyrelease_time;
- ::XIC xic;
- ::XIM xim;
- ::XIMStyle xim_style;
- static void xim_destroy_callback(::XIM im, ::XPointer client_data,
- ::XPointer call_data);
-
- // IME
- bool im_active;
- Vector2 im_position;
- Vector2 last_position_before_fs;
-
- Size2 min_size;
- Size2 max_size;
-
- Point2 last_mouse_pos;
- bool last_mouse_pos_valid;
- Point2i last_click_pos;
- uint64_t last_click_ms;
- int last_click_button_index;
- uint32_t last_button_state;
-
- struct {
- int opcode;
- Vector<int> touch_devices;
- Map<int, Vector2> absolute_devices;
- Map<int, Vector3> pen_devices;
- XIEventMask all_event_mask;
- XIEventMask all_master_event_mask;
- Map<int, Vector2> state;
- double pressure;
- Vector2 tilt;
- Vector2 mouse_pos_to_filter;
- Vector2 relative_motion;
- Vector2 raw_pos;
- Vector2 old_raw_pos;
- ::Time last_relative_time;
- } xi;
-
- bool refresh_device_info();
-
- unsigned int get_mouse_button_state(unsigned int p_x11_button, int p_x11_type);
- void get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state);
- void flush_mouse_motion();
-
- MouseMode mouse_mode;
- Point2i center;
-
- void handle_key_event(XKeyEvent *p_event, bool p_echo = false);
- void process_xevents();
- virtual void delete_main_loop();
-
- bool force_quit;
- bool minimized;
- bool window_has_focus;
- bool do_mouse_warp;
-
- const char *cursor_theme;
- int cursor_size;
- XcursorImage *img[CURSOR_MAX];
- Cursor cursors[CURSOR_MAX];
- Cursor null_cursor;
- CursorShape current_cursor;
- Map<CursorShape, Vector<Variant>> cursors_cache;
-
- InputDefault *input;
-
-#ifdef JOYDEV_ENABLED
- JoypadLinux *joypad;
-#endif
-
-#ifdef ALSA_ENABLED
- AudioDriverALSA driver_alsa;
-#endif
-
-#ifdef ALSAMIDI_ENABLED
- MIDIDriverALSAMidi driver_alsamidi;
-#endif
-
-#ifdef PULSEAUDIO_ENABLED
- AudioDriverPulseAudio driver_pulseaudio;
-#endif
-
- bool layered_window;
-
- CrashHandler crash_handler;
-
- int video_driver_index;
- bool maximized;
- bool window_focused;
- //void set_wm_border(bool p_enabled);
- void set_wm_fullscreen(bool p_enabled);
- void set_wm_above(bool p_enabled);
-
- typedef xrr_monitor_info *(*xrr_get_monitors_t)(Display *dpy, Window window, Bool get_active, int *nmonitors);
- typedef void (*xrr_free_monitors_t)(xrr_monitor_info *monitors);
- xrr_get_monitors_t xrr_get_monitors;
- xrr_free_monitors_t xrr_free_monitors;
- void *xrandr_handle;
- Bool xrandr_ext_ok;
-
-protected:
- 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 finalize();
-
- virtual void set_main_loop(MainLoop *p_main_loop);
-
- void _window_changed(XEvent *event);
-
- bool is_window_maximize_allowed();
-
-public:
- virtual String get_name() 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);
-
- void set_mouse_mode(MouseMode p_mode);
- MouseMode get_mouse_mode() const;
-
- virtual void warp_mouse_position(const Point2 &p_to);
- virtual Point2 get_mouse_position() const;
- virtual int get_mouse_button_state() const;
- virtual void set_window_title(const String &p_title);
-
- virtual void set_icon(const Ref<Image> &p_icon);
-
- virtual MainLoop *get_main_loop() const;
-
- virtual bool can_draw() const;
-
- virtual void set_clipboard(const String &p_text);
- virtual String get_clipboard() const;
-
- virtual void release_rendering_thread();
- virtual void make_rendering_thread();
- virtual void swap_buffers();
-
- virtual String get_config_path() const;
- virtual String get_data_path() const;
- virtual String get_cache_path() const;
-
- virtual String get_system_dir(SystemDir p_dir) const;
-
- virtual Error shell_open(String p_uri);
-
- 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 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 int get_screen_dpi(int p_screen = -1) const;
- virtual Point2 get_window_position() const;
- virtual void set_window_position(const Point2 &p_position);
- virtual Size2 get_window_size() const;
- virtual Size2 get_real_window_size() const;
- virtual Size2 get_max_window_size() const;
- virtual Size2 get_min_window_size() const;
- virtual void set_min_window_size(const Size2 p_size);
- virtual void set_max_window_size(const Size2 p_size);
- virtual void set_window_size(const Size2 p_size);
- 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_window_always_on_top(bool p_enabled);
- virtual bool is_window_always_on_top() const;
- virtual bool is_window_focused() const;
- virtual void request_attention();
-
- virtual void set_borderless_window(bool p_borderless);
- virtual bool get_borderless_window();
-
- virtual bool get_window_per_pixel_transparency_enabled() const;
- virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
-
- virtual void set_ime_active(const bool p_active);
- virtual void set_ime_position(const Point2 &p_pos);
-
- virtual String get_unique_id() const;
-
- virtual void move_window_to_foreground();
- virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
-
- virtual bool is_joy_known(int p_device);
- virtual String get_joy_guid(int p_device) const;
-
- virtual void set_context(int p_context);
-
- virtual void _set_use_vsync(bool p_enable);
- //virtual bool is_vsync_enabled() const;
-
- virtual bool _check_internal_feature_support(const String &p_feature);
-
- virtual void force_process_input();
- void run();
-
- void disable_crash_handler();
- bool is_disable_crash_handler() const;
-
- virtual Error move_to_trash(const String &p_path);
-
- virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
-
- void update_real_mouse_position();
- OS_X11();
-};
-
-#endif