summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/SCsub26
-rw-r--r--platform/android/SCsub79
-rw-r--r--platform/android/api/api.cpp9
-rw-r--r--platform/android/api/api.h5
-rw-r--r--platform/android/api/java_class_wrapper.h6
-rw-r--r--platform/android/api/jni_singleton.h242
-rw-r--r--platform/android/audio_driver_jandroid.cpp14
-rw-r--r--platform/android/audio_driver_opensl.cpp10
-rw-r--r--platform/android/detect.py246
-rw-r--r--platform/android/dir_access_jandroid.cpp12
-rw-r--r--platform/android/display_server_android.cpp655
-rw-r--r--platform/android/display_server_android.h174
-rw-r--r--platform/android/export/export.cpp77
-rw-r--r--platform/android/export/export.h5
-rw-r--r--platform/android/file_access_android.cpp8
-rw-r--r--platform/android/file_access_jandroid.cpp2
-rw-r--r--platform/android/java/app/build.gradle6
-rw-r--r--platform/android/java/app/config.gradle4
-rw-r--r--platform/android/java/app/settings.gradle2
-rw-r--r--platform/android/java/build.gradle6
-rw-r--r--platform/android/java/lib/AndroidManifest.xml2
-rw-r--r--platform/android/java/lib/build.gradle5
-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/Constants.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java94
-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)39
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotIO.java40
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java7
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java (renamed from platform/iphone/platform_refcount.h)25
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java2
-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.java32
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java12
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java42
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java38
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java122
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/GodotPaymentInterface.java)135
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt43
-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/regular/RegularContextFactory.java1
-rw-r--r--platform/android/java/plugins/godotpayment/build.gradle1
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl (renamed from platform/android/java/lib/aidl/com/android/vending/billing/IInAppBillingService.aidl)0
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/ConsumeTask.java)2
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java59
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/HandlePurchaseTask.java)2
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsCache.java)3
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java)23
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/PurchaseTask.java)2
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java)2
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java (renamed from platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java)10
-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)3
-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)3
-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)3
-rw-r--r--platform/android/java_class_wrapper.cpp20
-rw-r--r--platform/android/java_godot_io_wrapper.cpp41
-rw-r--r--platform/android/java_godot_io_wrapper.h10
-rw-r--r--platform/android/java_godot_lib_jni.cpp114
-rw-r--r--platform/android/java_godot_lib_jni.h2
-rw-r--r--platform/android/java_godot_wrapper.cpp24
-rw-r--r--platform/android/java_godot_wrapper.h12
-rw-r--r--platform/android/jni_utils.cpp8
-rw-r--r--platform/android/jni_utils.h188
-rw-r--r--platform/android/os_android.cpp529
-rw-r--r--platform/android/os_android.h116
-rw-r--r--platform/android/plugin/godot_plugin_jni.cpp48
-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.cpp16
-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.h49
-rw-r--r--platform/haiku/SCsub27
-rw-r--r--platform/haiku/audio_driver_media_kit.cpp8
-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/os_haiku.cpp28
-rw-r--r--platform/haiku/os_haiku.h6
-rw-r--r--platform/iphone/SCsub38
-rw-r--r--platform/iphone/app_delegate.h8
-rw-r--r--platform/iphone/app_delegate.mm1
-rw-r--r--platform/iphone/detect.py257
-rw-r--r--platform/iphone/export/export.cpp43
-rw-r--r--platform/iphone/export/export.h5
-rw-r--r--platform/iphone/godot_iphone.cpp4
-rw-r--r--platform/iphone/os_iphone.cpp32
-rw-r--r--platform/iphone/os_iphone.h10
-rw-r--r--platform/iphone/vulkan_context_iphone.h2
-rw-r--r--platform/iphone/vulkan_context_iphone.mm2
-rw-r--r--platform/javascript/SCsub68
-rw-r--r--platform/javascript/api/api.cpp4
-rw-r--r--platform/javascript/api/api.h5
-rw-r--r--platform/javascript/audio_driver_javascript.cpp6
-rw-r--r--platform/javascript/detect.py152
-rw-r--r--platform/javascript/emscripten_helpers.py25
-rw-r--r--platform/javascript/export/export.cpp4
-rw-r--r--platform/javascript/http_client_javascript.cpp4
-rw-r--r--platform/javascript/http_request.h2
-rw-r--r--platform/javascript/os_javascript.cpp48
-rw-r--r--platform/javascript/os_javascript.h8
-rw-r--r--platform/linuxbsd/SCsub22
-rw-r--r--platform/linuxbsd/context_gl_x11.cpp (renamed from platform/x11/context_gl_x11.cpp)12
-rw-r--r--platform/linuxbsd/context_gl_x11.h (renamed from platform/x11/context_gl_x11.h)0
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.cpp (renamed from platform/x11/crash_handler_x11.cpp)16
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.h (renamed from platform/x11/crash_handler_x11.h)2
-rw-r--r--platform/linuxbsd/detect.py377
-rw-r--r--platform/linuxbsd/detect_prime_x11.cpp (renamed from platform/x11/detect_prime.cpp)12
-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)4170
-rw-r--r--platform/linuxbsd/display_server_x11.h351
-rw-r--r--platform/linuxbsd/export/export.cpp (renamed from platform/x11/export/export.cpp)6
-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)6
-rw-r--r--platform/linuxbsd/joypad_linux.cpp (renamed from platform/x11/joypad_linux.cpp)37
-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)0
-rw-r--r--platform/linuxbsd/key_mapping_x11.h (renamed from platform/x11/key_mapping_x11.h)0
-rw-r--r--platform/linuxbsd/logo.png (renamed from platform/x11/logo.png)bin1679 -> 1679 bytes
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp381
-rw-r--r--platform/linuxbsd/os_linuxbsd.h106
-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)10
-rw-r--r--platform/linuxbsd/vulkan_context_x11.h (renamed from platform/x11/vulkan_context_x11.h)2
-rw-r--r--platform/osx/SCsub20
-rw-r--r--platform/osx/context_gl_osx.h2
-rw-r--r--platform/osx/detect.py208
-rw-r--r--platform/osx/display_server_osx.h308
-rw-r--r--platform/osx/display_server_osx.mm3595
-rw-r--r--platform/osx/export/export.cpp40
-rw-r--r--platform/osx/export/export.h5
-rw-r--r--platform/osx/joypad_osx.cpp58
-rw-r--r--platform/osx/joypad_osx.h6
-rw-r--r--platform/osx/os_osx.h244
-rw-r--r--platform/osx/os_osx.mm2880
-rw-r--r--platform/osx/platform_osx_builders.py10
-rw-r--r--platform/osx/vulkan_context_osx.h2
-rw-r--r--platform/osx/vulkan_context_osx.mm6
-rw-r--r--platform/server/SCsub12
-rw-r--r--platform/server/detect.py265
-rw-r--r--platform/server/os_server.cpp18
-rw-r--r--platform/server/os_server.h10
-rw-r--r--platform/uwp/SCsub20
-rw-r--r--platform/uwp/app.cpp12
-rw-r--r--platform/uwp/context_egl_uwp.cpp2
-rw-r--r--platform/uwp/detect.py172
-rw-r--r--platform/uwp/export/export.cpp36
-rw-r--r--platform/uwp/export/export.h5
-rw-r--r--platform/uwp/joypad_uwp.h2
-rw-r--r--platform/uwp/os_uwp.cpp30
-rw-r--r--platform/uwp/os_uwp.h11
-rw-r--r--platform/windows/SCsub11
-rw-r--r--platform/windows/context_gl_windows.cpp8
-rw-r--r--platform/windows/crash_handler_windows.cpp6
-rw-r--r--platform/windows/detect.py427
-rw-r--r--platform/windows/display_server_windows.cpp3007
-rw-r--r--platform/windows/display_server_windows.h418
-rw-r--r--platform/windows/export/export.cpp2
-rw-r--r--platform/windows/godot_windows.cpp14
-rw-r--r--platform/windows/joypad_windows.cpp44
-rw-r--r--platform/windows/joypad_windows.h10
-rw-r--r--platform/windows/os_windows.cpp2648
-rw-r--r--platform/windows/os_windows.h299
-rw-r--r--platform/windows/platform_windows_builders.py10
-rw-r--r--platform/windows/vulkan_context_win.cpp8
-rw-r--r--platform/windows/vulkan_context_win.h2
-rw-r--r--platform/windows/windows_terminal_logger.cpp2
-rw-r--r--platform/x11/SCsub21
-rw-r--r--platform/x11/detect.py368
-rw-r--r--platform/x11/os_x11.h339
177 files changed, 14546 insertions, 11334 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 46f0703a65..ec42bc42b5 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -1,27 +1,24 @@
#!/usr/bin/env python
-from detect import get_ndk_version
-from distutils.version import LooseVersion
-
-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()
@@ -32,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/api/api.cpp b/platform/android/api/api.cpp
index 7efb545524..4fe868d4f0 100644
--- a/platform/android/api/api.cpp
+++ b/platform/android/api/api.cpp
@@ -32,15 +32,20 @@
#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>();
@@ -73,7 +78,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 48b581958b..59fcd94b4d 100644
--- a/platform/android/api/java_class_wrapper.h
+++ b/platform/android/api/java_class_wrapper.h
@@ -163,7 +163,7 @@ class JavaClass : public Reference {
bool _call_method(JavaObject *p_instance, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error, Variant &ret);
friend class JavaClassWrapper;
- Map<StringName, List<MethodInfo> > methods;
+ Map<StringName, List<MethodInfo>> methods;
jclass _class;
#endif
@@ -198,7 +198,7 @@ class JavaClassWrapper : public Object {
GDCLASS(JavaClassWrapper, Object);
#ifdef ANDROID_ENABLED
- Map<String, Ref<JavaClass> > class_cache;
+ Map<String, Ref<JavaClass>> class_cache;
friend class JavaClass;
jclass activityClass;
jmethodID findClass;
@@ -236,7 +236,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..917c3f5029
--- /dev/null
+++ b/platform/android/api/jni_singleton.h
@@ -0,0 +1,242 @@
+/*************************************************************************/
+/* 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..802d85e7be 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,10 +46,10 @@ 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 {
@@ -83,7 +83,7 @@ Error AudioDriverAndroid::init() {
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);
@@ -181,8 +181,8 @@ void AudioDriverAndroid::finish() {
if (audioBuffer) {
env->DeleteGlobalRef(audioBuffer);
- audioBuffer = NULL;
- audioBufferPinned = NULL;
+ audioBuffer = nullptr;
+ audioBufferPinned = nullptr;
}
active = false;
diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp
index 222120f81f..e59850e016 100644
--- a/platform/android/audio_driver_opensl.cpp
+++ b/platform/android/audio_driver_opensl.cpp
@@ -83,7 +83,7 @@ void AudioDriverOpenSL::_buffer_callbacks(
ad->_buffer_callback(queueItf);
}
-AudioDriverOpenSL *AudioDriverOpenSL::s_ad = NULL;
+AudioDriverOpenSL *AudioDriverOpenSL::s_ad = nullptr;
const char *AudioDriverOpenSL::get_name() const {
@@ -96,7 +96,7 @@ Error AudioDriverOpenSL::init() {
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);
@@ -161,7 +161,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;
@@ -228,9 +228,9 @@ Error AudioDriverOpenSL::capture_init_device() {
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,
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 8f74ae0ef0..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,70 +147,65 @@ 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()
- mt_link = True
- 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:
- mt_link = False
host_subpath = "windows"
- if env["android_arch"] == "arm64v8":
- mt_link = False
-
compiler_path = env["ANDROID_NDK_ROOT"] + "/toolchains/llvm/prebuilt/" + host_subpath + "/bin"
gcc_toolchain_path = env["ANDROID_NDK_ROOT"] + "/toolchains/" + target_subpath + "/prebuilt/" + host_subpath
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
@@ -207,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"
@@ -222,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)
@@ -259,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..f8571e6277 100644
--- a/platform/android/dir_access_jandroid.cpp
+++ b/platform/android/dir_access_jandroid.cpp
@@ -34,12 +34,12 @@
#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() {
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
new file mode 100644
index 0000000000..9534387d35
--- /dev/null
+++ b/platform/android/display_server_android.cpp
@@ -0,0 +1,655 @@
+/*************************************************************************/
+/* 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) {
+ 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);
+ } 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);
+}
+
+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
+
+ InputFilter::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:
+ InputFilter::get_singleton()->joy_button(p_event.device, p_event.index, p_event.pressed);
+ break;
+ case JOY_EVENT_AXIS:
+ InputFilter::JoyAxis value;
+ value.min = -1;
+ value.value = p_event.value;
+ InputFilter::get_singleton()->joy_axis(p_event.device, p_event.index, value);
+ break;
+ case JOY_EVENT_HAT:
+ InputFilter::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();
+ }
+
+ InputFilter::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);
+ InputFilter::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);
+ InputFilter::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);
+ InputFilter::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);
+ InputFilter::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);
+ InputFilter::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);
+ InputFilter::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);
+ InputFilter::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);
+ InputFilter::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);
+ InputFilter::get_singleton()->parse_input_event(ev);
+ scroll_prev_pos = p_pos;
+}
+
+void DisplayServerAndroid::process_accelerometer(const Vector3 &p_accelerometer) {
+ InputFilter::get_singleton()->set_accelerometer(p_accelerometer);
+}
+
+void DisplayServerAndroid::process_gravity(const Vector3 &p_gravity) {
+ InputFilter::get_singleton()->set_gravity(p_gravity);
+}
+
+void DisplayServerAndroid::process_magnetometer(const Vector3 &p_magnetometer) {
+ InputFilter::get_singleton()->set_magnetometer(p_magnetometer);
+}
+
+void DisplayServerAndroid::process_gyroscope(const Vector3 &p_gyroscope) {
+ InputFilter::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..2096ba68f1
--- /dev/null
+++ b/platform/android/display_server_android.h
@@ -0,0 +1,174 @@
+/*************************************************************************/
+/* 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);
+ 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();
+
+ 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..1eb1ee0d29 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -194,7 +194,7 @@ static const char *android_perms[] = {
"WRITE_SOCIAL_STREAM",
"WRITE_SYNC_SETTINGS",
"WRITE_USER_DICTIONARY",
- NULL
+ nullptr
};
struct LauncherIcon {
@@ -274,7 +274,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
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;
@@ -332,7 +332,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;
@@ -447,7 +447,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
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;
@@ -537,7 +537,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) {
@@ -591,11 +591,11 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
zipOpenNewFileInZip(ed->apk,
p_path.utf8().get_data(),
&zipfi,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
compression_method,
Z_DEFAULT_COMPRESSION);
@@ -1530,7 +1530,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 +1545,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);
@@ -1563,7 +1563,7 @@ public:
args.push_back(devices[p_device].id);
args.push_back("reverse");
args.push_back("--remove-all");
- OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
+ OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) {
@@ -1575,7 +1575,7 @@ 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));
}
@@ -1590,7 +1590,7 @@ 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 {
@@ -1619,7 +1619,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);
@@ -1806,7 +1806,7 @@ public:
/*{ 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));
}
*/
@@ -1851,7 +1851,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)) {
@@ -1868,7 +1868,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 +1879,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 +1932,13 @@ 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 +1978,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 +2003,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 +2018,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);
}
@@ -2108,11 +2117,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 +2129,7 @@ public:
zipCloseFileInZip(unaligned_apk);
}
- zipClose(unaligned_apk, NULL);
+ zipClose(unaligned_apk, nullptr);
unzClose(pkg);
if (err != OK) {
@@ -2188,7 +2197,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 +2214,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);
@@ -2230,9 +2239,9 @@ 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,
@@ -2245,7 +2254,7 @@ public:
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 +2286,9 @@ public:
&zipfi,
extra,
info.size_file_extra + padding,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
method,
level,
1); // raw write
@@ -2291,7 +2300,7 @@ public:
ret = unzGoToNextFile(tmp_unaligned);
}
- zipClose(final_apk, NULL);
+ zipClose(final_apk, nullptr);
unzClose(tmp_unaligned);
CLEANUP_AND_RETURN(OK);
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..fa805ec4f3 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() {
@@ -68,12 +68,12 @@ 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) {
@@ -175,7 +175,7 @@ bool FileAccessAndroid::file_exists(const String &p_path) {
}
FileAccessAndroid::FileAccessAndroid() {
- a = NULL;
+ a = nullptr;
eof = false;
}
diff --git a/platform/android/file_access_jandroid.cpp b/platform/android/file_access_jandroid.cpp
index db3aa4255e..e088eca8ef 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;
diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle
index 5e37f538e9..99080eeb3c 100644
--- a/platform/android/java/app/build.gradle
+++ b/platform/android/java/app/build.gradle
@@ -56,6 +56,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,6 +76,7 @@ android {
packagingOptions {
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
+ 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..aa98194a10 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -1,9 +1,9 @@
ext.versions = [
- androidGradlePlugin: '3.6.0',
+ androidGradlePlugin: '3.5.3',
compileSdk : 29,
minSdk : 18,
targetSdk : 29,
- buildTools : '29.0.1',
+ buildTools : '29.0.3',
supportCoreUtils : '28.0.0',
kotlinVersion : '1.3.61',
v4Support : '28.0.0'
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 976a5bda99..865b61956c 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -25,7 +25,7 @@ ext {
sconsExt = org.gradle.internal.os.OperatingSystem.current().isWindows() ? ".bat" : ""
supportedAbis = ["armv7", "arm64v8", "x86", "x86_64"]
- supportedTargets = ['release': "release", 'debug': "release_debug"]
+ supportedTargets = ["release", "debug"]
// Used by gradle to specify which architecture to build for by default when running `./gradlew build`.
// This command is usually used by Android Studio.
@@ -136,14 +136,14 @@ task zipCustomBuild(type: Zip) {
*/
task generateGodotTemplates(type: GradleBuild) {
// We exclude these gradle tasks so we can run the scons command manually.
- for (String buildType : supportedTargets.keySet()) {
+ for (String buildType : supportedTargets) {
startParameter.excludedTaskNames += ":lib:" + getSconsTaskName(buildType)
}
tasks = ["copyGodotPaymentPluginToAppModule"]
// Only build the apks and aar files for which we have native shared libraries.
- for (String target : supportedTargets.keySet()) {
+ for (String target : supportedTargets) {
File targetLibs = new File("lib/libs/" + target)
if (targetLibs != null
&& targetLibs.isDirectory()
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 ca8aaf8af0..6bb438c249 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,6 +26,7 @@ android {
packagingOptions {
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
+ doNotStrip '**/*.so'
}
sourceSets {
@@ -56,7 +57,7 @@ android {
// files is only setup for editing support.
gradle.startParameter.excludedTaskNames += taskPrefix + "externalNativeBuild" + buildType
- def releaseTarget = supportedTargets[buildType.toLowerCase()]
+ def releaseTarget = buildType.toLowerCase()
if (releaseTarget == null || releaseTarget == "") {
throw new GradleException("Invalid build type: " + buildType)
}
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/Constants.java b/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Constants.java
index 1dcc370d83..0700b78a28 100644
--- a/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Constants.java
+++ b/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Constants.java
@@ -233,4 +233,4 @@ public class Constants {
*/
public static final long ACTIVE_THREAD_WATCHDOG = 5*1000;
-} \ No newline at end of file
+}
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 7db4aa6597..bf0d1c6273 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -55,8 +55,6 @@ import android.hardware.SensorManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
-import android.os.Handler;
-import android.os.Looper;
import android.os.Messenger;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -64,7 +62,6 @@ import android.provider.Settings.Secure;
import android.support.annotation.CallSuper;
import android.support.annotation.Keep;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.view.Display;
import android.view.KeyEvent;
@@ -95,7 +92,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import org.godotengine.godot.input.GodotEditText;
-import org.godotengine.godot.payments.PaymentsManager;
import org.godotengine.godot.plugin.GodotPlugin;
import org.godotengine.godot.plugin.GodotPluginRegistry;
import org.godotengine.godot.utils.GodotNetUtils;
@@ -157,7 +153,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;
@@ -174,21 +170,17 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
public ResultCallback result_callback;
- private PaymentsManager mPaymentsManager = null;
-
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == PaymentsManager.REQUEST_CODE_FOR_PURCHASE) {
- mPaymentsManager.processPurchaseResponse(resultCode, data);
- } else if (result_callback != null) {
+ if (result_callback != null) {
result_callback.callback(requestCode, resultCode, data);
result_callback = null;
- };
+ }
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
plugin.onMainActivityResult(requestCode, resultCode, data);
}
- };
+ }
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
@@ -202,12 +194,12 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
};
/**
- * Invoked on the GL thread when the Godot main loop has started.
+ * Invoked on the render thread when the Godot main loop has started.
*/
@CallSuper
- protected void onGLGodotMainLoopStarted() {
+ protected void onGodotMainLoopStarted() {
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
- plugin.onGLGodotMainLoopStarted();
+ plugin.onGodotMainLoopStarted();
}
}
@@ -221,38 +213,45 @@ 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);
+
+ GodotLib.setup(command_line);
- 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);
+ 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);
+ }
+
+ View view = mRenderView.getView();
+ layout.addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ editText.setView(mRenderView);
+ io.setEdit(editText);
- mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ 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.onGLRegisterPluginWithGodotNative();
+ plugin.onRegisterPluginWithGodotNative();
}
setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
@@ -392,7 +391,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
*/
@Keep
private Surface getSurface() {
- return mView.getHolder().getSurface();
+ return mRenderView.getView().getHolder().getSurface();
}
/**
@@ -445,8 +444,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
result_callback = null;
- mPaymentsManager = PaymentsManager.createManager(this).initService();
-
godot_initialized = true;
}
@@ -603,7 +600,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
@Override
protected void onDestroy() {
- if (mPaymentsManager != null) mPaymentsManager.destroy();
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
plugin.onMainDestroy();
}
@@ -628,7 +624,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
return;
}
- mView.onPause();
+ mRenderView.onActivityPaused();
mSensorManager.unregisterListener(this);
@@ -666,7 +662,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);
@@ -732,8 +728,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) {
@@ -784,8 +780,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();
@@ -795,13 +791,13 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
/**
- * Queue a runnable to be run on the GL thread.
+ * Queue a runnable to be run on the render thread.
* <p>
- * This must be called after the GL thread has started.
+ * This must be called after the render thread has started.
*/
- public final void runOnGLThread(@NonNull Runnable action) {
- if (mView != null) {
- mView.queueEvent(action);
+ public final void runOnRenderThread(@NonNull Runnable action) {
+ if (mRenderView != null) {
+ mRenderView.queueOnRenderThread(action);
}
}
@@ -858,7 +854,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
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++) {
@@ -871,7 +867,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) {
@@ -922,7 +918,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
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() {
+ 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++) {
@@ -938,10 +934,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
return true;
}
- public PaymentsManager getPaymentsManager() {
- return mPaymentsManager;
- }
-
public boolean requestPermission(String p_name) {
return PermissionsUtil.requestPermission(p_name, this);
}
@@ -1048,6 +1040,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/GodotView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
index 8d3c2ae319..9be93243b8 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 */
@@ -35,6 +35,7 @@ import android.opengl.GLSurfaceView;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.SurfaceView;
import org.godotengine.godot.input.GodotGestureHandler;
import org.godotengine.godot.input.GodotInputHandler;
import org.godotengine.godot.utils.GLUtils;
@@ -64,16 +65,14 @@ 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 +84,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) {
@@ -170,10 +195,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..016a3a8d18 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
@@ -53,8 +53,6 @@ public class GodotIO {
Godot activity;
GodotEditText edit;
- MediaPlayer mediaPlayer;
-
final int SCREEN_LANDSCAPE = 0;
final int SCREEN_PORTRAIT = 1;
final int SCREEN_REVERSE_LANDSCAPE = 2;
@@ -530,44 +528,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 int getScreenOrientation() {
+ return activity.getRequestedOrientation();
}
- public void pauseVideo() {
- if (mediaPlayer != null) {
- mediaPlayer.pause();
- }
- }
-
- 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;
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..71fe822233 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,7 @@ 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;
@@ -72,11 +73,11 @@ public class GodotLib {
public static native void resize(int width, int 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.
diff --git a/platform/iphone/platform_refcount.h b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
index 9029418462..170c433c9c 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,20 @@
/* 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 {
-#include <libkern/OSAtomic.h>
+ abstract public SurfaceView getView();
-inline int atomic_conditional_increment(volatile int *v) {
- return (*v == 0) ? 0 : OSAtomicIncrement32(v);
-}
+ abstract public void initInputDevices();
-inline int atomic_decrement(volatile int *v) {
- return OSAtomicDecrement32(v);
-}
+ abstract public void queueOnRenderThread(Runnable event);
-#endif
+ abstract public void onActivityPaused();
+ abstract public void onActivityResumed();
+
+ 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..3e5bb4a4c9 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
@@ -70,7 +70,7 @@ class GodotRenderer implements GLSurfaceView.Renderer {
}
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..30197d5729
--- /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 android.annotation.SuppressLint;
+import android.view.GestureDetector;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.SurfaceView;
+import org.godotengine.godot.input.GodotGestureHandler;
+import org.godotengine.godot.input.GodotInputHandler;
+import org.godotengine.godot.vulkan.VkRenderer;
+import org.godotengine.godot.vulkan.VkSurfaceView;
+
+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..92bb118e44 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
@@ -51,7 +51,7 @@ public class GodotEditText extends EditText {
// ===========================================================
// Fields
// ===========================================================
- private GodotView mView;
+ private GodotRenderView mRenderView;
private GodotTextInputWrapper mInputWrapper;
private EditHandler sHandler = new EditHandler(this);
private String mOriginText;
@@ -76,22 +76,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) {
@@ -106,7 +106,7 @@ public class GodotEditText extends EditText {
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,9 +115,9 @@ 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;
}
}
@@ -135,12 +135,12 @@ public class GodotEditText extends EditText {
// ===========================================================
// 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 +152,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;
@@ -162,7 +162,7 @@ public class GodotEditText extends EditText {
// Methods
// ===========================================================
public void showKeyboard(String p_existing_text, int p_max_input_length) {
- this.mOriginText = p_existing_text;
+ mOriginText = p_existing_text;
final Message msg = new Message();
msg.what = HANDLER_OPEN_IME_KEYBOARD;
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..b1e0f66373 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
@@ -34,22 +34,22 @@ import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import org.godotengine.godot.GodotLib;
-import org.godotengine.godot.GodotView;
+import org.godotengine.godot.GodotRenderView;
/**
- * 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 GodotView godotView;
+ private final GodotRenderView mRenderView;
- 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..0e4fc65119 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
@@ -42,27 +42,27 @@ 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.GodotRenderView;
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> joysticksDevices = new ArrayList<Joystick>();
+ private final ArrayList<Joystick> mJoysticksDevices = 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) {
@@ -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;
@@ -164,7 +164,7 @@ public class GodotInputHandler implements InputDeviceListener {
// 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 +208,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 +224,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 +249,7 @@ public class GodotInputHandler implements InputDeviceListener {
}
}
- joysticksDevices.add(joy);
+ mJoysticksDevices.add(joy);
final int device_id = id;
final String name = joy.name;
@@ -270,7 +270,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 +360,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 8d9b5461a1..e12ff266bf 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
@@ -48,7 +48,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
// ===========================================================
// Fields
// ===========================================================
- private final GodotView mView;
+ private final GodotRenderView mRenderView;
private final GodotEditText mEdit;
private String mOriginText;
@@ -56,9 +56,9 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
// 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 +66,13 @@ 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;
}
// ===========================================================
@@ -87,7 +87,7 @@ 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) {
@@ -106,12 +106,17 @@ 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) {
- GodotLib.key(0, 0, newChars[i], true);
- GodotLib.key(0, 0, newChars[i], false);
+ int key = newChars[i];
+ if (key == '\n') {
+ // Return keys are handled through action events
+ continue;
+ }
+ GodotLib.key(0, 0, key, true);
+ GodotLib.key(0, 0, key, false);
}
}
});
@@ -119,10 +124,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++) {
@@ -134,8 +139,13 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
});
}
- if (pActionID == EditorInfo.IME_ACTION_DONE) {
- this.mView.requestFocus();
+ if (pActionID == EditorInfo.IME_NULL) {
+ // Enter key has been pressed
+ GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, true);
+ GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, false);
+
+ mRenderView.getView().requestFocus();
+ return true;
}
return false;
}
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 d5bf4fc70e..a051164d15 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 */
@@ -34,14 +34,19 @@ import android.app.Activity;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Surface;
import android.view.View;
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.BuildConfig;
import org.godotengine.godot.Godot;
/**
@@ -69,7 +74,10 @@ import org.godotengine.godot.Godot;
*/
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;
@@ -84,8 +92,10 @@ public abstract class GodotPlugin {
/**
* Register the plugin with Godot native code.
+ *
+ * This method is invoked on the render thread.
*/
- public final void onGLRegisterPluginWithGodotNative() {
+ public final void onRegisterPluginWithGodotNative() {
nativeRegisterSingleton(getPluginName());
Class clazz = getClass();
@@ -115,6 +125,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()) {
@@ -169,9 +186,9 @@ public abstract class GodotPlugin {
public boolean onMainBackPressed() { return false; }
/**
- * Invoked on the GL thread when the Godot main loop has started.
+ * Invoked on the render thread when the Godot main loop has started.
*/
- public void onGLGodotMainLoopStarted() {}
+ public void onGodotMainLoopStarted() {}
/**
* Invoked once per frame on the GL thread after the frame is drawn.
@@ -190,6 +207,22 @@ public abstract class GodotPlugin {
public void onGLSurfaceCreated(GL10 gl, EGLConfig config) {}
/**
+ * Invoked once per frame on the Vulkan thread after the frame is drawn.
+ */
+ public void onVkDrawFrame() {}
+
+ /**
+ * Called on the Vulkan thread after the surface is created and whenever the surface size
+ * changes.
+ */
+ public void onVkSurfaceChanged(Surface surface, int width, int height) {}
+
+ /**
+ * Called on the Vulkan thread when the surface is created or recreated.
+ */
+ public void onVkSurfaceCreated(Surface surface) {}
+
+ /**
* Returns the name of the plugin.
* <p>
* This value must match the one listed in the plugin '<meta-data>' manifest entry.
@@ -201,7 +234,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.
@@ -225,12 +268,55 @@ public abstract class GodotPlugin {
}
/**
- * Queue the specified action to be run on the GL thread.
+ * Queue the specified action to be run on the render thread.
*
- * @param action the action to run on the GL thread
+ * @param action the action to run on the render thread
*/
- protected void runOnGLThread(Runnable action) {
- godot.runOnGLThread(action);
+ protected void runOnRenderThread(Runnable action) {
+ godot.runOnRenderThread(action);
+ }
+
+ /**
+ * 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;
+ }
+ }
}
/**
@@ -253,4 +339,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..e13a9c15d8 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 */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/GodotPaymentInterface.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java
index 6ac7338b30..f907706889 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/GodotPaymentInterface.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* GodotPaymentInterface.java */
+/* SignalInfo.java */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,70 +28,71 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.payments;
-
-public interface GodotPaymentInterface {
- void purchase(String sku, String transactionId);
-
- void consumeUnconsumedPurchases();
-
- String getSignature();
-
- void callbackSuccess(String ticket, String signature, String sku);
-
- void callbackSuccessProductMassConsumed(String ticket, String signature, String sku);
-
- void callbackSuccessNoUnconsumedPurchases();
-
- void callbackFailConsume(String message);
-
- void callbackFail(String message);
-
- void callbackCancel();
-
- void callbackAlreadyOwned(String sku);
-
- int getPurchaseCallbackId();
-
- void setPurchaseCallbackId(int purchaseCallbackId);
-
- String getPurchaseValidationUrlPrefix();
-
- void setPurchaseValidationUrlPrefix(String url);
-
- String getAccessToken();
-
- void setAccessToken(String accessToken);
-
- void setTransactionId(String transactionId);
-
- String getTransactionId();
-
- // request purchased items are not consumed
- void requestPurchased();
-
- // callback for requestPurchased()
- void callbackPurchased(String receipt, String signature, String sku);
-
- void callbackDisconnected();
-
- void callbackConnected();
-
- // true if connected, false otherwise
- boolean isConnected();
-
- // consume item automatically after purchase. default is true.
- void setAutoConsume(boolean autoConsume);
-
- // consume a specific item
- void consume(String sku);
-
- // query in app item detail info
- void querySkuDetails(String[] list);
-
- void addSkuDetail(String itemJson);
-
- void completeSkuDetail();
-
- void errorSkuDetail(String errorMessage);
+package org.godotengine.godot.plugin;
+
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+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/vulkan/VkRenderer.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
index 67faad8ddd..608ad48df9 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,64 @@ 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)
+ // TODO: properly implement surface re-creation:
+ // GodotLib.newcontext should be called here once it's done.
+ //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(width, height)
+
+ // TODO: properly implement surface re-creation:
+ // Update the native renderer instead of restarting the app.
+ // GodotLib.newcontext should not be called here once it's done.
+ GodotLib.newcontext(surface, false)
+
+ 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/regular/RegularContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
index f2b4c95a2c..31cf696195 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
@@ -51,7 +51,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/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/lib/aidl/com/android/vending/billing/IInAppBillingService.aidl b/platform/android/java/plugins/godotpayment/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl
index 0f2bcae338..0f2bcae338 100644
--- a/platform/android/java/lib/aidl/com/android/vending/billing/IInAppBillingService.aidl
+++ b/platform/android/java/plugins/godotpayment/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/ConsumeTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java
index 95cc48f536..c15bc232ce 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/ConsumeTask.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.payments;
+package org.godotengine.godot.plugin.payment;
import android.content.Context;
import android.os.AsyncTask;
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 4a6b611c4d..c7d0a5de65 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,6 +30,7 @@
package org.godotengine.godot.plugin.payment;
+import android.content.Intent;
import android.support.annotation.NonNull;
import android.util.Log;
import java.util.ArrayList;
@@ -38,28 +39,40 @@ import java.util.List;
import org.godotengine.godot.Dictionary;
import org.godotengine.godot.Godot;
import org.godotengine.godot.GodotLib;
-import org.godotengine.godot.payments.GodotPaymentInterface;
-import org.godotengine.godot.payments.PaymentsManager;
import org.godotengine.godot.plugin.GodotPlugin;
import org.json.JSONException;
import org.json.JSONObject;
-public class GodotPayment extends GodotPlugin implements GodotPaymentInterface {
+public class GodotPayment extends GodotPlugin {
private Integer purchaseCallbackId = 0;
private String accessToken;
private String purchaseValidationUrlPrefix;
private String transactionId;
- private PaymentsManager mPaymentManager;
- private Dictionary mSkuDetails = new Dictionary();
+ private final PaymentsManager mPaymentManager;
+ private final Dictionary mSkuDetails = new Dictionary();
public GodotPayment(Godot godot) {
super(godot);
- mPaymentManager = godot.getPaymentsManager();
- mPaymentManager.setBaseSingleton(this);
+ mPaymentManager = new PaymentsManager(godot, this);
+ mPaymentManager.initService();
}
@Override
+ public void onMainActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == PaymentsManager.REQUEST_CODE_FOR_PURCHASE) {
+ mPaymentManager.processPurchaseResponse(resultCode, data);
+ }
+ }
+
+ @Override
+ public void onMainDestroy() {
+ super.onMainDestroy();
+ if (mPaymentManager != null) {
+ mPaymentManager.destroy();
+ }
+ }
+
public void purchase(final String sku, final String transactionId) {
runOnUiThread(new Runnable() {
@Override
@@ -69,7 +82,6 @@ public class GodotPayment extends GodotPlugin implements GodotPaymentInterface {
});
}
- @Override
public void consumeUnconsumedPurchases() {
runOnUiThread(new Runnable() {
@Override
@@ -81,89 +93,72 @@ public class GodotPayment extends GodotPlugin implements GodotPaymentInterface {
private String signature;
- @Override
public String getSignature() {
return this.signature;
}
- @Override
public void callbackSuccess(String ticket, String signature, String sku) {
GodotLib.calldeferred(purchaseCallbackId, "purchase_success", new Object[] { ticket, signature, sku });
}
- @Override
public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku) {
Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > " + ticket + "," + signature + "," + sku);
GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[] { ticket, signature, sku });
}
- @Override
public void callbackSuccessNoUnconsumedPurchases() {
GodotLib.calldeferred(purchaseCallbackId, "consume_not_required", new Object[] {});
}
- @Override
public void callbackFailConsume(String message) {
GodotLib.calldeferred(purchaseCallbackId, "consume_fail", new Object[] { message });
}
- @Override
public void callbackFail(String message) {
GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[] { message });
}
- @Override
public void callbackCancel() {
GodotLib.calldeferred(purchaseCallbackId, "purchase_cancel", new Object[] {});
}
- @Override
public void callbackAlreadyOwned(String sku) {
GodotLib.calldeferred(purchaseCallbackId, "purchase_owned", new Object[] { sku });
}
- @Override
public int getPurchaseCallbackId() {
return purchaseCallbackId;
}
- @Override
public void setPurchaseCallbackId(int purchaseCallbackId) {
this.purchaseCallbackId = purchaseCallbackId;
}
- @Override
public String getPurchaseValidationUrlPrefix() {
return this.purchaseValidationUrlPrefix;
}
- @Override
public void setPurchaseValidationUrlPrefix(String url) {
this.purchaseValidationUrlPrefix = url;
}
- @Override
public String getAccessToken() {
return accessToken;
}
- @Override
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
- @Override
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
- @Override
public String getTransactionId() {
return this.transactionId;
}
// request purchased items are not consumed
- @Override
public void requestPurchased() {
runOnUiThread(new Runnable() {
@Override
@@ -174,41 +169,34 @@ public class GodotPayment extends GodotPlugin implements GodotPaymentInterface {
}
// callback for requestPurchased()
- @Override
public void callbackPurchased(String receipt, String signature, String sku) {
GodotLib.calldeferred(purchaseCallbackId, "has_purchased", new Object[] { receipt, signature, sku });
}
- @Override
public void callbackDisconnected() {
GodotLib.calldeferred(purchaseCallbackId, "iap_disconnected", new Object[] {});
}
- @Override
public void callbackConnected() {
GodotLib.calldeferred(purchaseCallbackId, "iap_connected", new Object[] {});
}
// true if connected, false otherwise
- @Override
public boolean isConnected() {
return mPaymentManager.isConnected();
}
// consume item automatically after purchase. default is true.
- @Override
public void setAutoConsume(boolean autoConsume) {
mPaymentManager.setAutoConsume(autoConsume);
}
// consume a specific item
- @Override
public void consume(String sku) {
mPaymentManager.consume(sku);
}
// query in app item detail info
- @Override
public void querySkuDetails(String[] list) {
List<String> nKeys = Arrays.asList(list);
List<String> cKeys = Arrays.asList(mSkuDetails.get_keys());
@@ -225,7 +213,6 @@ public class GodotPayment extends GodotPlugin implements GodotPaymentInterface {
}
}
- @Override
public void addSkuDetail(String itemJson) {
JSONObject o = null;
try {
@@ -244,12 +231,10 @@ public class GodotPayment extends GodotPlugin implements GodotPaymentInterface {
}
}
- @Override
public void completeSkuDetail() {
GodotLib.calldeferred(purchaseCallbackId, "sku_details_complete", new Object[] { mSkuDetails });
}
- @Override
public void errorSkuDetail(String errorMessage) {
GodotLib.calldeferred(purchaseCallbackId, "sku_details_error", new Object[] { errorMessage });
}
@@ -263,6 +248,8 @@ public class GodotPayment extends GodotPlugin implements GodotPaymentInterface {
@NonNull
@Override
public List<String> getPluginMethods() {
- return Arrays.asList("purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", "setAutoConsume", "consume", "querySkuDetails", "isConnected");
+ return Arrays.asList("purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix",
+ "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased",
+ "setAutoConsume", "consume", "querySkuDetails", "isConnected");
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/HandlePurchaseTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java
index 23d693cc8c..fe5685288b 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/HandlePurchaseTask.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.payments;
+package org.godotengine.godot.plugin.payment;
import android.app.Activity;
import android.content.Intent;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsCache.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java
index 84a7eda6e0..d5919e3d9d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsCache.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java
@@ -28,11 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.payments;
+package org.godotengine.godot.plugin.payment;
import android.content.Context;
import android.content.SharedPreferences;
-import android.util.Log;
public class PaymentsCache {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java
index 9bf6650f84..bded1f452f 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/PaymentsManager.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.payments;
+package org.godotengine.godot.plugin.payment;
import android.app.Activity;
import android.content.ComponentName;
@@ -52,20 +52,13 @@ public class PaymentsManager {
public static final int REQUEST_CODE_FOR_PURCHASE = 0x1001;
private static boolean auto_consume = true;
- private Activity activity;
+ private final Activity activity;
+ private final GodotPayment godotPayment;
IInAppBillingService mService;
- public void setActivity(Activity activity) {
- this.activity = activity;
- }
-
- public static PaymentsManager createManager(Activity activity) {
- PaymentsManager manager = new PaymentsManager(activity);
- return manager;
- }
-
- private PaymentsManager(Activity activity) {
+ PaymentsManager(Activity activity, GodotPayment godotPayment) {
this.activity = activity;
+ this.godotPayment = godotPayment;
}
public PaymentsManager initService() {
@@ -409,10 +402,4 @@ public class PaymentsManager {
}))
.start();
}
-
- private GodotPaymentInterface godotPayment;
-
- public void setBaseSingleton(GodotPaymentInterface godotPaymentInterface) {
- this.godotPayment = godotPaymentInterface;
- }
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/PurchaseTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java
index 09c9349124..eecd1d2151 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/PurchaseTask.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.payments;
+package org.godotengine.godot.plugin.payment;
import android.app.Activity;
import android.app.PendingIntent;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java
index a101780511..b7bd638feb 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.payments;
+package org.godotengine.godot.plugin.payment;
import android.content.Context;
import android.os.AsyncTask;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java
index 10c314aecf..d42ded0c9b 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/payments/ValidateTask.java
+++ b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java
@@ -28,21 +28,21 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.payments;
+package org.godotengine.godot.plugin.payment;
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.godotengine.godot.plugin.payment.utils.HttpRequester;
+import org.godotengine.godot.plugin.payment.utils.RequestParams;
import org.json.JSONException;
import org.json.JSONObject;
abstract public class ValidateTask {
private Activity context;
- private GodotPaymentInterface godotPayments;
+ private GodotPayment godotPayments;
private ProgressDialog dialog;
private String mSku;
@@ -79,7 +79,7 @@ abstract public class ValidateTask {
}
}
- public ValidateTask(Activity context, GodotPaymentInterface godotPayments) {
+ public ValidateTask(Activity context, GodotPayment godotPayments) {
this.context = context;
this.godotPayments = godotPayments;
}
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..9571769cd3 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;
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..dcb983201e 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,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.utils;
+package org.godotengine.godot.plugin.payment.utils;
import android.content.Context;
import android.content.SharedPreferences;
@@ -61,6 +61,7 @@ import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
+import org.godotengine.godot.utils.Crypt;
/**
*
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..4be8b37473 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,10 +28,9 @@
/* 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;
diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp
index 9e9b17fb99..6b9105b8e5 100644
--- a/platform/android/java_class_wrapper.cpp
+++ b/platform/android/java_class_wrapper.cpp
@@ -34,13 +34,13 @@
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);
+ 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) {
@@ -160,7 +160,7 @@ 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()) {
@@ -173,7 +173,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
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: {
@@ -285,7 +285,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
argv[i].l = jo->instance;
} else {
- argv[i].l = NULL; //I hope this works
+ argv[i].l = nullptr; //I hope this works
}
} break;
@@ -386,7 +386,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
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];
@@ -400,7 +400,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
} break;
case ARG_ARRAY_BIT | ARG_TYPE_CLASS: {
- argv[i].l = NULL;
+ argv[i].l = nullptr;
} break;
}
}
@@ -520,7 +520,7 @@ 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;
}
@@ -1205,7 +1205,7 @@ 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;
@@ -1236,7 +1236,7 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
return Ref<JavaClass>();
}
-JavaClassWrapper *JavaClassWrapper::singleton = NULL;
+JavaClassWrapper *JavaClassWrapper::singleton = nullptr;
JavaClassWrapper::JavaClassWrapper(jobject p_activity) {
diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp
index 8d075f8e97..0da0bd6387 100644
--- a/platform/android/java_godot_io_wrapper.cpp
+++ b/platform/android/java_godot_io_wrapper.cpp
@@ -56,11 +56,8 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc
_show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;I)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");
}
}
@@ -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..dbb3b564f6 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);
@@ -78,11 +75,8 @@ public:
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 0b1d070441..8b38113e09 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_filter.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;
@@ -121,14 +126,14 @@ 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++) {
@@ -158,22 +163,26 @@ 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) {
if (os_android)
- os_android->set_display_size(Size2(width, height));
+ os_android->set_display_size(Size2i(width, 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_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 +202,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());
@@ -207,14 +215,14 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl
}
os_android->main_loop_begin();
- godot_java->on_gl_godot_main_loop_started(env);
+ godot_java->on_godot_main_loop_started(env);
++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()) {
@@ -227,18 +235,18 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jc
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 +258,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 |= InputFilter::HAT_MASK_LEFT;
else
- hat |= InputDefault::HAT_MASK_RIGHT;
+ hat |= InputFilter::HAT_MASK_RIGHT;
}
if (p_hat_y != 0) {
if (p_hat_y < 0)
- hat |= InputDefault::HAT_MASK_UP;
+ hat |= InputFilter::HAT_MASK_UP;
else
- hat |= InputDefault::HAT_MASK_DOWN;
+ hat |= InputFilter::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);
+ InputFilter::get_singleton()->joy_connection_changed(p_device, p_connected, name);
}
}
@@ -329,29 +337,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) {
@@ -428,7 +414,7 @@ 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) {
@@ -454,7 +440,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..221d701e2b 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -41,7 +41,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
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_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);
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 7b677c186e..8ef99dfab0 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -66,7 +66,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) {
_is_activity_resumed = p_env->GetMethodID(cls, "isActivityResumed", "()Z");
_vibrate = p_env->GetMethodID(cls, "vibrate", "(I)V");
_get_input_fallback_mapping = p_env->GetMethodID(cls, "getInputFallbackMapping", "()Ljava/lang/String;");
- _on_gl_godot_main_loop_started = p_env->GetMethodID(cls, "onGLGodotMainLoopStarted", "()V");
+ _on_godot_main_loop_started = p_env->GetMethodID(cls, "onGodotMainLoopStarted", "()V");
}
GodotJavaWrapper::~GodotJavaWrapper() {
@@ -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,30 +96,30 @@ 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);
}
-void GodotJavaWrapper::on_gl_godot_main_loop_started(JNIEnv *p_env) {
- if (_on_gl_godot_main_loop_started) {
- if (p_env == NULL) {
+void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) {
+ if (_on_godot_main_loop_started) {
+ if (p_env == nullptr) {
p_env = ThreadAndroid::get_env();
}
}
- p_env->CallVoidMethod(godot_instance, _on_gl_godot_main_loop_started);
+ p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started);
}
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 cdab2ecc9c..89d6b6db46 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -61,21 +61,21 @@ private:
jmethodID _is_activity_resumed = 0;
jmethodID _vibrate = 0;
jmethodID _get_input_fallback_mapping = 0;
- jmethodID _on_gl_godot_main_loop_started = 0;
+ jmethodID _on_godot_main_loop_started = 0;
public:
GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance);
~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_gl_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..ded79a668f 100644
--- a/platform/android/jni_utils.cpp
+++ b/platform/android/jni_utils.cpp
@@ -130,7 +130,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]];
@@ -211,7 +211,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();
}
@@ -384,7 +384,7 @@ 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;
@@ -417,7 +417,7 @@ 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;
diff --git a/platform/android/jni_utils.h b/platform/android/jni_utils.h
index 925340a680..c2baa51b4a 100644
--- a/platform/android/jni_utils.h
+++ b/platform/android/jni_utils.h
@@ -40,7 +40,7 @@ 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 +53,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/os_android.cpp b/platform/android/os_android.cpp
index 7e2b0d948e..760157595c 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,29 +56,6 @@ 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();
@@ -91,7 +64,7 @@ void OS_Android::initialize_core() {
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
else {
#ifdef USE_JAVA_FILE_ACCESS
- FileAccess::make_default<FileAccessBufferedFA<FileAccessJAndroid> >(FileAccess::ACCESS_RESOURCES);
+ FileAccess::make_default<FileAccessBufferedFA<FileAccessJAndroid>>(FileAccess::ACCESS_RESOURCES);
#else
//FileAccess::make_default<FileAccessBufferedFA<FileAccessAndroid> >(FileAccess::ACCESS_RESOURCES);
FileAccess::make_default<FileAccessAndroid>(FileAccess::ACCESS_RESOURCES);
@@ -110,71 +83,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() {
+ InputFilter::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,12 +120,6 @@ 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);
@@ -212,63 +141,6 @@ 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";
@@ -279,11 +151,6 @@ 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)
@@ -304,277 +171,17 @@ void OS_Android::main_loop_end() {
}
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) {
@@ -597,26 +204,6 @@ 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();
@@ -626,11 +213,6 @@ 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())
@@ -662,11 +244,6 @@ 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();
@@ -676,50 +253,46 @@ 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 +320,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 +344,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_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp
index 7413236e5d..c3bfb2f2ed 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>
@@ -43,7 +44,7 @@ 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;
@@ -86,6 +87,51 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis
s->add_method(mname, mid, types, get_jni_type(retval));
}
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types) {
+ 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..729327f6f0 100644
--- a/platform/android/thread_jandroid.cpp
+++ b/platform/android/thread_jandroid.cpp
@@ -66,7 +66,7 @@ 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 &) {
@@ -100,7 +100,7 @@ void ThreadAndroid::wait_to_finish_func_jandroid(Thread *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;
}
@@ -108,21 +108,21 @@ 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);
}
@@ -142,8 +142,8 @@ JNIEnv *ThreadAndroid::get_env() {
setup_thread();
}
- JNIEnv *env = NULL;
- java_vm->AttachCurrentThread(&env, NULL);
+ JNIEnv *env = nullptr;
+ java_vm->AttachCurrentThread(&env, nullptr);
return env;
}
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..7e698ada4f
--- /dev/null
+++ b/platform/android/vulkan/vulkan_context_android.h
@@ -0,0 +1,49 @@
+/*************************************************************************/
+/* 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..94c9e83368 100644
--- a/platform/haiku/audio_driver_media_kit.cpp
+++ b/platform/haiku/audio_driver_media_kit.cpp
@@ -34,7 +34,7 @@
#include "core/project_settings.h"
-int32_t *AudioDriverMediaKit::samples_in = NULL;
+int32_t *AudioDriverMediaKit::samples_in = nullptr;
Error AudioDriverMediaKit::init() {
active = false;
@@ -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..925bb9ac5d 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_filter.h"
#include "core/os/os.h"
-#include "main/input_default.h"
#include "haiku_gl_view.h"
diff --git a/platform/haiku/os_haiku.cpp b/platform/haiku/os_haiku.cpp
index a082ba53f9..07cb18d7cd 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 {
diff --git a/platform/haiku/os_haiku.h b/platform/haiku/os_haiku.h
index fc8cb77a91..64f5690dd1 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_filter.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.h b/platform/iphone/app_delegate.h
index 6b3b7ad5bc..27552d781a 100644
--- a/platform/iphone/app_delegate.h
+++ b/platform/iphone/app_delegate.h
@@ -36,9 +36,11 @@
#import <CoreMotion/CoreMotion.h>
-#if defined(OPENGL_ENABLED)
-@interface AppDelegate : NSObject <UIApplicationDelegate, GLViewDelegate> {
-#endif
+// FIXME: Add support for both GLES2 and Vulkan when GLES2 is implemented again,
+// so it can't be done with compilation time branching.
+//#if defined(OPENGL_ENABLED)
+//@interface AppDelegate : NSObject <UIApplicationDelegate, GLViewDelegate> {
+//#endif
#if defined(VULKAN_ENABLED)
@interface AppDelegate : NSObject <UIApplicationDelegate> {
#endif
diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm
index acc3e5d4e0..0ac8bb7a56 100644
--- a/platform/iphone/app_delegate.mm
+++ b/platform/iphone/app_delegate.mm
@@ -648,7 +648,6 @@ static int frame_count = 0;
view_controller = [[ViewController alloc] init];
view_controller.view = glView;
-
_set_keep_screen_on(bool(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true)) ? YES : NO);
glView.useCADisplayLink =
bool(GLOBAL_DEF("display.iOS/use_cadisplaylink", true)) ? YES : NO;
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 7cef2351e3..3efe338ac7 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -71,8 +71,8 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
String modules_buildphase;
String modules_buildgrp;
};
-
struct ExportArchitecture {
+
String name;
bool is_default;
@@ -106,7 +106,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;
@@ -410,7 +410,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
}
String EditorExportPlatformIOS::_get_additional_plist_content() {
- Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
String result;
for (int i = 0; i < export_plugins.size(); ++i) {
result += export_plugins[i]->get_ios_plist_content();
@@ -419,7 +419,7 @@ String EditorExportPlatformIOS::_get_additional_plist_content() {
}
String EditorExportPlatformIOS::_get_linker_flags() {
- Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
String result;
for (int i = 0; i < export_plugins.size(); ++i) {
String flags = export_plugins[i]->get_ios_linker_flags();
@@ -434,7 +434,7 @@ String EditorExportPlatformIOS::_get_linker_flags() {
}
String EditorExportPlatformIOS::_get_cpp_code() {
- Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
String result;
for (int i = 0; i < export_plugins.size(); ++i) {
result += export_plugins[i]->get_ios_cpp_code();
@@ -776,7 +776,7 @@ struct ExportLibsData {
};
void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets) {
- Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
Vector<String> frameworks;
for (int i = 0; i < export_plugins.size(); ++i) {
Vector<String> plugin_frameworks = export_plugins[i]->get_ios_frameworks();
@@ -795,7 +795,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();
@@ -920,11 +920,18 @@ Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir
}
Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets) {
- Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
for (int i = 0; i < export_plugins.size(); i++) {
Vector<String> frameworks = export_plugins[i]->get_ios_frameworks();
Error err = _export_additional_assets(p_out_dir, frameworks, true, r_exported_assets);
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);
@@ -1076,7 +1083,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 +1105,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,6 +1209,22 @@ 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)) {
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/godot_iphone.cpp b/platform/iphone/godot_iphone.cpp
index 8c3eddc5f7..3e67362e16 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);
@@ -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);
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
index 497f2f747d..3ef521a61a 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"
@@ -68,7 +68,7 @@ const char *OSIPhone::get_video_driver_name(int p_driver) const {
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() {
@@ -134,16 +134,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
@@ -339,7 +339,7 @@ void OSIPhone::delete_main_loop() {
memdelete(main_loop);
};
- main_loop = NULL;
+ main_loop = nullptr;
};
void OSIPhone::finalize() {
@@ -361,8 +361,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
@@ -608,7 +608,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 +631,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..96cf041c37 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_filter.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"
@@ -62,7 +60,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;
diff --git a/platform/iphone/vulkan_context_iphone.h b/platform/iphone/vulkan_context_iphone.h
index 200057e14d..625e41f4b9 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 */
diff --git a/platform/iphone/vulkan_context_iphone.mm b/platform/iphone/vulkan_context_iphone.mm
index f49b85c097..701ac0d9bb 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 */
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub
index d3cd8f76b7..7239648937 100644
--- a/platform/javascript/SCsub
+++ b/platform/javascript/SCsub
@@ -1,67 +1,63 @@
#!/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",
+ "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',
+ "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',
+ "id_handler.js",
]
for module in js_modules:
- env.Append(LINKFLAGS=['--pre-js', env.File(module).path])
+ env.Append(LINKFLAGS=["--pre-js", env.File(module).path])
env.Depends(build, js_modules)
engine = [
- 'engine/preloader.js',
- 'engine/loader.js',
- 'engine/utils.js',
- 'engine/engine.js',
+ "engine/preloader.js",
+ "engine/loader.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')
-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'
-]
-if env['threads_enabled']:
+zip_dir = env.Dir("#bin/.javascript_zip")
+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"]
+if env["threads_enabled"]:
in_files.append(build[2])
- out_files.append(zip_dir.File('godot.worker.js'))
+ out_files.append(zip_dir.File("godot.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..45cb82b351 100644
--- a/platform/javascript/api/api.cpp
+++ b/platform/javascript/api/api.cpp
@@ -46,7 +46,7 @@ void unregister_javascript_api() {
memdelete(javascript_eval);
}
-JavaScript *JavaScript::singleton = NULL;
+JavaScript *JavaScript::singleton = nullptr;
JavaScript *JavaScript::get_singleton() {
@@ -55,7 +55,7 @@ JavaScript *JavaScript::get_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;
}
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..8f857478e5 100644
--- a/platform/javascript/audio_driver_javascript.cpp
+++ b/platform/javascript/audio_driver_javascript.cpp
@@ -32,7 +32,7 @@
#include <emscripten.h>
-AudioDriverJavaScript *AudioDriverJavaScript::singleton = NULL;
+AudioDriverJavaScript *AudioDriverJavaScript::singleton = nullptr;
const char *AudioDriverJavaScript::get_name() const {
@@ -200,7 +200,7 @@ void AudioDriverJavaScript::finish() {
if (internal_buffer) {
memdelete_arr(internal_buffer);
- internal_buffer = NULL;
+ internal_buffer = nullptr;
}
_driver_id = 0;
}
@@ -264,7 +264,7 @@ 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/detect.py b/platform/javascript/detect.py
index fb02752aa7..9486e10717 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,125 @@ 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"])
# callMain for manual start, FS for preloading.
- env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS=["callMain", "FS"]'])
+ env.Append(LINKFLAGS=["-s", 'EXTRA_EXPORTED_RUNTIME_METHODS=["callMain", "FS"]'])
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/export/export.cpp b/platform/javascript/export/export.cpp
index da61425747..39faae2d17 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -390,7 +390,7 @@ 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);
@@ -410,7 +410,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;
diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp
index 472384cf30..863c207896 100644
--- a/platform/javascript/http_client_javascript.cpp
+++ b/platform/javascript/http_client_javascript.cpp
@@ -88,8 +88,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(": ");
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/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index 1d7a16db80..ad06aef86e 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -36,7 +36,7 @@
#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 "servers/rendering/rendering_server_raster.h"
#include <emscripten.h>
#include <png.h>
@@ -167,7 +167,7 @@ void OS_JavaScript::set_window_maximized(bool p_enabled) {
strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
- strategy.canvasResizedCallback = NULL;
+ strategy.canvasResizedCallback = nullptr;
emscripten_enter_soft_fullscreen(GODOT_CANVAS_SELECTOR, &strategy);
window_maximized = p_enabled;
}
@@ -196,7 +196,7 @@ void OS_JavaScript::set_window_fullscreen(bool p_enabled) {
strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
- strategy.canvasResizedCallback = NULL;
+ strategy.canvasResizedCallback = nullptr;
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.");
@@ -470,7 +470,7 @@ void OS_JavaScript::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_s
if (p_cursor.is_valid()) {
- Map<CursorShape, Vector<Variant> >::Element *cursor_c = cursors_cache.find(p_shape);
+ 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) {
@@ -541,10 +541,10 @@ void OS_JavaScript::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_s
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));
+ 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, NULL));
+ ERR_FAIL_COND(!png_image_write_to_memory(&png_meta, png.ptrw(), &len, 0, data.ptr(), 0, nullptr));
char *object_url;
/* clang-format off */
@@ -824,7 +824,7 @@ const char *OS_JavaScript::get_video_driver_name(int p_driver) const {
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) + ".");
}
// Audio
@@ -887,7 +887,7 @@ int OS_JavaScript::get_current_video_driver() const {
void OS_JavaScript::initialize_core() {
OS_Unix::initialize_core();
- FileAccess::make_default<FileAccessBufferedFA<FileAccessUnix> >(FileAccess::ACCESS_RESOURCES);
+ FileAccess::make_default<FileAccessBufferedFA<FileAccessUnix>>(FileAccess::ACCESS_RESOURCES);
}
Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
@@ -962,18 +962,18 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver,
setenv("LANG", locale_ptr, true);
AudioDriverManager::initialize(p_audio_driver);
- VisualServer *visual_server = memnew(VisualServerRaster());
+ RenderingServer *rendering_server = memnew(RenderingServerRaster());
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); \
+#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(NULL, true, &cb); \
+#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
@@ -1009,14 +1009,14 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver,
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
+ NOTIFICATION_WM_MOUSE_ENTER,
+ NOTIFICATION_WM_MOUSE_EXIT,
+ NOTIFICATION_WM_FOCUS_IN,
+ NOTIFICATION_WM_FOCUS_OUT
);
/* clang-format on */
- visual_server->init();
+ rendering_server->init();
return OK;
}
@@ -1072,7 +1072,7 @@ bool OS_JavaScript::main_loop_iterate() {
strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
- strategy.canvasResizedCallback = NULL;
+ strategy.canvasResizedCallback = nullptr;
emscripten_enter_soft_fullscreen(GODOT_CANVAS_SELECTOR, &strategy);
} else {
emscripten_set_canvas_element_size(GODOT_CANVAS_SELECTOR, windowed_size.width, windowed_size.height);
@@ -1121,8 +1121,8 @@ int OS_JavaScript::get_process_id() const {
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;
+ if (p_notification == NOTIFICATION_WM_MOUSE_ENTER || p_notification == NOTIFICATION_WM_MOUSE_EXIT) {
+ cursor_inside_canvas = p_notification == NOTIFICATION_WM_MOUSE_ENTER;
}
OS_JavaScript::get_singleton()->get_main_loop()->notification(p_notification);
}
@@ -1182,10 +1182,10 @@ void OS_JavaScript::set_icon(const Ref<Image> &p_icon) {
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));
+ 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, NULL));
+ ERR_FAIL_COND(!png_image_write_to_memory(&png_meta, png.ptrw(), &len, 0, data.ptr(), 0, nullptr));
/* clang-format off */
EM_ASM_ARGS({
@@ -1284,7 +1284,7 @@ OS_JavaScript::OS_JavaScript(int p_argc, char *p_argv[]) {
just_exited_fullscreen = false;
transparency_enabled = false;
- main_loop = NULL;
+ main_loop = nullptr;
idb_available = false;
sync_wait_time = -1;
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index 5319ea121c..81fe4cf0cc 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -32,10 +32,10 @@
#define OS_JAVASCRIPT_H
#include "audio_driver_javascript.h"
+#include "core/input/input_filter.h"
#include "drivers/unix/os_unix.h"
-#include "main/input_default.h"
#include "servers/audio_server.h"
-#include "servers/visual/rasterizer.h"
+#include "servers/rendering/rasterizer.h"
#include <emscripten/html5.h>
@@ -52,7 +52,7 @@ class OS_JavaScript : public OS_Unix {
Ref<InputEventKey> deferred_key_event;
CursorShape cursor_shape;
String cursors[CURSOR_MAX];
- Map<CursorShape, Vector<Variant> > cursors_cache;
+ Map<CursorShape, Vector<Variant>> cursors_cache;
Point2 touches[32];
Point2i last_click_pos;
@@ -145,7 +145,7 @@ public:
void run_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;
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..308d68521a 100644
--- a/platform/x11/context_gl_x11.cpp
+++ b/platform/linuxbsd/context_gl_x11.cpp
@@ -52,7 +52,7 @@ struct ContextGL_X11_Private {
void ContextGL_X11::release_current() {
- glXMakeCurrent(x11_display, None, NULL);
+ glXMakeCurrent(x11_display, None, nullptr);
}
void ContextGL_X11::make_current() {
@@ -117,7 +117,7 @@ Error ContextGL_X11::initialize() {
int fbcount;
GLXFBConfig fbconfig = 0;
- XVisualInfo *vi = NULL;
+ XVisualInfo *vi = nullptr;
XSetWindowAttributes swa;
swa.event_mask = StructureNotifyMask;
@@ -136,7 +136,7 @@ Error ContextGL_X11::initialize() {
XRenderPictFormat *pict_format = XRenderFindVisualFormat(x11_display, vi->visual);
if (!pict_format) {
XFree(vi);
- vi = NULL;
+ vi = nullptr;
continue;
}
@@ -208,9 +208,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;
diff --git a/platform/x11/context_gl_x11.h b/platform/linuxbsd/context_gl_x11.h
index 2c0643c95a..2c0643c95a 100644
--- a/platform/x11/context_gl_x11.h
+++ b/platform/linuxbsd/context_gl_x11.h
diff --git a/platform/x11/crash_handler_x11.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp
index 19c8f71d0e..dbdb15918e 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();
}
@@ -79,7 +79,7 @@ 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);
@@ -102,7 +102,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);
}
@@ -132,9 +132,9 @@ void CrashHandler::disable() {
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..94b4649690 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 */
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
new file mode 100644
index 0000000000..5d8b4fba48
--- /dev/null
+++ b/platform/linuxbsd/detect.py
@@ -0,0 +1,377 @@
+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.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/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..1bec65ff04 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);
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 c74981fd55..78ddef5ff6 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,117 @@
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;
- }
-
- if (getenv("LD_LIBRARY_PATH")) {
- String ld_library_path(getenv("LD_LIBRARY_PATH"));
- Vector<String> libraries = ld_library_path.split(":");
+ return false;
+}
+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);
- }
-
- //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 (program.length())
+ break;
}
- 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);
+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;
- window_has_focus = true; // Set focus to true at init
-#ifdef JOYDEV_ENABLED
- joypad = memnew(JoypadLinux(input));
-#endif
- _ensure_user_data_dir();
+ Bool xquerypointer_result = XQueryPointer(x11_display, wd.x11_window, &root_return, &child_return, &root_x, &root_y,
+ &win_x, &win_y, &mask_return);
- if (p_desired.layered) {
- set_window_per_pixel_transparency_enabled(true);
- }
+ if (xquerypointer_result) {
+ if (win_x > 0 && win_y > 0 && win_x <= wd.size.width && win_y <= wd.size.height) {
- XEvent xevent;
- while (XPending(x11_display) > 0) {
- XNextEvent(x11_display, &xevent);
- if (xevent.type == ConfigureNotify) {
- _window_changed(&xevent);
+ last_mouse_pos.x = win_x;
+ last_mouse_pos.y = win_y;
+ last_mouse_pos_valid = true;
+ InputFilter::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.");
@@ -717,10 +254,10 @@ bool OS_X11::refresh_device_info() {
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;
+ double range_min_x = 0;
+ double range_min_y = 0;
+ double range_max_x = 0;
+ double range_max_y = 0;
int pressure_resolution = 0;
int tilt_resolution_x = 0;
int tilt_resolution_y = 0;
@@ -786,136 +323,35 @@ 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)
return;
@@ -926,34 +362,37 @@ void OS_X11::set_mouse_mode(MouseMode p_mode) {
// 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);
+ InputFilter::get_singleton()->set_mouse_position(center);
}
} else {
do_mouse_warp = false;
@@ -961,8 +400,13 @@ void OS_X11::set_mouse_mode(MouseMode p_mode) {
XFlush(x11_display);
}
+DisplayServerX11::MouseMode DisplayServerX11::mouse_get_mode() const {
+ return mouse_mode;
+}
+
+void DisplayServerX11::mouse_warp_to_position(const Point2i &p_to) {
-void OS_X11::warp_mouse_position(const Point2 &p_to) {
+ _THREAD_SAFE_METHOD_
if (mouse_mode == MOUSE_MODE_CAPTURED) {
@@ -973,184 +417,133 @@ void OS_X11::warp_mouse_position(const Point2 &p_to) {
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;
+ return Vector2i();
}
-OS::MouseMode OS_X11::get_mouse_mode() const {
- return mouse_mode;
-}
-
-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;
-}
+void DisplayServerX11::clipboard_set(const String &p_text) {
-bool OS_X11::get_window_per_pixel_transparency_enabled() const {
+ _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) {
+static String _clipboard_get_impl(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard, Atom target) {
- if (!is_layered_allowed()) return;
- if (layered_window != p_enabled) {
- if (p_enabled) {
- set_borderless_window(true);
- layered_window = true;
- } else {
- layered_window = false;
- }
- }
-}
-
-void OS_X11::set_window_title(const String &p_title) {
- XStoreName(x11_display, x11_window, p_title.utf8().get_data());
+ String ret;
- 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());
-}
+ 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);
-void OS_X11::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
-}
+ if (Sown == x11_window) {
-OS::VideoMode OS_X11::get_video_mode(int p_screen) const {
- return current_videomode;
-}
+ return p_internal_clipboard;
+ };
-void OS_X11::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
-}
+ 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;
+ };
+ };
-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);
+ //
+ // 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);
+ }
+ }
}
- if (p_enabled && !is_window_resizable()) {
- // Set the window as resizable to prevent window managers to ignore the fullscreen state flag.
- XSizeHints *xsh;
+ return ret;
+}
- xsh = XAllocSizeHints();
- xsh->flags = 0L;
- XSetWMNormalHints(x11_display, x11_window, xsh);
- XFree(xsh);
+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 (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);
+String DisplayServerX11::clipboard_get() const {
- // 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);
+ _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);
+int DisplayServerX11::get_screen_count() const {
- 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);
-}
+ _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);
@@ -1161,42 +554,12 @@ int OS_X11::get_screen_count() const {
XFree(xsi);
return count;
}
+Point2i DisplayServerX11::screen_get_position(int p_screen) const {
-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);
+ _THREAD_SAFE_METHOD_
- 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);
- }
- }
-}
-
-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 +582,48 @@ 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;
@@ -1279,18 +651,249 @@ int OS_X11::get_screen_dpi(int p_screen) const {
//could not get dpi
return 96;
}
+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];
-Point2 OS_X11::get_window_position() const {
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 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];
-void OS_X11::set_window_position(const Point2 &p_position) {
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 +902,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,153 +913,151 @@ 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();
-}
-
-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);
-}
-
-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);
+ XMoveWindow(x11_display, wd.x11_window, p_position.x - x, p_position.y - y);
+ _update_real_mouse_position(wd);
}
-Size2 OS_X11::get_max_window_size() const {
- return max_size;
-}
+void DisplayServerX11::window_set_max_size(const Size2i p_size, WindowID p_window) {
-Size2 OS_X11::get_min_window_size() const {
- return min_size;
-}
+ _THREAD_SAFE_METHOD_
-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);
}
}
+Size2i DisplayServerX11::window_get_max_size(WindowID p_window) const {
-void OS_X11::set_max_window_size(const Size2 p_size) {
+ _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);
}
}
+Size2i DisplayServerX11::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 OS_X11::set_window_size(const Size2 p_size) {
+void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) {
- if (current_videomode.width == p_size.width && current_videomode.height == p_size.height)
+ _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)
break;
@@ -1464,136 +1065,99 @@ void OS_X11::set_window_size(const Size2 p_size) {
usleep(10000);
}
}
+Size2i DisplayServerX11::window_get_size(WindowID p_window) const {
-void OS_X11::set_window_fullscreen(bool p_enabled) {
+ _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;
}
+Size2i DisplayServerX11::window_get_real_size(WindowID p_window) const {
-bool OS_X11::is_window_fullscreen() const {
- return current_videomode.fullscreen;
-}
+ _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;
-}
-
-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);
+bool DisplayServerX11::window_is_maximize_allowed(WindowID p_window) const {
- 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;
+ _THREAD_SAFE_METHOD_
- 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];
-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 +1167,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 +1176,457 @@ 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;
+void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
- int result = XGetWindowProperty(
- x11_display,
- x11_window,
- property,
- 0,
- 1024,
- False,
- XA_ATOM,
- &type,
- &format,
- &len,
- &remaining,
- &data);
+ 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) {
- 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);
+ _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
}
+ //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);
-bool OS_X11::is_window_focused() const {
- return window_focused;
-}
+ wd.fullscreen = false;
-void OS_X11::set_borderless_window(bool p_borderless) {
+ window_set_position(wd.last_position_before_fs, p_window);
- if (get_borderless_window() == p_borderless)
- return;
+ if (on_top) {
+ _set_wm_maximized(p_window, false);
+ }
- if (!p_borderless && layered_window)
- set_window_per_pixel_transparency_enabled(false);
+ } break;
+ case WINDOW_MODE_MAXIMIZED: {
- current_videomode.borderless_window = p_borderless;
+ _set_wm_maximized(p_window, false);
+ } break;
+ }
- 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);
+ 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);
- // Preserve window size
- set_window_size(Size2(current_videomode.width, current_videomode.height));
+ } break;
+ }
}
-bool OS_X11::get_borderless_window() {
+DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) const {
- bool borderless = current_videomode.borderless_window;
- Atom prop = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- if (prop != None) {
+ _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;
+ }
+ { //test maximized
+ // Using EWMH -- Extended Window Manager Hints
+ Atom property = XInternAtom(x11_display, "_NET_WM_STATE", False);
+ Atom type;
+ int format;
+ unsigned long len;
+ unsigned long remaining;
+ unsigned char *data = nullptr;
+ bool retval = false;
+
+ int result = XGetWindowProperty(
+ x11_display,
+ wd.x11_window,
+ property,
+ 0,
+ 1024,
+ False,
+ XA_ATOM,
+ &type,
+ &format,
+ &len,
+ &remaining,
+ &data);
+
+ if (result == Success && data) {
+ Atom *atoms = (Atom *)data;
+ Atom wm_max_horz = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
+ Atom wm_max_vert = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
+ bool found_wm_max_horz = false;
+ bool found_wm_max_vert = false;
+
+ for (uint64_t i = 0; i < len; i++) {
+ if (atoms[i] == wm_max_horz)
+ found_wm_max_horz = true;
+ if (atoms[i] == wm_max_vert)
+ found_wm_max_vert = true;
+
+ if (found_wm_max_horz && found_wm_max_vert) {
+ retval = true;
+ break;
+ }
+ }
+
+ XFree(data);
+ }
+
+ if (retval) {
+ return WINDOW_MODE_MAXIMIZED;
+ }
+ }
+ { // test 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 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: {
-void OS_X11::request_attention() {
+ 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 +1638,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,7 +1648,314 @@ 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));
@@ -1813,7 +1963,7 @@ void OS_X11::get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWith
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);
@@ -1826,8 +1976,9 @@ 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 +2004,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 +2020,18 @@ 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';
@@ -1902,11 +2053,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 +2077,7 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
k->set_shift(true);
}
- input->accumulate_input_event(k);
+ InputFilter::get_singleton()->accumulate_input_event(k);
}
memfree(utf8string);
return;
@@ -1944,7 +2097,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 +2123,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 +2141,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 */
@@ -2012,14 +2168,16 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
// is correct, but the xorg developers are
// not very helpful today.
- ::Time tresh = ABSDIFF(peek_event.xkey.time, xkeyevent->time);
- if (peek_event.type == KeyPress && tresh < 5) {
+#define ABSDIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y)))
+ ::Time threshold = ABSDIFF(peek_event.xkey.time, xkeyevent->time);
+#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
}
}
@@ -2062,104 +2220,133 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
k->set_metakey(false);
}
- bool last_is_pressed = Input::get_singleton()->is_key_pressed(k->get_keycode());
+ bool last_is_pressed = InputFilter::get_singleton()->is_key_pressed(k->get_keycode());
if (k->is_pressed()) {
if (last_is_pressed) {
k->set_echo(true);
}
}
- //printf("key: %x\n",k->get_keycode());
- input->accumulate_input_event(k);
+ InputFilter::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) {
-
- Atom actual_type;
- int actual_format;
- unsigned long nitems;
- unsigned long bytes_after;
- unsigned char *ret = 0;
-
- int read_bytes = 1024;
-
- //Keep trying to read the property until there are no
- //bytes unread.
- do {
- if (ret != 0)
- XFree(ret);
+void DisplayServerX11::_xim_destroy_callback(::XIM im, ::XPointer client_data,
+ ::XPointer call_data) {
- XGetWindowProperty(p_display, p_window, p_property, 0, read_bytes, False, AnyPropertyType,
- &actual_type, &actual_format, &nitems, &bytes_after,
- &ret);
+ WARN_PRINT("Input method stopped");
+ DisplayServerX11 *ds = reinterpret_cast<DisplayServerX11 *>(client_data);
+ ds->xim = nullptr;
- read_bytes *= 2;
+ for (Map<WindowID, WindowData>::Element *E = ds->windows.front(); E; E = E->next()) {
+ E->get().xic = nullptr;
+ }
+}
- } while (bytes_after != 0);
+void DisplayServerX11::_window_changed(XEvent *event) {
- Property p = { ret, actual_format, (int)nitems, actual_type };
+ WindowID window_id = MAIN_WINDOW_ID;
- return p;
-}
+ // 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;
+ }
+ }
-static Atom pick_target_from_list(Display *p_display, Atom *p_list, int p_count) {
+ Rect2i new_rect;
- static const char *target_type = "text/uri-list";
+ 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;
+ }
- for (int i = 0; i < p_count; i++) {
+ {
+ //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;
- Atom atom = p_list[i];
+ new_rect.size.width = event->xconfigure.width;
+ new_rect.size.height = event->xconfigure.height;
+ }
- if (atom != None && String(XGetAtomName(p_display, atom)) == target_type)
- return atom;
+ if (new_rect == Rect2i(wd.position, wd.size)) {
+ return;
+ }
+ if (wd.xic) {
+ // Not portable.
+ window_set_ime_position(Point2(0, 1));
}
- return None;
-}
-static Atom pick_target_from_atoms(Display *p_disp, Atom p_t1, Atom p_t2, Atom p_t3) {
+ wd.position = new_rect.position;
+ wd.size = new_rect.size;
- static const char *target_type = "text/uri-list";
- if (p_t1 != None && String(XGetAtomName(p_disp, p_t1)) == target_type)
- return p_t1;
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_resize(window_id, wd.size.width, wd.size.height);
+ }
+#endif
- if (p_t2 != None && String(XGetAtomName(p_disp, p_t2)) == target_type)
- return p_t2;
+ print_line("DisplayServer::_window_changed: " + itos(window_id) + " rect: " + new_rect);
+ if (!wd.rect_changed_callback.is_null()) {
+ Rect2i r = new_rect;
- if (p_t3 != None && String(XGetAtomName(p_disp, p_t3)) == target_type)
- return p_t3;
+ Variant rect = r;
- return None;
+ Variant *rectp = &rect;
+ Variant ret;
+ Callable::CallError ce;
+ wd.rect_changed_callback.call((const Variant **)&rectp, 1, ret, ce);
+ }
}
-void OS_X11::_window_changed(XEvent *event) {
+void DisplayServerX11::_dispatch_input_events(const Ref<InputEvent> &p_event) {
+ ((DisplayServerX11 *)(get_singleton()))->_dispatch_input_event(p_event);
+}
- if (xic) {
- // Not portable.
- set_ime_position(Point2(0, 1));
- }
- if ((event->xconfigure.width == current_videomode.width) &&
- (event->xconfigure.height == current_videomode.height))
- return;
+void DisplayServerX11::_dispatch_input_event(const Ref<InputEvent> &p_event) {
- current_videomode.width = event->xconfigure.width;
- current_videomode.height = event->xconfigure.height;
+ Variant ev = p_event;
+ Variant *evp = &ev;
+ Variant ret;
+ Callable::CallError ce;
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
- context_vulkan->window_resize(0, current_videomode.width, current_videomode.height);
+ 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);
+ }
}
-#endif
}
-void OS_X11::process_xevents() {
+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);
+ }
+}
+void DisplayServerX11::process_events() {
- //printf("checking events %i\n", XPending(x11_display));
+ _THREAD_SAFE_METHOD_
do_mouse_warp = false;
@@ -2170,6 +2357,16 @@ void OS_X11::process_xevents() {
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;
}
@@ -2185,7 +2382,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;
@@ -2275,6 +2472,7 @@ void OS_X11::process_xevents() {
Ref<InputEventScreenTouch> st;
st.instance();
+ st->set_window_id(window_id);
st->set_index(index);
st->set_position(pos);
st->set_pressed(is_begin);
@@ -2288,12 +2486,12 @@ void OS_X11::process_xevents() {
// 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);
+ InputFilter::get_singleton()->accumulate_input_event(st);
} else {
if (!xi.state.has(index)) // Defensive
break;
xi.state.erase(index);
- input->accumulate_input_event(st);
+ InputFilter::get_singleton()->accumulate_input_event(st);
}
} break;
@@ -2308,10 +2506,11 @@ void OS_X11::process_xevents() {
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);
+ InputFilter::get_singleton()->accumulate_input_event(sd);
curr_pos_elem->value() = pos;
}
@@ -2336,31 +2535,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
@@ -2368,22 +2573,25 @@ 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);
+ InputFilter::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);
}
@@ -2399,13 +2607,14 @@ void OS_X11::process_xevents() {
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);
+ InputFilter::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;
@@ -2425,13 +2634,14 @@ 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)
mb->set_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());
@@ -2439,14 +2649,14 @@ void OS_X11::process_xevents() {
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);
}
@@ -2457,11 +2667,11 @@ 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);
+ InputFilter::get_singleton()->accumulate_input_event(mb);
} break;
case MotionNotify: {
@@ -2471,7 +2681,7 @@ void OS_X11::process_xevents() {
// 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;
@@ -2495,7 +2705,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;
@@ -2532,7 +2742,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) {
@@ -2546,24 +2756,25 @@ 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_window_id(window_id);
mm->set_pressure(xi.pressure);
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());
+ InputFilter::get_singleton()->set_mouse_position(posi);
+ mm->set_speed(InputFilter::get_singleton()->get_last_mouse_speed());
mm->set_relative(rel);
@@ -2574,7 +2785,7 @@ void OS_X11::process_xevents() {
// 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);
+ InputFilter::get_singleton()->accumulate_input_event(mm);
} break;
case KeyPress:
@@ -2584,7 +2795,7 @@ void OS_X11::process_xevents() {
// 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: {
@@ -2599,7 +2810,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,
@@ -2652,13 +2863,20 @@ void OS_X11::process_xevents() {
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;
@@ -2668,7 +2886,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.
@@ -2678,8 +2896,9 @@ 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) {
@@ -2688,7 +2907,7 @@ void OS_X11::process_xevents() {
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
requested = pick_target_from_atoms(x11_display, event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]);
@@ -2703,7 +2922,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;
@@ -2716,9 +2935,9 @@ void OS_X11::process_xevents() {
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]);
+ 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), x11_window, CurrentTime);
+ 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;
@@ -2728,7 +2947,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);
@@ -2744,8 +2963,8 @@ void OS_X11::process_xevents() {
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;
@@ -2759,808 +2978,879 @@ void OS_X11::process_xevents() {
*/
}
- input->flush_accumulated_events();
+ InputFilter::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) {
+void DisplayServerX11::_update_context(WindowData &wd) {
+ XClassHint *classHint = XAllocClassHint();
- return p_internal_clipboard;
- };
+ if (classHint) {
- 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) {
+ 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;
}
+void DisplayServerX11::set_context(Context p_context) {
-String OS_X11::get_clipboard() const {
+ _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;
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ _update_context(E->get());
+ }
}
-
-String OS_X11::get_name() const {
-
- return "X11";
+void DisplayServerX11::set_native_icon(const String &p_filename) {
+ WARN_PRINT("Native icon not supported by this display server.");
}
-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;
+bool g_set_icon_error = false;
+int set_icon_errorhandler(Display *dpy, XErrorEvent *ev) {
+ g_set_icon_error = true;
+ return 0;
}
-bool OS_X11::_check_internal_feature_support(const String &p_feature) {
+void DisplayServerX11::set_icon(const Ref<Image> &p_icon) {
+ _THREAD_SAFE_METHOD_
- return p_feature == "pc";
-}
+ WindowData &wd = windows[MAIN_WINDOW_ID];
-String OS_X11::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 ".";
- }
-}
+ int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&set_icon_errorhandler);
-String OS_X11::get_data_path() const {
+ Atom net_wm_icon = XInternAtom(x11_display, "_NET_WM_ICON", False);
- 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();
- }
-}
+ if (p_icon.is_valid()) {
+ Ref<Image> img = p_icon->duplicate();
+ img->convert(Image::FORMAT_RGBA8);
-String OS_X11::get_cache_path() const {
+ while (true) {
+ int w = img->get_width();
+ int h = img->get_height();
- 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();
- }
-}
+ if (g_set_icon_error) {
+ g_set_icon_error = false;
-String OS_X11::get_system_dir(SystemDir p_dir) const {
+ WARN_PRINT("Icon too large, attempting to resize icon.");
- String xdgparam;
+ 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;
+ }
- switch (p_dir) {
- case SYSTEM_DIR_DESKTOP: {
+ w = new_width;
+ h = new_height;
- xdgparam = "DESKTOP";
- } break;
- case SYSTEM_DIR_DCIM: {
+ if (!w || !h) {
+ WARN_PRINT("Unable to set icon.");
+ break;
+ }
- xdgparam = "PICTURES";
+ img->resize(w, h, Image::INTERPOLATE_CUBIC);
+ }
- } break;
- case SYSTEM_DIR_DOCUMENTS: {
+ // We're using long to have wordsize (32Bit build -> 32 Bits, 64 Bit build -> 64 Bits
+ Vector<long> pd;
- xdgparam = "DOCUMENTS";
+ pd.resize(2 + w * h);
- } break;
- case SYSTEM_DIR_DOWNLOADS: {
+ pd.write[0] = w;
+ pd.write[1] = h;
- xdgparam = "DOWNLOAD";
+ const uint8_t *r = img->get_data().ptr();
- } break;
- case SYSTEM_DIR_MOVIES: {
+ long *wr = &pd.write[2];
+ uint8_t const *pr = r;
- xdgparam = "VIDEOS";
+ 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;
+ }
- } break;
- case SYSTEM_DIR_MUSIC: {
+ XChangeProperty(x11_display, wd.x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size());
- xdgparam = "MUSIC";
+ if (!g_set_icon_error)
+ break;
+ }
+ } else {
+ XDeleteProperty(x11_display, wd.x11_window, net_wm_icon);
+ }
- } break;
- case SYSTEM_DIR_PICTURES: {
+ XFlush(x11_display);
+ XSetErrorHandler(oldHandler);
+}
- xdgparam = "PICTURES";
+Vector<String> DisplayServerX11::get_rendering_drivers_func() {
+ Vector<String> drivers;
- } break;
- case SYSTEM_DIR_RINGTONES: {
+#ifdef VULKAN_ENABLED
+ drivers.push_back("vulkan");
+#endif
+#ifdef OPENGL_ENABLED
+ drivers.push_back("opengl");
+#endif
- xdgparam = "MUSIC";
+ return drivers;
+}
- } break;
- }
+DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- 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();
+ 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) {
- XEvent xev;
- Atom net_active_window = XInternAtom(x11_display, "_NET_ACTIVE_WINDOW", False);
+ //Create window
- 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;
+ long visualMask = VisualScreenMask;
+ int numberOfVisuals;
+ XVisualInfo vInfoTemplate = {};
+ vInfoTemplate.screen = DefaultScreen(x11_display);
+ XVisualInfo *visualInfo = XGetVisualInfo(x11_display, visualMask, &vInfoTemplate, &numberOfVisuals);
- XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
- XFlush(x11_display);
-}
+ 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;
-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);
+ {
- image = texture->get_data();
+ if (p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT) {
- ERR_FAIL_COND(!image.is_valid());
+ XSizeHints *xsh;
+ xsh = XAllocSizeHints();
- // 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;
+ 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;
- cursor_image->version = 1;
- cursor_image->size = size;
- cursor_image->xhot = p_hotspot.x;
- cursor_image->yhot = p_hotspot.y;
+ XSetWMNormalHints(x11_display, wd.x11_window, xsh);
+ XFree(xsh);
+ }
- // allocate memory to contain the whole file
- cursor_image->pixels = (XcursorPixel *)memalloc(size);
+ bool make_utility = false;
- 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 (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);
- 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);
+ make_utility = true;
+ }
+ if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
+ make_utility = true;
}
- *(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32();
- }
+ 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
- ERR_FAIL_COND(cursor_image->pixels == NULL);
+ Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
+ Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
- // Save it for a further usage
- cursors[p_shape] = XcursorImageLoadCursor(x11_display, cursor_image);
+ XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
- Vector<Variant> params;
- params.push_back(p_cursor);
- params.push_back(p_hotspot);
- cursors_cache.insert(p_shape, params);
+ 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);
- if (p_shape == current_cursor) {
- if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
- XDefineCursor(x11_display, x11_window, cursors[p_shape]);
- }
- }
+ 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;
- 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]);
+ 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);
+
+ XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
+ }
}
- CursorShape c = current_cursor;
- current_cursor = CURSOR_MAX;
- set_cursor_shape(c);
+ if (id != MAIN_WINDOW_ID) {
- cursors_cache.erase(p_shape);
- }
-}
+ 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*/
-}
+ 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
-void OS_X11::alert(const String &p_alert, const String &p_title) {
- const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" };
+ //set_class_hint(x11_display, wd.x11_window);
+ XFlush(x11_display);
- String path = get_environment("PATH");
- Vector<String> path_elems = path.split(":", false);
- String program;
+ XSync(x11_display, False);
+ //XSetErrorHandler(oldHandler);
- 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]);
+ XFree(visualInfo);
+ }
- if (FileAccess::exists(tested_path)) {
- program = tested_path;
- break;
- }
- }
+ WindowData &wd = windows[id];
- if (program.length())
- break;
- }
+ window_set_mode(p_mode, id);
- List<String> args;
+ //sync size
+ {
+ XWindowAttributes xwa;
- 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);
- }
+ XSync(x11_display, False);
+ XGetWindowAttributes(x11_display, wd.x11_window, &xwa);
- if (program.ends_with("kdialog")) {
- args.push_back("--error");
- args.push_back(p_alert);
- args.push_back("--title");
- args.push_back(p_title);
- }
+ wd.position.x = xwa.x;
+ wd.position.y = xwa.y;
+ wd.size.width = xwa.width;
+ wd.size.height = xwa.height;
- 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");
+ print_line("DisplayServer::_create_window " + itos(id) + " want rect: " + p_rect + " got rect " + Rect2i(xwa.x, xwa.y, xwa.width, xwa.height));
}
- if (program.ends_with("xmessage")) {
- args.push_back("-center");
- args.push_back("-title");
- args.push_back(p_title);
- args.push_back(p_alert);
- }
+ //set cursor
+ if (cursors[current_cursor] != None) {
- if (program.length()) {
- execute(program, args, true);
- } else {
- print_line(p_alert);
+ XDefineCursor(x11_display, wd.x11_window, cursors[current_cursor]);
}
+ return id;
}
-bool g_set_icon_error = false;
-int set_icon_errorhandler(Display *dpy, XErrorEvent *ev) {
- g_set_icon_error = true;
- return 0;
-}
+DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
-void OS_X11::set_icon(const Ref<Image> &p_icon) {
- int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&set_icon_errorhandler);
+ InputFilter::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
- Atom net_wm_icon = XInternAtom(x11_display, "_NET_WM_ICON", False);
+ r_error = OK;
- if (p_icon.is_valid()) {
- Ref<Image> img = p_icon->duplicate();
- img->convert(Image::FORMAT_RGBA8);
+ current_cursor = CURSOR_ARROW;
+ mouse_mode = MOUSE_MODE_VISIBLE;
- while (true) {
- int w = img->get_width();
- int h = img->get_height();
+ for (int i = 0; i < CURSOR_MAX; i++) {
- if (g_set_icon_error) {
- g_set_icon_error = false;
+ 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) {
- long *wr = &pd.write[2];
- uint8_t const *pr = r;
+#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
+ }
- 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;
+ if (modifiers == nullptr) {
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ WARN_PRINT("IME is disabled");
+ }
+ XSetLocaleModifiers("@im=none");
+ WARN_PRINT("Error setting locale modifiers");
+ }
+
+ 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 (!_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;
+ }
- if (!g_set_icon_error)
- break;
- }
+ xim = XOpenIM(x11_display, nullptr, nullptr, nullptr);
+
+ if (xim == nullptr) {
+ WARN_PRINT("XOpenIM failed");
+ xim_style = 0L;
} else {
- XDeleteProperty(x11_display, x11_window, net_wm_icon);
- }
+ ::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");
+ }
- XFlush(x11_display);
- XSetErrorHandler(oldHandler);
-}
+ ::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");
+ }
-void OS_X11::force_process_input() {
- process_xevents(); // get rid of pending events
-#ifdef JOYDEV_ENABLED
- joypad->process_joypads();
-#endif
-}
+ if (xim_styles) {
+ xim_style = 0L;
+ for (int i = 0; i < xim_styles->count_styles; i++) {
-void OS_X11::run() {
+ if (xim_styles->supported_styles[i] ==
+ (XIMPreeditNothing | XIMStatusNothing)) {
- force_quit = false;
+ xim_style = xim_styles->supported_styles[i];
+ break;
+ }
+ }
- if (!main_loop)
- return;
+ XFree(xim_styles);
+ }
+ XFree(imvalret);
+ }
- main_loop->init();
+ /* 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);
- //uint64_t last_ticks=get_ticks_usec();
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!
+ //TODO - do Vulkan and GLES2 support checks, driver selection and fallback
+ rendering_driver = p_rendering_driver;
- //int frames=0;
- //uint64_t frame=0;
+#ifndef _MSC_VER
+#warning Forcing vulkan rendering driver because OpenGL not implemented yet
+#endif
+ rendering_driver = "vulkan";
- while (!force_quit) {
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
- process_xevents(); // get rid of pending events
-#ifdef JOYDEV_ENABLED
- joypad->process_joypads();
+ 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")) {
-bool OS_X11::is_joy_known(int p_device) {
- return input->is_joy_mapped(p_device);
-}
+ print_verbose("Optirun/primusrun detected. Skipping GPU detection");
+ use_prime = 0;
+ }
-String OS_X11::get_joy_guid(int p_device) const {
- return input->get_joy_guid_remapped(p_device);
-}
+ if (getenv("LD_LIBRARY_PATH")) {
+ String ld_library_path(getenv("LD_LIBRARY_PATH"));
+ Vector<String> libraries = ld_library_path.split(":");
-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
-}
+ for (int i = 0; i < libraries.size(); ++i) {
+ if (FileAccess::exists(libraries[i] + "/libGL.so.1") ||
+ FileAccess::exists(libraries[i] + "/libGL.so")) {
-void OS_X11::set_context(int p_context) {
+ print_verbose("Custom libGL override detected. Skipping GPU detection");
+ use_prime = 0;
+ }
+ }
+ }
- XClassHint *classHint = XAllocClassHint();
+ if (use_prime == -1) {
+ print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic.");
+ use_prime = detect_prime();
+ }
- if (classHint) {
+ 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);
+ }
+ }
- 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;
+ 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 = 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
- classHint->res_class = class_str.ptrw();
- classHint->res_name = name_str.ptrw();
-
- XSetClassHint(x11_display, x11_window, classHint);
- XFree(classHint);
+ WindowID main_window = _create_window(p_mode, p_flags, Rect2i(Point2(), p_resolution));
+ for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
+ if (p_flags & (1 << i)) {
+ window_set_flag(WindowFlags(i), true, main_window);
+ }
}
-}
-void OS_X11::disable_crash_handler() {
- crash_handler.disable();
-}
+//create RenderingDevice if used
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
-bool OS_X11::is_disable_crash_handler() const {
- return crash_handler.is_disabled();
-}
+ //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;
}
+DisplayServerX11::~DisplayServerX11() {
-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;
+ //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") {
- 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 (rendering_device_vulkan) {
+ rendering_device_vulkan->finalize();
+ memdelete(rendering_device_vulkan);
}
+
+ 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..113e504e9b
--- /dev/null
+++ b/platform/linuxbsd/display_server_x11.h
@@ -0,0 +1,351 @@
+/*************************************************************************/
+/* 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_filter.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, Vector3> pen_devices;
+ XIEventMask all_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(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..53e3ce8f85 100644
--- a/platform/x11/export/export.cpp
+++ b/platform/linuxbsd/export/export.cpp
@@ -32,17 +32,17 @@
#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);
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..710ba3ca40 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,11 @@
#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, "");
diff --git a/platform/x11/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index a9fe7275c2..381eb909ba 100644
--- a/platform/x11/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -54,7 +54,7 @@ JoypadLinux::Joypad::Joypad() {
dpad = 0;
devpath = "";
for (int i = 0; i < MAX_ABS; i++) {
- abs_info[i] = NULL;
+ abs_info[i] = nullptr;
}
}
@@ -71,7 +71,7 @@ void JoypadLinux::Joypad::reset() {
dpad = 0;
fd = -1;
- InputDefault::JoyAxis jx;
+ InputFilter::JoyAxis jx;
jx.min = -1;
jx.value = 0.0f;
for (int i = 0; i < MAX_ABS; i++) {
@@ -80,7 +80,7 @@ void JoypadLinux::Joypad::reset() {
}
}
-JoypadLinux::JoypadLinux(InputDefault *in) {
+JoypadLinux::JoypadLinux(InputFilter *in) {
exit_udev = false;
input = in;
joy_thread = Thread::create(joy_thread_func, this);
@@ -146,9 +146,9 @@ 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);
@@ -163,7 +163,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)) {
@@ -299,7 +299,7 @@ void JoypadLinux::setup_joypad_properties(int p_id) {
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;
}
}
}
@@ -340,7 +340,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;
}
@@ -433,11 +436,11 @@ 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 {
+InputFilter::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;
+ InputFilter::JoyAxis jx;
if (min < 0) {
jx.min = -1;
@@ -489,11 +492,11 @@ void JoypadLinux::process_joypads() {
case ABS_HAT0X:
if (ev.value != 0) {
if (ev.value < 0)
- joy->dpad |= InputDefault::HAT_MASK_LEFT;
+ joy->dpad |= InputFilter::HAT_MASK_LEFT;
else
- joy->dpad |= InputDefault::HAT_MASK_RIGHT;
+ joy->dpad |= InputFilter::HAT_MASK_RIGHT;
} else
- joy->dpad &= ~(InputDefault::HAT_MASK_LEFT | InputDefault::HAT_MASK_RIGHT);
+ joy->dpad &= ~(InputFilter::HAT_MASK_LEFT | InputFilter::HAT_MASK_RIGHT);
input->joy_hat(i, joy->dpad);
break;
@@ -501,11 +504,11 @@ void JoypadLinux::process_joypads() {
case ABS_HAT0Y:
if (ev.value != 0) {
if (ev.value < 0)
- joy->dpad |= InputDefault::HAT_MASK_UP;
+ joy->dpad |= InputFilter::HAT_MASK_UP;
else
- joy->dpad |= InputDefault::HAT_MASK_DOWN;
+ joy->dpad |= InputFilter::HAT_MASK_DOWN;
} else
- joy->dpad &= ~(InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_DOWN);
+ joy->dpad &= ~(InputFilter::HAT_MASK_UP | InputFilter::HAT_MASK_DOWN);
input->joy_hat(i, joy->dpad);
break;
@@ -514,7 +517,7 @@ void JoypadLinux::process_joypads() {
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);
+ InputFilter::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..1d2ed5bbc1 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_filter.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(InputFilter *in);
~JoypadLinux();
void process_joypads();
@@ -53,7 +53,7 @@ private:
};
struct Joypad {
- InputDefault::JoyAxis curr_axis[MAX_ABS];
+ InputFilter::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;
+ InputFilter *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;
+ InputFilter::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..78bd2b71a0 100644
--- a/platform/x11/key_mapping_x11.cpp
+++ b/platform/linuxbsd/key_mapping_x11.cpp
diff --git a/platform/x11/key_mapping_x11.h b/platform/linuxbsd/key_mapping_x11.h
index 10db43bcc4..10db43bcc4 100644
--- a/platform/x11/key_mapping_x11.h
+++ b/platform/linuxbsd/key_mapping_x11.h
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..5b9a25bd8b
--- /dev/null
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -0,0 +1,381 @@
+/*************************************************************************/
+/* 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(InputFilter::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..100cb53ba3
--- /dev/null
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -0,0 +1,106 @@
+/*************************************************************************/
+/* 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_filter.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..1798a7026e 100644
--- a/platform/x11/vulkan_context_x11.cpp
+++ b/platform/linuxbsd/vulkan_context_x11.cpp
@@ -35,19 +35,19 @@ 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..6e144ab2d9 100644
--- a/platform/x11/vulkan_context_x11.h
+++ b/platform/linuxbsd/vulkan_context_x11.h
@@ -39,7 +39,7 @@ 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 6e73c2203a..7e436c5e36 100644
--- a/platform/osx/context_gl_osx.h
+++ b/platform/osx/context_gl_osx.h
@@ -72,4 +72,4 @@ public:
};
#endif
-#endif \ No newline at end of file
+#endif
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/display_server_osx.h b/platform/osx/display_server_osx.h
new file mode 100644
index 0000000000..86ceb51763
--- /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_filter.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..074fc3be0d
--- /dev/null
+++ b/platform/osx/display_server_osx.mm
@@ -0,0 +1,3595 @@
+/*************************************************************************/
+/* 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
+
+#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;
+ InputFilter::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]);
+
+ InputFilter::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 {
+ ERR_FAIL_COND_V(!DS_OSX->windows.has(window_id), YES);
+ DS_OSX->_send_window_event(DS_OSX->windows[window_id], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
+ return NO;
+}
+
+- (void)windowWillClose:(NSNotification *)notification {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ 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 {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ 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 {
+ DS_OSX->_release_pressed_events();
+}
+
+- (void)windowDidBecomeKey:(NSNotification *)notification {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ 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);
+ InputFilter::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 {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ 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 {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ 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 {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ 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);
+ }
+
+ InputFilter::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(InputFilter::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);
+
+ InputFilter::get_singleton()->set_mouse_position(wd.mouse_pos);
+ InputFilter::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);
+
+ InputFilter::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);
+
+ InputFilter::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);
+
+ InputFilter::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));
+
+ InputFilter::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;
+ InputFilter::get_singleton()->accumulate_input_event(ev);
+}
+
+void DisplayServerOSX::_release_pressed_events() {
+ _THREAD_SAFE_METHOD_
+ if (InputFilter::get_singleton()) {
+ InputFilter::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();
+ InputFilter::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) {
+ InputFilter::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
+
+ WindowID main_window = _create_window(p_mode, Rect2i(Point2i(), 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..c2df9c7082 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -402,7 +402,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);
@@ -435,7 +435,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);
@@ -476,7 +476,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)) {
@@ -507,11 +507,11 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
Error err = OK;
String tmp_app_path_name = "";
zlib_filefunc_def io2 = io;
- FileAccess *dst_f = NULL;
+ FileAccess *dst_f = nullptr;
io2.opaque = &dst_f;
- zipFile dst_pkg_zip = NULL;
+ zipFile dst_pkg_zip = nullptr;
- 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
@@ -539,7 +539,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
}
} else {
// Open our destination zip file
- dst_pkg_zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
+ dst_pkg_zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2);
if (!dst_pkg_zip) {
err = ERR_CANT_CREATE;
}
@@ -555,7 +555,7 @@ Error EditorExportPlatformOSX::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;
@@ -672,11 +672,11 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
zipOpenNewFileInZip(dst_pkg_zip,
file.utf8().get_data(),
&fi,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
Z_DEFLATED,
Z_DEFAULT_COMPRESSION);
@@ -754,12 +754,12 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
if (err == OK) {
zipOpenNewFileInZip(dst_pkg_zip,
(pkg_name + ".app/Contents/Resources/" + pkg_name + ".pck").utf8().get_data(),
- NULL,
- NULL,
+ nullptr,
+ nullptr,
0,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
Z_DEFLATED,
Z_DEFAULT_COMPRESSION);
@@ -791,12 +791,12 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
zipOpenNewFileInZip(dst_pkg_zip,
(pkg_name + ".app/Contents/Frameworks/").plus_file(shared_objects[i].path.get_file()).utf8().get_data(),
- NULL,
- NULL,
+ nullptr,
+ nullptr,
0,
- NULL,
+ nullptr,
0,
- NULL,
+ nullptr,
Z_DEFLATED,
Z_DEFAULT_COMPRESSION);
@@ -811,7 +811,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
}
if (dst_pkg_zip) {
- zipClose(dst_pkg_zip, NULL);
+ zipClose(dst_pkg_zip, nullptr);
}
return err;
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/joypad_osx.cpp b/platform/osx/joypad_osx.cpp
index e9f46fb5a4..643acd8944 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);
@@ -105,7 +105,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:
@@ -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)) {
@@ -330,7 +330,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);
@@ -395,38 +395,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 = InputFilter::HAT_MASK_CENTER;
if (range == 4) {
value *= 2;
}
switch (value) {
case 0:
- hat_value = InputDefault::HAT_MASK_UP;
+ hat_value = InputFilter::HAT_MASK_UP;
break;
case 1:
- hat_value = InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_RIGHT;
+ hat_value = InputFilter::HAT_MASK_UP | InputFilter::HAT_MASK_RIGHT;
break;
case 2:
- hat_value = InputDefault::HAT_MASK_RIGHT;
+ hat_value = InputFilter::HAT_MASK_RIGHT;
break;
case 3:
- hat_value = InputDefault::HAT_MASK_DOWN | InputDefault::HAT_MASK_RIGHT;
+ hat_value = InputFilter::HAT_MASK_DOWN | InputFilter::HAT_MASK_RIGHT;
break;
case 4:
- hat_value = InputDefault::HAT_MASK_DOWN;
+ hat_value = InputFilter::HAT_MASK_DOWN;
break;
case 5:
- hat_value = InputDefault::HAT_MASK_DOWN | InputDefault::HAT_MASK_LEFT;
+ hat_value = InputFilter::HAT_MASK_DOWN | InputFilter::HAT_MASK_LEFT;
break;
case 6:
- hat_value = InputDefault::HAT_MASK_LEFT;
+ hat_value = InputFilter::HAT_MASK_LEFT;
break;
case 7:
- hat_value = InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_LEFT;
+ hat_value = InputFilter::HAT_MASK_UP | InputFilter::HAT_MASK_LEFT;
break;
default:
- hat_value = InputDefault::HAT_MASK_CENTER;
+ hat_value = InputFilter::HAT_MASK_CENTER;
break;
}
return hat_value;
@@ -438,8 +438,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 InputFilter::JoyAxis axis_correct(int p_value, int p_min, int p_max) {
+ InputFilter::JoyAxis jx;
if (p_min < 0) {
jx.min = -1;
if (p_value < 0) {
@@ -531,7 +531,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) };
@@ -562,8 +562,8 @@ void JoypadOSX::config_hid_manager(CFArrayRef p_matching_array) const {
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 +571,9 @@ void JoypadOSX::config_hid_manager(CFArrayRef p_matching_array) const {
}
}
-JoypadOSX::JoypadOSX() {
+JoypadOSX::JoypadOSX(InputFilter *in) {
self = this;
- input = (InputDefault *)Input::get_singleton();
+ input = in;
int okay = 1;
const void *vals[] = {
@@ -582,7 +582,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 +592,7 @@ JoypadOSX::JoypadOSX() {
if (array) {
hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
- if (hid_manager != NULL) {
+ if (hid_manager != nullptr) {
config_hid_manager(array);
}
CFRelease(array);
@@ -608,5 +608,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..62027c6a30 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_filter.h"
struct rec_element {
IOHIDElementRef ref;
@@ -94,7 +94,7 @@ class JoypadOSX {
};
private:
- InputDefault *input;
+ InputFilter *input;
IOHIDManagerRef hid_manager;
Vector<joypad> device_list;
@@ -118,7 +118,7 @@ public:
void _device_added(IOReturn p_res, IOHIDDeviceRef p_device);
void _device_removed(IOReturn p_res, IOHIDDeviceRef p_device);
- JoypadOSX();
+ JoypadOSX(InputFilter *in);
~JoypadOSX();
};
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index 3140d9bac4..d2c67cff9f 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_filter.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 4c70beee00..49cb056c9f 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -30,1668 +30,21 @@
#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;
-
- 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 {
-
- // 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];
-
- 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];
- }
- }
-}
-
-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;
-}
-
-String OS_OSX::get_unique_id() const {
-
- static String serial_number;
-
- if (serial_number.empty()) {
- io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
- CFStringRef serialNumberAsCFString = NULL;
- if (platformExpert) {
- serialNumberAsCFString = (CFStringRef)IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0);
- IOObjectRelease(platformExpert);
- }
-
- NSString *serialNumberAsNSString = nil;
- if (serialNumberAsCFString) {
- serialNumberAsNSString = [NSString stringWithString:(NSString *)serialNumberAsCFString];
- CFRelease(serialNumberAsCFString);
- }
-
- serial_number = [serialNumberAsNSString UTF8String];
- }
-
- 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);
- DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_USERDATA);
- 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;
-}
-
-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();
-
- return OK;
-}
-
-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);
- main_loop = NULL;
-}
-
-String OS_OSX::get_name() const {
+#include <mach-o/dyld.h>
+#include <os/log.h>
- return "OSX";
-}
+/*************************************************************************/
+/* OSXTerminalLogger */
+/*************************************************************************/
-#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) {
@@ -1747,333 +100,101 @@ public:
}
};
-#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];
-}
+/*************************************************************************/
+/* OS_OSX */
+/*************************************************************************/
-Error OS_OSX::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
+String OS_OSX::get_unique_id() const {
+ static String serial_number;
- String path = p_path;
+ if (serial_number.empty()) {
+ io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
+ CFStringRef serialNumberAsCFString = NULL;
+ if (platformExpert) {
+ serialNumberAsCFString = (CFStringRef)IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0);
+ IOObjectRelease(platformExpert);
+ }
- if (!FileAccess::exists(path)) {
- //this code exists so gdnative can load .dylib files from within the executable path
- path = get_executable_path().get_base_dir().plus_file(p_path.get_file());
- }
+ NSString *serialNumberAsNSString = nil;
+ if (serialNumberAsCFString) {
+ serialNumberAsNSString = [NSString stringWithString:(NSString *)serialNumberAsCFString];
+ CFRelease(serialNumberAsCFString);
+ }
- if (!FileAccess::exists(path)) {
- //this code exists so gdnative can load .dylib files from a standard macOS location
- path = get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(p_path.get_file());
+ serial_number = [serialNumberAsNSString UTF8String];
}
- p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
- ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + ".");
- return OK;
+ return serial_number;
}
-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: {
- };
- }
- }
+void OS_OSX::initialize_core() {
+ OS_Unix::initialize_core();
- cursor_shape = p_shape;
+ DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_RESOURCES);
+ DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_USERDATA);
+ DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_FILESYSTEM);
}
-OS::CursorShape OS_OSX::get_cursor_shape() const {
-
- return cursor_shape;
+void OS_OSX::initialize_joypads() {
+ joypad_osx = memnew(JoypadOSX(InputFilter::get_singleton()));
}
-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::initialize() {
+ crash_handler.initialize();
-void OS_OSX::set_mouse_grab(bool p_grab) {
+ initialize_core();
+ //ensure_user_data_dir();
}
-bool OS_OSX::is_mouse_grab_enabled() const {
-
- return mouse_grab;
-}
+void OS_OSX::finalize() {
-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);
- }
-}
+#ifdef COREMIDI_ENABLED
+ midi_driver.close();
+#endif
-void OS_OSX::update_real_mouse_position() {
+ delete_main_loop();
- get_mouse_pos([window_object mouseLocationOutsideOfEventStream], [window_view backingScaleFactor]);
- input->set_mouse_position(Point2(mouse_x, mouse_y));
+ memdelete(joypad_osx);
}
-Point2 OS_OSX::get_mouse_position() const {
-
- return Vector2(mouse_x, mouse_y);
+void OS_OSX::set_main_loop(MainLoop *p_main_loop) {
+ main_loop = p_main_loop;
}
-int OS_OSX::get_mouse_button_state() const {
- return button_mask;
+void OS_OSX::delete_main_loop() {
+ if (!main_loop)
+ return;
+ memdelete(main_loop);
+ main_loop = NULL;
}
-void OS_OSX::set_window_title(const String &p_title) {
- title = p_title;
-
- [window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]];
+String OS_OSX::get_name() const {
+ return "macOS";
}
-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];
-}
+Error OS_OSX::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
+ String path = p_path;
-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;
+ if (!FileAccess::exists(path)) {
+ //this code exists so gdnative can load .dylib files from within the executable path
+ path = get_executable_path().get_base_dir().plus_file(p_path.get_file());
}
- NSImage *nsimg = [[[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())] autorelease];
- ERR_FAIL_COND(nsimg == nil);
- [nsimg addRepresentation:imgrep];
+ if (!FileAccess::exists(path)) {
+ //this code exists so gdnative can load .dylib files from a standard macOS location
+ path = get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(p_path.get_file());
+ }
- [NSApp setApplicationIconImage:nsimg];
+ p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
+ ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + ".");
+ return OK;
}
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 +205,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 +213,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 +223,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 +236,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;
@@ -2166,62 +282,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 +292,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 +309,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 +317,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 +332,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 +348,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..09a5494ae8 100644
--- a/platform/osx/vulkan_context_osx.h
+++ b/platform/osx/vulkan_context_osx.h
@@ -39,7 +39,7 @@ 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..320401cdcb 100644
--- a/platform/osx/vulkan_context_osx.mm
+++ b/platform/osx/vulkan_context_osx.mm
@@ -35,7 +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;
@@ -45,8 +45,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/os_server.cpp b/platform/server/os_server.cpp
index 3257ec261c..0fda0663a2 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"
@@ -74,14 +74,14 @@ Error OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int
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);
@@ -99,10 +99,10 @@ 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);
@@ -163,7 +163,7 @@ 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) {
@@ -289,7 +289,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();
diff --git a/platform/server/os_server.h b/platform/server/os_server.h
index 7584293722..62028b26e4 100644
--- a/platform/server/os_server.h
+++ b/platform/server/os_server.h
@@ -31,24 +31,24 @@
#ifndef OS_SERVER_H
#define OS_SERVER_H
+#include "core/input/input_filter.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..d3870b0b6c 100644
--- a/platform/uwp/app.cpp
+++ b/platform/uwp/app.cpp
@@ -507,12 +507,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;
@@ -558,7 +558,7 @@ char **App::get_command_line(unsigned int *out_argc) {
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);
@@ -583,14 +583,14 @@ char **App::get_command_line(unsigned int *out_argc) {
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/context_egl_uwp.cpp b/platform/uwp/context_egl_uwp.cpp
index 7ac9489bb4..bc8ca2e36c 100644
--- a/platform/uwp/context_egl_uwp.cpp
+++ b/platform/uwp/context_egl_uwp.cpp
@@ -155,7 +155,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");
}
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..06bf738dc1 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,7 +79,7 @@ static const char *uwp_device_capabilities[] = {
"microphone",
"proximity",
"webcam",
- NULL
+ nullptr
};
class AppxPackager {
@@ -478,7 +478,7 @@ 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;
@@ -641,7 +641,7 @@ void AppxPackager::finish() {
package->close();
memdelete(package);
- package = NULL;
+ package = nullptr;
}
AppxPackager::AppxPackager() {}
@@ -670,7 +670,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
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;
@@ -726,7 +726,7 @@ 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;
@@ -876,22 +876,22 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
Vector<uint8_t> _get_image_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
Vector<uint8_t> data;
- StreamTexture *image = NULL;
+ StreamTexture *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<StreamTexture>(((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<StreamTexture>(((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<StreamTexture>(((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<StreamTexture>(((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<StreamTexture>(((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<StreamTexture>(((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<StreamTexture>(((Object *)p_preset->get("images/splash_screen")));
} else {
ERR_PRINT("Unable to load logo");
}
@@ -961,7 +961,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) {
@@ -1251,7 +1251,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)) {
@@ -1283,7 +1283,7 @@ public:
// 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;
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.h b/platform/uwp/joypad_uwp.h
index f2a721f3dd..054b67ddc8 100644
--- a/platform/uwp/joypad_uwp.h
+++ b/platform/uwp/joypad_uwp.h
@@ -31,7 +31,7 @@
#ifndef JOYPAD_UWP_H
#define JOYPAD_UWP_H
-#include "main/input_default.h"
+#include "core/input/input_filter.h"
ref class JoypadUWP sealed {
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index 4ddb5463d0..f5e989b370 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>
@@ -180,7 +180,7 @@ void OS_UWP::screen_size_changed() {
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 +193,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;
}
@@ -254,13 +254,13 @@ Error OS_UWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
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);
@@ -333,7 +333,7 @@ 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) {
@@ -347,10 +347,10 @@ 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);
@@ -773,9 +773,9 @@ void OS_UWP::hide_virtual_keyboard() {
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);
@@ -869,14 +869,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..ad0efa1d03 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_filter.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>
@@ -89,7 +88,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 +202,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;
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..5a36b5546d 100644
--- a/platform/windows/context_gl_windows.cpp
+++ b/platform/windows/context_gl_windows.cpp
@@ -52,7 +52,7 @@ typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int
void ContextGL_Windows::release_current() {
- wglMakeCurrent(hDC, NULL);
+ wglMakeCurrent(hDC, nullptr);
}
void ContextGL_Windows::make_current() {
@@ -185,10 +185,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 +199,7 @@ Error ContextGL_Windows::initialize() {
wglDeleteContext(hRC);
return ERR_CANT_CREATE; // Return false
}
- wglMakeCurrent(hDC, NULL);
+ wglMakeCurrent(hDC, nullptr);
wglDeleteContext(hRC);
hRC = new_hRC;
diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp
index 6145751e00..1d9eba22d8 100644
--- a/platform/windows/crash_handler_windows.cpp
+++ b/platform/windows/crash_handler_windows.cpp
@@ -121,7 +121,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 +131,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 +193,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/detect.py b/platform/windows/detect.py
index 3ab0d38a6a..9f79e92dcb 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -1,6 +1,9 @@
import methods
import os
+# To match other platforms
+STACK_SIZE = 8388608
+
def is_active():
return True
@@ -11,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.
@@ -23,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
@@ -45,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]):
@@ -97,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
@@ -111,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"""
@@ -135,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])
@@ -243,21 +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)])
+
def configure_mingw(env):
# Workaround for MinGW. See:
@@ -266,124 +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)])
## 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"]:
@@ -391,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
@@ -403,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..ebe9a7d27a
--- /dev/null
+++ b/platform/windows/display_server_windows.cpp
@@ -0,0 +1,3007 @@
+/*************************************************************************/
+/* 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>
+
+#ifndef WM_POINTERUPDATE
+#define WM_POINTERUPDATE 0x0245
+#endif
+
+#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
+
+ 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;
+ InputFilter::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();
+ InputFilter::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));
+
+ InputFilter::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());
+
+ InputFilter::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) {
+
+ 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()) {
+ 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);
+ }
+ }
+}
+
+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
+ InputFilter::get_singleton()->release_pressed_events();
+ _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_OUT);
+ windows[window_id].window_focused = false;
+ alt_mem = false;
+ };
+
+ 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_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);
+ InputFilter::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())
+ InputFilter::get_singleton()->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 (InputFilter::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_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(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);
+ }
+
+ InputFilter::get_singleton()->set_mouse_position(mm->get_position());
+ mm->set_speed(InputFilter::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) {
+ InputFilter::get_singleton()->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 (InputFilter::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);
+
+ 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);
+ }
+
+ InputFilter::get_singleton()->set_mouse_position(mm->get_position());
+ mm->set_speed(InputFilter::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)
+ InputFilter::get_singleton()->accumulate_input_event(mm);
+
+ } break;
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ if (InputFilter::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());
+
+ InputFilter::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);
+ InputFilter::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: {
+ InputFilter::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: {
+ if ((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);
+
+ InputFilter::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))));
+
+ InputFilter::get_singleton()->accumulate_input_event(k);
+
+ } break;
+ }
+ }
+
+ key_event_pos = 0;
+}
+
+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);
+
+ // 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;
+}
+
+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) {
+
+ //Note: Functions for pen input, available on Windows 8+
+ HMODULE user32_lib = LoadLibraryW(L"user32.dll");
+ if (user32_lib) {
+ win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType");
+ win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo");
+ }
+
+ 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
+ WindowID main_window = _create_window(p_mode, 0, Rect2i(Point2i(), 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);
+ InputFilter::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 defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+
+ if (rendering_device_vulkan) {
+ rendering_device_vulkan->finalize();
+ memdelete(rendering_device_vulkan);
+ }
+
+ if (context_vulkan)
+ memdelete(context_vulkan);
+ }
+#endif
+
+ if (user_proc) {
+ SetWindowLongPtr(windows[MAIN_WINDOW_ID].hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);
+ };
+
+ if (windows.has(MAIN_WINDOW_ID)) {
+#ifdef VULKAN_ENABLED
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_destroy(MAIN_WINDOW_ID);
+ }
+#endif
+
+ DestroyWindow(windows[MAIN_WINDOW_ID].hWnd);
+ }
+}
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
new file mode 100644
index 0000000000..5cd240ffb0
--- /dev/null
+++ b/platform/windows/display_server_windows.h
@@ -0,0 +1,418 @@
+/*************************************************************************/
+/* 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_filter.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>
+
+#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 //POINTER_STRUCTURES
+
+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_
+
+ static GetPointerTypePtr win8p_GetPointerType;
+ static GetPointerPenInfoPtr win8p_GetPointerPenInfo;
+
+ 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;
+
+ 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 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..d63067587c 100644
--- a/platform/windows/export/export.cpp
+++ b/platform/windows/export/export.cpp
@@ -315,7 +315,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);
diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp
index dcc12b7649..2aa928c2a7 100644
--- a/platform/windows/godot_windows.cpp
+++ b/platform/windows/godot_windows.cpp
@@ -121,23 +121,23 @@ 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 +176,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 +202,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..437c3b733d 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -52,15 +52,15 @@ DWORD WINAPI _xinput_set_state(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration)
JoypadWindows::JoypadWindows() {
}
-JoypadWindows::JoypadWindows(InputDefault *_input, HWND *hwnd) {
+JoypadWindows::JoypadWindows(HWND *hwnd) {
- input = _input;
+ input = InputFilter::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);
}
@@ -105,10 +105,10 @@ 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);
@@ -130,7 +130,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;
@@ -157,7 +157,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;
@@ -436,46 +436,46 @@ 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 = InputFilter::HAT_MASK_CENTER;
}
if (p_dpad == 0) {
- dpad_val = InputDefault::HAT_MASK_UP;
+ dpad_val = InputFilter::HAT_MASK_UP;
} else if (p_dpad == 4500) {
- dpad_val = (InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_RIGHT);
+ dpad_val = (InputFilter::HAT_MASK_UP | InputFilter::HAT_MASK_RIGHT);
} else if (p_dpad == 9000) {
- dpad_val = InputDefault::HAT_MASK_RIGHT;
+ dpad_val = InputFilter::HAT_MASK_RIGHT;
} else if (p_dpad == 13500) {
- dpad_val = (InputDefault::HAT_MASK_RIGHT | InputDefault::HAT_MASK_DOWN);
+ dpad_val = (InputFilter::HAT_MASK_RIGHT | InputFilter::HAT_MASK_DOWN);
} else if (p_dpad == 18000) {
- dpad_val = InputDefault::HAT_MASK_DOWN;
+ dpad_val = InputFilter::HAT_MASK_DOWN;
} else if (p_dpad == 22500) {
- dpad_val = (InputDefault::HAT_MASK_DOWN | InputDefault::HAT_MASK_LEFT);
+ dpad_val = (InputFilter::HAT_MASK_DOWN | InputFilter::HAT_MASK_LEFT);
} else if (p_dpad == 27000) {
- dpad_val = InputDefault::HAT_MASK_LEFT;
+ dpad_val = InputFilter::HAT_MASK_LEFT;
} else if (p_dpad == 31500) {
- dpad_val = (InputDefault::HAT_MASK_LEFT | InputDefault::HAT_MASK_UP);
+ dpad_val = (InputFilter::HAT_MASK_LEFT | InputFilter::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 {
+InputFilter::JoyAxis JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const {
- InputDefault::JoyAxis jx;
+ InputFilter::JoyAxis jx;
if (Math::abs(p_val) < MIN_JOY_AXIS) {
jx.min = p_trigger ? 0 : -1;
jx.value = 0.0f;
diff --git a/platform/windows/joypad_windows.h b/platform/windows/joypad_windows.h
index ab85bc60ac..0db789c335 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();
@@ -117,7 +117,7 @@ private:
HWND *hWnd;
HANDLE xinput_dll;
LPDIRECTINPUT8 dinput;
- InputDefault *input;
+ InputFilter *input;
int id_to_change;
int joypad_count;
@@ -141,7 +141,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;
+ InputFilter::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/os_windows.cpp b/platform/windows/os_windows.cpp
index a112f26ac4..0a67a591b7 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,12 @@ __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,8 +93,6 @@ static String format_error_message(DWORD id) {
}
#endif // DEBUG_ENABLED
-extern HINSTANCE godot_hinstance;
-
void RedirectIOToConsole() {
int hConHandle;
@@ -163,7 +129,7 @@ void RedirectIOToConsole() {
*stdout = *fp;
- setvbuf(stdout, NULL, _IONBF, 0);
+ setvbuf(stdout, nullptr, _IONBF, 0);
// redirect unbuffered STDIN to the console
@@ -175,7 +141,7 @@ void RedirectIOToConsole() {
*stdin = *fp;
- setvbuf(stdin, NULL, _IONBF, 0);
+ setvbuf(stdin, nullptr, _IONBF, 0);
// redirect unbuffered STDERR to the console
@@ -187,7 +153,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 +174,16 @@ 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,1381 +213,18 @@ void OS_Windows::initialize_core() {
process_map = memnew((Map<ProcessID, ProcessInfo>));
IP_Unix::make_default();
-
- cursor_shape = CURSOR_ARROW;
-}
-
-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;
+ main_loop = nullptr;
}
-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;
}
@@ -1642,39 +237,7 @@ 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() {
@@ -1685,576 +248,6 @@ void OS_Windows::finalize_core() {
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;
@@ -2270,14 +263,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,17 +299,6 @@ 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";
@@ -2448,253 +430,6 @@ 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;
- }
-
- 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);
-}
-
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) {
@@ -2755,7 +490,7 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments,
modstr.resize(cmdline.size());
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) {
@@ -2807,165 +542,20 @@ 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
@@ -2996,19 +586,9 @@ 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;
}
@@ -3068,101 +648,6 @@ 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)
@@ -3172,7 +657,7 @@ void OS_Windows::run() {
while (!force_quit) {
- process_events(); // get rid of pending events
+ DisplayServer::get_singleton()->process_events(); // get rid of pending events
if (Main::iteration())
break;
};
@@ -3254,7 +739,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);
@@ -3287,50 +772,6 @@ String OS_Windows::get_unique_id() const {
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 +785,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;
@@ -3379,36 +813,12 @@ Error OS_Windows::move_to_trash(const String &p_path) {
OS_Windows::OS_Windows(HINSTANCE _hInstance) {
- 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+
- HMODULE user32_lib = LoadLibraryW(L"user32.dll");
- if (user32_lib) {
- win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType");
- win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo");
- }
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 +827,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 6c3769c98c..6bdfc75ebb 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_filter.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,19 @@
#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;
-
- int video_driver_index;
#ifdef WASAPI_ENABLED
AudioDriverWASAPI driver_wasapi;
#endif
@@ -251,28 +86,19 @@ 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();
-
- void process_events();
- void process_key_events();
+ virtual String get_stdin_string(bool p_block);
struct ProcessInfo {
@@ -281,75 +107,7 @@ protected:
};
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 +116,8 @@ public:
virtual String get_name() const;
+ 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 +125,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 +138,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 +154,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..98aa21411f 100644
--- a/platform/windows/vulkan_context_win.cpp
+++ b/platform/windows/vulkan_context_win.cpp
@@ -35,19 +35,19 @@ 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..c9fea9369b 100644
--- a/platform/windows/vulkan_context_win.h
+++ b/platform/windows/vulkan_context_win.h
@@ -39,7 +39,7 @@ 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..884d95e082 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;
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 55d24d64a3..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