diff options
Diffstat (limited to 'platform')
112 files changed, 1695 insertions, 4804 deletions
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp index f6a0776017..bd194478d9 100644 --- a/platform/android/android_input_handler.cpp +++ b/platform/android/android_input_handler.cpp @@ -292,7 +292,7 @@ void AndroidInputHandler::_release_mouse_event_info(bool p_source_mouse_relative mouse_event_info.valid = false; } -void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative) { +void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative, float p_pressure, Vector2 p_tilt) { BitField<MouseButtonMask> event_buttons_mask = _android_button_mask_to_godot_button_mask(p_event_android_buttons_mask); switch (p_event_action) { case AMOTION_EVENT_ACTION_HOVER_MOVE: // hover move @@ -349,6 +349,8 @@ void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_an hover_prev_pos = p_event_pos; } ev->set_button_mask(event_buttons_mask); + ev->set_pressure(p_pressure); + ev->set_tilt(p_tilt); Input::get_singleton()->parse_input_event(ev); } break; diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h index c74c5020e3..78a484cf05 100644 --- a/platform/android/android_input_handler.h +++ b/platform/android/android_input_handler.h @@ -96,7 +96,7 @@ private: void _cancel_all_touch(); public: - void process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative); + void process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative, float p_pressure, Vector2 p_tilt); void process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points, bool p_double_tap); void process_magnify(Point2 p_pos, float p_factor); void process_pan(Point2 p_pos, Vector2 p_delta); diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp index 6bd09fe00a..d24d3fa389 100644 --- a/platform/android/dir_access_jandroid.cpp +++ b/platform/android/dir_access_jandroid.cpp @@ -67,7 +67,7 @@ String DirAccessJAndroid::get_next() { ERR_FAIL_COND_V(id == 0, ""); if (_dir_next) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, ""); + ERR_FAIL_NULL_V(env, ""); jstring str = (jstring)env->CallObjectMethod(dir_access_handler, _dir_next, get_access_type(), id); if (!str) { return ""; @@ -84,7 +84,7 @@ String DirAccessJAndroid::get_next() { bool DirAccessJAndroid::current_is_dir() const { if (_dir_is_dir) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, false); + ERR_FAIL_NULL_V(env, false); return env->CallBooleanMethod(dir_access_handler, _dir_is_dir, get_access_type(), id); } else { return false; @@ -94,7 +94,7 @@ bool DirAccessJAndroid::current_is_dir() const { bool DirAccessJAndroid::current_is_hidden() const { if (_current_is_hidden) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, false); + ERR_FAIL_NULL_V(env, false); return env->CallBooleanMethod(dir_access_handler, _current_is_hidden, get_access_type(), id); } return false; @@ -112,7 +112,7 @@ void DirAccessJAndroid::list_dir_end() { int DirAccessJAndroid::get_drive_count() { if (_get_drive_count) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, 0); + ERR_FAIL_NULL_V(env, 0); return env->CallIntMethod(dir_access_handler, _get_drive_count, get_access_type()); } else { return 0; @@ -122,7 +122,7 @@ int DirAccessJAndroid::get_drive_count() { String DirAccessJAndroid::get_drive(int p_drive) { if (_get_drive) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, ""); + ERR_FAIL_NULL_V(env, ""); jstring j_drive = (jstring)env->CallObjectMethod(dir_access_handler, _get_drive, get_access_type(), p_drive); if (!j_drive) { return ""; @@ -191,7 +191,7 @@ String DirAccessJAndroid::get_absolute_path(String p_path) { bool DirAccessJAndroid::file_exists(String p_file) { if (_file_exists) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, false); + ERR_FAIL_NULL_V(env, false); String path = get_absolute_path(p_file); jstring j_path = env->NewStringUTF(path.utf8().get_data()); @@ -206,7 +206,7 @@ bool DirAccessJAndroid::file_exists(String p_file) { bool DirAccessJAndroid::dir_exists(String p_dir) { if (_dir_exists) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, false); + ERR_FAIL_NULL_V(env, false); String path = get_absolute_path(p_dir); jstring j_path = env->NewStringUTF(path.utf8().get_data()); @@ -226,7 +226,7 @@ Error DirAccessJAndroid::make_dir_recursive(String p_dir) { if (_make_dir) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, ERR_UNCONFIGURED); + ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED); String path = get_absolute_path(p_dir); jstring j_dir = env->NewStringUTF(path.utf8().get_data()); @@ -249,7 +249,7 @@ Error DirAccessJAndroid::make_dir(String p_dir) { Error DirAccessJAndroid::rename(String p_from, String p_to) { if (_rename) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, ERR_UNCONFIGURED); + ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED); String from_path = get_absolute_path(p_from); jstring j_from = env->NewStringUTF(from_path.utf8().get_data()); @@ -273,7 +273,7 @@ Error DirAccessJAndroid::rename(String p_from, String p_to) { Error DirAccessJAndroid::remove(String p_name) { if (_remove) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, ERR_UNCONFIGURED); + ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED); String path = get_absolute_path(p_name); jstring j_name = env->NewStringUTF(path.utf8().get_data()); @@ -292,7 +292,7 @@ Error DirAccessJAndroid::remove(String p_name) { uint64_t DirAccessJAndroid::get_space_left() { if (_get_space_left) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, 0); + ERR_FAIL_NULL_V(env, 0); return env->CallLongMethod(dir_access_handler, _get_space_left, get_access_type()); } else { return 0; @@ -331,7 +331,7 @@ DirAccessJAndroid::~DirAccessJAndroid() { int DirAccessJAndroid::dir_open(String p_path) { if (_dir_open) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, 0); + ERR_FAIL_NULL_V(env, 0); String path = get_absolute_path(p_path); jstring js = env->NewStringUTF(path.utf8().get_data()); @@ -346,7 +346,7 @@ int DirAccessJAndroid::dir_open(String p_path) { void DirAccessJAndroid::dir_close(int p_id) { if (_dir_close) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND(env == nullptr); + ERR_FAIL_NULL(env); env->CallVoidMethod(dir_access_handler, _dir_close, get_access_type(), p_id); } } diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index f02b292868..11c0945ce0 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -111,6 +111,20 @@ void DisplayServerAndroid::tts_stop() { TTS_Android::stop(); } +bool DisplayServerAndroid::is_dark_mode_supported() const { + GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java(); + ERR_FAIL_NULL_V(godot_java, false); + + return godot_java->is_dark_mode_supported(); +} + +bool DisplayServerAndroid::is_dark_mode() const { + GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java(); + ERR_FAIL_NULL_V(godot_java, false); + + return godot_java->is_dark_mode(); +} + void DisplayServerAndroid::clipboard_set(const String &p_text) { GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java(); ERR_FAIL_NULL(godot_java); @@ -543,7 +557,7 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis #if defined(GLES3_ENABLED) if (rendering_driver == "opengl3") { - RasterizerGLES3::make_current(); + RasterizerGLES3::make_current(false); } #endif diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index e0ad2cb916..54912212dc 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -103,6 +103,9 @@ public: virtual void tts_resume() override; virtual void tts_stop() override; + virtual bool is_dark_mode_supported() const override; + virtual bool is_dark_mode() const override; + virtual void clipboard_set(const String &p_text) override; virtual String clipboard_get() const override; virtual bool clipboard_has() const override; diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml index d270980d72..0f1da54376 100644 --- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml +++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml @@ -8,6 +8,7 @@ <tutorials> <link title="Exporting for Android">$DOCS_URL/tutorials/export/exporting_for_android.html</link> <link title="Custom builds for Android">$DOCS_URL/tutorials/export/android_custom_build.html</link> + <link title="Android plugins documentation index">$DOCS_URL/tutorials/platform/index.html</link> </tutorials> <members> <member name="apk_expansion/SALT" type="String" setter="" getter=""> diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 32b46271fd..682603e46b 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -53,6 +53,9 @@ void register_android_exporter() { EDITOR_DEF("export/android/shutdown_adb_on_exit", true); EDITOR_DEF("export/android/one_click_deploy_clear_previous_install", false); + + EDITOR_DEF("export/android/use_wifi_for_remote_debug", false); + EDITOR_DEF("export/android/wifi_remote_debug_host", "localhost"); #endif Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>(memnew(EditorExportPlatformAndroid)); diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 21de46f4fc..aeaa7b9ce7 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -1832,7 +1832,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname", false, true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "com.example.$genname", false, true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "package/app_category", PROPERTY_HINT_ENUM, "Accessibility,Audio,Game,Image,Maps,News,Productivity,Social,Video"), APP_CATEGORY_GAME)); @@ -1954,8 +1954,9 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, return ERR_SKIP; } + const bool use_wifi_for_remote_debug = EDITOR_GET("export/android/use_wifi_for_remote_debug"); const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT); - const bool use_reverse = devices[p_device].api_level >= 21; + const bool use_reverse = devices[p_device].api_level >= 21 && !use_wifi_for_remote_debug; if (use_reverse) { p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST; @@ -2068,7 +2069,10 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, print_line("Reverse result2: " + itos(rv)); } } else { - static const char *const msg = "--- Device API < 21; debugging over Wi-Fi ---"; + static const char *const api_version_msg = "--- Device API < 21; debugging over Wi-Fi ---"; + static const char *const manual_override_msg = "--- Wi-Fi remote debug enabled in project settings; debugging over Wi-Fi ---"; + + const char *const msg = use_wifi_for_remote_debug ? manual_override_msg : api_version_msg; EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR); print_line(String(msg).to_upper()); } @@ -2631,6 +2635,8 @@ void EditorExportPlatformAndroid::_clear_assets_directory() { if (da_res->dir_exists(APK_ASSETS_DIRECTORY)) { print_verbose("Clearing APK assets directory..."); Ref<DirAccess> da_assets = DirAccess::open(APK_ASSETS_DIRECTORY); + ERR_FAIL_COND(da_assets.is_null()); + da_assets->erase_contents_recursive(); da_res->remove(APK_ASSETS_DIRECTORY); } @@ -2639,6 +2645,8 @@ void EditorExportPlatformAndroid::_clear_assets_directory() { if (da_res->dir_exists(AAB_ASSETS_DIRECTORY)) { print_verbose("Clearing AAB assets directory..."); Ref<DirAccess> da_assets = DirAccess::open(AAB_ASSETS_DIRECTORY); + ERR_FAIL_COND(da_assets.is_null()); + da_assets->erase_contents_recursive(); da_res->remove(AAB_ASSETS_DIRECTORY); } diff --git a/platform/android/file_access_filesystem_jandroid.cpp b/platform/android/file_access_filesystem_jandroid.cpp index a1865fb1d4..beea73fd61 100644 --- a/platform/android/file_access_filesystem_jandroid.cpp +++ b/platform/android/file_access_filesystem_jandroid.cpp @@ -69,7 +69,7 @@ Error FileAccessFilesystemJAndroid::open_internal(const String &p_path, int p_mo if (_file_open) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, ERR_UNCONFIGURED); + ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED); String path = fix_path(p_path).simplify_path(); jstring js = env->NewStringUTF(path.utf8().get_data()); @@ -103,7 +103,7 @@ void FileAccessFilesystemJAndroid::_close() { if (_file_close) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND(env == nullptr); + ERR_FAIL_NULL(env); env->CallVoidMethod(file_access_handler, _file_close, id); } id = 0; @@ -116,7 +116,7 @@ bool FileAccessFilesystemJAndroid::is_open() const { void FileAccessFilesystemJAndroid::seek(uint64_t p_position) { if (_file_seek) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND(env == nullptr); + ERR_FAIL_NULL(env); ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use."); env->CallVoidMethod(file_access_handler, _file_seek, id, p_position); } @@ -125,7 +125,7 @@ void FileAccessFilesystemJAndroid::seek(uint64_t p_position) { void FileAccessFilesystemJAndroid::seek_end(int64_t p_position) { if (_file_seek_end) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND(env == nullptr); + ERR_FAIL_NULL(env); ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use."); env->CallVoidMethod(file_access_handler, _file_seek_end, id, p_position); } @@ -134,7 +134,7 @@ void FileAccessFilesystemJAndroid::seek_end(int64_t p_position) { uint64_t FileAccessFilesystemJAndroid::get_position() const { if (_file_tell) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, 0); + ERR_FAIL_NULL_V(env, 0); ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use."); return env->CallLongMethod(file_access_handler, _file_tell, id); } else { @@ -145,7 +145,7 @@ uint64_t FileAccessFilesystemJAndroid::get_position() const { uint64_t FileAccessFilesystemJAndroid::get_length() const { if (_file_get_size) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, 0); + ERR_FAIL_NULL_V(env, 0); ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use."); return env->CallLongMethod(file_access_handler, _file_get_size, id); } else { @@ -156,7 +156,7 @@ uint64_t FileAccessFilesystemJAndroid::get_length() const { bool FileAccessFilesystemJAndroid::eof_reached() const { if (_file_eof) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, false); + ERR_FAIL_NULL_V(env, false); ERR_FAIL_COND_V_MSG(!is_open(), false, "File must be opened before use."); return env->CallBooleanMethod(file_access_handler, _file_eof, id); } else { @@ -169,7 +169,7 @@ void FileAccessFilesystemJAndroid::_set_eof(bool eof) { ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use."); JNIEnv *env = get_jni_env(); - ERR_FAIL_COND(env == nullptr); + ERR_FAIL_NULL(env); env->CallVoidMethod(file_access_handler, _file_set_eof, id, eof); } } @@ -235,7 +235,7 @@ uint64_t FileAccessFilesystemJAndroid::get_buffer(uint8_t *p_dst, uint64_t p_len } JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, 0); + ERR_FAIL_NULL_V(env, 0); jobject j_buffer = env->NewDirectByteBuffer(p_dst, p_length); int length = env->CallIntMethod(file_access_handler, _file_read, id, j_buffer); @@ -258,7 +258,7 @@ void FileAccessFilesystemJAndroid::store_buffer(const uint8_t *p_src, uint64_t p } JNIEnv *env = get_jni_env(); - ERR_FAIL_COND(env == nullptr); + ERR_FAIL_NULL(env); jobject j_buffer = env->NewDirectByteBuffer((void *)p_src, p_length); env->CallVoidMethod(file_access_handler, _file_write, id, j_buffer); @@ -276,7 +276,7 @@ Error FileAccessFilesystemJAndroid::get_error() const { void FileAccessFilesystemJAndroid::flush() { if (_file_flush) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND(env == nullptr); + ERR_FAIL_NULL(env); ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use."); env->CallVoidMethod(file_access_handler, _file_flush, id); } @@ -285,7 +285,7 @@ void FileAccessFilesystemJAndroid::flush() { bool FileAccessFilesystemJAndroid::file_exists(const String &p_path) { if (_file_exists) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, false); + ERR_FAIL_NULL_V(env, false); String path = fix_path(p_path).simplify_path(); jstring js = env->NewStringUTF(path.utf8().get_data()); @@ -300,7 +300,7 @@ bool FileAccessFilesystemJAndroid::file_exists(const String &p_path) { uint64_t FileAccessFilesystemJAndroid::_get_modified_time(const String &p_file) { if (_file_last_modified) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, false); + ERR_FAIL_NULL_V(env, false); String path = fix_path(p_file).simplify_path(); jstring js = env->NewStringUTF(path.utf8().get_data()); diff --git a/platform/android/java/editor/src/.gitignore b/platform/android/java/editor/src/.gitignore new file mode 100644 index 0000000000..c081ec3425 --- /dev/null +++ b/platform/android/java/editor/src/.gitignore @@ -0,0 +1 @@ +!/debug diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt index 9c1165bf8a..0e111d5247 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -35,6 +35,7 @@ import android.app.Activity import android.app.AlertDialog import android.content.* import android.content.pm.PackageManager +import android.content.res.Configuration import android.content.res.Resources import android.graphics.Rect import android.hardware.Sensor @@ -694,6 +695,25 @@ class Godot(private val context: Context) : SensorEventListener { } } + /** + * Returns true if dark mode is supported, false otherwise. + */ + @Keep + private fun isDarkModeSupported(): Boolean { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q + } + + /** + * Returns true if dark mode is supported and enabled, false otherwise. + */ + @Keep + private fun isDarkMode(): Boolean { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return context.resources?.configuration?.uiMode?.and(Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES + } + return false + } + fun hasClipboard(): Boolean { return mClipboard.hasPrimaryClip() } @@ -907,6 +927,19 @@ class Godot(private val context: Context) : SensorEventListener { return PermissionsUtil.getGrantedPermissions(getActivity()) } + /** + * Get the list of gdextension modules to register. + */ + @Keep + private fun getGDExtensionConfigFiles(): Array<String> { + val configFiles = mutableSetOf<String>() + for (plugin in pluginRegistry.allPlugins) { + configFiles.addAll(plugin.pluginGDExtensionLibrariesPaths) + } + + return configFiles.toTypedArray() + } + @Keep private fun getCACertificates(): String { return GodotNetUtils.getCACertificates() 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 b9ecd6971d..fee50e93c2 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -114,7 +114,7 @@ public class GodotLib { /** * Dispatch mouse events */ - public static native void dispatchMouseEvent(int event, int buttonMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative); + public static native void dispatchMouseEvent(int event, int buttonMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY); public static native void magnify(float x, float y, float factor); 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 a7064dfc1d..3070a8a207 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 @@ -33,6 +33,7 @@ package org.godotengine.godot.input; import org.godotengine.godot.*; import android.content.Context; +import android.content.res.Configuration; import android.os.Handler; import android.os.Message; import android.text.InputFilter; @@ -209,6 +210,13 @@ public class GodotEditText extends EditText { mRenderView.getView().requestFocus(); } + // When a hardware keyboard is connected, all key events come through so we can route them + // directly to the engine. + // This is not the case when using a soft keyboard, requiring extra processing from this class. + if (hasHardwareKeyboard()) { + return mRenderView.getInputHandler().onKeyDown(keyCode, keyEvent); + } + // pass event to godot in special cases if (needHandlingInGodot(keyCode, keyEvent) && mRenderView.getInputHandler().onKeyDown(keyCode, keyEvent)) { return true; @@ -219,6 +227,13 @@ public class GodotEditText extends EditText { @Override public boolean onKeyUp(int keyCode, KeyEvent keyEvent) { + // When a hardware keyboard is connected, all key events come through so we can route them + // directly to the engine. + // This is not the case when using a soft keyboard, requiring extra processing from this class. + if (hasHardwareKeyboard()) { + return mRenderView.getInputHandler().onKeyUp(keyCode, keyEvent); + } + if (needHandlingInGodot(keyCode, keyEvent) && mRenderView.getInputHandler().onKeyUp(keyCode, keyEvent)) { return true; } else { @@ -235,10 +250,20 @@ public class GodotEditText extends EditText { isModifiedKey; } + boolean hasHardwareKeyboard() { + Configuration config = getResources().getConfiguration(); + return config.keyboard != Configuration.KEYBOARD_NOKEYS && + config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO; + } + // =========================================================== // Methods // =========================================================== public void showKeyboard(String p_existing_text, VirtualKeyboardType p_type, int p_max_input_length, int p_cursor_start, int p_cursor_end) { + if (hasHardwareKeyboard()) { + return; + } + int maxInputLength = (p_max_input_length <= 0) ? Integer.MAX_VALUE : p_max_input_length; if (p_cursor_start == -1) { // cursor position not given this.mOriginText = p_existing_text; @@ -262,6 +287,10 @@ public class GodotEditText extends EditText { } public void hideKeyboard() { + if (hasHardwareKeyboard()) { + return; + } + final Message msg = new Message(); msg.what = HANDLER_CLOSE_IME_KEYBOARD; msg.obj = this; 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 1a25be0460..c8b222254e 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 @@ -433,7 +433,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } private static boolean isMouseEvent(int eventSource) { - boolean mouseSource = ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & (InputDevice.SOURCE_TOUCHSCREEN | InputDevice.SOURCE_STYLUS)) == InputDevice.SOURCE_STYLUS); + boolean mouseSource = ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mouseSource = mouseSource || ((eventSource & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE); } @@ -470,13 +470,27 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { final float y = event.getY(); final int buttonsMask = event.getButtonState(); + final float pressure = event.getPressure(); + + // Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise. + final float orientation = event.getOrientation(); + + // Tilt is zero is perpendicular to the screen and pi/2 is flat on the surface. + final float tilt = event.getAxisValue(MotionEvent.AXIS_TILT); + + float tiltMult = (float)Math.sin(tilt); + + // To be consistent with expected tilt. + final float tiltX = (float)-Math.sin(orientation) * tiltMult; + final float tiltY = (float)Math.cos(orientation) * tiltMult; + final float verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL); final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL); boolean sourceMouseRelative = false; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { sourceMouseRelative = event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE); } - return handleMouseEvent(eventAction, buttonsMask, x, y, horizontalFactor, verticalFactor, false, sourceMouseRelative); + return handleMouseEvent(eventAction, buttonsMask, x, y, horizontalFactor, verticalFactor, false, sourceMouseRelative, pressure, tiltX, tiltY); } static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y) { @@ -484,6 +498,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative) { + return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, 1, 0, 0); + } + + static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) { // Fix the buttonsMask switch (eventAction) { case MotionEvent.ACTION_CANCEL: @@ -511,7 +529,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { case MotionEvent.ACTION_HOVER_MOVE: case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_SCROLL: { - GodotLib.dispatchMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative); + GodotLib.dispatchMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY); return true; } } 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 48aa231c7a..7f3a3ac7a3 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 @@ -57,27 +57,28 @@ import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; /** - * Base class for the Godot Android plugins. + * Base class for Godot Android plugins. * <p> - * A Godot Android plugin is a regular Android library packaged as an aar archive file with the following caveats: + * A Godot Android plugin is an Android library with the following requirements: * <p> - * - The library must have a dependency on the Godot Android library (godot-lib.aar). - * A stable version is available for each release. + * - The plugin must have a dependency on the Godot Android library: `implementation "org.godotengine:godot:<godotLibVersion>"` + * <p> + * - The plugin must include a <meta-data> tag in its Android manifest with the following format: + * <meta-data android:name="org.godotengine.plugin.v2.[PluginName]" android:value="[plugin.init.ClassFullName]" /> * <p> - * - The library must include a <meta-data> tag in its manifest file setup as follow: - * <meta-data android:name="org.godotengine.plugin.v1.[PluginName]" android:value="[plugin.init.ClassFullName]" /> * Where: + * <p> * - 'PluginName' is the name of the plugin. - * - 'plugin.init.ClassFullName' is the full name (package + class name) of the plugin class + * <p> + * - 'plugin.init.ClassFullName' is the full name (package + class name) of the plugin init class * extending {@link GodotPlugin}. + * <p> + * A Godot Android plugin can also define and provide c/c++ gdextension libraries, which will be + * automatically bundled by the aar build system. + * GDExtension ('*.gdextension') config files must be located in the project 'assets' directory and + * their paths specified by {@link GodotPlugin#getPluginGDExtensionLibrariesPaths()}. * - * A plugin can also define and provide c/c++ gdextension libraries and nativescripts for the target - * app/game to leverage. - * The shared library for the gdextension library will be automatically bundled by the aar build - * system. - * Godot '*.gdextension' resource files must however be manually defined in the project - * 'assets' directory. The recommended path for these resources in the 'assets' directory should be: - * 'godot/plugin/v1/[PluginName]/' + * @see <a href="https://docs.godotengine.org/en/stable/tutorials/platform/android/index.html">Android plugins</a> */ public abstract class GodotPlugin { private static final String TAG = GodotPlugin.class.getSimpleName(); @@ -85,6 +86,10 @@ public abstract class GodotPlugin { private final Godot godot; private final ConcurrentHashMap<String, SignalInfo> registeredSignals = new ConcurrentHashMap<>(); + /** + * Base constructor passing a {@link Godot} instance through which the plugin can access Godot's + * APIs and lifecycle events. + */ public GodotPlugin(Godot godot) { this.godot = godot; } @@ -97,7 +102,7 @@ public abstract class GodotPlugin { } /** - * Provides access to the underlying {@link Activity}. + * Provides access to the hosting {@link Activity}. */ @Nullable protected Activity getActivity() { @@ -106,33 +111,16 @@ public abstract class GodotPlugin { /** * Register the plugin with Godot native code. - * - * This method is invoked on the render thread. + * <p> + * This method is invoked by the Godot Engine on the render thread. */ public final void onRegisterPluginWithGodotNative() { registeredSignals.putAll( - registerPluginWithGodotNative(this, getPluginName(), getPluginMethods(), getPluginSignals(), - getPluginGDExtensionLibrariesPaths())); - } - - /** - * Register the plugin with Godot native code. - * - * This method must be invoked on the render thread. - */ - public static void registerPluginWithGodotNative(Object pluginObject, - GodotPluginInfoProvider pluginInfoProvider) { - registerPluginWithGodotNative(pluginObject, pluginInfoProvider.getPluginName(), - Collections.emptyList(), pluginInfoProvider.getPluginSignals(), - pluginInfoProvider.getPluginGDExtensionLibrariesPaths()); - - // Notify that registration is complete. - pluginInfoProvider.onPluginRegistered(); + registerPluginWithGodotNative(this, getPluginName(), getPluginMethods(), getPluginSignals())); } private static Map<String, SignalInfo> registerPluginWithGodotNative(Object pluginObject, - String pluginName, List<String> pluginMethods, Set<SignalInfo> pluginSignals, - Set<String> pluginGDExtensionLibrariesPaths) { + String pluginName, List<String> pluginMethods, Set<SignalInfo> pluginSignals) { nativeRegisterSingleton(pluginName, pluginObject); Set<Method> filteredMethods = new HashSet<>(); @@ -176,23 +164,18 @@ public abstract class GodotPlugin { registeredSignals.put(signalName, signalInfo); } - // Get the list of gdextension libraries to register. - if (!pluginGDExtensionLibrariesPaths.isEmpty()) { - nativeRegisterGDExtensionLibraries(pluginGDExtensionLibrariesPaths.toArray(new String[0])); - } - return registeredSignals; } /** - * Invoked once during the Godot Android initialization process after creation of the + * Invoked once during the initialization process after creation of the * {@link org.godotengine.godot.GodotRenderView} view. * <p> - * The plugin can return a non-null {@link View} layout in order to add it to the Godot view + * The plugin can return a non-null {@link View} layout which will be added to the Godot view * hierarchy. - * - * Use shouldBeOnTop() to set whether the plugin's {@link View} should be added on top or behind - * the main Godot view. + * <p> + * Use {@link GodotPlugin#shouldBeOnTop()} to specify whether the plugin's {@link View} should + * be added on top or behind the main Godot view. * * @see Activity#onCreate(Bundle) * @return the plugin's view to be included; null if no views should be included. @@ -235,44 +218,52 @@ public abstract class GodotPlugin { public boolean onMainBackPressed() { return false; } /** - * Invoked on the render thread when the Godot setup is complete. + * Invoked on the render thread when set up of the Godot engine is complete. + * <p> + * This is invoked before {@link GodotPlugin#onGodotMainLoopStarted()}. */ public void onGodotSetupCompleted() {} /** * Invoked on the render thread when the Godot main loop has started. + * + * This is invoked after {@link GodotPlugin#onGodotSetupCompleted()}. */ public void onGodotMainLoopStarted() {} /** - * Invoked once per frame on the GL thread after the frame is drawn. + * When using the OpenGL renderer, this is invoked once per frame on the GL thread after the + * frame is drawn. */ public void onGLDrawFrame(GL10 gl) {} /** - * Called on the GL thread after the surface is created and whenever the OpenGL ES surface size - * changes. + * When using the OpenGL renderer, this is called on the GL thread after the surface is created + * and whenever the OpenGL ES surface size changes. */ public void onGLSurfaceChanged(GL10 gl, int width, int height) {} /** - * Called on the GL thread when the surface is created or recreated. + * When using the OpenGL renderer, this is called on the GL thread when the surface is created + * or recreated. */ public void onGLSurfaceCreated(GL10 gl, EGLConfig config) {} /** - * Invoked once per frame on the Vulkan thread after the frame is drawn. + * When using the Vulkan renderer, this is 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. + * When using the Vulkan renderer, this is 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. + * When using the Vulkan renderer, this is called on the Vulkan thread when the surface is + * created or recreated. */ public void onVkSurfaceCreated(Surface surface) {} @@ -287,7 +278,7 @@ public abstract class GodotPlugin { /** * Returns the list of methods to be exposed to Godot. * - * @deprecated Used the {@link UsedByGodot} annotation instead. + * @deprecated Use the {@link UsedByGodot} annotation instead. */ @NonNull @Deprecated @@ -304,19 +295,19 @@ public abstract class GodotPlugin { } /** - * Returns the paths for the plugin's gdextension libraries. - * - * The paths must be relative to the 'assets' directory and point to a '*.gdextension' file. + * Returns the paths for the plugin's gdextension libraries (if any). + * <p> + * Each returned path must be relative to the 'assets' directory and point to a '*.gdextension' file. */ @NonNull - protected Set<String> getPluginGDExtensionLibrariesPaths() { + public Set<String> getPluginGDExtensionLibrariesPaths() { return Collections.emptySet(); } /** - * Returns whether the plugin's {@link View} returned in onMainCreate() should be placed on - * top of the main Godot view. - * + * Returns whether the plugin's {@link View} returned in + * {@link GodotPlugin#onMainCreate(Activity)} should be placed on top of the main Godot view. + * <p> * Returning false causes the plugin's {@link View} to be placed behind, which can be useful * when used with transparency in order to let the Godot view handle inputs. */ @@ -359,7 +350,7 @@ public abstract class GodotPlugin { } emitSignal(getGodot(), getPluginName(), signalInfo, signalArgs); } catch (IllegalArgumentException exception) { - Log.w(TAG, exception.getMessage()); + Log.w(TAG, exception); if (BuildConfig.DEBUG) { throw exception; } @@ -368,7 +359,7 @@ public abstract class GodotPlugin { /** * Emit a Godot signal. - * @param godot + * @param godot Godot instance * @param pluginName Name of the Godot plugin the signal will be emitted from. The plugin must already be registered with the Godot engine. * @param signalInfo Information about the signal to emit. * @param signalArgs Arguments used to populate the emitted signal. The arguments will be validated against the given {@link SignalInfo} parameter. @@ -397,7 +388,7 @@ public abstract class GodotPlugin { godot.runOnRenderThread(() -> nativeEmitSignal(pluginName, signalInfo.getName(), signalArgs)); } catch (IllegalArgumentException exception) { - Log.w(TAG, exception.getMessage()); + Log.w(TAG, exception); if (BuildConfig.DEBUG) { throw exception; } @@ -420,13 +411,7 @@ public abstract class GodotPlugin { private static native void nativeRegisterMethod(String p_sname, String p_name, String p_ret, String[] p_params); /** - * Used to register gdextension libraries bundled by the plugin. - * @param gdextensionPaths Paths to the libraries relative to the 'assets' directory. - */ - private static native void nativeRegisterGDExtensionLibraries(String[] gdextensionPaths); - - /** - * Used to complete registration of the {@link GodotPlugin} instance's methods. + * Used to complete registration of the {@link GodotPlugin} instance's signals. * @param pluginName Name of the plugin * @param signalName Name of the signal to register * @param signalParamTypes Signal parameters types 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 c2428de2e1..d338b72441 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 @@ -52,7 +52,14 @@ import java.util.concurrent.ConcurrentHashMap; public final class GodotPluginRegistry { private static final String TAG = GodotPluginRegistry.class.getSimpleName(); + /** + * Prefix used for version 1 of the Godot plugin, mostly compatible with Godot 3.x + */ private static final String GODOT_PLUGIN_V1_NAME_PREFIX = "org.godotengine.plugin.v1."; + /** + * Prefix used for version 2 of the Godot plugin, compatible with Godot 4.2+ + */ + private static final String GODOT_PLUGIN_V2_NAME_PREFIX = "org.godotengine.plugin.v2."; private static GodotPluginRegistry instance; private final ConcurrentHashMap<String, GodotPlugin> registry; @@ -123,11 +130,17 @@ public final class GodotPluginRegistry { return; } - int godotPluginV1NamePrefixLength = GODOT_PLUGIN_V1_NAME_PREFIX.length(); for (String metaDataName : metaData.keySet()) { // Parse the meta-data looking for entry with the Godot plugin name prefix. - if (metaDataName.startsWith(GODOT_PLUGIN_V1_NAME_PREFIX)) { - String pluginName = metaDataName.substring(godotPluginV1NamePrefixLength).trim(); + String pluginName = null; + if (metaDataName.startsWith(GODOT_PLUGIN_V2_NAME_PREFIX)) { + pluginName = metaDataName.substring(GODOT_PLUGIN_V2_NAME_PREFIX.length()).trim(); + } else if (metaDataName.startsWith(GODOT_PLUGIN_V1_NAME_PREFIX)) { + pluginName = metaDataName.substring(GODOT_PLUGIN_V1_NAME_PREFIX.length()).trim(); + Log.w(TAG, "Godot v1 plugin are deprecated in Godot 4.2 and higher: " + pluginName); + } + + if (!TextUtils.isEmpty(pluginName)) { Log.i(TAG, "Initializing Godot plugin " + pluginName); // Retrieve the plugin class full name. @@ -149,15 +162,7 @@ public final class GodotPluginRegistry { } registry.put(pluginName, pluginHandle); Log.i(TAG, "Completed initialization for Godot plugin " + pluginHandle.getPluginName()); - } catch (ClassNotFoundException e) { - Log.w(TAG, "Unable to load Godot plugin " + pluginName, e); - } catch (IllegalAccessException e) { - Log.w(TAG, "Unable to load Godot plugin " + pluginName, e); - } catch (InstantiationException e) { - Log.w(TAG, "Unable to load Godot plugin " + pluginName, e); - } catch (NoSuchMethodException e) { - Log.w(TAG, "Unable to load Godot plugin " + pluginName, e); - } catch (InvocationTargetException e) { + } catch (Exception e) { Log.w(TAG, "Unable to load Godot plugin " + pluginName, e); } } else { @@ -165,7 +170,7 @@ public final class GodotPluginRegistry { } } } - } catch (PackageManager.NameNotFoundException e) { + } catch (Exception e) { Log.e(TAG, "Unable load Godot Android plugins from the manifest file.", e); } } diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp index edc934e927..d6455cbf1c 100644 --- a/platform/android/java_class_wrapper.cpp +++ b/platform/android/java_class_wrapper.cpp @@ -50,14 +50,14 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } int pc = E.param_types.size(); - if (pc > p_argcount) { + if (p_argcount < pc) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = pc; + r_error.expected = pc; continue; } - if (pc < p_argcount) { + if (p_argcount > pc) { r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_error.argument = pc; + r_error.expected = pc; continue; } uint32_t *ptypes = E.param_types.ptrw(); diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 74605e3377..50075ed3f5 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -274,12 +274,12 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, } // Called on the UI thread -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative, jfloat p_pressure, jfloat p_tilt_x, jfloat p_tilt_y) { if (step.get() <= 0) { return; } - input_handler->process_mouse_event(p_event_type, p_button_mask, Point2(p_x, p_y), Vector2(p_delta_x, p_delta_y), p_double_click, p_source_mouse_relative); + input_handler->process_mouse_event(p_event_type, p_button_mask, Point2(p_x, p_y), Vector2(p_delta_x, p_delta_y), p_double_click, p_source_mouse_relative, p_pressure, Vector2(p_tilt_x, p_tilt_y)); } // Called on the UI thread diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index ee6a19034c..1ddda6c1c8 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -45,7 +45,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ttsCallback(JNIEnv *env, jclass clazz, jint event, jint id, jint pos); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative, jfloat p_pressure, jfloat p_tilt_x, jfloat p_tilt_y); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jboolean p_double_tap); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y); diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 79ba2528ba..1703179b8e 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -62,6 +62,8 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ _finish = p_env->GetMethodID(godot_class, "forceQuit", "(I)Z"); _set_keep_screen_on = p_env->GetMethodID(godot_class, "setKeepScreenOn", "(Z)V"); _alert = p_env->GetMethodID(godot_class, "alert", "(Ljava/lang/String;Ljava/lang/String;)V"); + _is_dark_mode_supported = p_env->GetMethodID(godot_class, "isDarkModeSupported", "()Z"); + _is_dark_mode = p_env->GetMethodID(godot_class, "isDarkMode", "()Z"); _get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;"); _set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V"); _has_clipboard = p_env->GetMethodID(godot_class, "hasClipboard", "()Z"); @@ -79,6 +81,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ _begin_benchmark_measure = p_env->GetMethodID(godot_class, "nativeBeginBenchmarkMeasure", "(Ljava/lang/String;)V"); _end_benchmark_measure = p_env->GetMethodID(godot_class, "nativeEndBenchmarkMeasure", "(Ljava/lang/String;)V"); _dump_benchmark = p_env->GetMethodID(godot_class, "nativeDumpBenchmark", "(Ljava/lang/String;)V"); + _get_gdextension_list_config_file = p_env->GetMethodID(godot_class, "getGDExtensionConfigFiles", "()[Ljava/lang/String;"); } GodotJavaWrapper::~GodotJavaWrapper() { @@ -171,6 +174,26 @@ void GodotJavaWrapper::alert(const String &p_message, const String &p_title) { } } +bool GodotJavaWrapper::is_dark_mode_supported() { + if (_is_dark_mode_supported) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL_V(env, false); + return env->CallBooleanMethod(godot_instance, _is_dark_mode_supported); + } else { + return false; + } +} + +bool GodotJavaWrapper::is_dark_mode() { + if (_is_dark_mode) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL_V(env, false); + return env->CallBooleanMethod(godot_instance, _is_dark_mode); + } else { + return false; + } +} + bool GodotJavaWrapper::has_get_clipboard() { return _get_clipboard != nullptr; } @@ -264,6 +287,25 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const { return permissions_list; } +Vector<String> GodotJavaWrapper::get_gdextension_list_config_file() const { + Vector<String> config_file_list; + if (_get_gdextension_list_config_file) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL_V(env, config_file_list); + jobject config_file_list_object = env->CallObjectMethod(godot_instance, _get_gdextension_list_config_file); + jobjectArray *arr = reinterpret_cast<jobjectArray *>(&config_file_list_object); + + jsize len = env->GetArrayLength(*arr); + for (int i = 0; i < len; i++) { + jstring j_config_file = (jstring)env->GetObjectArrayElement(*arr, i); + String config_file = jstring_to_string(j_config_file, env); + config_file_list.push_back(config_file); + env->DeleteLocalRef(j_config_file); + } + } + return config_file_list; +} + String GodotJavaWrapper::get_ca_certificates() const { if (_get_ca_certificates) { JNIEnv *env = get_jni_env(); diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index ba42d5dccd..f427a2937c 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -53,12 +53,15 @@ private: jmethodID _finish = nullptr; jmethodID _set_keep_screen_on = nullptr; jmethodID _alert = nullptr; + jmethodID _is_dark_mode_supported = nullptr; + jmethodID _is_dark_mode = nullptr; jmethodID _get_clipboard = nullptr; jmethodID _set_clipboard = nullptr; jmethodID _has_clipboard = nullptr; jmethodID _request_permission = nullptr; jmethodID _request_permissions = nullptr; jmethodID _get_granted_permissions = nullptr; + jmethodID _get_gdextension_list_config_file = nullptr; jmethodID _get_ca_certificates = nullptr; jmethodID _init_input_devices = nullptr; jmethodID _vibrate = nullptr; @@ -85,6 +88,8 @@ public: bool force_quit(JNIEnv *p_env = nullptr, int p_instance_id = 0); void set_keep_screen_on(bool p_enabled); void alert(const String &p_message, const String &p_title); + bool is_dark_mode_supported(); + bool is_dark_mode(); bool has_get_clipboard(); String get_clipboard(); bool has_set_clipboard(); @@ -102,6 +107,9 @@ public: void begin_benchmark_measure(const String &p_label); void end_benchmark_measure(const String &p_label); void dump_benchmark(const String &benchmark_file); + + // Return the list of gdextensions config file. + Vector<String> get_gdextension_list_config_file() const; }; #endif // JAVA_GODOT_WRAPPER_H diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index c040d8c4c6..8f80516a9f 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -39,6 +39,8 @@ #include "net_socket_android.h" #include "core/config/project_settings.h" +#include "core/extension/gdextension_manager.h" +#include "core/io/xml_parser.h" #include "drivers/unix/dir_access_unix.h" #include "drivers/unix/file_access_unix.h" #include "main/main.h" @@ -162,11 +164,39 @@ Vector<String> OS_Android::get_granted_permissions() const { Error OS_Android::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) { String path = p_path; + bool so_file_exists = true; if (!FileAccess::exists(path)) { path = p_path.get_file(); + so_file_exists = false; } p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); + if (!p_library_handle && so_file_exists) { + // The library may be on the sdcard and thus inaccessible. Try to copy it to the internal + // directory. + uint64_t so_modified_time = FileAccess::get_modified_time(p_path); + String dynamic_library_path = get_dynamic_libraries_path().path_join(String::num_uint64(so_modified_time)); + String internal_path = dynamic_library_path.path_join(p_path.get_file()); + + bool internal_so_file_exists = FileAccess::exists(internal_path); + if (!internal_so_file_exists) { + Ref<DirAccess> da_ref = DirAccess::create_for_path(p_path); + if (da_ref.is_valid()) { + Error create_dir_result = da_ref->make_dir_recursive(dynamic_library_path); + if (create_dir_result == OK || create_dir_result == ERR_ALREADY_EXISTS) { + internal_so_file_exists = da_ref->copy(path, internal_path) == OK; + } + } + } + + if (internal_so_file_exists) { + p_library_handle = dlopen(internal_path.utf8().get_data(), RTLD_NOW); + if (p_library_handle) { + path = internal_path; + } + } + } + ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror())); if (r_resolved_path != nullptr) { @@ -584,6 +614,10 @@ String OS_Android::get_user_data_dir() const { return "."; } +String OS_Android::get_dynamic_libraries_path() const { + return get_cache_path().path_join("dynamic_libraries"); +} + String OS_Android::get_cache_path() const { if (!cache_dir_cache.is_empty()) { return cache_dir_cache; @@ -791,5 +825,13 @@ Error OS_Android::setup_remote_filesystem(const String &p_server_host, int p_por return err; } +void OS_Android::load_platform_gdextensions() const { + Vector<String> extension_list_config_file = godot_java->get_gdextension_list_config_file(); + for (String config_file_path : extension_list_config_file) { + GDExtensionManager::LoadStatus err = GDExtensionManager::get_singleton()->load_extension(config_file_path); + ERR_CONTINUE_MSG(err == GDExtensionManager::LOAD_STATUS_FAILED, "Error loading platform extension: " + config_file_path); + } +} + OS_Android::~OS_Android() { } diff --git a/platform/android/os_android.h b/platform/android/os_android.h index abcc412588..f88f3e0518 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -169,9 +169,15 @@ public: virtual void benchmark_end_measure(const String &p_what) override; virtual void benchmark_dump() override; + virtual void load_platform_gdextensions() const override; + virtual bool _check_internal_feature_support(const String &p_feature) override; OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion); ~OS_Android(); + +private: + // Location where we relocate external dynamic libraries to make them accessible. + String get_dynamic_libraries_path() const; }; #endif // OS_ANDROID_H diff --git a/platform/uwp/context_egl_uwp.h b/platform/android/platform_gl.h index 810c3dcbc1..af6edb103d 100644 --- a/platform/uwp/context_egl_uwp.h +++ b/platform/android/platform_gl.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* context_egl_uwp.h */ +/* platform_gl.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,57 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef CONTEXT_EGL_UWP_H -#define CONTEXT_EGL_UWP_H +#ifndef PLATFORM_GL_H +#define PLATFORM_GL_H -#include "core/error/error_list.h" -#include "core/os/os.h" +#ifndef GLES_API_ENABLED +#define GLES_API_ENABLED // Allow using GLES. +#endif #include <EGL/egl.h> -#include <wrl.h> +#include <EGL/eglext.h> +#include <GLES3/gl3.h> +#include <GLES3/gl3ext.h> -using namespace Windows::UI::Core; - -class ContextEGL_UWP { -public: - enum Driver { - GLES_2_0, - VULKAN, // FIXME: Add Vulkan support. - }; - -private: - CoreWindow ^ window; - - EGLDisplay mEglDisplay; - EGLContext mEglContext; - EGLSurface mEglSurface; - - EGLint width; - EGLint height; - - bool vsync; - - Driver driver; - -public: - void release_current(); - - void make_current(); - - int get_window_width(); - int get_window_height(); - void swap_buffers(); - - void set_use_vsync(bool use) { vsync = use; } - bool is_using_vsync() const { return vsync; } - - Error initialize(); - void reset(); - - void cleanup(); - - ContextEGL_UWP(CoreWindow ^ p_window, Driver p_driver); - ~ContextEGL_UWP(); -}; - -#endif // CONTEXT_EGL_UWP_H +#endif // PLATFORM_GL_H diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp index 5d48c4e248..fd60ba4ae7 100644 --- a/platform/android/plugin/godot_plugin_jni.cpp +++ b/platform/android/plugin/godot_plugin_jni.cpp @@ -129,31 +129,4 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitS singleton->emit_signalp(StringName(signal_name), args, count); } - -JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDExtensionLibraries(JNIEnv *env, jclass clazz, jobjectArray gdextension_paths) { - int gdextension_count = env->GetArrayLength(gdextension_paths); - if (gdextension_count == 0) { - return; - } - - // Retrieve the current list of gdextension libraries. - Array singletons; - if (ProjectSettings::get_singleton()->has_setting("gdextension/singletons")) { - singletons = GLOBAL_GET("gdextension/singletons"); - } - - // Insert the libraries provided by the plugin - for (int i = 0; i < gdextension_count; i++) { - jstring relative_path = (jstring)env->GetObjectArrayElement(gdextension_paths, i); - - String path = "res://" + jstring_to_string(relative_path, env); - if (!singletons.has(path)) { - singletons.push_back(path); - } - env->DeleteLocalRef(relative_path); - } - - // Insert the updated list back into project settings. - ProjectSettings::get_singleton()->set("gdextension/singletons", singletons); -} } diff --git a/platform/android/plugin/godot_plugin_jni.h b/platform/android/plugin/godot_plugin_jni.h index 36a992246d..baa29a79ea 100644 --- a/platform/android/plugin/godot_plugin_jni.h +++ b/platform/android/plugin/godot_plugin_jni.h @@ -39,7 +39,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jclass clazz, jstring sname, jstring name, jstring ret, jobjectArray args); JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types); JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params); -JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDExtensionLibraries(JNIEnv *env, jclass clazz, jobjectArray gdextension_paths); } #endif // GODOT_PLUGIN_JNI_H diff --git a/platform/android/tts_android.cpp b/platform/android/tts_android.cpp index aef59c2584..93517d8045 100644 --- a/platform/android/tts_android.cpp +++ b/platform/android/tts_android.cpp @@ -54,7 +54,7 @@ void TTS_Android::setup(jobject p_tts) { bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech"); if (tts_enabled) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND(env == nullptr); + ERR_FAIL_NULL(env); tts = env->NewGlobalRef(p_tts); @@ -103,7 +103,7 @@ bool TTS_Android::is_speaking() { if (_is_speaking) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, false); + ERR_FAIL_NULL_V(env, false); return env->CallBooleanMethod(tts, _is_speaking); } else { return false; @@ -115,7 +115,7 @@ bool TTS_Android::is_paused() { if (_is_paused) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, false); + ERR_FAIL_NULL_V(env, false); return env->CallBooleanMethod(tts, _is_paused); } else { return false; @@ -127,7 +127,7 @@ Array TTS_Android::get_voices() { Array list; if (_get_voices) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND_V(env == nullptr, list); + ERR_FAIL_NULL_V(env, list); jobject voices_object = env->CallObjectMethod(tts, _get_voices); jobjectArray *arr = reinterpret_cast<jobjectArray *>(&voices_object); @@ -165,7 +165,7 @@ void TTS_Android::speak(const String &p_text, const String &p_voice, int p_volum if (_speak) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND(env == nullptr); + ERR_FAIL_NULL(env); jstring jStrT = env->NewStringUTF(p_text.utf8().get_data()); jstring jStrV = env->NewStringUTF(p_voice.utf8().get_data()); @@ -178,7 +178,7 @@ void TTS_Android::pause() { if (_pause_speaking) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND(env == nullptr); + ERR_FAIL_NULL(env); env->CallVoidMethod(tts, _pause_speaking); } } @@ -188,7 +188,7 @@ void TTS_Android::resume() { if (_resume_speaking) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND(env == nullptr); + ERR_FAIL_NULL(env); env->CallVoidMethod(tts, _resume_speaking); } } @@ -203,7 +203,7 @@ void TTS_Android::stop() { if (_stop_speaking) { JNIEnv *env = get_jni_env(); - ERR_FAIL_COND(env == nullptr); + ERR_FAIL_NULL(env); env->CallVoidMethod(tts, _stop_speaking); } } diff --git a/platform/ios/detect.py b/platform/ios/detect.py index e11c0b7d91..40eb61abc8 100644 --- a/platform/ios/detect.py +++ b/platform/ios/detect.py @@ -103,13 +103,13 @@ def configure(env: "Environment"): if env["ios_simulator"]: detect_darwin_sdk_path("iossimulator", env) - env.Append(ASFLAGS=["-mios-simulator-version-min=11.0"]) - env.Append(CCFLAGS=["-mios-simulator-version-min=11.0"]) + env.Append(ASFLAGS=["-mios-simulator-version-min=12.0"]) + env.Append(CCFLAGS=["-mios-simulator-version-min=12.0"]) env.extra_suffix = ".simulator" + env.extra_suffix else: detect_darwin_sdk_path("ios", env) - env.Append(ASFLAGS=["-miphoneos-version-min=11.0"]) - env.Append(CCFLAGS=["-miphoneos-version-min=11.0"]) + env.Append(ASFLAGS=["-miphoneos-version-min=12.0"]) + env.Append(CCFLAGS=["-miphoneos-version-min=12.0"]) if env["arch"] == "x86_64": if not env["ios_simulator"]: @@ -154,7 +154,7 @@ def configure(env: "Environment"): env.Append(CPPDEFINES=["VULKAN_ENABLED"]) if env["opengl3"]: - env.Append(CPPDEFINES=["GLES3_ENABLED"]) + env.Append(CPPDEFINES=["GLES3_ENABLED", "GLES_SILENCE_DEPRECATION"]) env.Prepend( CPPPATH=[ "$IOS_SDK_PATH/System/Library/Frameworks/OpenGLES.framework/Headers", diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h index da16449c61..be4ea1e6ab 100644 --- a/platform/ios/display_server_ios.h +++ b/platform/ios/display_server_ios.h @@ -141,6 +141,9 @@ public: virtual void tts_resume() override; virtual void tts_stop() override; + virtual bool is_dark_mode_supported() const override; + virtual bool is_dark_mode() const override; + virtual Rect2i get_display_safe_area() const override; virtual int get_screen_count() const override; diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index 7d91274a0c..489894a135 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -103,7 +103,7 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode ERR_FAIL_MSG("Failed to create iOS OpenGLES rendering layer."); } - RasterizerGLES3::make_current(); + RasterizerGLES3::make_current(false); } #endif @@ -354,20 +354,32 @@ void DisplayServerIOS::tts_stop() { [tts stopSpeaking]; } -Rect2i DisplayServerIOS::get_display_safe_area() const { - if (@available(iOS 11, *)) { - UIEdgeInsets insets = UIEdgeInsetsZero; - UIView *view = AppDelegate.viewController.godotView; - if ([view respondsToSelector:@selector(safeAreaInsets)]) { - insets = [view safeAreaInsets]; - } - float scale = screen_get_scale(); - Size2i insets_position = Size2i(insets.left, insets.top) * scale; - Size2i insets_size = Size2i(insets.left + insets.right, insets.top + insets.bottom) * scale; - return Rect2i(screen_get_position() + insets_position, screen_get_size() - insets_size); +bool DisplayServerIOS::is_dark_mode_supported() const { + if (@available(iOS 13.0, *)) { + return true; + } else { + return false; + } +} + +bool DisplayServerIOS::is_dark_mode() const { + if (@available(iOS 13.0, *)) { + return [UITraitCollection currentTraitCollection].userInterfaceStyle == UIUserInterfaceStyleDark; } else { - return Rect2i(screen_get_position(), screen_get_size()); + return false; + } +} + +Rect2i DisplayServerIOS::get_display_safe_area() const { + UIEdgeInsets insets = UIEdgeInsetsZero; + UIView *view = AppDelegate.viewController.godotView; + if ([view respondsToSelector:@selector(safeAreaInsets)]) { + insets = [view safeAreaInsets]; } + float scale = screen_get_scale(); + Size2i insets_position = Size2i(insets.left, insets.top) * scale; + Size2i insets_size = Size2i(insets.left + insets.right, insets.top + insets.bottom) * scale; + return Rect2i(screen_get_position() + insets_position, screen_get_size() - insets_size); } int DisplayServerIOS::get_screen_count() const { diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml index 563c057266..ecae6d721e 100644 --- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml +++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml @@ -7,6 +7,7 @@ </description> <tutorials> <link title="Exporting for iOS">$DOCS_URL/tutorials/export/exporting_for_ios.html</link> + <link title="iOS plugins documentation index">$DOCS_URL/tutorials/platform/ios/index.html</link> </tutorials> <members> <member name="application/app_store_team_id" type="String" setter="" getter=""> diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index b6320fb22b..a8596c30a6 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -38,6 +38,7 @@ #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_scale.h" +#include "editor/editor_string_names.h" #include "editor/export/editor_export.h" #include "editor/import/resource_importer_texture_settings.h" #include "editor/plugins/script_editor_plugin.h" @@ -2013,11 +2014,11 @@ Ref<ImageTexture> EditorExportPlatformIOS::get_option_icon(int p_index) const { Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); if (theme.is_valid()) { if (devices[p_index].simulator) { - icon = theme->get_icon("IOSSimulator", "EditorIcons"); + icon = theme->get_icon("IOSSimulator", EditorStringName(EditorIcons)); } else if (devices[p_index].wifi) { - icon = theme->get_icon("IOSDeviceWireless", "EditorIcons"); + icon = theme->get_icon("IOSDeviceWireless", EditorStringName(EditorIcons)); } else { - icon = theme->get_icon("IOSDeviceWired", "EditorIcons"); + icon = theme->get_icon("IOSDeviceWired", EditorStringName(EditorIcons)); } } } diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h index 7de4c0b69d..27a4d73fcd 100644 --- a/platform/ios/export/export_plugin.h +++ b/platform/ios/export/export_plugin.h @@ -181,9 +181,17 @@ public: virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { List<String> list; - list.push_back("ipa"); + if (p_preset.is_valid()) { + bool project_only = p_preset->get("application/export_project_only"); + if (project_only) { + list.push_back("xcodeproj"); + } else { + list.push_back("ipa"); + } + } return list; } + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override; diff --git a/platform/ios/godot_view.mm b/platform/ios/godot_view.mm index 4c9a75fdc0..ff8a4f8921 100644 --- a/platform/ios/godot_view.mm +++ b/platform/ios/godot_view.mm @@ -82,10 +82,10 @@ static const float earth_gravity = 9.80665; layer = [GodotMetalLayer layer]; #endif } else if ([driverName isEqualToString:@"opengl3"]) { - if (@available(iOS 13, *)) { - NSLog(@"OpenGL ES is deprecated on iOS 13"); - } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" // OpenGL is deprecated in iOS 12.0 layer = [GodotOpenGLLayer layer]; +#pragma clang diagnostic pop } else { return nil; } diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm index 50102e02cc..68e6d4c934 100644 --- a/platform/ios/os_ios.mm +++ b/platform/ios/os_ios.mm @@ -257,7 +257,7 @@ Error OS_IOS::open_dynamic_library(const String p_path, void *&p_library_handle, } p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); - ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror())); + ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror())); if (r_resolved_path != nullptr) { *r_resolved_path = path; diff --git a/platform/ios/platform_config.h b/platform/ios/platform_config.h index fc0e165d6b..01b0a12b5d 100644 --- a/platform/ios/platform_config.h +++ b/platform/ios/platform_config.h @@ -30,8 +30,6 @@ #include <alloca.h> -#define OPENGL_INCLUDE_H <ES3/gl.h> - #define PTHREAD_RENAME_SELF #define _weakify(var) __weak typeof(var) GDWeak_##var = var; diff --git a/platform/uwp/export/export.h b/platform/ios/platform_gl.h index d2053e02b1..974ea9d2c2 100644 --- a/platform/uwp/export/export.h +++ b/platform/ios/platform_gl.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* export.h */ +/* platform_gl.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,10 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef UWP_EXPORT_H -#define UWP_EXPORT_H +#ifndef PLATFORM_GL_H +#define PLATFORM_GL_H -void register_uwp_exporter_types(); -void register_uwp_exporter(); +#ifndef GLES_API_ENABLED +#define GLES_API_ENABLED // Allow using GLES. +#endif -#endif // UWP_EXPORT_H +#include <ES3/gl.h> + +#endif // PLATFORM_GL_H diff --git a/platform/ios/view_controller.mm b/platform/ios/view_controller.mm index 0ef61da646..1f55670b68 100644 --- a/platform/ios/view_controller.mm +++ b/platform/ios/view_controller.mm @@ -161,9 +161,7 @@ [self observeKeyboard]; [self displayLoadingOverlay]; - if (@available(iOS 11.0, *)) { - [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; - } + [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; } - (void)observeKeyboard { diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index a723bb5d58..72bffceb1f 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -106,7 +106,7 @@ def configure(env: "Environment"): print("Using linker program: " + env["linker"]) if env["linker"] == "mold" and using_gcc(env): # GCC < 12.1 doesn't support -fuse-ld=mold. cc_version = get_compiler_version(env) - cc_semver = (int(cc_version["major"]), int(cc_version["minor"])) + cc_semver = (cc_version["major"], cc_version["minor"]) if cc_semver < (12, 1): found_wrapper = False for path in ["/usr/libexec", "/usr/local/libexec", "/usr/lib", "/usr/local/lib"]: @@ -455,7 +455,7 @@ def configure(env: "Environment"): linker_version_str = subprocess.check_output( [env.subst(env["LINK"]), "-Wl,--version"] + env.subst(env["LINKFLAGS"]) ).decode("utf-8") - gnu_ld_version = re.search("^GNU ld [^$]*(\d+\.\d+)$", linker_version_str, re.MULTILINE) + gnu_ld_version = re.search(r"^GNU ld [^$]*(\d+\.\d+)$", linker_version_str, re.MULTILINE) if not gnu_ld_version: print( "Warning: Creating export template binaries enabled for PCK embedding is currently only supported with GNU ld, not gold, LLD or mold." diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp index 40151b1a02..9d1e058b76 100644 --- a/platform/linuxbsd/export/export_plugin.cpp +++ b/platform/linuxbsd/export/export_plugin.cpp @@ -37,6 +37,7 @@ #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_scale.h" +#include "editor/editor_string_names.h" #include "editor/export/editor_export.h" #include "modules/modules_enabled.gen.h" // For svg. @@ -530,7 +531,7 @@ EditorExportPlatformLinuxBSD::EditorExportPlatformLinuxBSD() { Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); if (theme.is_valid()) { - stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons")); + stop_icon = theme->get_icon(SNAME("Stop"), EditorStringName(EditorIcons)); } else { stop_icon.instantiate(); } diff --git a/platform/linuxbsd/godot_linuxbsd.cpp b/platform/linuxbsd/godot_linuxbsd.cpp index d059d60b72..efad9c8594 100644 --- a/platform/linuxbsd/godot_linuxbsd.cpp +++ b/platform/linuxbsd/godot_linuxbsd.cpp @@ -56,7 +56,7 @@ int main(int argc, char *argv[]) { TEST_MAIN_OVERRIDE char *cwd = (char *)malloc(PATH_MAX); - ERR_FAIL_COND_V(!cwd, ERR_OUT_OF_MEMORY); + ERR_FAIL_NULL_V(cwd, ERR_OUT_OF_MEMORY); char *ret = getcwd(cwd, PATH_MAX); Error err = Main::setup(argv[0], argc - 1, &argv[1]); diff --git a/platform/linuxbsd/platform_config.h b/platform/linuxbsd/platform_config.h index 82c9c54879..c372ef28f6 100644 --- a/platform/linuxbsd/platform_config.h +++ b/platform/linuxbsd/platform_config.h @@ -43,5 +43,3 @@ #define PTHREAD_BSD_SET_NAME #endif #endif - -#define OPENGL_INCLUDE_H "thirdparty/glad/glad/gl.h" diff --git a/platform/linuxbsd/platform_gl.h b/platform/linuxbsd/platform_gl.h new file mode 100644 index 0000000000..1c19c4518a --- /dev/null +++ b/platform/linuxbsd/platform_gl.h @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* platform_gl.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef PLATFORM_GL_H +#define PLATFORM_GL_H + +#ifndef GL_API_ENABLED +#define GL_API_ENABLED // Allow using desktop GL. +#endif + +#include "thirdparty/glad/glad/egl.h" +#include "thirdparty/glad/glad/gl.h" + +#endif // PLATFORM_GL_H diff --git a/platform/linuxbsd/tts_linux.cpp b/platform/linuxbsd/tts_linux.cpp index 8bc83f5b9c..96745ab8ae 100644 --- a/platform/linuxbsd/tts_linux.cpp +++ b/platform/linuxbsd/tts_linux.cpp @@ -186,7 +186,7 @@ bool TTS_Linux::is_paused() const { Array TTS_Linux::get_voices() const { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(!synth, Array()); + ERR_FAIL_NULL_V(synth, Array()); const_cast<TTS_Linux *>(this)->_load_voices(); Array list; @@ -204,7 +204,7 @@ Array TTS_Linux::get_voices() const { void TTS_Linux::speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND(!synth); + ERR_FAIL_NULL(synth); if (p_interrupt) { stop(); } @@ -233,7 +233,7 @@ void TTS_Linux::speak(const String &p_text, const String &p_voice, int p_volume, void TTS_Linux::pause() { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND(!synth); + ERR_FAIL_NULL(synth); if (spd_pause(synth) == 0) { paused = true; } @@ -242,7 +242,7 @@ void TTS_Linux::pause() { void TTS_Linux::resume() { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND(!synth); + ERR_FAIL_NULL(synth); spd_resume(synth); paused = false; } @@ -250,7 +250,7 @@ void TTS_Linux::resume() { void TTS_Linux::stop() { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND(!synth); + ERR_FAIL_NULL(synth); for (DisplayServer::TTSUtterance &message : queue) { DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, message.id); } diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index f38a9dd278..897a6438d2 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -307,37 +307,37 @@ void DisplayServerX11::_flush_mouse_motion() { #ifdef SPEECHD_ENABLED bool DisplayServerX11::tts_is_speaking() const { - ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return tts->is_speaking(); } bool DisplayServerX11::tts_is_paused() const { - ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return tts->is_paused(); } TypedArray<Dictionary> DisplayServerX11::tts_get_voices() const { - ERR_FAIL_COND_V_MSG(!tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_V_MSG(tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return tts->get_voices(); } void DisplayServerX11::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt); } void DisplayServerX11::tts_pause() { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); tts->pause(); } void DisplayServerX11::tts_resume() { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); tts->resume(); } void DisplayServerX11::tts_stop() { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); tts->stop(); } @@ -5100,7 +5100,7 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V XVisualInfo vInfoTemplate = {}; vInfoTemplate.screen = DefaultScreen(x11_display); XVisualInfo *vi_list = XGetVisualInfo(x11_display, visualMask, &vInfoTemplate, &numberOfVisuals); - ERR_FAIL_COND_V(!vi_list, INVALID_WINDOW_ID); + ERR_FAIL_NULL_V(vi_list, INVALID_WINDOW_ID); visualInfo = vi_list[0]; if (OS::get_singleton()->is_layered_allowed()) { @@ -5821,7 +5821,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode driver_found = true; if (true) { - RasterizerGLES3::make_current(); + RasterizerGLES3::make_current(true); } else { memdelete(gl_manager); gl_manager = nullptr; diff --git a/platform/linuxbsd/x11/gl_manager_x11.cpp b/platform/linuxbsd/x11/gl_manager_x11.cpp index f24bac5e19..95947301cf 100644 --- a/platform/linuxbsd/x11/gl_manager_x11.cpp +++ b/platform/linuxbsd/x11/gl_manager_x11.cpp @@ -103,7 +103,7 @@ Error GLManager_X11::_create_context(GLDisplay &gl_display) { GLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (GLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((const GLubyte *)"glXCreateContextAttribsARB"); - ERR_FAIL_COND_V(!glXCreateContextAttribsARB, ERR_UNCONFIGURED); + ERR_FAIL_NULL_V(glXCreateContextAttribsARB, ERR_UNCONFIGURED); static int visual_attribs[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT, @@ -134,7 +134,7 @@ Error GLManager_X11::_create_context(GLDisplay &gl_display) { if (OS::get_singleton()->is_layered_allowed()) { GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs_layered, &fbcount); - ERR_FAIL_COND_V(!fbc, ERR_UNCONFIGURED); + ERR_FAIL_NULL_V(fbc, ERR_UNCONFIGURED); for (int i = 0; i < fbcount; i++) { vi = (XVisualInfo *)glXGetVisualFromFBConfig(x11_display, fbc[i]); @@ -156,10 +156,10 @@ Error GLManager_X11::_create_context(GLDisplay &gl_display) { } XFree(fbc); - ERR_FAIL_COND_V(!fbconfig, ERR_UNCONFIGURED); + ERR_FAIL_NULL_V(fbconfig, ERR_UNCONFIGURED); } else { GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount); - ERR_FAIL_COND_V(!fbc, ERR_UNCONFIGURED); + ERR_FAIL_NULL_V(fbc, ERR_UNCONFIGURED); vi = glXGetVisualFromFBConfig(x11_display, fbc[0]); @@ -347,12 +347,6 @@ Error GLManager_X11::initialize(Display *p_display) { } void GLManager_X11::set_use_vsync(bool p_use) { - // force vsync in the editor for now, as a safety measure - bool is_editor = Engine::get_singleton()->is_editor_hint(); - if (is_editor) { - p_use = true; - } - // we need an active window to get a display to set the vsync if (!_current_window) { return; @@ -368,6 +362,7 @@ void GLManager_X11::set_use_vsync(bool p_use) { GLXDrawable drawable = glXGetCurrentDrawable(); glXSwapIntervalEXT(disp.x11_display, drawable, val); } else { + WARN_PRINT("Could not set V-Sync mode. V-Sync is not supported."); return; } use_vsync = p_use; diff --git a/platform/macos/SCsub b/platform/macos/SCsub index 7ffb80f70b..30202e5de7 100644 --- a/platform/macos/SCsub +++ b/platform/macos/SCsub @@ -24,6 +24,7 @@ files = [ "tts_macos.mm", "joypad_macos.cpp", "vulkan_context_macos.mm", + "gl_manager_macos_angle.mm", "gl_manager_macos_legacy.mm", ] diff --git a/platform/macos/detect.py b/platform/macos/detect.py index 21e824b2d3..b41d2141fb 100644 --- a/platform/macos/detect.py +++ b/platform/macos/detect.py @@ -1,6 +1,6 @@ import os import sys -from methods import detect_darwin_sdk_path +from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang from platform_methods import detect_arch from typing import TYPE_CHECKING @@ -32,6 +32,7 @@ def get_opts(): BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN)", False), BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN)", False), BoolVariable("use_coverage", "Use instrumentation codes in the binary (e.g. for code coverage)", False), + ("angle_libs", "Path to the ANGLE static libraries", ""), ] @@ -119,6 +120,15 @@ def configure(env: "Environment"): env.Append(CCFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.13"]) env.Append(LINKFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.13"]) + cc_version = get_compiler_version(env) + cc_version_major = cc_version["major"] + cc_version_minor = cc_version["minor"] + vanilla = is_vanilla_clang(env) + + # Workaround for Xcode 15 linker bug. + if not vanilla and cc_version_major == 15 and cc_version_minor == 0: + env.Prepend(LINKFLAGS=["-ld_classic"]) + env.Append(CCFLAGS=["-fobjc-arc"]) if not "osxcross" in env: # regular native build @@ -239,7 +249,13 @@ def configure(env: "Environment"): if env["opengl3"]: env.Append(CPPDEFINES=["GLES3_ENABLED"]) - env.Append(LINKFLAGS=["-framework", "OpenGL"]) + if env["angle_libs"] != "": + env.AppendUnique(CPPDEFINES=["EGL_STATIC"]) + env.Append(LINKFLAGS=["-L" + env["angle_libs"]]) + env.Append(LINKFLAGS=["-lANGLE.macos." + env["arch"]]) + env.Append(LINKFLAGS=["-lEGL.macos." + env["arch"]]) + env.Append(LINKFLAGS=["-lGLES.macos." + env["arch"]]) + env.Prepend(CPPPATH=["#thirdparty/angle/include"]) env.Append(LINKFLAGS=["-rpath", "@executable_path/../Frameworks", "-rpath", "@executable_path"]) diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index 69f6008043..d89511927e 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -35,6 +35,7 @@ #include "servers/display_server.h" #if defined(GLES3_ENABLED) +#include "gl_manager_macos_angle.h" #include "gl_manager_macos_legacy.h" #endif // GLES3_ENABLED @@ -127,7 +128,8 @@ public: private: #if defined(GLES3_ENABLED) - GLManager_MacOS *gl_manager = nullptr; + GLManagerLegacy_MacOS *gl_manager_legacy = nullptr; + GLManagerANGLE_MacOS *gl_manager_angle = nullptr; #endif #if defined(VULKAN_ENABLED) VulkanContextMacOS *context_vulkan = nullptr; @@ -244,7 +246,6 @@ public: void mouse_enter_window(WindowID p_window); void mouse_exit_window(WindowID p_window); - void window_update(WindowID p_window); void window_destroy(WindowID p_window); void window_resize(WindowID p_window, int p_width, int p_height); void window_set_custom_window_buttons(WindowData &p_wd, bool p_enabled); @@ -367,6 +368,7 @@ public: virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override; virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override; + virtual Size2i window_get_title_size(const String &p_title, WindowID p_window) const override; virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID) override; virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index e79d6acc3f..4e1ceac8bf 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -139,7 +139,7 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod // initWithContentRect uses bottom-left corner of the window’s frame as origin. wd.window_object = [[GodotWindow alloc] - initWithContentRect:NSMakeRect(100, 100, p_rect.size.width / scale, p_rect.size.height / scale) + initWithContentRect:NSMakeRect(100, 100, MAX(1, p_rect.size.width / scale), MAX(1, p_rect.size.height / scale)) styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO]; @@ -185,9 +185,13 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod } #endif #if defined(GLES3_ENABLED) - if (gl_manager) { - Error err = gl_manager->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 an OpenGL context"); + if (gl_manager_legacy) { + Error err = gl_manager_legacy->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 an OpenGL context."); + } + if (gl_manager_angle) { + Error err = gl_manager_angle->window_create(window_id_counter, nullptr, (__bridge void *)[wd.window_view layer], p_rect.size.width, p_rect.size.height); + ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create an OpenGL context."); } window_set_vsync_mode(p_vsync_mode, window_id_counter); #endif @@ -219,8 +223,11 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod } #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_resize(id, wd.size.width, wd.size.height); + if (gl_manager_legacy) { + gl_manager_legacy->window_resize(id, wd.size.width, wd.size.height); + } + if (gl_manager_angle) { + gl_manager_angle->window_resize(id, wd.size.width, wd.size.height); } #endif #if defined(VULKAN_ENABLED) @@ -279,8 +286,8 @@ void DisplayServerMacOS::_set_window_per_pixel_transparency_enabled(bool p_enabl [layer setOpaque:NO]; } #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_set_per_pixel_transparency_enabled(p_window, true); + if (gl_manager_legacy) { + gl_manager_legacy->window_set_per_pixel_transparency_enabled(p_window, true); } #endif wd.layered_window = true; @@ -299,8 +306,8 @@ void DisplayServerMacOS::_set_window_per_pixel_transparency_enabled(bool p_enabl [layer setOpaque:YES]; } #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_set_per_pixel_transparency_enabled(p_window, false); + if (gl_manager_legacy) { + gl_manager_legacy->window_set_per_pixel_transparency_enabled(p_window, false); } #endif wd.layered_window = false; @@ -730,18 +737,10 @@ bool DisplayServerMacOS::get_is_resizing() const { return is_resizing; } -void DisplayServerMacOS::window_update(WindowID p_window) { -#if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_update(p_window); - } -#endif -} - void DisplayServerMacOS::window_destroy(WindowID p_window) { #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_destroy(p_window); + if (gl_manager_legacy) { + gl_manager_legacy->window_destroy(p_window); } #endif #ifdef VULKAN_ENABLED @@ -754,8 +753,11 @@ void DisplayServerMacOS::window_destroy(WindowID p_window) { void DisplayServerMacOS::window_resize(WindowID p_window, int p_width, int p_height) { #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_resize(p_window, p_width, p_height); + if (gl_manager_legacy) { + gl_manager_legacy->window_resize(p_window, p_width, p_height); + } + if (gl_manager_angle) { + gl_manager_angle->window_resize(p_window, p_width, p_height); } #endif #if defined(VULKAN_ENABLED) @@ -1451,7 +1453,7 @@ void DisplayServerMacOS::global_menu_set_item_checkable(const String &p_menu_roo NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_CHECK_BOX : CHECKABLE_TYPE_NONE; } } @@ -1470,7 +1472,7 @@ void DisplayServerMacOS::global_menu_set_item_radio_checkable(const String &p_me NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_RADIO_BUTTON : CHECKABLE_TYPE_NONE; } } @@ -1489,7 +1491,7 @@ void DisplayServerMacOS::global_menu_set_item_callback(const String &p_menu_root NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->callback = p_callback; } } @@ -1508,7 +1510,7 @@ void DisplayServerMacOS::global_menu_set_item_key_callback(const String &p_menu_ NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->key_callback = p_key_callback; } } @@ -1527,7 +1529,7 @@ void DisplayServerMacOS::global_menu_set_item_tag(const String &p_menu_root, int NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->meta = p_tag; } } @@ -1642,7 +1644,7 @@ void DisplayServerMacOS::global_menu_set_item_state(const String &p_menu_root, i NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->state = p_state; } } @@ -1661,7 +1663,7 @@ void DisplayServerMacOS::global_menu_set_item_max_states(const String &p_menu_ro NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); obj->max_states = p_max_states; } } @@ -1680,7 +1682,7 @@ void DisplayServerMacOS::global_menu_set_item_icon(const String &p_menu_root, in NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_COND(!obj); + ERR_FAIL_NULL(obj); if (p_icon.is_valid()) { obj->img = p_icon->get_image(); obj->img = obj->img->duplicate(); @@ -1761,37 +1763,37 @@ void DisplayServerMacOS::global_menu_clear(const String &p_menu_root) { } bool DisplayServerMacOS::tts_is_speaking() const { - ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return [tts isSpeaking]; } bool DisplayServerMacOS::tts_is_paused() const { - ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return [tts isPaused]; } TypedArray<Dictionary> DisplayServerMacOS::tts_get_voices() const { - ERR_FAIL_COND_V_MSG(!tts, Array(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_V_MSG(tts, Array(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return [tts getVoices]; } void DisplayServerMacOS::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); [tts speak:p_text voice:p_voice volume:p_volume pitch:p_pitch rate:p_rate utterance_id:p_utterance_id interrupt:p_interrupt]; } void DisplayServerMacOS::tts_pause() { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); [tts pauseSpeaking]; } void DisplayServerMacOS::tts_resume() { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); [tts resumeSpeaking]; } void DisplayServerMacOS::tts_stop() { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); [tts stopSpeaking]; } @@ -2624,6 +2626,47 @@ void DisplayServerMacOS::window_set_title(const String &p_title, WindowID p_wind [wd.window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]]; } +Size2i DisplayServerMacOS::window_get_title_size(const String &p_title, WindowID p_window) const { + _THREAD_SAFE_METHOD_ + + Size2i size; + ERR_FAIL_COND_V(!windows.has(p_window), size); + + const WindowData &wd = windows[p_window]; + if (wd.fullscreen || wd.borderless) { + return size; + } + if ([wd.window_object respondsToSelector:@selector(isMiniaturized)]) { + if ([wd.window_object isMiniaturized]) { + return size; + } + } + + float scale = screen_get_max_scale(); + + if (wd.window_button_view) { + size.x = ([wd.window_button_view getOffset].x + [wd.window_button_view frame].size.width); + size.y = ([wd.window_button_view getOffset].y + [wd.window_button_view frame].size.height); + } else { + NSButton *cb = [wd.window_object standardWindowButton:NSWindowCloseButton]; + NSButton *mb = [wd.window_object standardWindowButton:NSWindowMiniaturizeButton]; + float cb_frame = NSMinX([cb frame]); + float mb_frame = NSMinX([mb frame]); + bool is_rtl = ([wd.window_object windowTitlebarLayoutDirection] == NSUserInterfaceLayoutDirectionRightToLeft); + + float window_buttons_spacing = (is_rtl) ? (cb_frame - mb_frame) : (mb_frame - cb_frame); + size.x = window_buttons_spacing * 4; + size.y = [cb frame].origin.y + [cb frame].size.height; + } + + NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSFont titleBarFontOfSize:0], NSFontAttributeName, nil]; + NSSize text_size = [[[NSAttributedString alloc] initWithString:[NSString stringWithUTF8String:p_title.utf8().get_data()] attributes:attributes] size]; + size.x += text_size.width; + size.y = MAX(size.y, text_size.height); + + return size * scale; +} + void DisplayServerMacOS::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) { _THREAD_SAFE_METHOD_ @@ -2912,7 +2955,7 @@ void DisplayServerMacOS::window_set_size(const Size2i p_size, WindowID p_window) top_left.x = old_frame.origin.x; top_left.y = NSMaxY(old_frame); - NSRect new_frame = NSMakeRect(0, 0, size.x, size.y); + NSRect new_frame = NSMakeRect(0, 0, MAX(1, size.x), MAX(1, size.y)); new_frame = [wd.window_object frameRectForContentRect:new_frame]; new_frame.origin.x = top_left.x; @@ -3370,8 +3413,11 @@ int64_t DisplayServerMacOS::window_get_native_handle(HandleType p_handle_type, W } #ifdef GLES3_ENABLED case OPENGL_CONTEXT: { - if (gl_manager) { - return (int64_t)gl_manager->get_context(p_window); + if (gl_manager_legacy) { + return (int64_t)gl_manager_legacy->get_context(p_window); + } + if (gl_manager_angle) { + return (int64_t)gl_manager_angle->get_context(p_window); } return 0; } @@ -3398,8 +3444,11 @@ ObjectID DisplayServerMacOS::window_get_attached_instance_id(WindowID p_window) void DisplayServerMacOS::gl_window_make_current(DisplayServer::WindowID p_window_id) { #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_make_current(p_window_id); + if (gl_manager_legacy) { + gl_manager_legacy->window_make_current(p_window_id); + } + if (gl_manager_angle) { + gl_manager_angle->window_make_current(p_window_id); } #endif } @@ -3407,8 +3456,11 @@ void DisplayServerMacOS::gl_window_make_current(DisplayServer::WindowID p_window void DisplayServerMacOS::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { _THREAD_SAFE_METHOD_ #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED); + if (gl_manager_angle) { + gl_manager_angle->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED); + } + if (gl_manager_legacy) { + gl_manager_legacy->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED); } #endif #if defined(VULKAN_ENABLED) @@ -3421,8 +3473,11 @@ void DisplayServerMacOS::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_ DisplayServer::VSyncMode DisplayServerMacOS::window_get_vsync_mode(WindowID p_window) const { _THREAD_SAFE_METHOD_ #if defined(GLES3_ENABLED) - if (gl_manager) { - return (gl_manager->is_using_vsync() ? DisplayServer::VSyncMode::VSYNC_ENABLED : DisplayServer::VSyncMode::VSYNC_DISABLED); + if (gl_manager_angle) { + return (gl_manager_angle->is_using_vsync() ? DisplayServer::VSyncMode::VSYNC_ENABLED : DisplayServer::VSyncMode::VSYNC_DISABLED); + } + if (gl_manager_legacy) { + return (gl_manager_legacy->is_using_vsync() ? DisplayServer::VSyncMode::VSYNC_ENABLED : DisplayServer::VSyncMode::VSYNC_DISABLED); } #endif #if defined(VULKAN_ENABLED) @@ -3805,8 +3860,11 @@ void DisplayServerMacOS::make_rendering_thread() { void DisplayServerMacOS::swap_buffers() { #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->swap_buffers(); + if (gl_manager_angle) { + gl_manager_angle->swap_buffers(); + } + if (gl_manager_legacy) { + gl_manager_legacy->swap_buffers(); } #endif } @@ -3826,10 +3884,10 @@ void DisplayServerMacOS::set_native_icon(const String &p_filename) { @try { NSData *icon_data = [[NSData alloc] initWithBytes:&data.write[0] length:len]; - ERR_FAIL_COND_MSG(!icon_data, "Error reading icon data."); + ERR_FAIL_NULL_MSG(icon_data, "Error reading icon data."); NSImage *icon = [[NSImage alloc] initWithData:icon_data]; - ERR_FAIL_COND_MSG(!icon, "Error loading icon."); + ERR_FAIL_NULL_MSG(icon, "Error loading icon."); [NSApp setApplicationIconImage:icon]; } @catch (NSException *exception) { @@ -3917,6 +3975,7 @@ Vector<String> DisplayServerMacOS::get_rendering_drivers_func() { #endif #if defined(GLES3_ENABLED) drivers.push_back("opengl3"); + drivers.push_back("opengl3_angle"); #endif return drivers; @@ -4155,13 +4214,22 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM #if defined(GLES3_ENABLED) if (rendering_driver == "opengl3") { - GLManager_MacOS::ContextType opengl_api_type = GLManager_MacOS::GLES_3_0_COMPATIBLE; - gl_manager = memnew(GLManager_MacOS(opengl_api_type)); - if (gl_manager->initialize() != OK) { - memdelete(gl_manager); - gl_manager = nullptr; + gl_manager_legacy = memnew(GLManagerLegacy_MacOS); + if (gl_manager_legacy->initialize() != OK) { + memdelete(gl_manager_legacy); + gl_manager_legacy = nullptr; + r_error = ERR_UNAVAILABLE; + ERR_FAIL_MSG("Could not initialize OpenGL."); + return; + } + } + if (rendering_driver == "opengl3_angle") { + gl_manager_angle = memnew(GLManagerANGLE_MacOS); + if (gl_manager_angle->initialize() != OK) { + memdelete(gl_manager_angle); + gl_manager_angle = nullptr; r_error = ERR_UNAVAILABLE; - ERR_FAIL_MSG("Could not initialize OpenGL"); + ERR_FAIL_MSG("Could not initialize OpenGL."); } } #endif @@ -4199,7 +4267,10 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM #if defined(GLES3_ENABLED) if (rendering_driver == "opengl3") { - RasterizerGLES3::make_current(); + RasterizerGLES3::make_current(true); + } + if (rendering_driver == "opengl3_angle") { + RasterizerGLES3::make_current(false); } #endif #if defined(VULKAN_ENABLED) @@ -4230,9 +4301,13 @@ DisplayServerMacOS::~DisplayServerMacOS() { // Destroy drivers. #if defined(GLES3_ENABLED) - if (gl_manager) { - memdelete(gl_manager); - gl_manager = nullptr; + if (gl_manager_legacy) { + memdelete(gl_manager_legacy); + gl_manager_legacy = nullptr; + } + if (gl_manager_angle) { + memdelete(gl_manager_angle); + gl_manager_angle = nullptr; } #endif #if defined(VULKAN_ENABLED) diff --git a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml index c24ff4cb73..99187fe60e 100644 --- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml +++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml @@ -22,6 +22,9 @@ <member name="application/copyright_localized" type="Dictionary" setter="" getter=""> Copyright notice for the bundle visible to the user (localized). </member> + <member name="application/export_angle" type="int" setter="" getter=""> + If set to [code]1[/code], ANGLE libraries are exported with the exported application. If set to [code]0[/code], ANGLE libraries are exported only if [member ProjectSettings.rendering/gl_compatibility/driver] is set to [code]"opengl3_angle"[/code]. + </member> <member name="application/icon" type="String" setter="" getter=""> Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/macos_native_icon], and then to [member ProjectSettings.application/config/icon]. </member> @@ -155,7 +158,7 @@ If enabled, a wrapper that can be used to run the application with console output is created alongside the exported application. </member> <member name="display/high_res" type="bool" setter="" getter=""> - If [code]true[/code], the application is rendered at native display resolution, otherwise it is always rendered at loHPI resolution and upscaled by OS when required. + If [code]true[/code], the application is rendered at native display resolution, otherwise it is always rendered at loDPI resolution and upscaled by OS when required. </member> <member name="export/distribution_type" type="int" setter="" getter=""> Application distribution target. diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 6586fe7f82..639a7699ee 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -41,6 +41,7 @@ #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_scale.h" +#include "editor/editor_string_names.h" #include "editor/import/resource_importer_texture_settings.h" #include "scene/resources/image_texture.h" @@ -383,6 +384,7 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "application/copyright_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/min_macos_version"), "10.12")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_angle", PROPERTY_HINT_ENUM, "Auto,Yes,No"), 0, true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/platform_build"), "14C18")); @@ -1617,6 +1619,14 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p // Now process our template. bool found_binary = false; + int export_angle = p_preset->get("application/export_angle"); + bool include_angle_libs = false; + if (export_angle == 0) { + include_angle_libs = String(GLOBAL_GET("rendering/gl_compatibility/driver.macos")) == "opengl3_angle"; + } else if (export_angle == 1) { + include_angle_libs = true; + } + while (ret == UNZ_OK && err == OK) { // Get filename. unz_file_info info; @@ -1664,6 +1674,20 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p continue; // next } + if (file == "Contents/Frameworks/libEGL.dylib") { + if (!include_angle_libs) { + ret = unzGoToNextFile(src_pkg_zip); + continue; // skip + } + } + + if (file == "Contents/Frameworks/libGLESv2.dylib") { + if (!include_angle_libs) { + ret = unzGoToNextFile(src_pkg_zip); + continue; // skip + } + } + if (file == "Contents/Info.plist") { _fix_plist(p_preset, data, pkg_name); } @@ -2460,7 +2484,7 @@ EditorExportPlatformMacOS::EditorExportPlatformMacOS() { Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); if (theme.is_valid()) { - stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons")); + stop_icon = theme->get_icon(SNAME("Stop"), EditorStringName(EditorIcons)); } else { stop_icon.instantiate(); } diff --git a/platform/macos/gl_manager_macos_angle.h b/platform/macos/gl_manager_macos_angle.h new file mode 100644 index 0000000000..919b8ec9c8 --- /dev/null +++ b/platform/macos/gl_manager_macos_angle.h @@ -0,0 +1,63 @@ +/**************************************************************************/ +/* gl_manager_macos_angle.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef GL_MANAGER_MACOS_ANGLE_H +#define GL_MANAGER_MACOS_ANGLE_H + +#if defined(MACOS_ENABLED) && defined(GLES3_ENABLED) + +#include "core/error/error_list.h" +#include "core/os/os.h" +#include "core/templates/local_vector.h" +#include "drivers/egl/egl_manager.h" +#include "servers/display_server.h" + +#include <AppKit/AppKit.h> +#include <ApplicationServices/ApplicationServices.h> +#include <CoreVideo/CoreVideo.h> + +class GLManagerANGLE_MacOS : public EGLManager { +private: + virtual const char *_get_platform_extension_name() const override; + virtual EGLenum _get_platform_extension_enum() const override; + virtual EGLenum _get_platform_api_enum() const override; + virtual Vector<EGLAttrib> _get_platform_display_attributes() const override; + virtual Vector<EGLint> _get_platform_context_attribs() const override; + +public: + void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {} + + GLManagerANGLE_MacOS() {} + ~GLManagerANGLE_MacOS() {} +}; + +#endif // MACOS_ENABLED && GLES3_ENABLED + +#endif // GL_MANAGER_MACOS_ANGLE_H diff --git a/platform/macos/gl_manager_macos_angle.mm b/platform/macos/gl_manager_macos_angle.mm new file mode 100644 index 0000000000..ec0ca3e1f3 --- /dev/null +++ b/platform/macos/gl_manager_macos_angle.mm @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* gl_manager_macos_angle.mm */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "gl_manager_macos_angle.h" + +#if defined(MACOS_ENABLED) && defined(GLES3_ENABLED) + +#include <stdio.h> +#include <stdlib.h> + +#include <EGL/eglext_angle.h> + +const char *GLManagerANGLE_MacOS::_get_platform_extension_name() const { + return "EGL_ANGLE_platform_angle"; +} + +EGLenum GLManagerANGLE_MacOS::_get_platform_extension_enum() const { + return EGL_PLATFORM_ANGLE_ANGLE; +} + +Vector<EGLAttrib> GLManagerANGLE_MacOS::_get_platform_display_attributes() const { + Vector<EGLAttrib> ret; + ret.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); + ret.push_back(EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE); + ret.push_back(EGL_NONE); + + return ret; +} + +EGLenum GLManagerANGLE_MacOS::_get_platform_api_enum() const { + return EGL_OPENGL_ES_API; +} + +Vector<EGLint> GLManagerANGLE_MacOS::_get_platform_context_attribs() const { + Vector<EGLint> ret; + ret.push_back(EGL_CONTEXT_CLIENT_VERSION); + ret.push_back(3); + ret.push_back(EGL_NONE); + + return ret; +} + +#endif // MACOS_ENABLED && GLES3_ENABLED diff --git a/platform/macos/gl_manager_macos_legacy.h b/platform/macos/gl_manager_macos_legacy.h index 94d966f4ed..bafe825efb 100644 --- a/platform/macos/gl_manager_macos_legacy.h +++ b/platform/macos/gl_manager_macos_legacy.h @@ -45,17 +45,12 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" // OpenGL is deprecated in macOS 10.14 -class GLManager_MacOS { -public: - enum ContextType { - GLES_3_0_COMPATIBLE, - }; +typedef CGLError (*CGLEnablePtr)(CGLContextObj ctx, CGLContextEnable pname); +typedef CGLError (*CGLSetParameterPtr)(CGLContextObj ctx, CGLContextParameter pname, const GLint *params); +typedef CGLContextObj (*CGLGetCurrentContextPtr)(void); -private: +class GLManagerLegacy_MacOS { struct GLWindow { - int width = 0; - int height = 0; - id window_view = nullptr; NSOpenGLContext *context = nullptr; }; @@ -68,23 +63,21 @@ private: Error create_context(GLWindow &win); bool use_vsync = false; - ContextType context_type; + CGLEnablePtr CGLEnable = nullptr; + CGLSetParameterPtr CGLSetParameter = nullptr; + CGLGetCurrentContextPtr CGLGetCurrentContext = nullptr; public: Error window_create(DisplayServer::WindowID p_window_id, id p_view, int p_width, int p_height); void window_destroy(DisplayServer::WindowID p_window_id); void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height); - int window_get_width(DisplayServer::WindowID p_window_id = 0); - int window_get_height(DisplayServer::WindowID p_window_id = 0); - void release_current(); void make_current(); void swap_buffers(); void window_make_current(DisplayServer::WindowID p_window_id); - void window_update(DisplayServer::WindowID p_window_id); void window_set_per_pixel_transparency_enabled(DisplayServer::WindowID p_window_id, bool p_enabled); Error initialize(); @@ -94,8 +87,8 @@ public: NSOpenGLContext *get_context(DisplayServer::WindowID p_window_id); - GLManager_MacOS(ContextType p_context_type); - ~GLManager_MacOS(); + GLManagerLegacy_MacOS(); + ~GLManagerLegacy_MacOS(); }; #pragma clang diagnostic push diff --git a/platform/macos/gl_manager_macos_legacy.mm b/platform/macos/gl_manager_macos_legacy.mm index 550e2d5c59..3e5a96bffd 100644 --- a/platform/macos/gl_manager_macos_legacy.mm +++ b/platform/macos/gl_manager_macos_legacy.mm @@ -38,7 +38,7 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" // OpenGL is deprecated in macOS 10.14 -Error GLManager_MacOS::create_context(GLWindow &win) { +Error GLManagerLegacy_MacOS::create_context(GLWindow &win) { NSOpenGLPixelFormatAttribute attributes[] = { NSOpenGLPFADoubleBuffer, NSOpenGLPFAClosestPolicy, @@ -64,10 +64,8 @@ Error GLManager_MacOS::create_context(GLWindow &win) { return OK; } -Error GLManager_MacOS::window_create(DisplayServer::WindowID p_window_id, id p_view, int p_width, int p_height) { +Error GLManagerLegacy_MacOS::window_create(DisplayServer::WindowID p_window_id, id p_view, int p_width, int p_height) { GLWindow win; - win.width = p_width; - win.height = p_height; win.window_view = p_view; if (create_context(win) != OK) { @@ -80,16 +78,13 @@ Error GLManager_MacOS::window_create(DisplayServer::WindowID p_window_id, id p_v return OK; } -void GLManager_MacOS::window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) { +void GLManagerLegacy_MacOS::window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) { if (!windows.has(p_window_id)) { return; } GLWindow &win = windows[p_window_id]; - win.width = p_width; - win.height = p_height; - GLint dim[2]; dim[0] = p_width; dim[1] = p_height; @@ -104,25 +99,7 @@ void GLManager_MacOS::window_resize(DisplayServer::WindowID p_window_id, int p_w [win.context update]; } -int GLManager_MacOS::window_get_width(DisplayServer::WindowID p_window_id) { - if (!windows.has(p_window_id)) { - return 0; - } - - GLWindow &win = windows[p_window_id]; - return win.width; -} - -int GLManager_MacOS::window_get_height(DisplayServer::WindowID p_window_id) { - if (!windows.has(p_window_id)) { - return 0; - } - - GLWindow &win = windows[p_window_id]; - return win.height; -} - -void GLManager_MacOS::window_destroy(DisplayServer::WindowID p_window_id) { +void GLManagerLegacy_MacOS::window_destroy(DisplayServer::WindowID p_window_id) { if (!windows.has(p_window_id)) { return; } @@ -134,7 +111,7 @@ void GLManager_MacOS::window_destroy(DisplayServer::WindowID p_window_id) { windows.erase(p_window_id); } -void GLManager_MacOS::release_current() { +void GLManagerLegacy_MacOS::release_current() { if (current_window == DisplayServer::INVALID_WINDOW_ID) { return; } @@ -142,7 +119,7 @@ void GLManager_MacOS::release_current() { [NSOpenGLContext clearCurrentContext]; } -void GLManager_MacOS::window_make_current(DisplayServer::WindowID p_window_id) { +void GLManagerLegacy_MacOS::window_make_current(DisplayServer::WindowID p_window_id) { if (current_window == p_window_id) { return; } @@ -156,7 +133,7 @@ void GLManager_MacOS::window_make_current(DisplayServer::WindowID p_window_id) { current_window = p_window_id; } -void GLManager_MacOS::make_current() { +void GLManagerLegacy_MacOS::make_current() { if (current_window == DisplayServer::INVALID_WINDOW_ID) { return; } @@ -168,21 +145,12 @@ void GLManager_MacOS::make_current() { [win.context makeCurrentContext]; } -void GLManager_MacOS::swap_buffers() { +void GLManagerLegacy_MacOS::swap_buffers() { GLWindow &win = windows[current_window]; [win.context flushBuffer]; } -void GLManager_MacOS::window_update(DisplayServer::WindowID p_window_id) { - if (!windows.has(p_window_id)) { - return; - } - - GLWindow &win = windows[p_window_id]; - [win.context update]; -} - -void GLManager_MacOS::window_set_per_pixel_transparency_enabled(DisplayServer::WindowID p_window_id, bool p_enabled) { +void GLManagerLegacy_MacOS::window_set_per_pixel_transparency_enabled(DisplayServer::WindowID p_window_id, bool p_enabled) { if (!windows.has(p_window_id)) { return; } @@ -198,26 +166,28 @@ void GLManager_MacOS::window_set_per_pixel_transparency_enabled(DisplayServer::W [win.context update]; } -Error GLManager_MacOS::initialize() { +Error GLManagerLegacy_MacOS::initialize() { return OK; } -void GLManager_MacOS::set_use_vsync(bool p_use) { +void GLManagerLegacy_MacOS::set_use_vsync(bool p_use) { use_vsync = p_use; CGLContextObj ctx = CGLGetCurrentContext(); if (ctx) { GLint swapInterval = p_use ? 1 : 0; - CGLSetParameter(ctx, kCGLCPSwapInterval, &swapInterval); + if (CGLSetParameter(ctx, kCGLCPSwapInterval, &swapInterval) != kCGLNoError) { + WARN_PRINT("Could not set V-Sync mode."); + } use_vsync = p_use; } } -bool GLManager_MacOS::is_using_vsync() const { +bool GLManagerLegacy_MacOS::is_using_vsync() const { return use_vsync; } -NSOpenGLContext *GLManager_MacOS::get_context(DisplayServer::WindowID p_window_id) { +NSOpenGLContext *GLManagerLegacy_MacOS::get_context(DisplayServer::WindowID p_window_id) { if (!windows.has(p_window_id)) { return nullptr; } @@ -226,11 +196,16 @@ NSOpenGLContext *GLManager_MacOS::get_context(DisplayServer::WindowID p_window_i return win.context; } -GLManager_MacOS::GLManager_MacOS(ContextType p_context_type) { - context_type = p_context_type; +GLManagerLegacy_MacOS::GLManagerLegacy_MacOS() { + CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); + CFBundleLoadExecutable(framework); + + CGLEnable = (CGLEnablePtr)CFBundleGetFunctionPointerForName(framework, CFSTR("CGLEnable")); + CGLSetParameter = (CGLSetParameterPtr)CFBundleGetFunctionPointerForName(framework, CFSTR("CGLSetParameter")); + CGLGetCurrentContext = (CGLGetCurrentContextPtr)CFBundleGetFunctionPointerForName(framework, CFSTR("CGLGetCurrentContext")); } -GLManager_MacOS::~GLManager_MacOS() { +GLManagerLegacy_MacOS::~GLManagerLegacy_MacOS() { release_current(); } diff --git a/platform/macos/godot_content_view.h b/platform/macos/godot_content_view.h index 0d18ac742a..c6060c96c6 100644 --- a/platform/macos/godot_content_view.h +++ b/platform/macos/godot_content_view.h @@ -47,9 +47,11 @@ @interface GodotContentLayerDelegate : NSObject <CALayerDelegate> { DisplayServer::WindowID window_id; + bool need_redraw; } - (void)setWindowID:(DisplayServer::WindowID)wid; +- (void)setNeedRedraw:(bool)redraw; @end diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm index 231be83a03..22a9aa14c0 100644 --- a/platform/macos/godot_content_view.mm +++ b/platform/macos/godot_content_view.mm @@ -40,6 +40,7 @@ - (id)init { self = [super init]; window_id = DisplayServer::INVALID_WINDOW_ID; + need_redraw = false; return self; } @@ -47,13 +48,18 @@ window_id = wid; } +- (void)setNeedRedraw:(bool)redraw { + need_redraw = redraw; +} + - (void)displayLayer:(CALayer *)layer { DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (OS::get_singleton()->get_main_loop() && ds->get_is_resizing()) { + if (OS::get_singleton()->get_main_loop() && ds->get_is_resizing() && need_redraw) { Main::force_redraw(); if (!Main::is_iterating()) { // Avoid cyclic loop. Main::iteration(); } + need_redraw = false; } } @@ -93,6 +99,7 @@ } [super setFrameSize:newSize]; + [layer_delegate setNeedRedraw:true]; [self.layer setNeedsDisplay]; // Force "drawRect" call. } @@ -132,12 +139,6 @@ return [[CAMetalLayer class] layer]; } -- (void)updateLayer { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - ds->window_update(window_id); - [super updateLayer]; -} - - (BOOL)wantsUpdateLayer { return YES; } diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm index c17ea95f4f..1b8ca0134d 100644 --- a/platform/macos/os_macos.mm +++ b/platform/macos/os_macos.mm @@ -231,7 +231,7 @@ Error OS_MacOS::open_dynamic_library(const String p_path, void *&p_library_handl } p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); - ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror())); + ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror())); if (r_resolved_path != nullptr) { *r_resolved_path = path; diff --git a/platform/macos/platform_config.h b/platform/macos/platform_config.h index 65a898dcc1..1a571b689a 100644 --- a/platform/macos/platform_config.h +++ b/platform/macos/platform_config.h @@ -30,5 +30,4 @@ #include <alloca.h> -#define OPENGL_INCLUDE_H "thirdparty/glad/glad/gl.h" #define PTHREAD_RENAME_SELF diff --git a/platform/macos/platform_gl.h b/platform/macos/platform_gl.h new file mode 100644 index 0000000000..3bad9d5a5f --- /dev/null +++ b/platform/macos/platform_gl.h @@ -0,0 +1,52 @@ +/**************************************************************************/ +/* platform_gl.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef PLATFORM_GL_H +#define PLATFORM_GL_H + +#ifndef GL_API_ENABLED +#define GL_API_ENABLED // Allow using desktop GL. +#endif + +#ifndef GLES_API_ENABLED +#define GLES_API_ENABLED // Allow using GLES (ANGLE). +#endif + +#ifdef EGL_STATIC +#define KHRONOS_STATIC 1 +#include "thirdparty/angle/include/EGL/egl.h" +#include "thirdparty/angle/include/EGL/eglext.h" +#undef KHRONOS_STATIC +#else +#include "thirdparty/glad/glad/egl.h" +#endif +#include "thirdparty/glad/glad/gl.h" + +#endif // PLATFORM_GL_H diff --git a/platform/uwp/README.md b/platform/uwp/README.md deleted file mode 100644 index d69a8a8850..0000000000 --- a/platform/uwp/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# UWP platform port - -> **Warning** -> -> The UWP platform port is not currently in a working state for the `master` -> branch, and may be dropped in the future. - -This folder contains the C++ code for the Universal Windows Platform (UWP) -platform port. **This is not to be confused with the "standard" Win32 port**, -which is available in [`platform/windows`](/platform/windows). - -See also [`misc/dist/uwp_template`](/misc/dist/uwp_template) folder for the UWP -project template used for packaging the UWP export templates. - -## Documentation - -- [Compiling for Universal Windows Platform](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_uwp.html) - - Instructions on building this platform port from source. -- [Exporting for Universal Windows Platform](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_uwp.html) - - Instructions on using the compiled export templates to export a project. diff --git a/platform/uwp/SCsub b/platform/uwp/SCsub deleted file mode 100644 index 8726d32d61..0000000000 --- a/platform/uwp/SCsub +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python - -Import("env") - -files = [ - "#platform/windows/key_mapping_windows.cpp", - "#platform/windows/windows_terminal_logger.cpp", - "joypad_uwp.cpp", - "context_egl_uwp.cpp", - "app_uwp.cpp", - "os_uwp.cpp", -] - -if "build_angle" in env and env["build_angle"]: - cmd = env.AlwaysBuild(env.ANGLE("libANGLE.lib", None)) - -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_uwp.cpp b/platform/uwp/app_uwp.cpp deleted file mode 100644 index 789adb5e5b..0000000000 --- a/platform/uwp/app_uwp.cpp +++ /dev/null @@ -1,560 +0,0 @@ -/**************************************************************************/ -/* app_uwp.cpp */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#include "app_uwp.h" - -#include "core/io/dir_access.h" -#include "core/io/file_access.h" -#include "core/os/keyboard.h" -#include "main/main.h" -#include "platform/windows/key_mapping_windows.h" - -#include <collection.h> - -using namespace Windows::ApplicationModel::Core; -using namespace Windows::ApplicationModel::Activation; -using namespace Windows::UI::Core; -using namespace Windows::UI::Input; -using namespace Windows::Devices::Input; -using namespace Windows::UI::Xaml::Input; -using namespace Windows::Foundation; -using namespace Windows::Graphics::Display; -using namespace Windows::System; -using namespace Windows::System::Threading::Core; -using namespace Microsoft::WRL; - -using namespace GodotUWP; - -// Helper to convert a length in device-independent pixels (DIPs) to a length in physical pixels. -inline float ConvertDipsToPixels(float dips, float dpi) { - static const float dipsPerInch = 96.0f; - return floor(dips * dpi / dipsPerInch + 0.5f); // Round to nearest integer. -} - -// Implementation of the IFrameworkViewSource interface, necessary to run our app. -ref class GodotUWPViewSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource { -public: - virtual Windows::ApplicationModel::Core::IFrameworkView ^ CreateView() { - return ref new App(); - } -}; - -// The main function creates an IFrameworkViewSource for our app, and runs the app. -[Platform::MTAThread] int main(Platform::Array<Platform::String ^> ^) { - auto godotApplicationSource = ref new GodotUWPViewSource(); - CoreApplication::Run(godotApplicationSource); - return 0; -} - -// The first method called when the IFrameworkView is being created. -void App::Initialize(CoreApplicationView ^ applicationView) { - // Register event handlers for app lifecycle. This example includes Activated, so that we - // can make the CoreWindow active and start rendering on the window. - applicationView->Activated += - ref new TypedEventHandler<CoreApplicationView ^, IActivatedEventArgs ^>(this, &App::OnActivated); - - // Logic for other event handlers could go here. - // Information about the Suspending and Resuming event handlers can be found here: - // http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh994930.aspx - - os = new OS_UWP; -} - -// Called when the CoreWindow object is created (or re-created). -void App::SetWindow(CoreWindow ^ p_window) { - window = p_window; - window->VisibilityChanged += - ref new TypedEventHandler<CoreWindow ^, VisibilityChangedEventArgs ^>(this, &App::OnVisibilityChanged); - - window->Closed += - ref new TypedEventHandler<CoreWindow ^, CoreWindowEventArgs ^>(this, &App::OnWindowClosed); - - window->SizeChanged += - ref new TypedEventHandler<CoreWindow ^, WindowSizeChangedEventArgs ^>(this, &App::OnWindowSizeChanged); - -#if !(WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) - // Disable all pointer visual feedback for better performance when touching. - // This is not supported on Windows Phone applications. - auto pointerVisualizationSettings = PointerVisualizationSettings::GetForCurrentView(); - pointerVisualizationSettings->IsContactFeedbackEnabled = false; - pointerVisualizationSettings->IsBarrelButtonFeedbackEnabled = false; -#endif - - window->PointerPressed += - ref new TypedEventHandler<CoreWindow ^, PointerEventArgs ^>(this, &App::OnPointerPressed); - window->PointerMoved += - ref new TypedEventHandler<CoreWindow ^, PointerEventArgs ^>(this, &App::OnPointerMoved); - window->PointerReleased += - ref new TypedEventHandler<CoreWindow ^, PointerEventArgs ^>(this, &App::OnPointerReleased); - window->PointerWheelChanged += - ref new TypedEventHandler<CoreWindow ^, PointerEventArgs ^>(this, &App::OnPointerWheelChanged); - - mouseChangedNotifier = SignalNotifier::AttachToEvent(L"os_mouse_mode_changed", ref new SignalHandler(this, &App::OnMouseModeChanged)); - - mouseChangedNotifier->Enable(); - - window->CharacterReceived += - ref new TypedEventHandler<CoreWindow ^, CharacterReceivedEventArgs ^>(this, &App::OnCharacterReceived); - window->KeyDown += - ref new TypedEventHandler<CoreWindow ^, KeyEventArgs ^>(this, &App::OnKeyDown); - window->KeyUp += - ref new TypedEventHandler<CoreWindow ^, KeyEventArgs ^>(this, &App::OnKeyUp); - - os->set_window(window); - - unsigned int argc; - char **argv = get_command_line(&argc); - - Main::setup("uwp", argc, argv, false); - - UpdateWindowSize(Size(window->Bounds.Width, window->Bounds.Height)); - - Main::setup2(); -} - -static MouseButton _get_button(Windows::UI::Input::PointerPoint ^ pt) { - using namespace Windows::UI::Input; - -#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP - return MOUSE_BUTTON_LEFT; -#else - switch (pt->Properties->PointerUpdateKind) { - case PointerUpdateKind::LeftButtonPressed: - case PointerUpdateKind::LeftButtonReleased: - return MOUSE_BUTTON_LEFT; - - case PointerUpdateKind::RightButtonPressed: - case PointerUpdateKind::RightButtonReleased: - return MOUSE_BUTTON_RIGHT; - - case PointerUpdateKind::MiddleButtonPressed: - case PointerUpdateKind::MiddleButtonReleased: - return MOUSE_BUTTON_MIDDLE; - - case PointerUpdateKind::XButton1Pressed: - case PointerUpdateKind::XButton1Released: - return MOUSE_BUTTON_WHEEL_UP; - - case PointerUpdateKind::XButton2Pressed: - case PointerUpdateKind::XButton2Released: - return MOUSE_BUTTON_WHEEL_DOWN; - - default: - break; - } -#endif - - return MOUSE_BUTTON_NONE; -} - -static bool _is_touch(Windows::UI::Input::PointerPoint ^ pointerPoint) { -#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP - return true; -#else - using namespace Windows::Devices::Input; - switch (pointerPoint->PointerDevice->PointerDeviceType) { - case PointerDeviceType::Touch: - case PointerDeviceType::Pen: - return true; - default: - return false; - } -#endif -} - -static Windows::Foundation::Point _get_pixel_position(CoreWindow ^ window, Windows::Foundation::Point rawPosition, OS *os) { - Windows::Foundation::Point outputPosition; - -// Compute coordinates normalized from 0..1. -// If the coordinates need to be sized to the SDL window, -// we'll do that after. -#if 1 || WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP - outputPosition.X = rawPosition.X / window->Bounds.Width; - outputPosition.Y = rawPosition.Y / window->Bounds.Height; -#else - switch (DisplayProperties::CurrentOrientation) { - case DisplayOrientations::Portrait: - outputPosition.X = rawPosition.X / window->Bounds.Width; - outputPosition.Y = rawPosition.Y / window->Bounds.Height; - break; - case DisplayOrientations::PortraitFlipped: - outputPosition.X = 1.0f - (rawPosition.X / window->Bounds.Width); - outputPosition.Y = 1.0f - (rawPosition.Y / window->Bounds.Height); - break; - case DisplayOrientations::Landscape: - outputPosition.X = rawPosition.Y / window->Bounds.Height; - outputPosition.Y = 1.0f - (rawPosition.X / window->Bounds.Width); - break; - case DisplayOrientations::LandscapeFlipped: - outputPosition.X = 1.0f - (rawPosition.Y / window->Bounds.Height); - outputPosition.Y = rawPosition.X / window->Bounds.Width; - break; - default: - break; - } -#endif - - OS::VideoMode vm = os->get_video_mode(); - outputPosition.X *= vm.width; - outputPosition.Y *= vm.height; - - return outputPosition; -} - -static int _get_finger(uint32_t p_touch_id) { - return p_touch_id % 31; // for now -} - -void App::pointer_event(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args, bool p_pressed, bool p_is_wheel) { - Windows::UI::Input::PointerPoint ^ point = args->CurrentPoint; - Windows::Foundation::Point pos = _get_pixel_position(window, point->Position, os); - MouseButton but = _get_button(point); - if (_is_touch(point)) { - Ref<InputEventScreenTouch> screen_touch; - screen_touch.instantiate(); - screen_touch->set_device(0); - screen_touch->set_pressed(p_pressed); - screen_touch->set_position(Vector2(pos.X, pos.Y)); - screen_touch->set_index(_get_finger(point->PointerId)); - - last_touch_x[screen_touch->get_index()] = pos.X; - last_touch_y[screen_touch->get_index()] = pos.Y; - - os->input_event(screen_touch); - } else { - Ref<InputEventMouseButton> mouse_button; - mouse_button.instantiate(); - mouse_button->set_device(0); - mouse_button->set_pressed(p_pressed); - mouse_button->set_button_index(but); - mouse_button->set_position(Vector2(pos.X, pos.Y)); - mouse_button->set_global_position(Vector2(pos.X, pos.Y)); - - if (p_is_wheel) { - if (point->Properties->MouseWheelDelta > 0) { - mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? MOUSE_BUTTON_WHEEL_RIGHT : MOUSE_BUTTON_WHEEL_UP); - } else if (point->Properties->MouseWheelDelta < 0) { - mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? MOUSE_BUTTON_WHEEL_LEFT : MOUSE_BUTTON_WHEEL_DOWN); - } - } - - last_touch_x[31] = pos.X; - last_touch_y[31] = pos.Y; - - os->input_event(mouse_button); - - if (p_is_wheel) { - // Send release for mouse wheel - mouse_button->set_pressed(false); - os->input_event(mouse_button); - } - } -} - -void App::OnPointerPressed(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args) { - pointer_event(sender, args, true); -} - -void App::OnPointerReleased(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args) { - pointer_event(sender, args, false); -} - -void App::OnPointerWheelChanged(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args) { - pointer_event(sender, args, true, true); -} - -void App::OnMouseModeChanged(Windows::System::Threading::Core::SignalNotifier ^ signalNotifier, bool timedOut) { - OS::MouseMode mode = os->get_mouse_mode(); - SignalNotifier ^ notifier = mouseChangedNotifier; - - window->Dispatcher->RunAsync( - CoreDispatcherPriority::High, - ref new DispatchedHandler( - [mode, notifier, this]() { - if (mode == OS::MOUSE_MODE_CAPTURED) { - this->MouseMovedToken = MouseDevice::GetForCurrentView()->MouseMoved += - ref new TypedEventHandler<MouseDevice ^, MouseEventArgs ^>(this, &App::OnMouseMoved); - - } else { - MouseDevice::GetForCurrentView()->MouseMoved -= MouseMovedToken; - } - - notifier->Enable(); - })); - - ResetEvent(os->mouse_mode_changed); -} - -void App::OnPointerMoved(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args) { - Windows::UI::Input::PointerPoint ^ point = args->CurrentPoint; - Windows::Foundation::Point pos = _get_pixel_position(window, point->Position, os); - - if (_is_touch(point)) { - Ref<InputEventScreenDrag> screen_drag; - screen_drag.instantiate(); - screen_drag->set_device(0); - screen_drag->set_position(Vector2(pos.X, pos.Y)); - screen_drag->set_index(_get_finger(point->PointerId)); - screen_drag->set_relative(Vector2(screen_drag->get_position().x - last_touch_x[screen_drag->get_index()], screen_drag->get_position().y - last_touch_y[screen_drag->get_index()])); - - os->input_event(screen_drag); - } else { - // In case the mouse grabbed, MouseMoved will handle this - if (os->get_mouse_mode() == OS::MouseMode::MOUSE_MODE_CAPTURED) { - return; - } - - Ref<InputEventMouseMotion> mouse_motion; - mouse_motion.instantiate(); - mouse_motion->set_device(0); - mouse_motion->set_position(Vector2(pos.X, pos.Y)); - mouse_motion->set_global_position(Vector2(pos.X, pos.Y)); - mouse_motion->set_relative(Vector2(pos.X - last_touch_x[31], pos.Y - last_touch_y[31])); - - last_mouse_pos = pos; - - os->input_event(mouse_motion); - } -} - -void App::OnMouseMoved(MouseDevice ^ mouse_device, MouseEventArgs ^ args) { - // In case the mouse isn't grabbed, PointerMoved will handle this - if (os->get_mouse_mode() != OS::MouseMode::MOUSE_MODE_CAPTURED) { - return; - } - - Windows::Foundation::Point pos; - pos.X = last_mouse_pos.X + args->MouseDelta.X; - pos.Y = last_mouse_pos.Y + args->MouseDelta.Y; - - Ref<InputEventMouseMotion> mouse_motion; - mouse_motion.instantiate(); - mouse_motion->set_device(0); - mouse_motion->set_position(Vector2(pos.X, pos.Y)); - mouse_motion->set_global_position(Vector2(pos.X, pos.Y)); - mouse_motion->set_relative(Vector2(args->MouseDelta.X, args->MouseDelta.Y)); - - last_mouse_pos = pos; - - os->input_event(mouse_motion); -} - -void App::key_event(Windows::UI::Core::CoreWindow ^ sender, bool p_pressed, Windows::UI::Core::KeyEventArgs ^ key_args, Windows::UI::Core::CharacterReceivedEventArgs ^ char_args) { - OS_UWP::KeyEvent ke; - - ke.control = sender->GetAsyncKeyState(VirtualKey::Control) == CoreVirtualKeyStates::Down; - ke.alt = sender->GetAsyncKeyState(VirtualKey::Menu) == CoreVirtualKeyStates::Down; - ke.shift = sender->GetAsyncKeyState(VirtualKey::Shift) == CoreVirtualKeyStates::Down; - - ke.pressed = p_pressed; - - if (key_args != nullptr) { - ke.type = OS_UWP::KeyEvent::MessageType::KEY_EVENT_MESSAGE; - ke.unicode = 0; - ke.keycode = KeyMappingWindows::get_keysym((unsigned int)key_args->VirtualKey); - ke.physical_keycode = KeyMappingWindows::get_scansym((unsigned int)key_args->KeyStatus.ScanCode, key_args->KeyStatus.IsExtendedKey); - ke.echo = (!p_pressed && !key_args->KeyStatus.IsKeyReleased) || (p_pressed && key_args->KeyStatus.WasKeyDown); - - } else { - ke.type = OS_UWP::KeyEvent::MessageType::CHAR_EVENT_MESSAGE; - ke.unicode = char_args->KeyCode; - ke.keycode = 0; - ke.physical_keycode = 0; - ke.echo = (!p_pressed && !char_args->KeyStatus.IsKeyReleased) || (p_pressed && char_args->KeyStatus.WasKeyDown); - } - - os->queue_key_event(ke); -} - -void App::OnKeyDown(CoreWindow ^ sender, KeyEventArgs ^ args) { - key_event(sender, true, args); -} - -void App::OnKeyUp(CoreWindow ^ sender, KeyEventArgs ^ args) { - key_event(sender, false, args); -} - -void App::OnCharacterReceived(CoreWindow ^ sender, CharacterReceivedEventArgs ^ args) { - key_event(sender, true, nullptr, args); -} - -// Initializes scene resources -void App::Load(Platform::String ^ entryPoint) { -} - -// This method is called after the window becomes active. -void App::Run() { - if (Main::start()) { - os->run(); - } -} - -// Terminate events do not cause Uninitialize to be called. It will be called if your IFrameworkView -// class is torn down while the app is in the foreground. -void App::Uninitialize() { - Main::cleanup(); - delete os; -} - -// Application lifecycle event handler. -void App::OnActivated(CoreApplicationView ^ applicationView, IActivatedEventArgs ^ args) { - // Run() won't start until the CoreWindow is activated. - CoreWindow::GetForCurrentThread()->Activate(); -} - -// Window event handlers. -void App::OnVisibilityChanged(CoreWindow ^ sender, VisibilityChangedEventArgs ^ args) { - mWindowVisible = args->Visible; -} - -void App::OnWindowClosed(CoreWindow ^ sender, CoreWindowEventArgs ^ args) { - mWindowClosed = true; -} - -void App::OnWindowSizeChanged(CoreWindow ^ sender, WindowSizeChangedEventArgs ^ args) { -#if (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) - // On Windows 8.1, apps are resized when they are snapped alongside other apps, or when the device is rotated. - // The default framebuffer will be automatically resized when either of these occur. - // In particular, on a 90 degree rotation, the default framebuffer's width and height will switch. - UpdateWindowSize(args->Size); -#else if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) - // On Windows Phone 8.1, the window size changes when the device is rotated. - // The default framebuffer will not be automatically resized when this occurs. - // It is therefore up to the app to handle rotation-specific logic in its rendering code. - //os->screen_size_changed(); - UpdateWindowSize(args->Size); -#endif -} - -void App::UpdateWindowSize(Size size) { - float dpi; -#if (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) - DisplayInformation ^ currentDisplayInformation = DisplayInformation::GetForCurrentView(); - dpi = currentDisplayInformation->LogicalDpi; -#else if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) - dpi = DisplayProperties::LogicalDpi; -#endif - Size pixelSize(ConvertDipsToPixels(size.Width, dpi), ConvertDipsToPixels(size.Height, dpi)); - - mWindowWidth = static_cast<GLsizei>(pixelSize.Width); - mWindowHeight = static_cast<GLsizei>(pixelSize.Height); - - OS::VideoMode vm; - vm.width = mWindowWidth; - vm.height = mWindowHeight; - vm.fullscreen = true; - vm.resizable = false; - os->set_video_mode(vm); -} - -char **App::get_command_line(unsigned int *out_argc) { - static char *fail_cl[] = { "--path", "game", nullptr }; - *out_argc = 2; - - FILE *f = _wfopen(L"__cl__.cl", L"rb"); - - if (f == nullptr) { - wprintf(L"Couldn't open command line file.\n"); - return fail_cl; - } - -#define READ_LE_4(v) ((int)(##v[3] & 0xFF) << 24) | ((int)(##v[2] & 0xFF) << 16) | ((int)(##v[1] & 0xFF) << 8) | ((int)(##v[0] & 0xFF)) -#define CMD_MAX_LEN 65535 - - uint8_t len[4]; - int r = fread(len, sizeof(uint8_t), 4, f); - - Platform::Collections::Vector<Platform::String ^> cl; - - if (r < 4) { - fclose(f); - wprintf(L"Wrong cmdline length.\n"); - return (fail_cl); - } - - int argc = READ_LE_4(len); - - for (int i = 0; i < argc; i++) { - r = fread(len, sizeof(uint8_t), 4, f); - - if (r < 4) { - fclose(f); - wprintf(L"Wrong cmdline param length.\n"); - return (fail_cl); - } - - int strlen = READ_LE_4(len); - - if (strlen > CMD_MAX_LEN) { - fclose(f); - wprintf(L"Wrong command length.\n"); - return (fail_cl); - } - - char *arg = new char[strlen + 1]; - r = fread(arg, sizeof(char), strlen, f); - arg[strlen] = '\0'; - - if (r == strlen) { - 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); - - cl.Append(ref new Platform::String(warg, warg_size)); - - } else { - delete[] arg; - fclose(f); - wprintf(L"Error reading command.\n"); - return (fail_cl); - } - } - -#undef READ_LE_4 -#undef CMD_MAX_LEN - - fclose(f); - - char **ret = new char *[cl.Size + 1]; - - for (int i = 0; i < cl.Size; i++) { - int arg_size = WideCharToMultiByte(CP_UTF8, 0, cl.GetAt(i)->Data(), -1, nullptr, 0, nullptr, nullptr); - char *arg = new char[arg_size]; - - WideCharToMultiByte(CP_UTF8, 0, cl.GetAt(i)->Data(), -1, arg, arg_size, nullptr, nullptr); - - ret[i] = arg; - } - ret[cl.Size] = nullptr; - *out_argc = cl.Size; - - return ret; -} diff --git a/platform/uwp/app_uwp.h b/platform/uwp/app_uwp.h deleted file mode 100644 index 87c66bd679..0000000000 --- a/platform/uwp/app_uwp.h +++ /dev/null @@ -1,115 +0,0 @@ -/**************************************************************************/ -/* app_uwp.h */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#ifndef APP_UWP_H -#define APP_UWP_H - -#include "os_uwp.h" - -#include <GLES2/gl2.h> -#include <wrl.h> -#include <string> - -/** clang-format does not play nice with this C++/CX hybrid, needs investigation. */ -/* clang-format off */ - -namespace GodotUWP -{ - ref class App sealed : public Windows::ApplicationModel::Core::IFrameworkView - { - public: - App() {} - - // IFrameworkView Methods. - virtual void Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView); - virtual void SetWindow(Windows::UI::Core::CoreWindow^ window); - virtual void Load(Platform::String^ entryPoint); - virtual void Run(); - virtual void Uninitialize(); - - property Windows::Foundation::EventRegistrationToken MouseMovedToken { - Windows::Foundation::EventRegistrationToken get() { return this->mouseMovedToken; } - void set(Windows::Foundation::EventRegistrationToken p_token) { this->mouseMovedToken = p_token; } - } - - private: - void RecreateRenderer(); - - // Application lifecycle event handlers. - void OnActivated(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args); - - // Window event handlers. - void OnWindowSizeChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::WindowSizeChangedEventArgs^ args); - void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args); - void OnWindowClosed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CoreWindowEventArgs^ args); - - void pointer_event(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args, bool p_pressed, bool p_is_wheel = false); - void OnPointerPressed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); - void OnPointerReleased(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); - void OnPointerMoved(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); - void OnMouseMoved(Windows::Devices::Input::MouseDevice^ mouse_device, Windows::Devices::Input::MouseEventArgs^ args); - void OnPointerWheelChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); - - Windows::System::Threading::Core::SignalNotifier^ mouseChangedNotifier; - Windows::Foundation::EventRegistrationToken mouseMovedToken; - void OnMouseModeChanged(Windows::System::Threading::Core::SignalNotifier^ signalNotifier, bool timedOut); - - void key_event(Windows::UI::Core::CoreWindow^ sender, bool p_pressed, Windows::UI::Core::KeyEventArgs^ key_args = nullptr, Windows::UI::Core::CharacterReceivedEventArgs^ char_args = nullptr); - void OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args); - void OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args); - void OnCharacterReceived(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ args); - - void UpdateWindowSize(Windows::Foundation::Size size); - void InitializeEGL(Windows::UI::Core::CoreWindow^ window); - void CleanupEGL(); - - char** get_command_line(unsigned int* out_argc); - - bool mWindowClosed = false; - bool mWindowVisible = true; - GLsizei mWindowWidth = 0; - GLsizei mWindowHeight = 0; - - EGLDisplay mEglDisplay = EGL_NO_DISPLAY; - EGLContext mEglContext = EGL_NO_CONTEXT; - EGLSurface mEglSurface = EGL_NO_SURFACE; - - CoreWindow^ window; - OS_UWP* os; - - int last_touch_x[32]; // 20 fingers, index 31 reserved for the mouse - int last_touch_y[32]; - Windows::Foundation::Point last_mouse_pos; - }; -} - -/* clang-format on */ - -#endif // APP_UWP_H diff --git a/platform/uwp/context_egl_uwp.cpp b/platform/uwp/context_egl_uwp.cpp deleted file mode 100644 index aa8cfb4d49..0000000000 --- a/platform/uwp/context_egl_uwp.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/**************************************************************************/ -/* context_egl_uwp.cpp */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#include "context_egl_uwp.h" - -#include <EGL/eglext.h> - -using Platform::Exception; - -void ContextEGL_UWP::release_current() { - eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, mEglContext); -} - -void ContextEGL_UWP::make_current() { - eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); -} - -int ContextEGL_UWP::get_window_width() { - return width; -} - -int ContextEGL_UWP::get_window_height() { - return height; -} - -void ContextEGL_UWP::reset() { - cleanup(); - - window = CoreWindow::GetForCurrentThread(); - initialize(); -} - -void ContextEGL_UWP::swap_buffers() { - if (eglSwapBuffers(mEglDisplay, mEglSurface) != EGL_TRUE) { - cleanup(); - - window = CoreWindow::GetForCurrentThread(); - initialize(); - - // tell rasterizer to reload textures and stuff? - } -} - -Error ContextEGL_UWP::initialize() { - EGLint configAttribList[] = { - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 8, - EGL_DEPTH_SIZE, 8, - EGL_STENCIL_SIZE, 8, - EGL_SAMPLE_BUFFERS, 0, - EGL_NONE - }; - - EGLint surfaceAttribList[] = { - EGL_NONE, EGL_NONE - }; - - EGLint numConfigs = 0; - EGLint majorVersion = 1; - EGLint minorVersion; - if (driver == GLES_2_0) { - minorVersion = 0; - } else { - minorVersion = 5; - } - EGLDisplay display = EGL_NO_DISPLAY; - EGLContext context = EGL_NO_CONTEXT; - EGLSurface surface = EGL_NO_SURFACE; - EGLConfig config = nullptr; - EGLint contextAttribs[3]; - if (driver == GLES_2_0) { - contextAttribs[0] = EGL_CONTEXT_CLIENT_VERSION; - contextAttribs[1] = 2; - contextAttribs[2] = EGL_NONE; - } else { - contextAttribs[0] = EGL_CONTEXT_CLIENT_VERSION; - contextAttribs[1] = 3; - contextAttribs[2] = EGL_NONE; - } - - try { - const EGLint displayAttributes[] = { - /*EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, - EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, 9, - EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, 3, - EGL_NONE,*/ - // These are the default display attributes, used to request ANGLE's D3D11 renderer. - // eglInitialize will only succeed with these attributes if the hardware supports D3D11 Feature Level 10_0+. - EGL_PLATFORM_ANGLE_TYPE_ANGLE, - EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, - - // EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER is an optimization that can have large performance benefits on mobile devices. - // Its syntax is subject to change, though. Please update your Visual Studio templates if you experience compilation issues with it. - EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, - EGL_TRUE, - - // EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that enables ANGLE to automatically call - // the IDXGIDevice3::Trim method on behalf of the application when it gets suspended. - // Calling IDXGIDevice3::Trim when an application is suspended is a Windows Store application certification requirement. - EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, - EGL_TRUE, - EGL_NONE, - }; - - PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(eglGetProcAddress("eglGetPlatformDisplayEXT")); - - if (!eglGetPlatformDisplayEXT) { - throw Exception::CreateException(E_FAIL, L"Failed to get function eglGetPlatformDisplayEXT"); - } - - display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, displayAttributes); - - if (display == EGL_NO_DISPLAY) { - throw Exception::CreateException(E_FAIL, L"Failed to get default EGL display"); - } - - if (eglInitialize(display, &majorVersion, &minorVersion) == EGL_FALSE) { - throw Exception::CreateException(E_FAIL, L"Failed to initialize EGL"); - } - - if (eglGetConfigs(display, nullptr, 0, &numConfigs) == EGL_FALSE) { - throw Exception::CreateException(E_FAIL, L"Failed to get EGLConfig count"); - } - - if (eglChooseConfig(display, configAttribList, &config, 1, &numConfigs) == EGL_FALSE) { - throw Exception::CreateException(E_FAIL, L"Failed to choose first EGLConfig count"); - } - - surface = eglCreateWindowSurface(display, config, reinterpret_cast<IInspectable *>(window), surfaceAttribList); - if (surface == EGL_NO_SURFACE) { - throw Exception::CreateException(E_FAIL, L"Failed to create EGL fullscreen surface"); - } - - context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs); - if (context == EGL_NO_CONTEXT) { - throw Exception::CreateException(E_FAIL, L"Failed to create EGL context"); - } - - if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) { - throw Exception::CreateException(E_FAIL, L"Failed to make fullscreen EGLSurface current"); - } - } catch (...) { - return FAILED; - } - - mEglDisplay = display; - mEglSurface = surface; - mEglContext = context; - - eglQuerySurface(display, surface, EGL_WIDTH, &width); - eglQuerySurface(display, surface, EGL_HEIGHT, &height); - - return OK; -} - -void ContextEGL_UWP::cleanup() { - if (mEglDisplay != EGL_NO_DISPLAY && mEglSurface != EGL_NO_SURFACE) { - eglDestroySurface(mEglDisplay, mEglSurface); - mEglSurface = EGL_NO_SURFACE; - } - - if (mEglDisplay != EGL_NO_DISPLAY && mEglContext != EGL_NO_CONTEXT) { - eglDestroyContext(mEglDisplay, mEglContext); - mEglContext = EGL_NO_CONTEXT; - } - - if (mEglDisplay != EGL_NO_DISPLAY) { - eglTerminate(mEglDisplay); - mEglDisplay = EGL_NO_DISPLAY; - } -} - -ContextEGL_UWP::ContextEGL_UWP(CoreWindow ^ p_window, Driver p_driver) : - mEglDisplay(EGL_NO_DISPLAY), - mEglContext(EGL_NO_CONTEXT), - mEglSurface(EGL_NO_SURFACE), - driver(p_driver), - window(p_window), - vsync(false) {} - -ContextEGL_UWP::~ContextEGL_UWP() { - cleanup(); -} diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py deleted file mode 100644 index 604f5e48e3..0000000000 --- a/platform/uwp/detect.py +++ /dev/null @@ -1,216 +0,0 @@ -import methods -import os -import sys -from platform_methods import detect_arch - -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from SCons import Environment - - -def get_name(): - return "UWP" - - -def can_build(): - if os.name == "nt": - # building natively on windows! - if os.getenv("VSINSTALLDIR"): - if os.getenv("ANGLE_SRC_PATH") is None: - return False - - return True - return False - - -def get_opts(): - return [ - ("msvc_version", "MSVC version to use (ignored if the VCINSTALLDIR environment variable is set)", None), - ] - - -def get_flags(): - return [ - ("arch", detect_arch()), - ("tools", False), - ("xaudio2", True), - ("builtin_pcre2_with_jit", False), - ] - - -def configure(env: "Environment"): - # Validate arch. - supported_arches = ["x86_32", "x86_64", "arm32"] - if env["arch"] not in supported_arches: - print( - 'Unsupported CPU architecture "%s" for UWP. Supported architectures are: %s.' - % (env["arch"], ", ".join(supported_arches)) - ) - sys.exit() - - env.msvc = True - - ## Build type - - if env["target"] == "release": - env.Append(CCFLAGS=["/MD"]) - env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS"]) - if env["optimize"] != "none": - env.Append(CCFLAGS=["/O2", "/GL"]) - env.Append(LINKFLAGS=["/LTCG"]) - - elif env["target"] == "release_debug": - env.Append(CCFLAGS=["/MD"]) - env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"]) - env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"]) - if env["optimize"] != "none": - env.Append(CCFLAGS=["/O2", "/Zi"]) - - elif env["target"] == "debug": - env.Append(CCFLAGS=["/Zi"]) - env.Append(CCFLAGS=["/MDd"]) - env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"]) - env.AppendUnique(CPPDEFINES=["WINDOWS_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"] - - # Force to use Unicode encoding - env.AppendUnique(CCFLAGS=["/utf-8"]) - - # ANGLE - angle_root = os.environ["ANGLE_SRC_PATH"] - 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=" - ) - - if os.path.isfile(f"{angle_root}/winrt/10/src/angle.sln"): - env["build_angle"] = True - - ## Architecture - - arch = "" - if str(os.getenv("Platform")).lower() == "arm": - print("Compiled program architecture will be an ARM executable (forcing arch=arm32).") - - arch = "arm" - env["arch"] = "arm32" - 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"]) - - else: - compiler_version_str = methods.detect_visual_c_compiler_version(env["ENV"]) - - if compiler_version_str == "amd64" or compiler_version_str == "x86_amd64": - env["arch"] = "x86_64" - print("Compiled program architecture will be a x64 executable (forcing arch=x86_64).") - elif compiler_version_str == "x86" or compiler_version_str == "amd64_x86": - env["arch"] = "x86_32" - print("Compiled program architecture will be a x86 executable (forcing arch=x86_32).") - else: - print( - "Failed to detect MSVC compiler architecture version... Defaulting to x86 32-bit executable settings" - " (forcing arch=x86_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["arch"] = "x86_32" - - if env["arch"] == "x86_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"]) - - 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["PROGSUFFIX"] = "." + arch + env["PROGSUFFIX"] - env["OBJSUFFIX"] = "." + arch + env["OBJSUFFIX"] - env["LIBSUFFIX"] = "." + arch + env["LIBSUFFIX"] - - ## 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", - ] - ) - - ## Link flags - - 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", - ] - 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.Append(BUILDERS={"ANGLE": env.Builder(action=angle_build_cmd)}) diff --git a/platform/uwp/export/app_packager.cpp b/platform/uwp/export/app_packager.cpp deleted file mode 100644 index ffb5e31851..0000000000 --- a/platform/uwp/export/app_packager.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/**************************************************************************/ -/* app_packager.cpp */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#include "app_packager.h" - -#include "editor/editor_node.h" -#include "editor/editor_paths.h" - -String AppxPackager::hash_block(const uint8_t *p_block_data, size_t p_block_len) { - unsigned char hash[32]; - char base64[45]; - - CryptoCore::sha256(p_block_data, p_block_len, hash); - size_t len = 0; - CryptoCore::b64_encode((unsigned char *)base64, 45, &len, (unsigned char *)hash, 32); - base64[44] = '\0'; - - return String(base64); -} - -void AppxPackager::make_block_map(const String &p_path) { - Ref<FileAccess> tmp_file = FileAccess::open(p_path, FileAccess::WRITE); - - tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"); - tmp_file->store_string("<BlockMap xmlns=\"http://schemas.microsoft.com/appx/2010/blockmap\" HashMethod=\"http://www.w3.org/2001/04/xmlenc#sha256\">"); - - for (int i = 0; i < file_metadata.size(); i++) { - FileMeta file = file_metadata[i]; - - tmp_file->store_string( - "<File Name=\"" + file.name.replace("/", "\\") + "\" Size=\"" + itos(file.uncompressed_size) + "\" LfhSize=\"" + itos(file.lfh_size) + "\">"); - - for (int j = 0; j < file.hashes.size(); j++) { - tmp_file->store_string("<Block Hash=\"" + file.hashes[j].base64_hash + "\" "); - if (file.compressed) { - tmp_file->store_string("Size=\"" + itos(file.hashes[j].compressed_size) + "\" "); - } - tmp_file->store_string("/>"); - } - - tmp_file->store_string("</File>"); - } - - tmp_file->store_string("</BlockMap>"); -} - -String AppxPackager::content_type(String p_extension) { - if (p_extension == "png") { - return "image/png"; - } else if (p_extension == "jpg") { - return "image/jpg"; - } else if (p_extension == "xml") { - return "application/xml"; - } else if (p_extension == "exe" || p_extension == "dll") { - return "application/x-msdownload"; - } else { - return "application/octet-stream"; - } -} - -void AppxPackager::make_content_types(const String &p_path) { - Ref<FileAccess> tmp_file = FileAccess::open(p_path, FileAccess::WRITE); - - tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); - tmp_file->store_string("<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">"); - - HashMap<String, String> types; - - for (int i = 0; i < file_metadata.size(); i++) { - String ext = file_metadata[i].name.get_extension().to_lower(); - - if (types.has(ext)) { - continue; - } - - types[ext] = content_type(ext); - - tmp_file->store_string("<Default Extension=\"" + ext + "\" ContentType=\"" + types[ext] + "\" />"); - } - - // Appx signature file - tmp_file->store_string("<Default Extension=\"p7x\" ContentType=\"application/octet-stream\" />"); - - // Override for package files - tmp_file->store_string("<Override PartName=\"/AppxManifest.xml\" ContentType=\"application/vnd.ms-appx.manifest+xml\" />"); - tmp_file->store_string("<Override PartName=\"/AppxBlockMap.xml\" ContentType=\"application/vnd.ms-appx.blockmap+xml\" />"); - tmp_file->store_string("<Override PartName=\"/AppxSignature.p7x\" ContentType=\"application/vnd.ms-appx.signature\" />"); - tmp_file->store_string("<Override PartName=\"/AppxMetadata/CodeIntegrity.cat\" ContentType=\"application/vnd.ms-pkiseccat\" />"); - - tmp_file->store_string("</Types>"); -} - -Vector<uint8_t> AppxPackager::make_file_header(FileMeta p_file_meta) { - Vector<uint8_t> buf; - buf.resize(BASE_FILE_HEADER_SIZE + p_file_meta.name.length()); - - int offs = 0; - // Write magic - offs += buf_put_int32(FILE_HEADER_MAGIC, &buf.write[offs]); - - // Version - offs += buf_put_int16(ZIP_VERSION, &buf.write[offs]); - - // Special flag - offs += buf_put_int16(GENERAL_PURPOSE, &buf.write[offs]); - - // Compression - offs += buf_put_int16(p_file_meta.compressed ? Z_DEFLATED : 0, &buf.write[offs]); - - // File date and time - offs += buf_put_int32(0, &buf.write[offs]); - - // CRC-32 - offs += buf_put_int32(p_file_meta.file_crc32, &buf.write[offs]); - - // Compressed size - offs += buf_put_int32(p_file_meta.compressed_size, &buf.write[offs]); - - // Uncompressed size - offs += buf_put_int32(p_file_meta.uncompressed_size, &buf.write[offs]); - - // File name length - offs += buf_put_int16(p_file_meta.name.length(), &buf.write[offs]); - - // Extra data length - offs += buf_put_int16(0, &buf.write[offs]); - - // File name - offs += buf_put_string(p_file_meta.name, &buf.write[offs]); - - // Done! - return buf; -} - -void AppxPackager::store_central_dir_header(const FileMeta &p_file, bool p_do_hash) { - Vector<uint8_t> &buf = central_dir_data; - int offs = buf.size(); - buf.resize(buf.size() + BASE_CENTRAL_DIR_SIZE + p_file.name.length()); - - // Write magic - offs += buf_put_int32(CENTRAL_DIR_MAGIC, &buf.write[offs]); - - // ZIP versions - offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); - offs += buf_put_int16(ZIP_VERSION, &buf.write[offs]); - - // General purpose flag - offs += buf_put_int16(GENERAL_PURPOSE, &buf.write[offs]); - - // Compression - offs += buf_put_int16(p_file.compressed ? Z_DEFLATED : 0, &buf.write[offs]); - - // Modification date/time - offs += buf_put_int32(0, &buf.write[offs]); - - // Crc-32 - offs += buf_put_int32(p_file.file_crc32, &buf.write[offs]); - - // File sizes - offs += buf_put_int32(p_file.compressed_size, &buf.write[offs]); - offs += buf_put_int32(p_file.uncompressed_size, &buf.write[offs]); - - // File name length - offs += buf_put_int16(p_file.name.length(), &buf.write[offs]); - - // Extra field length - offs += buf_put_int16(0, &buf.write[offs]); - - // Comment length - offs += buf_put_int16(0, &buf.write[offs]); - - // Disk number start, internal/external file attributes - for (int i = 0; i < 8; i++) { - buf.write[offs++] = 0; - } - - // Relative offset - offs += buf_put_int32(p_file.zip_offset, &buf.write[offs]); - - // File name - offs += buf_put_string(p_file.name, &buf.write[offs]); - - // Done! -} - -Vector<uint8_t> AppxPackager::make_end_of_central_record() { - Vector<uint8_t> buf; - buf.resize(ZIP64_END_OF_CENTRAL_DIR_SIZE + 12 + END_OF_CENTRAL_DIR_SIZE); // Size plus magic - - int offs = 0; - - // Write magic - offs += buf_put_int32(ZIP64_END_OF_CENTRAL_DIR_MAGIC, &buf.write[offs]); - - // Size of this record - offs += buf_put_int64(ZIP64_END_OF_CENTRAL_DIR_SIZE, &buf.write[offs]); - - // Version (yes, twice) - offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); - offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); - - // Disk number - for (int i = 0; i < 8; i++) { - buf.write[offs++] = 0; - } - - // Number of entries (total and per disk) - offs += buf_put_int64(file_metadata.size(), &buf.write[offs]); - offs += buf_put_int64(file_metadata.size(), &buf.write[offs]); - - // Size of central dir - offs += buf_put_int64(central_dir_data.size(), &buf.write[offs]); - - // Central dir offset - offs += buf_put_int64(central_dir_offset, &buf.write[offs]); - - ////// ZIP64 locator - - // Write magic for zip64 central dir locator - offs += buf_put_int32(ZIP64_END_DIR_LOCATOR_MAGIC, &buf.write[offs]); - - // Disk number - for (int i = 0; i < 4; i++) { - buf.write[offs++] = 0; - } - - // Relative offset - offs += buf_put_int64(end_of_central_dir_offset, &buf.write[offs]); - - // Number of disks - offs += buf_put_int32(1, &buf.write[offs]); - - /////// End of zip directory - - // Write magic for end central dir - offs += buf_put_int32(END_OF_CENTRAL_DIR_MAGIC, &buf.write[offs]); - - // Dummy stuff for Zip64 - for (int i = 0; i < 4; i++) { - buf.write[offs++] = 0x0; - } - for (int i = 0; i < 12; i++) { - buf.write[offs++] = 0xFF; - } - - // Size of comments - for (int i = 0; i < 2; i++) { - buf.write[offs++] = 0; - } - - // Done! - return buf; -} - -void AppxPackager::init(Ref<FileAccess> p_fa) { - package = p_fa; - central_dir_offset = 0; - end_of_central_dir_offset = 0; -} - -Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress) { - if (p_file_no >= 1 && p_total_files >= 1) { - if (EditorNode::progress_task_step(progress_task, "File: " + p_file_name, (p_file_no * 100) / p_total_files)) { - return ERR_SKIP; - } - } - - FileMeta meta; - meta.name = p_file_name; - meta.uncompressed_size = p_len; - meta.compressed = p_compress; - meta.zip_offset = package->get_position(); - - Vector<uint8_t> file_buffer; - - // Data for compression - z_stream strm{}; - Vector<uint8_t> strm_in; - strm_in.resize(BLOCK_SIZE); - Vector<uint8_t> strm_out; - - if (p_compress) { - strm.zalloc = zipio_alloc; - strm.zfree = zipio_free; - strm.opaque = Z_NULL; - - strm_out.resize(BLOCK_SIZE + 8); - - deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); - } - - int step = 0; - - while (p_len - step > 0) { - size_t block_size = (p_len - step) > BLOCK_SIZE ? (size_t)BLOCK_SIZE : (p_len - step); - - for (uint64_t i = 0; i < block_size; i++) { - strm_in.write[i] = p_buffer[step + i]; - } - - BlockHash bh; - bh.base64_hash = hash_block(strm_in.ptr(), block_size); - - if (p_compress) { - strm.avail_in = block_size; - strm.avail_out = strm_out.size(); - strm.next_in = (uint8_t *)strm_in.ptr(); - strm.next_out = strm_out.ptrw(); - - int total_out_before = strm.total_out; - - int err = deflate(&strm, Z_FULL_FLUSH); - ERR_FAIL_COND_V(err < 0, ERR_BUG); // Negative means bug - - bh.compressed_size = strm.total_out - total_out_before; - - //package->store_buffer(strm_out.ptr(), strm.total_out - total_out_before); - int start = file_buffer.size(); - file_buffer.resize(file_buffer.size() + bh.compressed_size); - for (uint64_t i = 0; i < bh.compressed_size; i++) { - file_buffer.write[start + i] = strm_out[i]; - } - } else { - bh.compressed_size = block_size; - //package->store_buffer(strm_in.ptr(), block_size); - int start = file_buffer.size(); - file_buffer.resize(file_buffer.size() + block_size); - for (uint64_t i = 0; i < bh.compressed_size; i++) { - file_buffer.write[start + i] = strm_in[i]; - } - } - - meta.hashes.push_back(bh); - - step += block_size; - } - - if (p_compress) { - strm.avail_in = 0; - strm.avail_out = strm_out.size(); - strm.next_in = (uint8_t *)strm_in.ptr(); - strm.next_out = strm_out.ptrw(); - - int total_out_before = strm.total_out; - - deflate(&strm, Z_FINISH); - - //package->store_buffer(strm_out.ptr(), strm.total_out - total_out_before); - int start = file_buffer.size(); - file_buffer.resize(file_buffer.size() + (strm.total_out - total_out_before)); - for (uint64_t i = 0; i < (strm.total_out - total_out_before); i++) { - file_buffer.write[start + i] = strm_out[i]; - } - - deflateEnd(&strm); - meta.compressed_size = strm.total_out; - - } else { - meta.compressed_size = p_len; - } - - // Calculate file CRC-32 - uLong crc = crc32(0L, Z_NULL, 0); - crc = crc32(crc, p_buffer, p_len); - meta.file_crc32 = crc; - - // Create file header - Vector<uint8_t> file_header = make_file_header(meta); - meta.lfh_size = file_header.size(); - - // Store the header and file; - package->store_buffer(file_header.ptr(), file_header.size()); - package->store_buffer(file_buffer.ptr(), file_buffer.size()); - - file_metadata.push_back(meta); - - return OK; -} - -void AppxPackager::finish() { - // Create and add block map file - EditorNode::progress_task_step("export", "Creating block map...", 4); - - const String &tmp_blockmap_file_path = EditorPaths::get_singleton()->get_cache_dir().path_join("tmpblockmap.xml"); - make_block_map(tmp_blockmap_file_path); - - { - Ref<FileAccess> blockmap_file = FileAccess::open(tmp_blockmap_file_path, FileAccess::READ); - Vector<uint8_t> blockmap_buffer; - blockmap_buffer.resize(blockmap_file->get_length()); - - blockmap_file->get_buffer(blockmap_buffer.ptrw(), blockmap_buffer.size()); - - add_file("AppxBlockMap.xml", blockmap_buffer.ptr(), blockmap_buffer.size(), -1, -1, true); - } - - // Add content types - - EditorNode::progress_task_step("export", "Setting content types...", 5); - - const String &tmp_content_types_file_path = EditorPaths::get_singleton()->get_cache_dir().path_join("tmpcontenttypes.xml"); - make_content_types(tmp_content_types_file_path); - - { - Ref<FileAccess> types_file = FileAccess::open(tmp_content_types_file_path, FileAccess::READ); - Vector<uint8_t> types_buffer; - types_buffer.resize(types_file->get_length()); - - types_file->get_buffer(types_buffer.ptrw(), types_buffer.size()); - - add_file("[Content_Types].xml", types_buffer.ptr(), types_buffer.size(), -1, -1, true); - } - - // Cleanup generated files. - DirAccess::remove_file_or_error(tmp_blockmap_file_path); - DirAccess::remove_file_or_error(tmp_content_types_file_path); - - // Pre-process central directory before signing - for (int i = 0; i < file_metadata.size(); i++) { - store_central_dir_header(file_metadata[i]); - } - - // Write central directory - EditorNode::progress_task_step("export", "Finishing package...", 6); - central_dir_offset = package->get_position(); - package->store_buffer(central_dir_data.ptr(), central_dir_data.size()); - - // End record - end_of_central_dir_offset = package->get_position(); - Vector<uint8_t> end_record = make_end_of_central_record(); - package->store_buffer(end_record.ptr(), end_record.size()); - - package.unref(); -} - -AppxPackager::AppxPackager() {} - -AppxPackager::~AppxPackager() {} diff --git a/platform/uwp/export/app_packager.h b/platform/uwp/export/app_packager.h deleted file mode 100644 index e989ba9b90..0000000000 --- a/platform/uwp/export/app_packager.h +++ /dev/null @@ -1,149 +0,0 @@ -/**************************************************************************/ -/* app_packager.h */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#ifndef UWP_APP_PACKAGER_H -#define UWP_APP_PACKAGER_H - -#include "core/config/project_settings.h" -#include "core/core_bind.h" -#include "core/crypto/crypto_core.h" -#include "core/io/dir_access.h" -#include "core/io/file_access.h" -#include "core/io/marshalls.h" -#include "core/io/zip_io.h" -#include "core/object/class_db.h" -#include "core/version.h" -#include "editor/export/editor_export_platform.h" - -#include "thirdparty/minizip/unzip.h" -#include "thirdparty/minizip/zip.h" - -#include <zlib.h> - -class AppxPackager { - enum { - FILE_HEADER_MAGIC = 0x04034b50, - DATA_DESCRIPTOR_MAGIC = 0x08074b50, - CENTRAL_DIR_MAGIC = 0x02014b50, - END_OF_CENTRAL_DIR_MAGIC = 0x06054b50, - ZIP64_END_OF_CENTRAL_DIR_MAGIC = 0x06064b50, - ZIP64_END_DIR_LOCATOR_MAGIC = 0x07064b50, - P7X_SIGNATURE = 0x58434b50, - ZIP64_HEADER_ID = 0x0001, - ZIP_VERSION = 20, - ZIP_ARCHIVE_VERSION = 45, - GENERAL_PURPOSE = 0x00, - BASE_FILE_HEADER_SIZE = 30, - DATA_DESCRIPTOR_SIZE = 24, - BASE_CENTRAL_DIR_SIZE = 46, - EXTRA_FIELD_LENGTH = 28, - ZIP64_HEADER_SIZE = 24, - ZIP64_END_OF_CENTRAL_DIR_SIZE = (56 - 12), - END_OF_CENTRAL_DIR_SIZE = 42, - BLOCK_SIZE = 65536, - }; - - struct BlockHash { - String base64_hash; - size_t compressed_size = 0; - }; - - struct FileMeta { - String name; - int lfh_size = 0; - bool compressed = false; - size_t compressed_size = 0; - size_t uncompressed_size = 0; - Vector<BlockHash> hashes; - uLong file_crc32 = 0; - ZPOS64_T zip_offset = 0; - }; - - String progress_task; - Ref<FileAccess> package; - - HashSet<String> mime_types; - - Vector<FileMeta> file_metadata; - - ZPOS64_T central_dir_offset = 0; - ZPOS64_T end_of_central_dir_offset = 0; - Vector<uint8_t> central_dir_data; - - String hash_block(const uint8_t *p_block_data, size_t p_block_len); - - void make_block_map(const String &p_path); - void make_content_types(const String &p_path); - - _FORCE_INLINE_ unsigned int buf_put_int16(uint16_t p_val, uint8_t *p_buf) { - for (int i = 0; i < 2; i++) { - *p_buf++ = (p_val >> (i * 8)) & 0xFF; - } - return 2; - } - - _FORCE_INLINE_ unsigned int buf_put_int32(uint32_t p_val, uint8_t *p_buf) { - for (int i = 0; i < 4; i++) { - *p_buf++ = (p_val >> (i * 8)) & 0xFF; - } - return 4; - } - - _FORCE_INLINE_ unsigned int buf_put_int64(uint64_t p_val, uint8_t *p_buf) { - for (int i = 0; i < 8; i++) { - *p_buf++ = (p_val >> (i * 8)) & 0xFF; - } - return 8; - } - - _FORCE_INLINE_ unsigned int buf_put_string(String p_val, uint8_t *p_buf) { - for (int i = 0; i < p_val.length(); i++) { - *p_buf++ = p_val.utf8().get(i); - } - return p_val.length(); - } - - Vector<uint8_t> make_file_header(FileMeta p_file_meta); - void store_central_dir_header(const FileMeta &p_file, bool p_do_hash = true); - Vector<uint8_t> make_end_of_central_record(); - - String content_type(String p_extension); - -public: - void set_progress_task(String p_task) { progress_task = p_task; } - void init(Ref<FileAccess> p_fa); - Error add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress = false); - void finish(); - - AppxPackager(); - ~AppxPackager(); -}; - -#endif // UWP_APP_PACKAGER_H diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp deleted file mode 100644 index 3e229c7419..0000000000 --- a/platform/uwp/export/export.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/**************************************************************************/ -/* export.cpp */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#include "export.h" - -#include "export_plugin.h" - -#include "editor/editor_settings.h" -#include "editor/export/editor_export.h" - -void register_uwp_exporter_types() { - // GDREGISTER_VIRTUAL_CLASS(EditorExportPlatformUWP); -} - -void register_uwp_exporter() { -#ifdef WINDOWS_ENABLED - EDITOR_DEF("export/uwp/signtool", ""); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/uwp/signtool", PROPERTY_HINT_GLOBAL_FILE, "*.exe")); - EDITOR_DEF("export/uwp/debug_certificate", ""); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/uwp/debug_certificate", PROPERTY_HINT_GLOBAL_FILE, "*.pfx")); - EDITOR_DEF("export/uwp/debug_password", ""); - EDITOR_DEF("export/uwp/debug_algorithm", 2); // SHA256 is the default - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "export/uwp/debug_algorithm", PROPERTY_HINT_ENUM, "MD5,SHA1,SHA256")); -#endif // WINDOWS_ENABLED - - Ref<EditorExportPlatformUWP> exporter; - exporter.instantiate(); - EditorExport::get_singleton()->add_export_platform(exporter); -} diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp deleted file mode 100644 index c92520b755..0000000000 --- a/platform/uwp/export/export_plugin.cpp +++ /dev/null @@ -1,525 +0,0 @@ -/**************************************************************************/ -/* export_plugin.cpp */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#include "export_plugin.h" - -#include "logo_svg.gen.h" - -#include "editor/editor_scale.h" -#include "editor/editor_settings.h" -#include "scene/resources/image_texture.h" - -#include "modules/modules_enabled.gen.h" // For svg and regex. -#ifdef MODULE_SVG_ENABLED -#include "modules/svg/image_loader_svg.h" -#endif - -String EditorExportPlatformUWP::get_name() const { - return "UWP"; -} -String EditorExportPlatformUWP::get_os_name() const { - return "UWP"; -} - -List<String> EditorExportPlatformUWP::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { - List<String> list; - list.push_back("appx"); - return list; -} - -Ref<Texture2D> EditorExportPlatformUWP::get_logo() const { - return logo; -} - -void EditorExportPlatformUWP::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const { - r_features->push_back("s3tc"); - r_features->push_back("etc"); - r_features->push_back(p_preset->get("binary_format/architecture")); -} - -void EditorExportPlatformUWP::get_export_options(List<ExportOption> *r_options) const { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "binary_format/architecture", PROPERTY_HINT_ENUM, "x86_64,x86_32,arm32"), "x86_64")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/display_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/short_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game.Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/description"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher", PROPERTY_HINT_PLACEHOLDER_TEXT, "CN=CompanyName"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher_display_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/product_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/publisher_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/certificate", PROPERTY_HINT_GLOBAL_FILE, "*.pfx", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "signing/algorithm", PROPERTY_HINT_ENUM, "MD5,SHA1,SHA256"), 2)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/major"), 1)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/minor"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/build"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/revision"), 0)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape_flipped"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait_flipped"), true)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "images/background_color"), "transparent")); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/store_logo", PROPERTY_HINT_RESOURCE_TYPE, "CompressedTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square44x44_logo", PROPERTY_HINT_RESOURCE_TYPE, "CompressedTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square71x71_logo", PROPERTY_HINT_RESOURCE_TYPE, "CompressedTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square150x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "CompressedTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square310x310_logo", PROPERTY_HINT_RESOURCE_TYPE, "CompressedTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/wide310x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "CompressedTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/splash_screen", PROPERTY_HINT_RESOURCE_TYPE, "CompressedTexture2D"), Variant())); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square150x150"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_wide310x150"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square310x310"), false)); - - // Capabilities - const char **basic = uwp_capabilities; - while (*basic) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*basic)), false)); - basic++; - } - - const char **uap = uwp_uap_capabilities; - while (*uap) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*uap)), false)); - uap++; - } - - const char **device = uwp_device_capabilities; - while (*device) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*device)), false)); - device++; - } -} - -bool EditorExportPlatformUWP::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const { -#ifndef DEV_ENABLED - // We don't provide export templates for the UWP platform currently as it - // has not been ported for Godot 4. This is skipped in DEV_ENABLED so that - // contributors can still test the pipeline if/when we can build it again. - r_error = "The UWP platform is currently not supported in Godot 4.\n"; - return false; -#else - - String err; - bool valid = false; - - // Look for export templates (first official, and if defined custom templates). - String arch = p_preset->get("binary_format/architecture"); - String arch_infix; - if (arch == "arm32") { - arch_infix = "arm"; - } else if (arch == "x86_32") { - arch_infix = "x86"; - } else if (arch == "x86_64") { - arch_infix = "x64"; - } - - bool dvalid = exists_export_template("uwp_" + arch_infix + "_debug.zip", &err); - bool rvalid = exists_export_template("uwp_" + arch_infix + "_release.zip", &err); - - if (p_preset->get("custom_template/debug") != "") { - dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); - if (!dvalid) { - err += TTR("Custom debug template not found.") + "\n"; - } - } - if (p_preset->get("custom_template/release") != "") { - rvalid = FileAccess::exists(p_preset->get("custom_template/release")); - if (!rvalid) { - err += TTR("Custom release template not found.") + "\n"; - } - } - - valid = dvalid || rvalid; - r_missing_templates = !valid; - - if (!err.is_empty()) { - r_error = err; - } - - return valid; -#endif // DEV_ENABLED -} - -bool EditorExportPlatformUWP::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const { -#ifndef DEV_ENABLED - // We don't provide export templates for the UWP platform currently as it - // has not been ported for Godot 4. This is skipped in DEV_ENABLED so that - // contributors can still test the pipeline if/when we can build it again. - r_error = "The UWP platform is currently not supported in Godot 4.\n"; - return false; -#else - - String err; - bool valid = true; - - // Validate the project configuration. - - if (!_valid_resource_name(p_preset->get("package/short_name"))) { - valid = false; - err += TTR("Invalid package short name.") + "\n"; - } - - if (!_valid_resource_name(p_preset->get("package/unique_name"))) { - valid = false; - err += TTR("Invalid package unique name.") + "\n"; - } - - if (!_valid_resource_name(p_preset->get("package/publisher_display_name"))) { - valid = false; - err += TTR("Invalid package publisher display name.") + "\n"; - } - - if (!_valid_guid(p_preset->get("identity/product_guid"))) { - valid = false; - err += TTR("Invalid product GUID.") + "\n"; - } - - if (!_valid_guid(p_preset->get("identity/publisher_guid"))) { - valid = false; - err += TTR("Invalid publisher GUID.") + "\n"; - } - - if (!_valid_bgcolor(p_preset->get("images/background_color"))) { - valid = false; - err += TTR("Invalid background color.") + "\n"; - } - - if (!p_preset->get("images/store_logo").is_zero() && !_valid_image((Object::cast_to<CompressedTexture2D>((Object *)p_preset->get("images/store_logo"))), 50, 50)) { - valid = false; - err += TTR("Invalid Store Logo image dimensions (should be 50x50).") + "\n"; - } - - if (!p_preset->get("images/square44x44_logo").is_zero() && !_valid_image((Object::cast_to<CompressedTexture2D>((Object *)p_preset->get("images/square44x44_logo"))), 44, 44)) { - valid = false; - err += TTR("Invalid square 44x44 logo image dimensions (should be 44x44).") + "\n"; - } - - if (!p_preset->get("images/square71x71_logo").is_zero() && !_valid_image((Object::cast_to<CompressedTexture2D>((Object *)p_preset->get("images/square71x71_logo"))), 71, 71)) { - valid = false; - err += TTR("Invalid square 71x71 logo image dimensions (should be 71x71).") + "\n"; - } - - if (!p_preset->get("images/square150x150_logo").is_zero() && !_valid_image((Object::cast_to<CompressedTexture2D>((Object *)p_preset->get("images/square150x150_logo"))), 150, 150)) { - valid = false; - err += TTR("Invalid square 150x150 logo image dimensions (should be 150x150).") + "\n"; - } - - if (!p_preset->get("images/square310x310_logo").is_zero() && !_valid_image((Object::cast_to<CompressedTexture2D>((Object *)p_preset->get("images/square310x310_logo"))), 310, 310)) { - valid = false; - err += TTR("Invalid square 310x310 logo image dimensions (should be 310x310).") + "\n"; - } - - if (!p_preset->get("images/wide310x150_logo").is_zero() && !_valid_image((Object::cast_to<CompressedTexture2D>((Object *)p_preset->get("images/wide310x150_logo"))), 310, 150)) { - valid = false; - err += TTR("Invalid wide 310x150 logo image dimensions (should be 310x150).") + "\n"; - } - - if (!p_preset->get("images/splash_screen").is_zero() && !_valid_image((Object::cast_to<CompressedTexture2D>((Object *)p_preset->get("images/splash_screen"))), 620, 300)) { - valid = false; - err += TTR("Invalid splash screen image dimensions (should be 620x300).") + "\n"; - } - - r_error = err; - return valid; -#endif // DEV_ENABLED -} - -Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { - ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - - String src_appx; - - EditorProgress ep("export", TTR("Exporting for UWP"), 7, true); - - if (p_debug) { - src_appx = p_preset->get("custom_template/debug"); - } else { - src_appx = p_preset->get("custom_template/release"); - } - - src_appx = src_appx.strip_edges(); - - String arch = p_preset->get("binary_format/architecture"); - - if (src_appx.is_empty()) { - String err, arch_infix; - if (arch == "arm32") { - arch_infix = "arm"; - } else if (arch == "x86_32") { - arch_infix = "x86"; - } else if (arch == "x86_64") { - arch_infix = "x64"; - } - if (p_debug) { - src_appx = find_export_template("uwp_" + arch_infix + "_debug.zip", &err); - } else { - src_appx = find_export_template("uwp_" + arch_infix + "_release.zip", &err); - } - if (src_appx.is_empty()) { - EditorNode::add_io_error(err); - return ERR_FILE_NOT_FOUND; - } - } - - if (!DirAccess::exists(p_path.get_base_dir())) { - return ERR_FILE_BAD_PATH; - } - - Error err = OK; - - Ref<FileAccess> fa_pack = FileAccess::open(p_path, FileAccess::WRITE, &err); - ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); - - AppxPackager packager; - packager.init(fa_pack); - - Ref<FileAccess> io_fa; - zlib_filefunc_def io = zipio_create_io(&io_fa); - - if (ep.step("Creating package...", 0)) { - return ERR_SKIP; - } - - unzFile pkg = unzOpen2(src_appx.utf8().get_data(), &io); - - if (!pkg) { - EditorNode::add_io_error("Could not find template appx to export:\n" + src_appx); - return ERR_FILE_NOT_FOUND; - } - - int ret = unzGoToFirstFile(pkg); - - if (ep.step("Copying template files...", 1)) { - return ERR_SKIP; - } - - EditorNode::progress_add_task("template_files", "Template files", 100); - packager.set_progress_task("template_files"); - - int template_files_amount = 9; - int template_file_no = 1; - - while (ret == UNZ_OK) { - // get file name - unz_file_info info; - char fname[16834]; - ret = unzGetCurrentFileInfo(pkg, &info, fname, 16834, nullptr, 0, nullptr, 0); - if (ret != UNZ_OK) { - break; - } - - String path = String::utf8(fname); - - if (path.ends_with("/")) { - // Ignore directories - ret = unzGoToNextFile(pkg); - continue; - } - - Vector<uint8_t> data; - bool do_read = true; - - if (path.begins_with("Assets/")) { - path = path.replace(".scale-100", ""); - - data = _get_image_data(p_preset, path); - if (data.size() > 0) { - do_read = false; - } - } - - //read - if (do_read) { - data.resize(info.uncompressed_size); - unzOpenCurrentFile(pkg); - unzReadCurrentFile(pkg, data.ptrw(), data.size()); - unzCloseCurrentFile(pkg); - } - - if (path == "AppxManifest.xml") { - data = _fix_manifest(p_preset, data, p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG)); - } - - print_line("ADDING: " + path); - - err = packager.add_file(path, data.ptr(), data.size(), template_file_no++, template_files_amount, _should_compress_asset(path, data)); - if (err != OK) { - return err; - } - - ret = unzGoToNextFile(pkg); - } - - EditorNode::progress_end_task("template_files"); - - if (ep.step("Creating command line...", 2)) { - return ERR_SKIP; - } - - Vector<String> cl = ((String)p_preset->get("command_line/extra_args")).strip_edges().split(" "); - for (int i = 0; i < cl.size(); i++) { - if (cl[i].strip_edges().length() == 0) { - cl.remove_at(i); - i--; - } - } - - if (!(p_flags & DEBUG_FLAG_DUMB_CLIENT)) { - cl.push_back("--path"); - cl.push_back("game"); - } - - gen_export_flags(cl, p_flags); - - // Command line file - Vector<uint8_t> clf; - - // Argc - clf.resize(4); - encode_uint32(cl.size(), clf.ptrw()); - - for (int i = 0; i < cl.size(); i++) { - CharString txt = cl[i].utf8(); - int base = clf.size(); - clf.resize(base + 4 + txt.length()); - encode_uint32(txt.length(), &clf.write[base]); - memcpy(&clf.write[base + 4], txt.ptr(), txt.length()); - print_line(itos(i) + " param: " + cl[i]); - } - - err = packager.add_file("__cl__.cl", clf.ptr(), clf.size(), -1, -1, false); - if (err != OK) { - return err; - } - - if (ep.step("Adding project files...", 3)) { - return ERR_SKIP; - } - - EditorNode::progress_add_task("project_files", "Project Files", 100); - packager.set_progress_task("project_files"); - - err = export_project_files(p_preset, p_debug, save_appx_file, &packager); - - EditorNode::progress_end_task("project_files"); - - if (ep.step("Closing package...", 7)) { - return ERR_SKIP; - } - - unzClose(pkg); - - packager.finish(); - -#ifdef WINDOWS_ENABLED - // Sign with signtool - String signtool_path = EDITOR_GET("export/uwp/signtool"); - if (signtool_path.is_empty()) { - return OK; - } - - if (!FileAccess::exists(signtool_path)) { - ERR_PRINT("Could not find signtool executable at " + signtool_path + ", aborting."); - return ERR_FILE_NOT_FOUND; - } - - static String algs[] = { "MD5", "SHA1", "SHA256" }; - - String cert_path = EDITOR_GET("export/uwp/debug_certificate"); - String cert_pass = EDITOR_GET("export/uwp/debug_password"); - int cert_alg = EDITOR_GET("export/uwp/debug_algorithm"); - - if (!p_debug) { - cert_path = p_preset->get_or_env("signing/certificate", ENV_UWP_SIGNING_CERT); - cert_pass = p_preset->get_or_env("signing/password", ENV_UWP_SIGNING_PASS); - cert_alg = p_preset->get("signing/algorithm"); - } - - if (cert_path.is_empty()) { - return OK; // Certificate missing, don't try to sign - } - - if (!FileAccess::exists(cert_path)) { - ERR_PRINT("Could not find certificate file at " + cert_path + ", aborting."); - return ERR_FILE_NOT_FOUND; - } - - if (cert_alg < 0 || cert_alg > 2) { - ERR_PRINT("Invalid certificate algorithm " + itos(cert_alg) + ", aborting."); - return ERR_INVALID_DATA; - } - - List<String> args; - args.push_back("sign"); - args.push_back("/fd"); - args.push_back(algs[cert_alg]); - args.push_back("/a"); - args.push_back("/f"); - args.push_back(cert_path); - args.push_back("/p"); - args.push_back(cert_pass); - args.push_back(p_path); - - OS::get_singleton()->execute(signtool_path, args); -#endif // WINDOWS_ENABLED - - return OK; -} - -void EditorExportPlatformUWP::get_platform_features(List<String> *r_features) const { - r_features->push_back("pc"); - r_features->push_back("uwp"); -} - -void EditorExportPlatformUWP::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) { -} - -EditorExportPlatformUWP::EditorExportPlatformUWP() { -#ifdef MODULE_SVG_ENABLED - Ref<Image> img = memnew(Image); - const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE); - - ImageLoaderSVG::create_image_from_string(img, _uwp_logo_svg, EDSCALE, upsample, false); - - logo = ImageTexture::create_from_image(img); -#endif -} diff --git a/platform/uwp/export/export_plugin.h b/platform/uwp/export/export_plugin.h deleted file mode 100644 index 147279e5c5..0000000000 --- a/platform/uwp/export/export_plugin.h +++ /dev/null @@ -1,450 +0,0 @@ -/**************************************************************************/ -/* export_plugin.h */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#ifndef UWP_EXPORT_PLUGIN_H -#define UWP_EXPORT_PLUGIN_H - -#include "app_packager.h" - -#include "core/config/project_settings.h" -#include "core/crypto/crypto_core.h" -#include "core/io/dir_access.h" -#include "core/io/file_access.h" -#include "core/io/marshalls.h" -#include "core/io/zip_io.h" -#include "core/object/class_db.h" -#include "core/version.h" -#include "editor/editor_node.h" -#include "editor/editor_paths.h" -#include "editor/export/editor_export_platform.h" -#include "scene/resources/compressed_texture.h" - -#include "thirdparty/minizip/unzip.h" -#include "thirdparty/minizip/zip.h" - -#include <zlib.h> - -// Capabilities -static const char *uwp_capabilities[] = { - "allJoyn", - "codeGeneration", - "internetClient", - "internetClientServer", - "privateNetworkClientServer", - nullptr -}; -static const char *uwp_uap_capabilities[] = { - "appointments", - "blockedChatMessages", - "chat", - "contacts", - "enterpriseAuthentication", - "musicLibrary", - "objects3D", - "picturesLibrary", - "phoneCall", - "removableStorage", - "sharedUserCertificates", - "userAccountInformation", - "videosLibrary", - "voipCall", - nullptr -}; -static const char *uwp_device_capabilities[] = { - "bluetooth", - "location", - "microphone", - "proximity", - "webcam", - nullptr -}; - -// Optional environment variables for defining confidential information. If any -// of these is set, they will override the values set in the credentials file. -const String ENV_UWP_SIGNING_CERT = "GODOT_UWP_SIGNING_CERTIFICATE"; -const String ENV_UWP_SIGNING_PASS = "GODOT_UWP_SIGNING_PASSWORD"; - -class EditorExportPlatformUWP : public EditorExportPlatform { - GDCLASS(EditorExportPlatformUWP, EditorExportPlatform); - - Ref<ImageTexture> logo; - - bool _valid_resource_name(const String &p_name) const { - if (p_name.is_empty()) { - return false; - } - if (p_name.ends_with(".")) { - return false; - } - - static const char *invalid_names[] = { - "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", - "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", - nullptr - }; - - const char **t = invalid_names; - while (*t) { - if (p_name == *t) { - return false; - } - t++; - } - - return true; - } - - bool _valid_guid(const String &p_guid) const { - Vector<String> parts = p_guid.split("-"); - - if (parts.size() != 5) { - return false; - } - if (parts[0].length() != 8) { - return false; - } - for (int i = 1; i < 4; i++) { - if (parts[i].length() != 4) { - return false; - } - } - if (parts[4].length() != 12) { - return false; - } - - return true; - } - - bool _valid_bgcolor(const String &p_color) const { - if (p_color.is_empty()) { - return true; - } - if (p_color.begins_with("#") && p_color.is_valid_html_color()) { - return true; - } - - // Colors from https://msdn.microsoft.com/en-us/library/windows/apps/dn934817.aspx - static const char *valid_colors[] = { - "aliceBlue", "antiqueWhite", "aqua", "aquamarine", "azure", "beige", - "bisque", "black", "blanchedAlmond", "blue", "blueViolet", "brown", - "burlyWood", "cadetBlue", "chartreuse", "chocolate", "coral", "cornflowerBlue", - "cornsilk", "crimson", "cyan", "darkBlue", "darkCyan", "darkGoldenrod", - "darkGray", "darkGreen", "darkKhaki", "darkMagenta", "darkOliveGreen", "darkOrange", - "darkOrchid", "darkRed", "darkSalmon", "darkSeaGreen", "darkSlateBlue", "darkSlateGray", - "darkTurquoise", "darkViolet", "deepPink", "deepSkyBlue", "dimGray", "dodgerBlue", - "firebrick", "floralWhite", "forestGreen", "fuchsia", "gainsboro", "ghostWhite", - "gold", "goldenrod", "gray", "green", "greenYellow", "honeydew", - "hotPink", "indianRed", "indigo", "ivory", "khaki", "lavender", - "lavenderBlush", "lawnGreen", "lemonChiffon", "lightBlue", "lightCoral", "lightCyan", - "lightGoldenrodYellow", "lightGreen", "lightGray", "lightPink", "lightSalmon", "lightSeaGreen", - "lightSkyBlue", "lightSlateGray", "lightSteelBlue", "lightYellow", "lime", "limeGreen", - "linen", "magenta", "maroon", "mediumAquamarine", "mediumBlue", "mediumOrchid", - "mediumPurple", "mediumSeaGreen", "mediumSlateBlue", "mediumSpringGreen", "mediumTurquoise", "mediumVioletRed", - "midnightBlue", "mintCream", "mistyRose", "moccasin", "navajoWhite", "navy", - "oldLace", "olive", "oliveDrab", "orange", "orangeRed", "orchid", - "paleGoldenrod", "paleGreen", "paleTurquoise", "paleVioletRed", "papayaWhip", "peachPuff", - "peru", "pink", "plum", "powderBlue", "purple", "red", - "rosyBrown", "royalBlue", "saddleBrown", "salmon", "sandyBrown", "seaGreen", - "seaShell", "sienna", "silver", "skyBlue", "slateBlue", "slateGray", - "snow", "springGreen", "steelBlue", "tan", "teal", "thistle", - "tomato", "transparent", "turquoise", "violet", "wheat", "white", - "whiteSmoke", "yellow", "yellowGreen", - nullptr - }; - - const char **color = valid_colors; - - while (*color) { - if (p_color == *color) { - return true; - } - color++; - } - - return false; - } - - bool _valid_image(const CompressedTexture2D *p_image, int p_width, int p_height) const { - if (!p_image) { - return false; - } - - // TODO: Add resource creation or image rescaling to enable other scales: - // 1.25, 1.5, 2.0 - return p_width == p_image->get_width() && p_height == p_image->get_height(); - } - - Vector<uint8_t> _fix_manifest(const Ref<EditorExportPreset> &p_preset, const Vector<uint8_t> &p_template, bool p_give_internet) const { - String result = String::utf8((const char *)p_template.ptr(), p_template.size()); - - result = result.replace("$godot_version$", VERSION_FULL_NAME); - - result = result.replace("$identity_name$", p_preset->get("package/unique_name")); - result = result.replace("$publisher$", p_preset->get("package/publisher")); - - result = result.replace("$product_guid$", p_preset->get("identity/product_guid")); - result = result.replace("$publisher_guid$", p_preset->get("identity/publisher_guid")); - - String version = itos(p_preset->get("version/major")) + "." + itos(p_preset->get("version/minor")) + "." + itos(p_preset->get("version/build")) + "." + itos(p_preset->get("version/revision")); - result = result.replace("$version_string$", version); - - String arch = p_preset->get("binary_format/architecture"); - String architecture = arch == "arm32" ? "arm" : (arch == "x86_32" ? "x86" : "x64"); - result = result.replace("$architecture$", architecture); - - result = result.replace("$display_name$", String(p_preset->get("package/display_name")).is_empty() ? (String)GLOBAL_GET("application/config/name") : String(p_preset->get("package/display_name"))); - - result = result.replace("$publisher_display_name$", p_preset->get("package/publisher_display_name")); - result = result.replace("$app_description$", p_preset->get("package/description")); - result = result.replace("$bg_color$", p_preset->get("images/background_color")); - result = result.replace("$short_name$", p_preset->get("package/short_name")); - - String name_on_tiles = ""; - if ((bool)p_preset->get("tiles/show_name_on_square150x150")) { - name_on_tiles += " <uap:ShowOn Tile=\"square150x150Logo\" />\n"; - } - if ((bool)p_preset->get("tiles/show_name_on_wide310x150")) { - name_on_tiles += " <uap:ShowOn Tile=\"wide310x150Logo\" />\n"; - } - if ((bool)p_preset->get("tiles/show_name_on_square310x310")) { - name_on_tiles += " <uap:ShowOn Tile=\"square310x310Logo\" />\n"; - } - - String show_name_on_tiles = ""; - if (!name_on_tiles.is_empty()) { - show_name_on_tiles = "<uap:ShowNameOnTiles>\n" + name_on_tiles + " </uap:ShowNameOnTiles>"; - } - - result = result.replace("$name_on_tiles$", name_on_tiles); - - String rotations = ""; - if ((bool)p_preset->get("orientation/landscape")) { - rotations += " <uap:Rotation Preference=\"landscape\" />\n"; - } - if ((bool)p_preset->get("orientation/portrait")) { - rotations += " <uap:Rotation Preference=\"portrait\" />\n"; - } - if ((bool)p_preset->get("orientation/landscape_flipped")) { - rotations += " <uap:Rotation Preference=\"landscapeFlipped\" />\n"; - } - if ((bool)p_preset->get("orientation/portrait_flipped")) { - rotations += " <uap:Rotation Preference=\"portraitFlipped\" />\n"; - } - - String rotation_preference = ""; - if (!rotations.is_empty()) { - rotation_preference = "<uap:InitialRotationPreference>\n" + rotations + " </uap:InitialRotationPreference>"; - } - - result = result.replace("$rotation_preference$", rotation_preference); - - String capabilities_elements = ""; - const char **basic = uwp_capabilities; - while (*basic) { - if ((bool)p_preset->get("capabilities/" + String(*basic))) { - capabilities_elements += " <Capability Name=\"" + String(*basic) + "\" />\n"; - } - basic++; - } - const char **uap = uwp_uap_capabilities; - while (*uap) { - if ((bool)p_preset->get("capabilities/" + String(*uap))) { - capabilities_elements += " <uap:Capability Name=\"" + String(*uap) + "\" />\n"; - } - uap++; - } - const char **device = uwp_device_capabilities; - while (*device) { - if ((bool)p_preset->get("capabilities/" + String(*device))) { - capabilities_elements += " <DeviceCapability Name=\"" + String(*device) + "\" />\n"; - } - device++; - } - - if (!((bool)p_preset->get("capabilities/internetClient")) && p_give_internet) { - capabilities_elements += " <Capability Name=\"internetClient\" />\n"; - } - - String capabilities_string = "<Capabilities />"; - if (!capabilities_elements.is_empty()) { - capabilities_string = "<Capabilities>\n" + capabilities_elements + " </Capabilities>"; - } - - result = result.replace("$capabilities_place$", capabilities_string); - - Vector<uint8_t> r_ret; - r_ret.resize(result.length()); - - for (int i = 0; i < result.length(); i++) { - r_ret.write[i] = result.utf8().get(i); - } - - return r_ret; - } - - Vector<uint8_t> _get_image_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) { - Vector<uint8_t> data; - CompressedTexture2D *texture = nullptr; - - if (p_path.find("StoreLogo") != -1) { - texture = p_preset->get("images/store_logo").is_zero() ? nullptr : Object::cast_to<CompressedTexture2D>(((Object *)p_preset->get("images/store_logo"))); - } else if (p_path.find("Square44x44Logo") != -1) { - texture = p_preset->get("images/square44x44_logo").is_zero() ? nullptr : Object::cast_to<CompressedTexture2D>(((Object *)p_preset->get("images/square44x44_logo"))); - } else if (p_path.find("Square71x71Logo") != -1) { - texture = p_preset->get("images/square71x71_logo").is_zero() ? nullptr : Object::cast_to<CompressedTexture2D>(((Object *)p_preset->get("images/square71x71_logo"))); - } else if (p_path.find("Square150x150Logo") != -1) { - texture = p_preset->get("images/square150x150_logo").is_zero() ? nullptr : Object::cast_to<CompressedTexture2D>(((Object *)p_preset->get("images/square150x150_logo"))); - } else if (p_path.find("Square310x310Logo") != -1) { - texture = p_preset->get("images/square310x310_logo").is_zero() ? nullptr : Object::cast_to<CompressedTexture2D>(((Object *)p_preset->get("images/square310x310_logo"))); - } else if (p_path.find("Wide310x150Logo") != -1) { - texture = p_preset->get("images/wide310x150_logo").is_zero() ? nullptr : Object::cast_to<CompressedTexture2D>(((Object *)p_preset->get("images/wide310x150_logo"))); - } else if (p_path.find("SplashScreen") != -1) { - texture = p_preset->get("images/splash_screen").is_zero() ? nullptr : Object::cast_to<CompressedTexture2D>(((Object *)p_preset->get("images/splash_screen"))); - } else { - ERR_PRINT("Unable to load logo"); - } - - if (!texture) { - return data; - } - - String tmp_path = EditorPaths::get_singleton()->get_cache_dir().path_join("uwp_tmp_logo.png"); - - Error err = texture->get_image()->save_png(tmp_path); - - if (err != OK) { - String err_string = "Couldn't save temp logo file."; - - EditorNode::add_io_error(err_string); - ERR_FAIL_V_MSG(data, err_string); - } - - { - Ref<FileAccess> f = FileAccess::open(tmp_path, FileAccess::READ, &err); - - if (err != OK) { - String err_string = "Couldn't open temp logo file."; - // Cleanup generated file. - DirAccess::remove_file_or_error(tmp_path); - EditorNode::add_io_error(err_string); - ERR_FAIL_V_MSG(data, err_string); - } - - data.resize(f->get_length()); - f->get_buffer(data.ptrw(), data.size()); - } - - DirAccess::remove_file_or_error(tmp_path); - - return data; - } - - static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) { - /* TODO: This was copied verbatim from Android export. It should be - * refactored to the parent class and also be used for .zip export. - */ - - /* - * By not compressing files with little or not benefit in doing so, - * a performance gain is expected at runtime. Moreover, if the APK is - * zip-aligned, assets stored as they are can be efficiently read by - * Android by memory-mapping them. - */ - - // -- Unconditional uncompress to mimic AAPT plus some other - - static const char *unconditional_compress_ext[] = { - // From https://github.com/android/platform_frameworks_base/blob/master/tools/aapt/Package.cpp - // These formats are already compressed, or don't compress well: - ".jpg", ".jpeg", ".png", ".gif", - ".wav", ".mp2", ".mp3", ".ogg", ".aac", - ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", - ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", - ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", - ".amr", ".awb", ".wma", ".wmv", - // Godot-specific: - ".webp", // Same reasoning as .png - ".cfb", // Don't let small config files slow-down startup - ".scn", // Binary scenes are usually already compressed - ".ctex", // Streamable textures are usually already compressed - // Trailer for easier processing - nullptr - }; - - for (const char **ext = unconditional_compress_ext; *ext; ++ext) { - if (p_path.to_lower().ends_with(String(*ext))) { - return false; - } - } - - // -- Compressed resource? - - if (p_data.size() >= 4 && p_data[0] == 'R' && p_data[1] == 'S' && p_data[2] == 'C' && p_data[3] == 'C') { - // Already compressed - return false; - } - - // --- TODO: Decide on texture resources according to their image compression setting - - return true; - } - - static Error save_appx_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { - AppxPackager *packager = static_cast<AppxPackager *>(p_userdata); - String dst_path = p_path.replace_first("res://", "game/"); - - return packager->add_file(dst_path, p_data.ptr(), p_data.size(), p_file, p_total, _should_compress_asset(p_path, p_data)); - } - -public: - virtual String get_name() const override; - virtual String get_os_name() const override; - - virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; - - virtual Ref<Texture2D> get_logo() const override; - - virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override; - - virtual void get_export_options(List<ExportOption> *r_options) const override; - - virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override; - virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override; - - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; - - virtual void get_platform_features(List<String> *r_features) const override; - - virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) override; - - EditorExportPlatformUWP(); -}; - -#endif // UWP_EXPORT_PLUGIN_H diff --git a/platform/uwp/export/logo.svg b/platform/uwp/export/logo.svg deleted file mode 100644 index 5bcbdcfcd4..0000000000 --- a/platform/uwp/export/logo.svg +++ /dev/null @@ -1 +0,0 @@ -<svg height="32" width="32" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g transform="translate(8.981 1.816)"><circle style="fill:#2f75bb;fill-opacity:1;stroke:none;stroke-width:.815427" cx="7.019" cy="14.184" r="13.825"/><path d="m-1.192 8.234 6.73-.927v6.503h-6.73Zm0 11.899 6.73.927v-6.422h-6.73Zm7.47 1.026 8.952 1.236v-7.757H6.278Zm0-13.951v6.602h8.952V5.973Z" fill="#00abed" style="fill:#fff;fill-opacity:1"/></g></svg> diff --git a/platform/uwp/joypad_uwp.cpp b/platform/uwp/joypad_uwp.cpp deleted file mode 100644 index e6a923c0c5..0000000000 --- a/platform/uwp/joypad_uwp.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/**************************************************************************/ -/* joypad_uwp.cpp */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#include "joypad_uwp.h" - -#include "core/os/os.h" - -using namespace Windows::Gaming::Input; -using namespace Windows::Foundation; - -void JoypadUWP::register_events() { - Gamepad::GamepadAdded += - ref new EventHandler<Gamepad ^>(this, &JoypadUWP::OnGamepadAdded); - Gamepad::GamepadRemoved += - ref new EventHandler<Gamepad ^>(this, &JoypadUWP::OnGamepadRemoved); -} - -void JoypadUWP::process_controllers() { - for (int i = 0; i < MAX_CONTROLLERS; i++) { - ControllerDevice &joy = controllers[i]; - - if (!joy.connected) { - break; - } - - switch (joy.type) { - case ControllerType::GAMEPAD_CONTROLLER: { - GamepadReading reading = ((Gamepad ^) joy.controller_reference)->GetCurrentReading(); - - int button_mask = (int)GamepadButtons::Menu; - for (int j = 0; j < 14; j++) { - input->joy_button(joy.id, j, (int)reading.Buttons & button_mask); - button_mask *= 2; - } - - input->joy_axis(joy.id, JoyAxis::LEFT_X, axis_correct(reading.LeftThumbstickX)); - input->joy_axis(joy.id, JoyAxis::LEFT_Y, axis_correct(reading.LeftThumbstickY, true)); - input->joy_axis(joy.id, JoyAxis::RIGHT_X, axis_correct(reading.RightThumbstickX)); - input->joy_axis(joy.id, JoyAxis::RIGHT_Y, axis_correct(reading.RightThumbstickY, true)); - input->joy_axis(joy.id, JoyAxis::TRIGGER_LEFT, axis_correct(reading.LeftTrigger, false, true)); - input->joy_axis(joy.id, JoyAxis::TRIGGER_RIGHT, axis_correct(reading.RightTrigger, false, true)); - - uint64_t timestamp = input->get_joy_vibration_timestamp(joy.id); - if (timestamp > joy.ff_timestamp) { - Vector2 strength = input->get_joy_vibration_strength(joy.id); - float duration = input->get_joy_vibration_duration(joy.id); - if (strength.x == 0 && strength.y == 0) { - joypad_vibration_stop(i, timestamp); - } else { - joypad_vibration_start(i, strength.x, strength.y, duration, timestamp); - } - } else if (joy.vibrating && joy.ff_end_timestamp != 0) { - uint64_t current_time = OS::get_singleton()->get_ticks_usec(); - if (current_time >= joy.ff_end_timestamp) { - joypad_vibration_stop(i, current_time); - } - } - - break; - } - } - } -} - -JoypadUWP::JoypadUWP() { - for (int i = 0; i < MAX_CONTROLLERS; i++) { - controllers[i].id = i; - } -} - -JoypadUWP::JoypadUWP(InputDefault *p_input) { - input = p_input; - - JoypadUWP(); -} - -void JoypadUWP::OnGamepadAdded(Platform::Object ^ sender, Windows::Gaming::Input::Gamepad ^ value) { - short idx = -1; - - for (int i = 0; i < MAX_CONTROLLERS; i++) { - if (!controllers[i].connected) { - idx = i; - break; - } - } - - ERR_FAIL_COND(idx == -1); - - controllers[idx].connected = true; - controllers[idx].controller_reference = value; - controllers[idx].id = idx; - controllers[idx].type = ControllerType::GAMEPAD_CONTROLLER; - - input->joy_connection_changed(controllers[idx].id, true, "Xbox Controller", "__UWP_GAMEPAD__"); -} - -void JoypadUWP::OnGamepadRemoved(Platform::Object ^ sender, Windows::Gaming::Input::Gamepad ^ value) { - short idx = -1; - - for (int i = 0; i < MAX_CONTROLLERS; i++) { - if (controllers[i].controller_reference == value) { - idx = i; - break; - } - } - - ERR_FAIL_COND(idx == -1); - - controllers[idx] = ControllerDevice(); - - input->joy_connection_changed(idx, false, "Xbox Controller"); -} - -float JoypadUWP::axis_correct(double p_val, bool p_negate, bool p_trigger) const { - if (p_trigger) { - // Convert to a value between -1.0f and 1.0f. - return 2.0f * p_val - 1.0f; - } - return (float)(p_negate ? -p_val : p_val); -} - -void JoypadUWP::joypad_vibration_start(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) { - ControllerDevice &joy = controllers[p_device]; - if (joy.connected) { - GamepadVibration vibration; - vibration.LeftMotor = p_strong_magnitude; - vibration.RightMotor = p_weak_magnitude; - ((Gamepad ^) joy.controller_reference)->Vibration = vibration; - - joy.ff_timestamp = p_timestamp; - joy.ff_end_timestamp = p_duration == 0 ? 0 : p_timestamp + (uint64_t)(p_duration * 1000000.0); - joy.vibrating = true; - } -} - -void JoypadUWP::joypad_vibration_stop(int p_device, uint64_t p_timestamp) { - ControllerDevice &joy = controllers[p_device]; - if (joy.connected) { - GamepadVibration vibration; - vibration.LeftMotor = 0.0; - vibration.RightMotor = 0.0; - ((Gamepad ^) joy.controller_reference)->Vibration = vibration; - - joy.ff_timestamp = p_timestamp; - joy.vibrating = false; - } -} diff --git a/platform/uwp/joypad_uwp.h b/platform/uwp/joypad_uwp.h deleted file mode 100644 index e9be647dd4..0000000000 --- a/platform/uwp/joypad_uwp.h +++ /dev/null @@ -1,81 +0,0 @@ -/**************************************************************************/ -/* joypad_uwp.h */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#ifndef JOYPAD_UWP_H -#define JOYPAD_UWP_H - -#include "core/input/input.h" - -ref class JoypadUWP sealed { - /** clang-format breaks this, it does not understand this token. */ - /* clang-format off */ -internal: - void register_events(); - void process_controllers(); - /* clang-format on */ - - JoypadUWP(); - JoypadUWP(InputDefault *p_input); - -private: - enum { - MAX_CONTROLLERS = 4, - }; - - enum ControllerType { - GAMEPAD_CONTROLLER, - ARCADE_STICK_CONTROLLER, - RACING_WHEEL_CONTROLLER, - }; - - struct ControllerDevice { - Windows::Gaming::Input::IGameController ^ controller_reference; - - int id = -1; - bool connected = false; - ControllerType type = ControllerType::GAMEPAD_CONTROLLER; - float ff_timestamp = 0; - float ff_end_timestamp = 0; - bool vibrating = false; - }; - - ControllerDevice controllers[MAX_CONTROLLERS]; - - InputDefault *input = nullptr; - - void OnGamepadAdded(Platform::Object ^ sender, Windows::Gaming::Input::Gamepad ^ value); - void OnGamepadRemoved(Platform::Object ^ sender, Windows::Gaming::Input::Gamepad ^ value); - - float axis_correct(double p_val, bool p_negate = false, bool p_trigger = false) const; - void joypad_vibration_start(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp); - void joypad_vibration_stop(int p_device, uint64_t p_timestamp); -}; - -#endif // JOYPAD_UWP_H diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp deleted file mode 100644 index b9cd9d0baa..0000000000 --- a/platform/uwp/os_uwp.cpp +++ /dev/null @@ -1,845 +0,0 @@ -/**************************************************************************/ -/* os_uwp.cpp */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#include "os_uwp.h" - -#include "core/config/project_settings.h" -#include "core/io/marshalls.h" -#include "drivers/unix/ip_unix.h" -#include "drivers/unix/net_socket_posix.h" -#include "drivers/windows/dir_access_windows.h" -#include "drivers/windows/file_access_windows.h" -#include "drivers/windows/mutex_windows.h" -#include "drivers/windows/semaphore_windows.h" -#include "main/main.h" -#include "platform/windows/windows_terminal_logger.h" -#include "servers/audio_server.h" -#include "servers/rendering/rendering_server_default.h" - -#include <ppltasks.h> -#include <wrl.h> - -using namespace Windows::ApplicationModel::Core; -using namespace Windows::ApplicationModel::Activation; -using namespace Windows::UI::Core; -using namespace Windows::UI::Input; -using namespace Windows::UI::Popups; -using namespace Windows::Foundation; -using namespace Windows::Graphics::Display; -using namespace Microsoft::WRL; -using namespace Windows::UI::ViewManagement; -using namespace Windows::Devices::Input; -using namespace Windows::Devices::Sensors; -using namespace Windows::ApplicationModel::DataTransfer; -using namespace concurrency; - -static const float earth_gravity = 9.80665; - -int OS_UWP::get_video_driver_count() const { - return 2; -} - -Size2 OS_UWP::get_window_size() const { - Size2 size; - size.width = video_mode.width; - size.height = video_mode.height; - return size; -} - -int OS_UWP::get_current_video_driver() const { - return video_driver_index; -} - -void OS_UWP::set_window_size(const Size2 p_size) { - Windows::Foundation::Size new_size; - new_size.Width = p_size.width; - new_size.Height = p_size.height; - - ApplicationView ^ view = ApplicationView::GetForCurrentView(); - - if (view->TryResizeView(new_size)) { - video_mode.width = p_size.width; - video_mode.height = p_size.height; - } -} - -void OS_UWP::set_window_fullscreen(bool p_enabled) { - ApplicationView ^ view = ApplicationView::GetForCurrentView(); - - video_mode.fullscreen = view->IsFullScreenMode; - - if (video_mode.fullscreen == p_enabled) { - return; - } - - if (p_enabled) { - video_mode.fullscreen = view->TryEnterFullScreenMode(); - } else { - view->ExitFullScreenMode(); - video_mode.fullscreen = false; - } -} - -bool OS_UWP::is_window_fullscreen() const { - return ApplicationView::GetForCurrentView()->IsFullScreenMode; -} - -void OS_UWP::set_keep_screen_on(bool p_enabled) { - if (is_keep_screen_on() == p_enabled) { - return; - } - - if (p_enabled) { - display_request->RequestActive(); - } else { - display_request->RequestRelease(); - } - - OS::set_keep_screen_on(p_enabled); -} - -void OS_UWP::initialize_core() { - //RedirectIOToConsole(); - - FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES); - FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA); - FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM); - DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_RESOURCES); - DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_USERDATA); - DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_FILESYSTEM); - - NetSocketPosix::make_default(); - - // We need to know how often the clock is updated - QueryPerformanceFrequency((LARGE_INTEGER *)&ticks_per_second); - QueryPerformanceCounter((LARGE_INTEGER *)&ticks_start); - - IPUnix::make_default(); - - cursor_shape = CURSOR_ARROW; -} - -void OS_UWP::set_window(Windows::UI::Core::CoreWindow ^ p_window) { - window = p_window; -} - -void OS_UWP::screen_size_changed() { - gl_context->reset(); -} - -Error OS_UWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { - main_loop = nullptr; - outside = true; - - // FIXME: Hardcoded for now, add Vulkan support. - p_video_driver = RENDERING_DRIVER_OPENGL3; - ContextEGL_UWP::Driver opengl_api_type = ContextEGL_UWP::GLES_2_0; - - bool gl_initialization_error = false; - - gl_context = memnew(ContextEGL_UWP(window, opengl_api_type)); - - if (gl_context->initialize() != OK) { - memdelete(gl_context); - gl_context = nullptr; - gl_initialization_error = true; - } - - if (opengl_api_type == ContextEGL_UWP::GLES_2_0) { - if (RasterizerGLES3::is_viable() == OK) { - RasterizerGLES3::register_config(); - RasterizerGLES3::make_current(); - } else { - gl_initialization_error = true; - } - } - - if (gl_initialization_error) { - OS::get_singleton()->alert("Your video card driver does not support any of the supported OpenGL versions.\n" - "Please update your drivers or if you have a very old or integrated GPU upgrade it.", - "Unable to initialize video driver"); - return ERR_UNAVAILABLE; - } - - video_driver_index = p_video_driver; - gl_context->make_current(); - gl_context->set_use_vsync(video_mode.use_vsync); - - VideoMode vm; - vm.width = gl_context->get_window_width(); - vm.height = gl_context->get_window_height(); - vm.resizable = false; - - ApplicationView ^ view = ApplicationView::GetForCurrentView(); - vm.fullscreen = view->IsFullScreenMode; - - view->SetDesiredBoundsMode(ApplicationViewBoundsMode::UseVisible); - view->PreferredLaunchWindowingMode = ApplicationViewWindowingMode::PreferredLaunchViewSize; - - if (p_desired.fullscreen != view->IsFullScreenMode) { - if (p_desired.fullscreen) { - vm.fullscreen = view->TryEnterFullScreenMode(); - - } else { - view->ExitFullScreenMode(); - vm.fullscreen = false; - } - } - - Windows::Foundation::Size desired; - desired.Width = p_desired.width; - desired.Height = p_desired.height; - - view->PreferredLaunchViewSize = desired; - - if (view->TryResizeView(desired)) { - vm.width = view->VisibleBounds.Width; - vm.height = view->VisibleBounds.Height; - } - - set_video_mode(vm); - - rendering_server = memnew(RenderingServerDefault); - // FIXME: Reimplement threaded rendering - if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { - rendering_server = memnew(RenderingServerWrapMT(rendering_server, false)); - } - - rendering_server->init(); - - input = memnew(InputDefault); - - joypad = ref new JoypadUWP(input); - joypad->register_events(); - - AudioDriverManager::initialize(p_audio_driver); - - managed_object->update_clipboard(); - - Clipboard::ContentChanged += ref new EventHandler<Platform::Object ^>(managed_object, &ManagedType::on_clipboard_changed); - - accelerometer = Accelerometer::GetDefault(); - if (accelerometer != nullptr) { - // 60 FPS - accelerometer->ReportInterval = (1.0f / 60.0f) * 1000; - accelerometer->ReadingChanged += - ref new TypedEventHandler<Accelerometer ^, AccelerometerReadingChangedEventArgs ^>(managed_object, &ManagedType::on_accelerometer_reading_changed); - } - - magnetometer = Magnetometer::GetDefault(); - if (magnetometer != nullptr) { - // 60 FPS - magnetometer->ReportInterval = (1.0f / 60.0f) * 1000; - magnetometer->ReadingChanged += - ref new TypedEventHandler<Magnetometer ^, MagnetometerReadingChangedEventArgs ^>(managed_object, &ManagedType::on_magnetometer_reading_changed); - } - - gyrometer = Gyrometer::GetDefault(); - if (gyrometer != nullptr) { - // 60 FPS - gyrometer->ReportInterval = (1.0f / 60.0f) * 1000; - gyrometer->ReadingChanged += - ref new TypedEventHandler<Gyrometer ^, GyrometerReadingChangedEventArgs ^>(managed_object, &ManagedType::on_gyroscope_reading_changed); - } - - _ensure_user_data_dir(); - - if (is_keep_screen_on()) { - display_request->RequestActive(); - } - - set_keep_screen_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on")); - - return OK; -} - -void OS_UWP::set_clipboard(const String &p_text) { - DataPackage ^ clip = ref new DataPackage(); - clip->RequestedOperation = DataPackageOperation::Copy; - clip->SetText(ref new Platform::String((LPCWSTR)(p_text.utf16().get_data()))); - - Clipboard::SetContent(clip); -} - -String OS_UWP::get_clipboard() const { - if (managed_object->clipboard != nullptr) { - return managed_object->clipboard->Data(); - } else { - return ""; - } -} - -void OS_UWP::input_event(const Ref<InputEvent> &p_event) { - input->parse_input_event(p_event); -} - -void OS_UWP::delete_main_loop() { - if (main_loop) { - memdelete(main_loop); - } - main_loop = nullptr; -} - -void OS_UWP::set_main_loop(MainLoop *p_main_loop) { - input->set_main_loop(p_main_loop); - main_loop = p_main_loop; -} - -void OS_UWP::finalize() { - if (main_loop) { - memdelete(main_loop); - } - - main_loop = nullptr; - - rendering_server->finish(); - memdelete(rendering_server); -#ifdef GLES3_ENABLED - if (gl_context) { - memdelete(gl_context); - } -#endif - - memdelete(input); - - joypad = nullptr; -} - -void OS_UWP::finalize_core() { - NetSocketPosix::cleanup(); -} - -void OS_UWP::alert(const String &p_alert, const String &p_title) { - Platform::String ^ alert = ref new Platform::String((LPCWSTR)(p_alert.utf16().get_data())); - Platform::String ^ title = ref new Platform::String((LPCWSTR)(p_title.utf16().get_data())); - - MessageDialog ^ msg = ref new MessageDialog(alert, title); - - UICommand ^ close = ref new UICommand("Close", ref new UICommandInvokedHandler(managed_object, &OS_UWP::ManagedType::alert_close)); - msg->Commands->Append(close); - msg->DefaultCommandIndex = 0; - - managed_object->alert_close_handle = true; - - msg->ShowAsync(); -} - -void OS_UWP::ManagedType::alert_close(IUICommand ^ command) { - alert_close_handle = false; -} - -void OS_UWP::ManagedType::on_clipboard_changed(Platform::Object ^ sender, Platform::Object ^ ev) { - update_clipboard(); -} - -void OS_UWP::ManagedType::update_clipboard() { - DataPackageView ^ data = Clipboard::GetContent(); - - if (data->Contains(StandardDataFormats::Text)) { - create_task(data->GetTextAsync()).then([this](Platform::String ^ clipboard_content) { - this->clipboard = clipboard_content; - }); - } -} - -void OS_UWP::ManagedType::on_accelerometer_reading_changed(Accelerometer ^ sender, AccelerometerReadingChangedEventArgs ^ args) { - AccelerometerReading ^ reading = args->Reading; - - os->input->set_accelerometer(Vector3( - reading->AccelerationX * earth_gravity, - reading->AccelerationY * earth_gravity, - reading->AccelerationZ * earth_gravity)); -} - -void OS_UWP::ManagedType::on_magnetometer_reading_changed(Magnetometer ^ sender, MagnetometerReadingChangedEventArgs ^ args) { - MagnetometerReading ^ reading = args->Reading; - - os->input->set_magnetometer(Vector3( - reading->MagneticFieldX, - reading->MagneticFieldY, - reading->MagneticFieldZ)); -} - -void OS_UWP::ManagedType::on_gyroscope_reading_changed(Gyrometer ^ sender, GyrometerReadingChangedEventArgs ^ args) { - GyrometerReading ^ reading = args->Reading; - - os->input->set_magnetometer(Vector3( - reading->AngularVelocityX, - reading->AngularVelocityY, - reading->AngularVelocityZ)); -} - -void OS_UWP::set_mouse_mode(MouseMode p_mode) { - if (p_mode == MouseMode::MOUSE_MODE_CAPTURED) { - CoreWindow::GetForCurrentThread()->SetPointerCapture(); - } else { - CoreWindow::GetForCurrentThread()->ReleasePointerCapture(); - } - - if (p_mode == MouseMode::MOUSE_MODE_HIDDEN || p_mode == MouseMode::MOUSE_MODE_CAPTURED || p_mode == MouseMode::MOUSE_MODE_CONFINED_HIDDEN) { - CoreWindow::GetForCurrentThread()->PointerCursor = nullptr; - } else { - CoreWindow::GetForCurrentThread()->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0); - } - - mouse_mode = p_mode; - - SetEvent(mouse_mode_changed); -} - -OS_UWP::MouseMode OS_UWP::get_mouse_mode() const { - return mouse_mode; -} - -Point2 OS_UWP::get_mouse_position() const { - return Point2(old_x, old_y); -} - -MouseButton OS_UWP::get_mouse_button_state() const { - return last_button_state; -} - -void OS_UWP::set_window_title(const String &p_title) { -} - -void OS_UWP::set_video_mode(const VideoMode &p_video_mode, int p_screen) { - video_mode = p_video_mode; -} - -OS::VideoMode OS_UWP::get_video_mode(int p_screen) const { - return video_mode; -} - -void OS_UWP::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const { -} - -String OS_UWP::get_name() const { - return "UWP"; -} - -String OS_UWP::get_distribution_name() const { - return get_name(); -} - -String OS_UWP::get_version() const { - winrt::hstring df_version = VersionInfo().DeviceFamilyVersion(); - return String(winrt::to_string(df_version).c_str()); -} - -OS::DateTime OS_UWP::get_datetime(bool p_utc) const { - SYSTEMTIME systemtime; - if (p_utc) { - GetSystemTime(&systemtime); - } else { - GetLocalTime(&systemtime); - } - - //Get DST information from Windows, but only if p_utc is false. - TIME_ZONE_INFORMATION info; - bool daylight = false; - if (!p_utc && GetTimeZoneInformation(&info) == TIME_ZONE_ID_DAYLIGHT) { - daylight = true; - } - - DateTime dt; - dt.year = systemtime.wYear; - dt.month = Month(systemtime.wMonth); - dt.day = systemtime.wDay; - dt.weekday = Weekday(systemtime.wDayOfWeek); - dt.hour = systemtime.wHour; - dt.minute = systemtime.wMinute; - dt.second = systemtime.wSecond; - dt.dst = daylight; - return dt; -} - -OS::TimeZoneInfo OS_UWP::get_time_zone_info() const { - TIME_ZONE_INFORMATION info; - bool daylight = false; - if (GetTimeZoneInformation(&info) == TIME_ZONE_ID_DAYLIGHT) { - daylight = true; - } - - TimeZoneInfo ret; - if (daylight) { - ret.name = info.DaylightName; - } else { - ret.name = info.StandardName; - } - - // Bias value returned by GetTimeZoneInformation is inverted of what we expect - // For example on GMT-3 GetTimeZoneInformation return a Bias of 180, so invert the value to get -180 - ret.bias = -info.Bias; - return ret; -} - -uint64_t OS_UWP::get_unix_time() const { - FILETIME ft; - SYSTEMTIME st; - GetSystemTime(&st); - SystemTimeToFileTime(&st, &ft); - - SYSTEMTIME ep; - ep.wYear = 1970; - ep.wMonth = 1; - ep.wDayOfWeek = 4; - ep.wDay = 1; - ep.wHour = 0; - ep.wMinute = 0; - ep.wSecond = 0; - ep.wMilliseconds = 0; - FILETIME fep; - SystemTimeToFileTime(&ep, &fep); - - return (*(uint64_t *)&ft - *(uint64_t *)&fep) / 10000000; -} - -void OS_UWP::delay_usec(uint32_t p_usec) const { - int msec = p_usec < 1000 ? 1 : p_usec / 1000; - - // no Sleep() - WaitForSingleObjectEx(GetCurrentThread(), msec, false); -} - -uint64_t OS_UWP::get_ticks_usec() const { - uint64_t ticks; - - // This is the number of clock ticks since start - QueryPerformanceCounter((LARGE_INTEGER *)&ticks); - // Subtract the ticks at game start to get - // the ticks since the game started - ticks -= ticks_start; - - // Divide by frequency to get the time in seconds - // original calculation shown below is subject to overflow - // with high ticks_per_second and a number of days since the last reboot. - // time = ticks * 1000000L / ticks_per_second; - - // we can prevent this by either using 128 bit math - // or separating into a calculation for seconds, and the fraction - uint64_t seconds = ticks / ticks_per_second; - - // compiler will optimize these two into one divide - uint64_t leftover = ticks % ticks_per_second; - - // remainder - uint64_t time = (leftover * 1000000L) / ticks_per_second; - - // seconds - time += seconds * 1000000L; - - return time; -} - -void OS_UWP::process_events() { - joypad->process_controllers(); - process_key_events(); - input->flush_buffered_events(); -} - -void OS_UWP::process_key_events() { - for (int i = 0; i < key_event_pos; i++) { - KeyEvent &kev = key_event_buffer[i]; - - Ref<InputEventKey> key_event; - key_event.instantiate(); - key_event->set_alt_pressed(kev.alt); - key_event->set_shift_pressed(kev.shift); - key_event->set_ctrl_pressed(kev.control); - key_event->set_echo(kev.echo); - key_event->set_keycode(kev.keycode); - key_event->set_physical_keycode((Key)kev.physical_keycode); - key_event->set_unicode(kev.unicode); - key_event->set_pressed(kev.pressed); - - input_event(key_event); - } - key_event_pos = 0; -} - -void OS_UWP::queue_key_event(KeyEvent &p_event) { - // This merges Char events with the previous Key event, so - // the unicode can be retrieved without sending duplicate events. - if (p_event.type == KeyEvent::MessageType::CHAR_EVENT_MESSAGE && key_event_pos > 0) { - KeyEvent &old = key_event_buffer[key_event_pos - 1]; - ERR_FAIL_COND(old.type != KeyEvent::MessageType::KEY_EVENT_MESSAGE); - - key_event_buffer[key_event_pos - 1].unicode = p_event.unicode; - return; - } - - ERR_FAIL_COND(key_event_pos >= KEY_EVENT_BUFFER_SIZE); - - key_event_buffer[key_event_pos++] = p_event; -} - -void OS_UWP::set_cursor_shape(CursorShape p_shape) { - ERR_FAIL_INDEX(p_shape, CURSOR_MAX); - - if (cursor_shape == p_shape) { - return; - } - - static const CoreCursorType uwp_cursors[CURSOR_MAX] = { - CoreCursorType::Arrow, - CoreCursorType::IBeam, - CoreCursorType::Hand, - CoreCursorType::Cross, - CoreCursorType::Wait, - CoreCursorType::Wait, - CoreCursorType::Arrow, - CoreCursorType::Arrow, - CoreCursorType::UniversalNo, - CoreCursorType::SizeNorthSouth, - CoreCursorType::SizeWestEast, - CoreCursorType::SizeNortheastSouthwest, - CoreCursorType::SizeNorthwestSoutheast, - CoreCursorType::SizeAll, - CoreCursorType::SizeNorthSouth, - CoreCursorType::SizeWestEast, - CoreCursorType::Help - }; - - CoreWindow::GetForCurrentThread()->PointerCursor = ref new CoreCursor(uwp_cursors[p_shape], 0); - - cursor_shape = p_shape; -} - -OS::CursorShape OS_UWP::get_cursor_shape() const { - return cursor_shape; -} - -void OS_UWP::set_custom_mouse_cursor(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { - // TODO -} - -Error OS_UWP::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) { - return FAILED; -} - -Error OS_UWP::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) { - return FAILED; -} - -Error OS_UWP::kill(const ProcessID &p_pid) { - return FAILED; -} - -bool OS_UWP::is_process_running(const ProcessID &p_pid) const { - return false; -} - -Error OS_UWP::set_cwd(const String &p_cwd) { - return FAILED; -} - -String OS_UWP::get_executable_path() const { - return ""; -} - -void OS_UWP::set_icon(const Ref<Image> &p_icon) { -} - -bool OS_UWP::has_environment(const String &p_var) const { - return false; -} - -String OS_UWP::get_environment(const String &p_var) const { - return ""; -} - -bool OS_UWP::set_environment(const String &p_var, const String &p_value) const { - return false; -} - -String OS_UWP::get_stdin_string() { - return String(); -} - -void OS_UWP::move_window_to_foreground() { -} - -Error OS_UWP::shell_open(String p_uri) { - return FAILED; -} - -String OS_UWP::get_locale() const { -#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP // this should work on phone 8.1, but it doesn't - return "en"; -#else - Platform::String ^ language = Windows::Globalization::Language::CurrentInputMethodLanguageTag; - return String(language->Data()).replace("-", "_"); -#endif -} - -void OS_UWP::release_rendering_thread() { - gl_context->release_current(); -} - -void OS_UWP::make_rendering_thread() { - gl_context->make_current(); -} - -void OS_UWP::swap_buffers() { - gl_context->swap_buffers(); -} - -bool OS_UWP::has_touchscreen_ui_hint() const { - TouchCapabilities ^ tc = ref new TouchCapabilities(); - return tc->TouchPresent != 0 || UIViewSettings::GetForCurrentView()->UserInteractionMode == UserInteractionMode::Touch; -} - -bool OS_UWP::has_virtual_keyboard() const { - return UIViewSettings::GetForCurrentView()->UserInteractionMode == UserInteractionMode::Touch; -} - -void OS_UWP::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, VirtualKeyboardType p_type, int p_max_input_length, int p_cursor_start, int p_cursor_end) { - InputPane ^ pane = InputPane::GetForCurrentView(); - pane->TryShow(); -} - -void OS_UWP::hide_virtual_keyboard() { - InputPane ^ pane = InputPane::GetForCurrentView(); - pane->TryHide(); -} - -static String format_error_message(DWORD id) { - LPWSTR messageBuffer = 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; -} - -Error OS_UWP::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) { - String full_path = "game/" + p_path; - p_library_handle = (void *)LoadPackagedLibrary((LPCWSTR)(full_path.utf16().get_data()), 0); - ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", full_path, format_error_message(GetLastError()))); - - if (r_resolved_path != nullptr) { - *r_resolved_path = full_path; - } - - return OK; -} - -Error OS_UWP::close_dynamic_library(void *p_library_handle) { - if (!FreeLibrary((HMODULE)p_library_handle)) { - return FAILED; - } - return OK; -} - -Error OS_UWP::get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional) { - p_symbol_handle = (void *)GetProcAddress((HMODULE)p_library_handle, p_name.utf8().get_data()); - if (!p_symbol_handle) { - if (!p_optional) { - ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Can't resolve symbol " + p_name + ", error: " + String::num(GetLastError()) + "."); - } else { - return ERR_CANT_RESOLVE; - } - } - return OK; -} - -void OS_UWP::run() { - if (!main_loop) { - return; - } - - main_loop->init(); - - uint64_t last_ticks = get_ticks_usec(); - - int frames = 0; - uint64_t frame = 0; - - while (true) { - CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent); - if (managed_object->alert_close_handle) { - continue; - } - process_events(); // get rid of pending events - if (Main::iteration()) { - break; - } - } - - main_loop->finish(); -} - -MainLoop *OS_UWP::get_main_loop() const { - return main_loop; -} - -String OS_UWP::get_user_data_dir() const { - Windows::Storage::StorageFolder ^ data_folder = Windows::Storage::ApplicationData::Current->LocalFolder; - - return String(data_folder->Path->Data()).replace("\\", "/"); -} - -bool OS_UWP::_check_internal_feature_support(const String &p_feature) { - return p_feature == "pc"; -} - -OS_UWP::OS_UWP() { - key_event_pos = 0; - alt_mem = false; - gr_mem = false; - shift_mem = false; - control_mem = false; - meta_mem = false; - minimized = false; - - pressrc = 0; - old_invalid = true; - mouse_mode = MOUSE_MODE_VISIBLE; - gl_context = nullptr; - - display_request = ref new Windows::System::Display::DisplayRequest(); - - managed_object = ref new ManagedType; - managed_object->os = this; - - mouse_mode_changed = CreateEvent(nullptr, TRUE, FALSE, L"os_mouse_mode_changed"); - - AudioDriverManager::add_driver(&audio_driver); - - Vector<Logger *> loggers; - loggers.push_back(memnew(WindowsTerminalLogger)); - _set_logger(memnew(CompositeLogger(loggers))); -} - -OS_UWP::~OS_UWP() { -} diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h deleted file mode 100644 index 7f2195e4fd..0000000000 --- a/platform/uwp/os_uwp.h +++ /dev/null @@ -1,256 +0,0 @@ -/**************************************************************************/ -/* os_uwp.h */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#ifndef OS_UWP_H -#define OS_UWP_H - -#include "context_egl_uwp.h" -#include "joypad_uwp.h" - -#include "core/input/input.h" -#include "core/math/transform_2d.h" -#include "core/os/os.h" -#include "core/string/ustring.h" -#include "drivers/xaudio2/audio_driver_xaudio2.h" -#include "servers/audio_server.h" -#include "servers/rendering/renderer_compositor.h" -#include "servers/rendering_server.h" - -#include <io.h> -#include <stdio.h> - -#define WIN32_LEAN_AND_MEAN -#include <windows.h> - -class OS_UWP : public OS { -public: - struct KeyEvent { - enum MessageType { - KEY_EVENT_MESSAGE, - CHAR_EVENT_MESSAGE - }; - - bool alt = false, shift = false, control = false; - MessageType type = KEY_EVENT_MESSAGE; - bool pressed = false; - Key keycode = Key::NONE; - unsigned int physical_keycode = 0; - unsigned int unicode = 0; - bool echo = false; - CorePhysicalKeyStatus status; - }; - -private: - enum { - JOYPADS_MAX = 8, - JOY_AXIS_COUNT = 6, - MAX_JOY_AXIS = 32768, // I've no idea - KEY_EVENT_BUFFER_SIZE = 512 - }; - - FILE *stdo = nullptr; - - KeyEvent key_event_buffer[KEY_EVENT_BUFFER_SIZE]; - int key_event_pos; - - uint64_t ticks_start; - uint64_t ticks_per_second; - - bool minimized; - bool old_invalid; - bool outside; - int old_x, old_y; - Point2i center; - RenderingServer *rendering_server = nullptr; - int pressrc; - - ContextEGL_UWP *gl_context = nullptr; - Windows::UI::Core::CoreWindow ^ window; - - VideoMode video_mode; - int video_driver_index; - - MainLoop *main_loop = nullptr; - - AudioDriverXAudio2 audio_driver; - - MouseMode mouse_mode; - bool alt_mem; - bool gr_mem; - bool shift_mem; - bool control_mem; - bool meta_mem; - MouseButton last_button_state = MouseButton::NONE; - - CursorShape cursor_shape; - - InputDefault *input = nullptr; - - JoypadUWP ^ joypad; - - Windows::System::Display::DisplayRequest ^ display_request; - - ref class ManagedType { - public: - property bool alert_close_handle; - property Platform::String ^ clipboard; - void alert_close(Windows::UI::Popups::IUICommand ^ command); - void on_clipboard_changed(Platform::Object ^ sender, Platform::Object ^ ev); - void update_clipboard(); - void on_accelerometer_reading_changed(Windows::Devices::Sensors::Accelerometer ^ sender, Windows::Devices::Sensors::AccelerometerReadingChangedEventArgs ^ args); - void on_magnetometer_reading_changed(Windows::Devices::Sensors::Magnetometer ^ sender, Windows::Devices::Sensors::MagnetometerReadingChangedEventArgs ^ args); - void on_gyroscope_reading_changed(Windows::Devices::Sensors::Gyrometer ^ sender, Windows::Devices::Sensors::GyrometerReadingChangedEventArgs ^ args); - - /** clang-format breaks this, it does not understand this token. */ - /* clang-format off */ - internal: - ManagedType() { alert_close_handle = false; } - property OS_UWP* os; - /* clang-format on */ - }; - ManagedType ^ managed_object; - Windows::Devices::Sensors::Accelerometer ^ accelerometer; - Windows::Devices::Sensors::Magnetometer ^ magnetometer; - Windows::Devices::Sensors::Gyrometer ^ gyrometer; - - // functions used by main to initialize/deinitialize the OS -protected: - virtual int get_video_driver_count() const; - virtual int get_current_video_driver() const; - - virtual void initialize_core(); - virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); - - virtual void set_main_loop(MainLoop *p_main_loop); - virtual void delete_main_loop(); - - virtual void finalize(); - virtual void finalize_core(); - - void process_events(); - - void process_key_events(); - -public: - // Event to send to the app wrapper - HANDLE mouse_mode_changed; - - virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); - String get_stdin_string(); - - void set_mouse_mode(MouseMode p_mode); - MouseMode get_mouse_mode() const; - - virtual Point2 get_mouse_position() const; - virtual MouseButton 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 Size2 get_window_size() const; - 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_keep_screen_on(bool p_enabled); - - virtual MainLoop *get_main_loop() const; - - virtual String get_name() const; - virtual String get_distribution_name() const; - virtual String get_version() const; - - virtual DateTime get_datetime(bool p_utc) const; - virtual TimeZoneInfo get_time_zone_info() const; - virtual uint64_t get_unix_time() 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, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false); - virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false); - virtual Error kill(const ProcessID &p_pid); - virtual bool is_process_running(const ProcessID &p_pid) const; - - virtual bool has_environment(const String &p_var) const; - virtual String get_environment(const String &p_var) const; - virtual bool set_environment(const String &p_var, const String &p_value) const; - - virtual 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 Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); - void set_icon(const Ref<Image> &p_icon); - - virtual String get_executable_path() const; - - virtual String get_locale() const; - - virtual void move_window_to_foreground(); - virtual String get_user_data_dir() const; - - virtual bool _check_internal_feature_support(const String &p_feature); - - void set_window(Windows::UI::Core::CoreWindow ^ p_window); - void screen_size_changed(); - - virtual void release_rendering_thread(); - virtual void make_rendering_thread(); - virtual void swap_buffers(); - - 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(), VirtualKeyboardType p_type = KEYBOARD_TYPE_DEFAULT, int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1); - virtual void hide_virtual_keyboard(); - - virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr); - virtual Error close_dynamic_library(void *p_library_handle); - virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false); - - virtual Error shell_open(String p_uri); - - void run(); - - virtual bool get_swap_cancel_ok() { return true; } - - void input_event(const Ref<InputEvent> &p_event); - - void queue_key_event(KeyEvent &p_event); - - OS_UWP(); - ~OS_UWP(); -}; - -#endif // OS_UWP_H diff --git a/platform/web/api/api.cpp b/platform/web/api/api.cpp index ab7154b0fb..a695091a04 100644 --- a/platform/web/api/api.cpp +++ b/platform/web/api/api.cpp @@ -96,7 +96,7 @@ Ref<JavaScriptObject> JavaScriptBridge::create_callback(const Callable &p_callab Variant JavaScriptBridge::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { if (p_argcount < 1) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 0; + r_error.expected = 1; return Ref<JavaScriptObject>(); } if (p_args[0]->get_type() != Variant::STRING) { diff --git a/platform/web/audio_driver_web.cpp b/platform/web/audio_driver_web.cpp index c6c67db3de..1298d28ebf 100644 --- a/platform/web/audio_driver_web.cpp +++ b/platform/web/audio_driver_web.cpp @@ -101,7 +101,7 @@ void AudioDriverWeb::_audio_driver_capture(int p_from, int p_samples) { } Error AudioDriverWeb::init() { - int latency = GLOBAL_GET("audio/driver/output_latency"); + int latency = Engine::get_singleton()->get_audio_output_latency(); if (!audio_context.inited) { audio_context.mix_rate = _get_configured_mix_rate(); audio_context.channel_count = godot_audio_init(&audio_context.mix_rate, latency, &_state_change_callback, &_latency_update_callback); diff --git a/platform/web/audio_driver_web.h b/platform/web/audio_driver_web.h index 7bfed834e1..12a61746c3 100644 --- a/platform/web/audio_driver_web.h +++ b/platform/web/audio_driver_web.h @@ -32,6 +32,7 @@ #define AUDIO_DRIVER_WEB_H #include "godot_audio.h" +#include "godot_js.h" #include "core/os/mutex.h" #include "core/os/thread.h" @@ -55,8 +56,8 @@ private: int mix_rate = 0; int channel_count = 0; - static void _state_change_callback(int p_state); - static void _latency_update_callback(float p_latency); + WASM_EXPORT static void _state_change_callback(int p_state); + WASM_EXPORT static void _latency_update_callback(float p_latency); static AudioDriverWeb *singleton; diff --git a/platform/web/detect.py b/platform/web/detect.py index 4015c8ff16..043ddca65d 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -60,6 +60,8 @@ def get_flags(): ("target", "template_debug"), ("builtin_pcre2_with_jit", False), ("vulkan", False), + # Embree is heavy and requires too much memory (GH-70621). + ("module_raycast_enabled", False), # Use -Os to prioritize optimizing for reduced file size. This is # particularly valuable for the web platform because it directly # decreases download time. @@ -199,9 +201,16 @@ def configure(env: "Environment"): env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"]) env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"]) + # Get version info for checks below. + cc_version = get_compiler_version(env) + cc_semver = (cc_version["major"], cc_version["minor"], cc_version["patch"]) + + if env["lto"] != "none": + # Workaround https://github.com/emscripten-core/emscripten/issues/19781. + if cc_semver >= (3, 1, 42) and cc_semver < (3, 1, 46): + env.Append(LINKFLAGS=["-Wl,-u,scalbnf"]) + if env["dlink_enabled"]: - cc_version = get_compiler_version(env) - cc_semver = (int(cc_version["major"]), int(cc_version["minor"]), int(cc_version["patch"])) if cc_semver < (3, 1, 14): print("GDExtension support requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver) sys.exit(255) diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index 93b0496d74..aac1401f23 100644 --- a/platform/web/display_server_web.cpp +++ b/platform/web/display_server_web.cpp @@ -817,7 +817,7 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode if (!emscripten_webgl_enable_extension(webgl_ctx, "OVR_multiview2")) { print_verbose("Failed to enable WebXR extension."); } - RasterizerGLES3::make_current(); + RasterizerGLES3::make_current(false); } else { OS::get_singleton()->alert( diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h index a4fd75f33b..1653deff80 100644 --- a/platform/web/display_server_web.h +++ b/platform/web/display_server_web.h @@ -33,6 +33,8 @@ #include "servers/display_server.h" +#include "godot_js.h" + #include <emscripten.h> #include <emscripten/html5.h> @@ -88,28 +90,28 @@ private: static const char *godot2dom_cursor(DisplayServer::CursorShape p_shape); // events - static void fullscreen_change_callback(int p_fullscreen); - static int mouse_button_callback(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers); - static void mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers); - static int mouse_wheel_callback(double p_delta_x, double p_delta_y); - static void touch_callback(int p_type, int p_count); - static void key_callback(int p_pressed, int p_repeat, int p_modifiers); - static void vk_input_text_callback(const char *p_text, int p_cursor); - static void gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid); + WASM_EXPORT static void fullscreen_change_callback(int p_fullscreen); + WASM_EXPORT static int mouse_button_callback(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers); + WASM_EXPORT static void mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers); + WASM_EXPORT static int mouse_wheel_callback(double p_delta_x, double p_delta_y); + WASM_EXPORT static void touch_callback(int p_type, int p_count); + WASM_EXPORT static void key_callback(int p_pressed, int p_repeat, int p_modifiers); + WASM_EXPORT static void vk_input_text_callback(const char *p_text, int p_cursor); + WASM_EXPORT static void gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid); void process_joypads(); - static void _js_utterance_callback(int p_event, int p_id, int p_pos); + WASM_EXPORT static void _js_utterance_callback(int p_event, int p_id, int p_pos); static Vector<String> get_rendering_drivers_func(); static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error); static void _dispatch_input_event(const Ref<InputEvent> &p_event); - static void request_quit_callback(); - static void window_blur_callback(); - static void update_voices_callback(int p_size, const char **p_voice); - static void update_clipboard_callback(const char *p_text); - static void send_window_event_callback(int p_notification); - static void drop_files_js_callback(char **p_filev, int p_filec); + WASM_EXPORT static void request_quit_callback(); + WASM_EXPORT static void window_blur_callback(); + WASM_EXPORT static void update_voices_callback(int p_size, const char **p_voice); + WASM_EXPORT static void update_clipboard_callback(const char *p_text); + WASM_EXPORT static void send_window_event_callback(int p_notification); + WASM_EXPORT static void drop_files_js_callback(char **p_filev, int p_filec); protected: int get_current_video_driver() const; diff --git a/platform/web/doc_classes/EditorExportPlatformWeb.xml b/platform/web/doc_classes/EditorExportPlatformWeb.xml index 607fdd0533..c4c4fd870b 100644 --- a/platform/web/doc_classes/EditorExportPlatformWeb.xml +++ b/platform/web/doc_classes/EditorExportPlatformWeb.xml @@ -7,6 +7,7 @@ </description> <tutorials> <link title="Exporting for the Web">$DOCS_URL/tutorials/export/exporting_for_web.html</link> + <link title="Web documentation index">$DOCS_URL/tutorials/platform/web/index.html</link> </tutorials> <members> <member name="custom_template/debug" type="String" setter="" getter=""> diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index 993abd2cee..a62ccdc2aa 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -36,6 +36,7 @@ #include "core/config/project_settings.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_string_names.h" #include "editor/export/editor_export.h" #include "editor/import/resource_importer_texture_settings.h" #include "scene/resources/image_texture.h" @@ -683,7 +684,7 @@ EditorExportPlatformWeb::EditorExportPlatformWeb() { Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); if (theme.is_valid()) { - stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons")); + stop_icon = theme->get_icon(SNAME("Stop"), EditorStringName(EditorIcons)); } else { stop_icon.instantiate(); } diff --git a/platform/web/export/export_plugin.h b/platform/web/export/export_plugin.h index 2de4a4c153..887000ac45 100644 --- a/platform/web/export/export_plugin.h +++ b/platform/web/export/export_plugin.h @@ -39,6 +39,7 @@ #include "core/io/tcp_server.h" #include "core/io/zip_io.h" #include "editor/editor_node.h" +#include "editor/editor_string_names.h" #include "editor/export/editor_export_platform.h" #include "main/splash.gen.h" @@ -73,7 +74,7 @@ class EditorExportPlatformWeb : public EditorExportPlatform { icon.instantiate(); const String icon_path = String(GLOBAL_GET("application/config/icon")).strip_edges(); if (icon_path.is_empty() || ImageLoader::load_image(icon_path, icon) != OK) { - return EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("DefaultProjectIcon"), SNAME("EditorIcons"))->get_image(); + return EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("DefaultProjectIcon"), EditorStringName(EditorIcons))->get_image(); } return icon; } diff --git a/platform/web/godot_js.h b/platform/web/godot_js.h index 3341cf8a67..f172148bf9 100644 --- a/platform/web/godot_js.h +++ b/platform/web/godot_js.h @@ -31,6 +31,8 @@ #ifndef GODOT_JS_H #define GODOT_JS_H +#define WASM_EXPORT __attribute__((visibility("default"))) + #ifdef __cplusplus extern "C" { #endif diff --git a/platform/web/javascript_bridge_singleton.cpp b/platform/web/javascript_bridge_singleton.cpp index 45bce1b480..41206f14a5 100644 --- a/platform/web/javascript_bridge_singleton.cpp +++ b/platform/web/javascript_bridge_singleton.cpp @@ -68,11 +68,11 @@ private: int _js_id = 0; Callable _callable; - static int _variant2js(const void **p_args, int p_pos, godot_js_wrapper_ex *r_val, void **p_lock); - static void _free_lock(void **p_lock, int p_type); - static Variant _js2variant(int p_type, godot_js_wrapper_ex *p_val); - static void *_alloc_variants(int p_size); - static void _callback(void *p_ref, int p_arg_id, int p_argc); + WASM_EXPORT static int _variant2js(const void **p_args, int p_pos, godot_js_wrapper_ex *r_val, void **p_lock); + WASM_EXPORT static void _free_lock(void **p_lock, int p_type); + WASM_EXPORT static Variant _js2variant(int p_type, godot_js_wrapper_ex *p_val); + WASM_EXPORT static void *_alloc_variants(int p_size); + WASM_EXPORT static void _callback(void *p_ref, int p_arg_id, int p_argc); protected: bool _set(const StringName &p_name, const Variant &p_value) override; @@ -289,7 +289,7 @@ Ref<JavaScriptObject> JavaScriptBridge::get_interface(const String &p_interface) Variant JavaScriptBridge::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { if (p_argcount < 1) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 0; + r_error.expected = 1; return Ref<JavaScriptObject>(); } if (p_args[0]->get_type() != Variant::STRING) { diff --git a/platform/web/js/libs/library_godot_javascript_singleton.js b/platform/web/js/libs/library_godot_javascript_singleton.js index dafc01a1fd..cbe59230ee 100644 --- a/platform/web/js/libs/library_godot_javascript_singleton.js +++ b/platform/web/js/libs/library_godot_javascript_singleton.js @@ -109,7 +109,7 @@ const GodotJSWrapper = { return 2; // INT } GodotRuntime.setHeapValue(p_exchange, p_val, 'double'); - return 3; // REAL + return 3; // FLOAT } else if (type === 'string') { const c_str = GodotRuntime.allocString(p_val); GodotRuntime.setHeapValue(p_exchange, c_str, '*'); @@ -210,7 +210,9 @@ const GodotJSWrapper = { // This is safe! JavaScript is single threaded (and using it in threads is not a good idea anyway). GodotJSWrapper.cb_ret = null; const args = Array.from(arguments); - func(p_ref, GodotJSWrapper.get_proxied(args), args.length); + const argsProxy = GodotJSWrapper.MyProxy(args); + func(p_ref, argsProxy.get_id(), args.length); + argsProxy.unref(); const ret = GodotJSWrapper.cb_ret; GodotJSWrapper.cb_ret = null; return ret; @@ -313,7 +315,7 @@ const GodotEval = { case 'number': GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'double'); - return 3; // REAL + return 3; // FLOAT case 'string': GodotRuntime.setHeapValue(p_union_ptr, GodotRuntime.allocString(eval_ret), '*'); @@ -333,7 +335,7 @@ const GodotEval = { const func = GodotRuntime.get_func(p_callback); const bytes_ptr = func(p_byte_arr, p_byte_arr_write, eval_ret.length); HEAPU8.set(eval_ret, bytes_ptr); - return 20; // POOL_BYTE_ARRAY + return 29; // PACKED_BYTE_ARRAY } break; diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp index 9ee8f90e89..186e4abf92 100644 --- a/platform/web/os_web.cpp +++ b/platform/web/os_web.cpp @@ -132,6 +132,10 @@ int OS_Web::get_processor_count() const { return godot_js_os_hw_concurrency_get(); } +String OS_Web::get_unique_id() const { + ERR_FAIL_V_MSG("", "OS::get_unique_id() is not available on the Web platform."); +} + bool OS_Web::_check_internal_feature_support(const String &p_feature) { if (p_feature == "web") { return true; @@ -232,7 +236,7 @@ bool OS_Web::is_userfs_persistent() const { Error OS_Web::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) { String path = p_path.get_file(); p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); - ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror())); + ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror())); if (r_resolved_path != nullptr) { *r_resolved_path = path; diff --git a/platform/web/os_web.h b/platform/web/os_web.h index b9570f9ca1..f262337f00 100644 --- a/platform/web/os_web.h +++ b/platform/web/os_web.h @@ -33,6 +33,8 @@ #include "audio_driver_web.h" +#include "godot_js.h" + #include "core/input/input.h" #include "drivers/unix/os_unix.h" #include "servers/audio_server.h" @@ -48,11 +50,11 @@ class OS_Web : public OS_Unix { bool idb_needs_sync = false; bool pwa_is_waiting = false; - static void main_loop_callback(); + WASM_EXPORT static void main_loop_callback(); - static void file_access_close_callback(const String &p_file, int p_flags); - static void fs_sync_callback(); - static void update_pwa_state_callback(); + WASM_EXPORT static void file_access_close_callback(const String &p_file, int p_flags); + WASM_EXPORT static void fs_sync_callback(); + WASM_EXPORT static void update_pwa_state_callback(); protected: void initialize() override; @@ -83,6 +85,7 @@ public: int get_process_id() const override; bool is_process_running(const ProcessID &p_pid) const override; int get_processor_count() const override; + String get_unique_id() const override; int get_default_thread_pool_size() const override { return 1; } String get_executable_path() const override; diff --git a/platform/web/platform_config.h b/platform/web/platform_config.h index 78deaadcfd..c3189bccfb 100644 --- a/platform/web/platform_config.h +++ b/platform/web/platform_config.h @@ -29,5 +29,3 @@ /**************************************************************************/ #include <alloca.h> - -#define OPENGL_INCLUDE_H "platform/web/godot_webgl2.h" diff --git a/platform/uwp/platform_config.h b/platform/web/platform_gl.h index 964e341ce4..be6e1462a7 100644 --- a/platform/uwp/platform_config.h +++ b/platform/web/platform_gl.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* platform_config.h */ +/* platform_gl.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,4 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include <malloc.h> +#ifndef PLATFORM_GL_H +#define PLATFORM_GL_H + +#ifndef GLES_API_ENABLED +#define GLES_API_ENABLED // Allow using GLES. +#endif + +#include "platform/web/godot_webgl2.h" + +#endif // PLATFORM_GL_H diff --git a/platform/windows/SCsub b/platform/windows/SCsub index efbb47d965..1b6908d2bb 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -16,7 +16,9 @@ common_win = [ "tts_windows.cpp", "windows_terminal_logger.cpp", "vulkan_context_win.cpp", - "gl_manager_windows.cpp", + "gl_manager_windows_native.cpp", + "gl_manager_windows_angle.cpp", + "wgl_detect_version.cpp", ] common_win_wrap = [ @@ -44,6 +46,7 @@ if env["windows_subsystem"] == "gui": env_wrap.Append(LIBS=["version"]) prog_wrap = env_wrap.add_program("#bin/godot", common_win_wrap + res_wrap_obj, PROGSUFFIX=env["PROGSUFFIX_WRAP"]) + env_wrap.Depends(prog_wrap, prog) # Microsoft Visual Studio Project Generation if env["vsproj"]: diff --git a/platform/windows/crash_handler_windows.h b/platform/windows/crash_handler_windows.h index ef5831f10c..3871210977 100644 --- a/platform/windows/crash_handler_windows.h +++ b/platform/windows/crash_handler_windows.h @@ -35,7 +35,7 @@ #include <windows.h> // Crash handler exception only enabled with MSVC -#if defined(DEBUG_ENABLED) && defined(MSVC) +#if defined(DEBUG_ENABLED) && defined(_MSC_VER) #define CRASH_HANDLER_EXCEPTION 1 extern DWORD CrashHandlerException(EXCEPTION_POINTERS *ep); diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 9548939695..7caa0153d7 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -185,6 +185,8 @@ def get_opts(): BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True), BoolVariable("use_asan", "Use address sanitizer (ASAN)", False), BoolVariable("debug_crt", "Compile with MSVC's debug CRT (/MDd)", False), + BoolVariable("incremental_link", "Use MSVC incremental linking. May increase or decrease build times.", False), + ("angle_libs", "Path to the ANGLE static libraries", ""), ] @@ -355,8 +357,9 @@ def configure_msvc(env, vcvars_msvc_config): else: env.AppendUnique(CCFLAGS=["/MD"]) - # MSVC incremental linking is broken and _increases_ link time (GH-77968). - env.Append(LINKFLAGS=["/INCREMENTAL:NO"]) + # MSVC incremental linking is broken and may _increase_ link time (GH-77968). + if not env["incremental_link"]: + env.Append(LINKFLAGS=["/INCREMENTAL:NO"]) if env["arch"] == "x86_32": env["x86_libtheora_opt_vc"] = True @@ -382,7 +385,6 @@ def configure_msvc(env, vcvars_msvc_config): "WINMIDI_ENABLED", "TYPED_METHOD_BIND", "WIN32", - "MSVC", "WINVER=%s" % env["target_win_version"], "_WIN32_WINNT=%s" % env["target_win_version"], ] @@ -429,7 +431,16 @@ def configure_msvc(env, vcvars_msvc_config): if env["opengl3"]: env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"]) - LIBS += ["opengl32"] + if env["angle_libs"] != "": + env.AppendUnique(CPPDEFINES=["EGL_STATIC"]) + env.Append(LIBPATH=[env["angle_libs"]]) + LIBS += [ + "libANGLE.windows." + env["arch"], + "libEGL.windows." + env["arch"], + "libGLES.windows." + env["arch"], + ] + LIBS += ["dxgi", "d3d9", "d3d11"] + env.Prepend(CPPPATH=["#thirdparty/angle/include"]) env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS]) @@ -612,7 +623,18 @@ def configure_mingw(env): if env["opengl3"]: env.Append(CPPDEFINES=["GLES3_ENABLED"]) - env.Append(LIBS=["opengl32"]) + if env["angle_libs"] != "": + env.AppendUnique(CPPDEFINES=["EGL_STATIC"]) + env.Append(LIBPATH=[env["angle_libs"]]) + env.Append( + LIBS=[ + "EGL.windows." + env["arch"], + "GLES.windows." + env["arch"], + "ANGLE.windows." + env["arch"], + ] + ) + env.Append(LIBS=["dxgi", "d3d9", "d3d11"]) + env.Prepend(CPPPATH=["#thirdparty/angle/include"]) env.Append(CPPDEFINES=["MINGW_ENABLED", ("MINGW_HAS_SECURE_API", 1)]) diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 5863a75324..70c6fe6549 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -31,6 +31,7 @@ #include "display_server_windows.h" #include "os_windows.h" +#include "wgl_detect_version.h" #include "core/config/project_settings.h" #include "core/io/marshalls.h" @@ -184,37 +185,37 @@ void DisplayServerWindows::_register_raw_input_devices(WindowID p_target_window) } bool DisplayServerWindows::tts_is_speaking() const { - ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return tts->is_speaking(); } bool DisplayServerWindows::tts_is_paused() const { - ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return tts->is_paused(); } TypedArray<Dictionary> DisplayServerWindows::tts_get_voices() const { - ERR_FAIL_COND_V_MSG(!tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_V_MSG(tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return tts->get_voices(); } void DisplayServerWindows::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt); } void DisplayServerWindows::tts_pause() { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); tts->pause(); } void DisplayServerWindows::tts_resume() { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); tts->resume(); } void DisplayServerWindows::tts_stop() { - ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); + ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); tts->stop(); } @@ -413,7 +414,7 @@ void DisplayServerWindows::clipboard_set(const String &p_text) { Char16String utf16 = text.utf16(); HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (utf16.length() + 1) * sizeof(WCHAR)); - ERR_FAIL_COND_MSG(mem == nullptr, "Unable to allocate memory for clipboard contents."); + ERR_FAIL_NULL_MSG(mem, "Unable to allocate memory for clipboard contents."); LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem); memcpy(lptstrCopy, utf16.get_data(), (utf16.length() + 1) * sizeof(WCHAR)); @@ -424,7 +425,7 @@ void DisplayServerWindows::clipboard_set(const String &p_text) { // 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."); + ERR_FAIL_NULL_MSG(mem, "Unable to allocate memory for clipboard contents."); LPTSTR ptr = (LPTSTR)GlobalLock(mem); memcpy(ptr, utf8.get_data(), utf8.length()); @@ -1070,8 +1071,11 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) { } #endif #ifdef GLES3_ENABLED - if (gl_manager) { - gl_manager->window_destroy(p_window); + if (gl_manager_angle) { + gl_manager_angle->window_destroy(p_window); + } + if (gl_manager_native) { + gl_manager_native->window_destroy(p_window); } #endif @@ -1089,8 +1093,11 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) { void DisplayServerWindows::gl_window_make_current(DisplayServer::WindowID p_window_id) { #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_make_current(p_window_id); + if (gl_manager_angle) { + gl_manager_angle->window_make_current(p_window_id); + } + if (gl_manager_native) { + gl_manager_native->window_make_current(p_window_id); } #endif } @@ -1106,14 +1113,18 @@ int64_t DisplayServerWindows::window_get_native_handle(HandleType p_handle_type, } #if defined(GLES3_ENABLED) case WINDOW_VIEW: { - if (gl_manager) { - return (int64_t)gl_manager->get_hdc(p_window); + if (gl_manager_native) { + return (int64_t)gl_manager_native->get_hdc(p_window); + } else { + return (int64_t)GetDC(windows[p_window].hWnd); } - return 0; } case OPENGL_CONTEXT: { - if (gl_manager) { - return (int64_t)gl_manager->get_hglrc(p_window); + if (gl_manager_native) { + return (int64_t)gl_manager_native->get_hglrc(p_window); + } + if (gl_manager_angle) { + return (int64_t)gl_manager_angle->get_context(p_window); } return 0; } @@ -1180,6 +1191,51 @@ void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_wi SetWindowTextW(windows[p_window].hWnd, (LPCWSTR)(p_title.utf16().get_data())); } +Size2i DisplayServerWindows::window_get_title_size(const String &p_title, WindowID p_window) const { + _THREAD_SAFE_METHOD_ + + Size2i size; + ERR_FAIL_COND_V(!windows.has(p_window), size); + + const WindowData &wd = windows[p_window]; + if (wd.fullscreen || wd.minimized || wd.borderless) { + return size; + } + + HDC hdc = GetDCEx(wd.hWnd, NULL, DCX_WINDOW); + if (hdc) { + Char16String s = p_title.utf16(); + SIZE text_size; + if (GetTextExtentPoint32W(hdc, (LPCWSTR)(s.get_data()), s.length(), &text_size)) { + size.x = text_size.cx; + size.y = text_size.cy; + } + + ReleaseDC(wd.hWnd, hdc); + } + RECT rect; + if (DwmGetWindowAttribute(wd.hWnd, DWMWA_CAPTION_BUTTON_BOUNDS, &rect, sizeof(RECT)) == S_OK) { + if (rect.right - rect.left > 0) { + ClientToScreen(wd.hWnd, (POINT *)&rect.left); + ClientToScreen(wd.hWnd, (POINT *)&rect.right); + + if (win81p_PhysicalToLogicalPointForPerMonitorDPI) { + win81p_PhysicalToLogicalPointForPerMonitorDPI(0, (POINT *)&rect.left); + win81p_PhysicalToLogicalPointForPerMonitorDPI(0, (POINT *)&rect.right); + } + + size.x += (rect.right - rect.left); + size.y = MAX(size.y, rect.bottom - rect.top); + } + } + if (icon.is_valid()) { + size.x += 32; + } else { + size.x += 16; + } + return size; +} + void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) { _THREAD_SAFE_METHOD_ @@ -1447,8 +1503,11 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo } #endif #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_resize(p_window, w, h); + if (gl_manager_native) { + gl_manager_native->window_resize(p_window, w, h); + } + if (gl_manager_angle) { + gl_manager_angle->window_resize(p_window, w, h); } #endif @@ -1511,7 +1570,7 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre if (p_fullscreen || p_borderless) { r_style |= WS_POPUP; // p_borderless was WS_EX_TOOLWINDOW in the past. - if (p_fullscreen && p_multiwindow_fs) { + if ((p_fullscreen && p_multiwindow_fs) || p_maximized) { r_style |= WS_BORDER; // Allows child windows to be displayed on top of full screen. } } else { @@ -2318,8 +2377,11 @@ void DisplayServerWindows::make_rendering_thread() { void DisplayServerWindows::swap_buffers() { #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->swap_buffers(); + if (gl_manager_angle) { + gl_manager_angle->swap_buffers(); + } + if (gl_manager_native) { + gl_manager_native->swap_buffers(); } #endif } @@ -2390,7 +2452,7 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { 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()) + "."); + ERR_FAIL_NULL_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; @@ -2400,7 +2462,7 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { 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()) + "."); + ERR_FAIL_NULL_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; @@ -2465,7 +2527,7 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) { } HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000); - ERR_FAIL_COND(!hicon); + ERR_FAIL_NULL(hicon); icon = img; @@ -2490,8 +2552,11 @@ void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsyn #endif #if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->set_use_vsync(p_window, p_vsync_mode != DisplayServer::VSYNC_DISABLED); + if (gl_manager_native) { + gl_manager_native->set_use_vsync(p_window, p_vsync_mode != DisplayServer::VSYNC_DISABLED); + } + if (gl_manager_angle) { + gl_manager_angle->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED); } #endif } @@ -2505,8 +2570,11 @@ DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_ #endif #if defined(GLES3_ENABLED) - if (gl_manager) { - return gl_manager->is_using_vsync(p_window) ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED; + if (gl_manager_native) { + return gl_manager_native->is_using_vsync(p_window) ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED; + } + if (gl_manager_angle) { + return gl_manager_angle->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED; } #endif @@ -2829,6 +2897,30 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA // Process window messages. switch (uMsg) { + case WM_NCPAINT: { + if (RenderingServer::get_singleton() && (windows[window_id].borderless || (windows[window_id].fullscreen && windows[window_id].multiwindow_fs))) { + Color color = RenderingServer::get_singleton()->get_default_clear_color(); + HDC hdc = GetWindowDC(hWnd); + if (hdc) { + HPEN pen = CreatePen(PS_SOLID, 1, RGB(color.r * 255.f, color.g * 255.f, color.b * 255.f)); + if (pen) { + HGDIOBJ prev_pen = SelectObject(hdc, pen); + HGDIOBJ prev_brush = SelectObject(hdc, GetStockObject(NULL_BRUSH)); + + RECT rc; + GetWindowRect(hWnd, &rc); + OffsetRect(&rc, -rc.left, -rc.top); + Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom); + + SelectObject(hdc, prev_pen); + SelectObject(hdc, prev_brush); + DeleteObject(pen); + } + ReleaseDC(hWnd, hdc); + } + return 0; + } + } break; case WM_NCHITTEST: { if (windows[window_id].mpass) { return HTTRANSPARENT; @@ -4224,10 +4316,20 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, #endif #ifdef GLES3_ENABLED - if (gl_manager) { - if (gl_manager->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) != OK) { - memdelete(gl_manager); - gl_manager = nullptr; + if (gl_manager_native) { + if (gl_manager_native->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) != OK) { + memdelete(gl_manager_native); + gl_manager_native = nullptr; + windows.erase(id); + ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create an OpenGL window."); + } + window_set_vsync_mode(p_vsync_mode, id); + } + + if (gl_manager_angle) { + if (gl_manager_angle->window_create(id, nullptr, wd.hWnd, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) != OK) { + memdelete(gl_manager_angle); + gl_manager_angle = nullptr; windows.erase(id); ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create an OpenGL window."); } @@ -4328,6 +4430,7 @@ bool DisplayServerWindows::winink_available = false; GetPointerTypePtr DisplayServerWindows::win8p_GetPointerType = nullptr; GetPointerPenInfoPtr DisplayServerWindows::win8p_GetPointerPenInfo = nullptr; LogicalToPhysicalPointForPerMonitorDPIPtr DisplayServerWindows::win81p_LogicalToPhysicalPointForPerMonitorDPI = nullptr; +PhysicalToLogicalPointForPerMonitorDPIPtr DisplayServerWindows::win81p_PhysicalToLogicalPointForPerMonitorDPI = nullptr; typedef enum _SHC_PROCESS_DPI_AWARENESS { SHC_PROCESS_DPI_UNAWARE = 0, @@ -4465,6 +4568,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType"); win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo"); win81p_LogicalToPhysicalPointForPerMonitorDPI = (LogicalToPhysicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "LogicalToPhysicalPointForPerMonitorDPI"); + win81p_PhysicalToLogicalPointForPerMonitorDPI = (PhysicalToLogicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "PhysicalToLogicalPointForPerMonitorDPI"); winink_available = win8p_GetPointerType && win8p_GetPointerPenInfo; } @@ -4523,18 +4627,36 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win #if defined(GLES3_ENABLED) if (rendering_driver == "opengl3") { - GLManager_Windows::ContextType opengl_api_type = GLManager_Windows::GLES_3_0_COMPATIBLE; + int gl_version = detect_wgl_version(); + if (gl_version < 30003) { + WARN_PRINT("Your video card drivers seem not to support the required OpenGL 3.3 version, switching to ANGLE."); + rendering_driver = "opengl3_angle"; + } + } + + if (rendering_driver == "opengl3") { + gl_manager_native = memnew(GLManagerNative_Windows); - gl_manager = memnew(GLManager_Windows(opengl_api_type)); + if (gl_manager_native->initialize() != OK) { + memdelete(gl_manager_native); + gl_manager_native = nullptr; + r_error = ERR_UNAVAILABLE; + return; + } - if (gl_manager->initialize() != OK) { - memdelete(gl_manager); - gl_manager = nullptr; + RasterizerGLES3::make_current(true); + } + if (rendering_driver == "opengl3_angle") { + gl_manager_angle = memnew(GLManagerANGLE_Windows); + + if (gl_manager_angle->initialize() != OK) { + memdelete(gl_manager_angle); + gl_manager_angle = nullptr; r_error = ERR_UNAVAILABLE; return; } - RasterizerGLES3::make_current(); + RasterizerGLES3::make_current(false); } #endif @@ -4608,6 +4730,7 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() { #endif #ifdef GLES3_ENABLED drivers.push_back("opengl3"); + drivers.push_back("opengl3_angle"); #endif return drivers; @@ -4693,9 +4816,13 @@ DisplayServerWindows::~DisplayServerWindows() { SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, 0, 0); } #ifdef GLES3_ENABLED - if (gl_manager) { - memdelete(gl_manager); - gl_manager = nullptr; + if (gl_manager_angle) { + memdelete(gl_manager_angle); + gl_manager_angle = nullptr; + } + if (gl_manager_native) { + memdelete(gl_manager_native); + gl_manager_native = nullptr; } #endif if (tts) { diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 59c4442604..48c8c20280 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -59,8 +59,9 @@ #endif #if defined(GLES3_ENABLED) -#include "gl_manager_windows.h" -#endif +#include "gl_manager_windows_angle.h" +#include "gl_manager_windows_native.h" +#endif // GLES3_ENABLED #include <io.h> #include <stdio.h> @@ -261,6 +262,7 @@ typedef struct tagPOINTER_PEN_INFO { 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 BOOL(WINAPI *LogicalToPhysicalPointForPerMonitorDPIPtr)(HWND hwnd, LPPOINT lpPoint); +typedef BOOL(WINAPI *PhysicalToLogicalPointForPerMonitorDPIPtr)(HWND hwnd, LPPOINT lpPoint); typedef struct { BYTE bWidth; // Width, in pixels, of the image @@ -308,6 +310,7 @@ class DisplayServerWindows : public DisplayServer { // DPI conversion API static LogicalToPhysicalPointForPerMonitorDPIPtr win81p_LogicalToPhysicalPointForPerMonitorDPI; + static PhysicalToLogicalPointForPerMonitorDPIPtr win81p_PhysicalToLogicalPointForPerMonitorDPI; void _update_tablet_ctx(const String &p_old_driver, const String &p_new_driver); String tablet_driver; @@ -335,7 +338,8 @@ class DisplayServerWindows : public DisplayServer { Point2i center; #if defined(GLES3_ENABLED) - GLManager_Windows *gl_manager = nullptr; + GLManagerANGLE_Windows *gl_manager_angle = nullptr; + GLManagerNative_Windows *gl_manager_native = nullptr; #endif #if defined(VULKAN_ENABLED) @@ -567,6 +571,7 @@ public: virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override; virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override; + virtual Size2i window_get_title_size(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) const override; virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID) override; virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override; diff --git a/platform/windows/doc_classes/EditorExportPlatformWindows.xml b/platform/windows/doc_classes/EditorExportPlatformWindows.xml index fc068efc75..2a286de100 100644 --- a/platform/windows/doc_classes/EditorExportPlatformWindows.xml +++ b/platform/windows/doc_classes/EditorExportPlatformWindows.xml @@ -18,6 +18,9 @@ <member name="application/copyright" type="String" setter="" getter=""> Copyright notice for the bundle visible to the user. Optional. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url]. </member> + <member name="application/export_angle" type="int" setter="" getter=""> + If set to [code]1[/code], ANGLE libraries are exported with the exported application. If set to [code]0[/code], ANGLE libraries are exported only if [member ProjectSettings.rendering/gl_compatibility/driver] is set to [code]"opengl3_angle"[/code]. + </member> <member name="application/file_description" type="String" setter="" getter=""> File description to be presented to users. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url]. </member> diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index c4be1821bd..4185c36d77 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -38,6 +38,7 @@ #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_scale.h" +#include "editor/editor_string_names.h" #include "editor/export/editor_export.h" #include "modules/modules_enabled.gen.h" // For svg. @@ -178,6 +179,35 @@ Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset> } Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + int export_angle = p_preset->get("application/export_angle"); + bool include_angle_libs = false; + if (export_angle == 0) { + include_angle_libs = String(GLOBAL_GET("rendering/gl_compatibility/driver.windows")) == "opengl3_angle"; + } else if (export_angle == 1) { + include_angle_libs = true; + } + + if (include_angle_libs) { + String custom_debug = p_preset->get("custom_template/debug"); + String custom_release = p_preset->get("custom_template/release"); + String arch = p_preset->get("binary_format/architecture"); + + String template_path = p_debug ? custom_debug : custom_release; + + template_path = template_path.strip_edges(); + + if (template_path.is_empty()) { + template_path = find_export_template(get_template_file_name(p_debug ? "debug" : "release", arch)); + } + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + if (da->file_exists(template_path.get_base_dir().path_join("libEGL." + arch + ".dll"))) { + da->copy(template_path.get_base_dir().path_join("libEGL." + arch + ".dll"), p_path.get_base_dir().path_join("libEGL.dll"), get_chmod_flags()); + } + if (da->file_exists(template_path.get_base_dir().path_join("libGLESv2." + arch + ".dll"))) { + da->copy(template_path.get_base_dir().path_join("libGLESv2." + arch + ".dll"), p_path.get_base_dir().path_join("libGLESv2.dll"), get_chmod_flags()); + } + } + bool export_as_zip = p_path.ends_with("zip"); bool embedded = p_preset->get("binary_format/embed_pck"); @@ -310,7 +340,7 @@ bool EditorExportPlatformWindows::get_export_option_visibility(const EditorExpor // Hide resources. bool mod_res = p_preset->get("application/modify_resources"); - if (!mod_res && p_option != "application/modify_resources" && p_option.begins_with("application/")) { + if (!mod_res && p_option != "application/modify_resources" && p_option != "application/export_angle" && p_option.begins_with("application/")) { return false; } @@ -349,6 +379,7 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_angle", PROPERTY_HINT_ENUM, "Auto,Yes,No"), 0, true)); String run_script = "Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'\n" "$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'\n" @@ -1020,7 +1051,7 @@ EditorExportPlatformWindows::EditorExportPlatformWindows() { Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); if (theme.is_valid()) { - stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons")); + stop_icon = theme->get_icon(SNAME("Stop"), EditorStringName(EditorIcons)); } else { stop_icon.instantiate(); } diff --git a/platform/windows/gl_manager_windows_angle.cpp b/platform/windows/gl_manager_windows_angle.cpp new file mode 100644 index 0000000000..3086edc7f2 --- /dev/null +++ b/platform/windows/gl_manager_windows_angle.cpp @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* gl_manager_windows_angle.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "gl_manager_windows_angle.h" + +#if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED) + +#include <stdio.h> +#include <stdlib.h> + +#include <EGL/eglext_angle.h> + +const char *GLManagerANGLE_Windows::_get_platform_extension_name() const { + return "EGL_ANGLE_platform_angle"; +} + +EGLenum GLManagerANGLE_Windows::_get_platform_extension_enum() const { + return EGL_PLATFORM_ANGLE_ANGLE; +} + +Vector<EGLAttrib> GLManagerANGLE_Windows::_get_platform_display_attributes() const { + Vector<EGLAttrib> ret; + ret.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); + ret.push_back(EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE); + ret.push_back(EGL_NONE); + + return ret; +} + +EGLenum GLManagerANGLE_Windows::_get_platform_api_enum() const { + return EGL_OPENGL_ES_API; +} + +Vector<EGLint> GLManagerANGLE_Windows::_get_platform_context_attribs() const { + Vector<EGLint> ret; + ret.push_back(EGL_CONTEXT_CLIENT_VERSION); + ret.push_back(3); + ret.push_back(EGL_NONE); + + return ret; +} + +#endif // WINDOWS_ENABLED && GLES3_ENABLED diff --git a/platform/windows/gl_manager_windows_angle.h b/platform/windows/gl_manager_windows_angle.h new file mode 100644 index 0000000000..d8dc651cfd --- /dev/null +++ b/platform/windows/gl_manager_windows_angle.h @@ -0,0 +1,61 @@ +/**************************************************************************/ +/* gl_manager_windows_angle.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef GL_MANAGER_WINDOWS_ANGLE_H +#define GL_MANAGER_WINDOWS_ANGLE_H + +#if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED) + +#include "core/error/error_list.h" +#include "core/os/os.h" +#include "core/templates/local_vector.h" +#include "drivers/egl/egl_manager.h" +#include "servers/display_server.h" + +#include <windows.h> + +class GLManagerANGLE_Windows : public EGLManager { +private: + virtual const char *_get_platform_extension_name() const override; + virtual EGLenum _get_platform_extension_enum() const override; + virtual EGLenum _get_platform_api_enum() const override; + virtual Vector<EGLAttrib> _get_platform_display_attributes() const override; + virtual Vector<EGLint> _get_platform_context_attribs() const override; + +public: + void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {} + + GLManagerANGLE_Windows(){}; + ~GLManagerANGLE_Windows(){}; +}; + +#endif // WINDOWS_ENABLED && GLES3_ENABLED + +#endif // GL_MANAGER_WINDOWS_ANGLE_H diff --git a/platform/windows/gl_manager_windows.cpp b/platform/windows/gl_manager_windows_native.cpp index d3972c7bbc..b350786d11 100644 --- a/platform/windows/gl_manager_windows.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -1,5 +1,5 @@ /**************************************************************************/ -/* gl_manager_windows.cpp */ +/* gl_manager_windows_native.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "gl_manager_windows.h" +#include "gl_manager_windows_native.h" #if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED) @@ -52,11 +52,14 @@ #if defined(__GNUC__) // Workaround GCC warning from -Wcast-function-type. -#define wglGetProcAddress (void *)wglGetProcAddress #define GetProcAddress (void *)GetProcAddress #endif +typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXT)(HDC); +typedef BOOL(APIENTRY *PFNWGLDELETECONTEXT)(HGLRC); +typedef BOOL(APIENTRY *PFNWGLMAKECURRENT)(HDC, HGLRC); typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *); +typedef void *(APIENTRY *PFNWGLGETPROCADDRESS)(LPCSTR); static String format_error_message(DWORD id) { LPWSTR messageBuffer = nullptr; @@ -85,6 +88,7 @@ typedef int(__cdecl *NvAPI_DRS_CreateApplication_t)(NvDRSSessionHandle, NvDRSPro typedef int(__cdecl *NvAPI_DRS_SaveSettings_t)(NvDRSSessionHandle); typedef int(__cdecl *NvAPI_DRS_SetSetting_t)(NvDRSSessionHandle, NvDRSProfileHandle, NVDRS_SETTING *); typedef int(__cdecl *NvAPI_DRS_FindProfileByName_t)(NvDRSSessionHandle, NvAPI_UnicodeString, NvDRSProfileHandle *); +typedef int(__cdecl *NvAPI_DRS_FindApplicationByName_t)(NvDRSSessionHandle, NvAPI_UnicodeString, NvDRSProfileHandle *, NVDRS_APPLICATION *); NvAPI_GetErrorMessage_t NvAPI_GetErrorMessage__; static bool nvapi_err_check(const char *msg, int status) { @@ -102,7 +106,7 @@ static bool nvapi_err_check(const char *msg, int status) { // On windows we have to disable threaded optimization when using NVIDIA graphics cards // to avoid stuttering, see https://github.com/microsoft/vscode-cpptools/issues/6592 // also see https://github.com/Ryujinx/Ryujinx/blob/master/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs -void GLManager_Windows::_nvapi_disable_threaded_optimization() { +void GLManagerNative_Windows::_nvapi_disable_threaded_optimization() { HMODULE nvapi = 0; #ifdef _WIN64 nvapi = LoadLibraryA("nvapi64.dll"); @@ -116,7 +120,7 @@ void GLManager_Windows::_nvapi_disable_threaded_optimization() { void *(__cdecl * NvAPI_QueryInterface)(unsigned int interface_id) = 0; - NvAPI_QueryInterface = (void *(__cdecl *)(unsigned int))GetProcAddress(nvapi, "nvapi_QueryInterface"); + NvAPI_QueryInterface = (void *(__cdecl *)(unsigned int))(void *)GetProcAddress(nvapi, "nvapi_QueryInterface"); if (NvAPI_QueryInterface == NULL) { print_verbose("Error getting NVAPI NvAPI_QueryInterface"); @@ -135,6 +139,7 @@ void GLManager_Windows::_nvapi_disable_threaded_optimization() { NvAPI_DRS_SaveSettings_t NvAPI_DRS_SaveSettings = (NvAPI_DRS_SaveSettings_t)NvAPI_QueryInterface(0xFCBC7E14); NvAPI_DRS_SetSetting_t NvAPI_DRS_SetSetting = (NvAPI_DRS_SetSetting_t)NvAPI_QueryInterface(0x577DD202); NvAPI_DRS_FindProfileByName_t NvAPI_DRS_FindProfileByName = (NvAPI_DRS_FindProfileByName_t)NvAPI_QueryInterface(0x7E4A9A0B); + NvAPI_DRS_FindApplicationByName_t NvAPI_DRS_FindApplicationByName = (NvAPI_DRS_FindApplicationByName_t)NvAPI_QueryInterface(0xEEE566B2); if (!nvapi_err_check("NVAPI: Init failed", NvAPI_Initialize())) { return; @@ -169,9 +174,9 @@ void GLManager_Windows::_nvapi_disable_threaded_optimization() { NvDRSProfileHandle profile_handle = 0; - int status = NvAPI_DRS_FindProfileByName(session_handle, (NvU16 *)(app_profile_name_u16.ptrw()), &profile_handle); + int profile_status = NvAPI_DRS_FindProfileByName(session_handle, (NvU16 *)(app_profile_name_u16.ptrw()), &profile_handle); - if (status != 0) { + if (profile_status != 0) { print_verbose("NVAPI: Profile not found, creating...."); NVDRS_PROFILE profile_info; @@ -184,9 +189,17 @@ void GLManager_Windows::_nvapi_disable_threaded_optimization() { NvAPI_Unload(); return; } + } + + NvDRSProfileHandle app_profile_handle = 0; + NVDRS_APPLICATION_V4 app; + app.version = NVDRS_APPLICATION_VER_V4; + + int app_status = NvAPI_DRS_FindApplicationByName(session_handle, (NvU16 *)(app_executable_name_u16.ptrw()), &app_profile_handle, &app); + + if (app_status != 0) { + print_verbose("NVAPI: Application not found, adding to profile..."); - NVDRS_APPLICATION_V4 app; - app.version = NVDRS_APPLICATION_VER_V4; app.isPredefined = 0; app.isMetro = 1; app.isCommandLine = 1; @@ -235,7 +248,7 @@ void GLManager_Windows::_nvapi_disable_threaded_optimization() { NvAPI_DRS_DestroySession(session_handle); } -int GLManager_Windows::_find_or_create_display(GLWindow &win) { +int GLManagerNative_Windows::_find_or_create_display(GLWindow &win) { // find display NYI, only 1 supported so far if (_displays.size()) { return 0; @@ -297,19 +310,36 @@ static Error _configure_pixel_format(HDC hDC) { return OK; } -Error GLManager_Windows::_create_context(GLWindow &win, GLDisplay &gl_display) { +PFNWGLCREATECONTEXT gd_wglCreateContext; +PFNWGLMAKECURRENT gd_wglMakeCurrent; +PFNWGLDELETECONTEXT gd_wglDeleteContext; +PFNWGLGETPROCADDRESS gd_wglGetProcAddress; + +Error GLManagerNative_Windows::_create_context(GLWindow &win, GLDisplay &gl_display) { Error err = _configure_pixel_format(win.hDC); if (err != OK) { return err; } - gl_display.hRC = wglCreateContext(win.hDC); + HMODULE module = LoadLibraryW(L"opengl32.dll"); + if (!module) { + return ERR_CANT_CREATE; + } + gd_wglCreateContext = (PFNWGLCREATECONTEXT)GetProcAddress(module, "wglCreateContext"); + gd_wglMakeCurrent = (PFNWGLMAKECURRENT)GetProcAddress(module, "wglMakeCurrent"); + gd_wglDeleteContext = (PFNWGLDELETECONTEXT)GetProcAddress(module, "wglDeleteContext"); + gd_wglGetProcAddress = (PFNWGLGETPROCADDRESS)GetProcAddress(module, "wglGetProcAddress"); + if (!gd_wglCreateContext || !gd_wglMakeCurrent || !gd_wglDeleteContext || !gd_wglGetProcAddress) { + return ERR_CANT_CREATE; + } + + gl_display.hRC = gd_wglCreateContext(win.hDC); if (!gl_display.hRC) // Are We Able To Get A Rendering Context? { return ERR_CANT_CREATE; // Return FALSE } - if (!wglMakeCurrent(win.hDC, gl_display.hRC)) { + if (!gd_wglMakeCurrent(win.hDC, gl_display.hRC)) { ERR_PRINT("Could not attach OpenGL context to newly created window: " + format_error_message(GetLastError())); } @@ -323,45 +353,45 @@ Error GLManager_Windows::_create_context(GLWindow &win, GLDisplay &gl_display) { }; //zero indicates the end of the array PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = nullptr; //pointer to the method - wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB"); + wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)gd_wglGetProcAddress("wglCreateContextAttribsARB"); if (wglCreateContextAttribsARB == nullptr) //OpenGL 3.0 is not supported { - wglDeleteContext(gl_display.hRC); + gd_wglDeleteContext(gl_display.hRC); gl_display.hRC = 0; return ERR_CANT_CREATE; } HGLRC new_hRC = wglCreateContextAttribsARB(win.hDC, 0, attribs); if (!new_hRC) { - wglDeleteContext(gl_display.hRC); + gd_wglDeleteContext(gl_display.hRC); gl_display.hRC = 0; return ERR_CANT_CREATE; } - if (!wglMakeCurrent(win.hDC, nullptr)) { + if (!gd_wglMakeCurrent(win.hDC, nullptr)) { ERR_PRINT("Could not detach OpenGL context from newly created window: " + format_error_message(GetLastError())); } - wglDeleteContext(gl_display.hRC); + gd_wglDeleteContext(gl_display.hRC); gl_display.hRC = new_hRC; - if (!wglMakeCurrent(win.hDC, gl_display.hRC)) // Try To Activate The Rendering Context + if (!gd_wglMakeCurrent(win.hDC, gl_display.hRC)) // Try to activate the rendering context. { ERR_PRINT("Could not attach OpenGL context to newly created window with replaced OpenGL context: " + format_error_message(GetLastError())); - wglDeleteContext(gl_display.hRC); + gd_wglDeleteContext(gl_display.hRC); gl_display.hRC = 0; return ERR_CANT_CREATE; } if (!wglSwapIntervalEXT) { - wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); + wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)gd_wglGetProcAddress("wglSwapIntervalEXT"); } return OK; } -Error GLManager_Windows::window_create(DisplayServer::WindowID p_window_id, HWND p_hwnd, HINSTANCE p_hinstance, int p_width, int p_height) { +Error GLManagerNative_Windows::window_create(DisplayServer::WindowID p_window_id, HWND p_hwnd, HINSTANCE p_hinstance, int p_width, int p_height) { HDC hDC = GetDC(p_hwnd); if (!hDC) { return ERR_CANT_CREATE; @@ -374,8 +404,6 @@ Error GLManager_Windows::window_create(DisplayServer::WindowID p_window_id, HWND } GLWindow win; - win.width = p_width; - win.height = p_height; win.hwnd = p_hwnd; win.hDC = hDC; @@ -395,24 +423,11 @@ Error GLManager_Windows::window_create(DisplayServer::WindowID p_window_id, HWND return OK; } -void GLManager_Windows::_internal_set_current_window(GLWindow *p_win) { +void GLManagerNative_Windows::_internal_set_current_window(GLWindow *p_win) { _current_window = p_win; } -void GLManager_Windows::window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) { - get_window(p_window_id).width = p_width; - get_window(p_window_id).height = p_height; -} - -int GLManager_Windows::window_get_width(DisplayServer::WindowID p_window_id) { - return get_window(p_window_id).width; -} - -int GLManager_Windows::window_get_height(DisplayServer::WindowID p_window_id) { - return get_window(p_window_id).height; -} - -void GLManager_Windows::window_destroy(DisplayServer::WindowID p_window_id) { +void GLManagerNative_Windows::window_destroy(DisplayServer::WindowID p_window_id) { GLWindow &win = get_window(p_window_id); if (_current_window == &win) { _current_window = nullptr; @@ -420,17 +435,17 @@ void GLManager_Windows::window_destroy(DisplayServer::WindowID p_window_id) { _windows.erase(p_window_id); } -void GLManager_Windows::release_current() { +void GLManagerNative_Windows::release_current() { if (!_current_window) { return; } - if (!wglMakeCurrent(_current_window->hDC, nullptr)) { + if (!gd_wglMakeCurrent(_current_window->hDC, nullptr)) { ERR_PRINT("Could not detach OpenGL context from window marked current: " + format_error_message(GetLastError())); } } -void GLManager_Windows::window_make_current(DisplayServer::WindowID p_window_id) { +void GLManagerNative_Windows::window_make_current(DisplayServer::WindowID p_window_id) { if (p_window_id == -1) { return; } @@ -444,33 +459,33 @@ void GLManager_Windows::window_make_current(DisplayServer::WindowID p_window_id) } const GLDisplay &disp = get_display(win.gldisplay_id); - if (!wglMakeCurrent(win.hDC, disp.hRC)) { + if (!gd_wglMakeCurrent(win.hDC, disp.hRC)) { ERR_PRINT("Could not switch OpenGL context to other window: " + format_error_message(GetLastError())); } _internal_set_current_window(&win); } -void GLManager_Windows::make_current() { +void GLManagerNative_Windows::make_current() { if (!_current_window) { return; } const GLDisplay &disp = get_current_display(); - if (!wglMakeCurrent(_current_window->hDC, disp.hRC)) { + if (!gd_wglMakeCurrent(_current_window->hDC, disp.hRC)) { ERR_PRINT("Could not switch OpenGL context to window marked current: " + format_error_message(GetLastError())); } } -void GLManager_Windows::swap_buffers() { +void GLManagerNative_Windows::swap_buffers() { SwapBuffers(_current_window->hDC); } -Error GLManager_Windows::initialize() { +Error GLManagerNative_Windows::initialize() { _nvapi_disable_threaded_optimization(); return OK; } -void GLManager_Windows::set_use_vsync(DisplayServer::WindowID p_window_id, bool p_use) { +void GLManagerNative_Windows::set_use_vsync(DisplayServer::WindowID p_window_id, bool p_use) { GLWindow &win = get_window(p_window_id); GLWindow *current = _current_window; @@ -480,7 +495,12 @@ void GLManager_Windows::set_use_vsync(DisplayServer::WindowID p_window_id, bool if (wglSwapIntervalEXT) { win.use_vsync = p_use; - wglSwapIntervalEXT(p_use ? 1 : 0); + + if (!wglSwapIntervalEXT(p_use ? 1 : 0)) { + WARN_PRINT("Could not set V-Sync mode."); + } + } else { + WARN_PRINT("Could not set V-Sync mode. V-Sync is not supported."); } if (current != _current_window) { @@ -489,29 +509,27 @@ void GLManager_Windows::set_use_vsync(DisplayServer::WindowID p_window_id, bool } } -bool GLManager_Windows::is_using_vsync(DisplayServer::WindowID p_window_id) const { +bool GLManagerNative_Windows::is_using_vsync(DisplayServer::WindowID p_window_id) const { return get_window(p_window_id).use_vsync; } -HDC GLManager_Windows::get_hdc(DisplayServer::WindowID p_window_id) { +HDC GLManagerNative_Windows::get_hdc(DisplayServer::WindowID p_window_id) { return get_window(p_window_id).hDC; } -HGLRC GLManager_Windows::get_hglrc(DisplayServer::WindowID p_window_id) { +HGLRC GLManagerNative_Windows::get_hglrc(DisplayServer::WindowID p_window_id) { const GLWindow &win = get_window(p_window_id); const GLDisplay &disp = get_display(win.gldisplay_id); return disp.hRC; } -GLManager_Windows::GLManager_Windows(ContextType p_context_type) { - context_type = p_context_type; - +GLManagerNative_Windows::GLManagerNative_Windows() { direct_render = false; glx_minor = glx_major = 0; _current_window = nullptr; } -GLManager_Windows::~GLManager_Windows() { +GLManagerNative_Windows::~GLManagerNative_Windows() { release_current(); } diff --git a/platform/windows/gl_manager_windows.h b/platform/windows/gl_manager_windows_native.h index 482b00a1ba..829c53b3d2 100644 --- a/platform/windows/gl_manager_windows.h +++ b/platform/windows/gl_manager_windows_native.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* gl_manager_windows.h */ +/* gl_manager_windows_native.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef GL_MANAGER_WINDOWS_H -#define GL_MANAGER_WINDOWS_H +#ifndef GL_MANAGER_WINDOWS_NATIVE_H +#define GL_MANAGER_WINDOWS_NATIVE_H #if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED) @@ -43,17 +43,10 @@ typedef bool(APIENTRY *PFNWGLSWAPINTERVALEXTPROC)(int interval); typedef int(APIENTRY *PFNWGLGETSWAPINTERVALEXTPROC)(void); -class GLManager_Windows { -public: - enum ContextType { - GLES_3_0_COMPATIBLE, - }; - +class GLManagerNative_Windows { private: // any data specific to the window struct GLWindow { - int width = 0; - int height = 0; bool use_vsync = false; // windows specific @@ -86,7 +79,6 @@ private: bool direct_render; int glx_minor, glx_major; - ContextType context_type; private: void _nvapi_disable_threaded_optimization(); @@ -96,11 +88,7 @@ private: public: Error window_create(DisplayServer::WindowID p_window_id, HWND p_hwnd, HINSTANCE p_hinstance, int p_width, int p_height); void window_destroy(DisplayServer::WindowID p_window_id); - void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height); - - // get directly from the cached GLWindow - int window_get_width(DisplayServer::WindowID p_window_id = 0); - int window_get_height(DisplayServer::WindowID p_window_id = 0); + void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {} void release_current(); void make_current(); @@ -116,10 +104,10 @@ public: HDC get_hdc(DisplayServer::WindowID p_window_id); HGLRC get_hglrc(DisplayServer::WindowID p_window_id); - GLManager_Windows(ContextType p_context_type); - ~GLManager_Windows(); + GLManagerNative_Windows(); + ~GLManagerNative_Windows(); }; #endif // WINDOWS_ENABLED && GLES3_ENABLED -#endif // GL_MANAGER_WINDOWS_H +#endif // GL_MANAGER_WINDOWS_NATIVE_H diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 7c94e38e14..1d3b80e21e 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -395,7 +395,7 @@ Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_han } } #else - ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, format_error_message(GetLastError()))); + ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, format_error_message(GetLastError()))); #endif if (cookie) { @@ -1356,18 +1356,13 @@ Error OS_Windows::shell_open(String p_uri) { } Error OS_Windows::shell_show_in_file_manager(String p_path, bool p_open_folder) { - p_path = p_path.trim_suffix("file://"); - bool open_folder = false; if (DirAccess::dir_exists_absolute(p_path) && p_open_folder) { open_folder = true; } - if (p_path.begins_with("\"")) { - p_path = String("\"") + p_path; - } - if (p_path.ends_with("\"")) { - p_path = p_path + String("\""); + if (!p_path.is_quoted()) { + p_path = p_path.quote(); } p_path = p_path.replace("/", "\\"); @@ -1683,7 +1678,7 @@ Error OS_Windows::move_to_trash(const String &p_path) { String OS_Windows::get_system_ca_certificates() { HCERTSTORE cert_store = CertOpenSystemStoreA(0, "ROOT"); - ERR_FAIL_COND_V_MSG(!cert_store, "", "Failed to read the root certificate store."); + ERR_FAIL_NULL_V_MSG(cert_store, "", "Failed to read the root certificate store."); FILETIME curr_time; GetSystemTimeAsFileTime(&curr_time); diff --git a/platform/windows/platform_config.h b/platform/windows/platform_config.h index ae4e51e3fb..964e341ce4 100644 --- a/platform/windows/platform_config.h +++ b/platform/windows/platform_config.h @@ -29,5 +29,3 @@ /**************************************************************************/ #include <malloc.h> - -#define OPENGL_INCLUDE_H "thirdparty/glad/glad/gl.h" diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java b/platform/windows/platform_gl.h index 63999a8321..e4af8b0ccd 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java +++ b/platform/windows/platform_gl.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* GodotPluginInfoProvider.java */ +/* platform_gl.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,45 +28,26 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -package org.godotengine.godot.plugin; +#ifndef PLATFORM_GL_H +#define PLATFORM_GL_H -import androidx.annotation.NonNull; +#ifndef GL_API_ENABLED +#define GL_API_ENABLED // Allow using desktop GL. +#endif -import java.util.Collections; -import java.util.Set; +#ifndef GLES_API_ENABLED +#define GLES_API_ENABLED // Allow using GLES (ANGLE). +#endif -/** - * Provides the set of information expected from a Godot plugin. - */ -public interface GodotPluginInfoProvider { - /** - * Returns the name of the plugin. - */ - @NonNull - String getPluginName(); +#ifdef EGL_STATIC +#define KHRONOS_STATIC 1 +#include "thirdparty/angle/include/EGL/egl.h" +#include "thirdparty/angle/include/EGL/eglext.h" +#undef KHRONOS_STATIC +#else +#include "thirdparty/glad/glad/egl.h" +#endif - /** - * Returns the list of signals to be exposed to Godot. - */ - @NonNull - default Set<SignalInfo> getPluginSignals() { - return Collections.emptySet(); - } +#include "thirdparty/glad/glad/gl.h" - /** - * Returns the paths for the plugin's gdextension libraries (if any). - * - * The paths must be relative to the 'assets' directory and point to a '*.gdextension' file. - */ - @NonNull - default Set<String> getPluginGDExtensionLibrariesPaths() { - return Collections.emptySet(); - } - - /** - * This is invoked on the render thread when the plugin described by this instance has been - * registered. - */ - default void onPluginRegistered() { - } -} +#endif // PLATFORM_GL_H diff --git a/platform/windows/platform_windows_builders.py b/platform/windows/platform_windows_builders.py index b522a75a9c..51652fa814 100644 --- a/platform/windows/platform_windows_builders.py +++ b/platform/windows/platform_windows_builders.py @@ -5,14 +5,24 @@ All such functions are invoked in a subprocess on Windows to prevent build flaki """ import os from detect import get_mingw_bin_prefix +from detect import try_cmd from platform_methods import subprocess_main def make_debug_mingw(target, source, env): mingw_bin_prefix = get_mingw_bin_prefix(env["mingw_prefix"], env["arch"]) - os.system(mingw_bin_prefix + "objcopy --only-keep-debug {0} {0}.debugsymbols".format(target[0])) - os.system(mingw_bin_prefix + "strip --strip-debug --strip-unneeded {0}".format(target[0])) - os.system(mingw_bin_prefix + "objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(target[0])) + if try_cmd("objcopy --version", env["mingw_prefix"], env["arch"]): + os.system(mingw_bin_prefix + "objcopy --only-keep-debug {0} {0}.debugsymbols".format(target[0])) + else: + os.system("objcopy --only-keep-debug {0} {0}.debugsymbols".format(target[0])) + if try_cmd("strip --version", env["mingw_prefix"], env["arch"]): + os.system(mingw_bin_prefix + "strip --strip-debug --strip-unneeded {0}".format(target[0])) + else: + os.system("strip --strip-debug --strip-unneeded {0}".format(target[0])) + if try_cmd("objcopy --version", env["mingw_prefix"], env["arch"]): + os.system(mingw_bin_prefix + "objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(target[0])) + else: + os.system("objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(target[0])) if __name__ == "__main__": diff --git a/platform/windows/tts_windows.cpp b/platform/windows/tts_windows.cpp index 907096d890..11d63d85ee 100644 --- a/platform/windows/tts_windows.cpp +++ b/platform/windows/tts_windows.cpp @@ -114,7 +114,7 @@ void TTS_Windows::_update_tts() { } bool TTS_Windows::is_speaking() const { - ERR_FAIL_COND_V(!synth, false); + ERR_FAIL_NULL_V(synth, false); SPVOICESTATUS status; synth->GetStatus(&status, nullptr); @@ -122,7 +122,7 @@ bool TTS_Windows::is_speaking() const { } bool TTS_Windows::is_paused() const { - ERR_FAIL_COND_V(!synth, false); + ERR_FAIL_NULL_V(synth, false); return paused; } @@ -185,7 +185,7 @@ Array TTS_Windows::get_voices() const { } void TTS_Windows::speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) { - ERR_FAIL_COND(!synth); + ERR_FAIL_NULL(synth); if (p_interrupt) { stop(); } @@ -212,7 +212,7 @@ void TTS_Windows::speak(const String &p_text, const String &p_voice, int p_volum } void TTS_Windows::pause() { - ERR_FAIL_COND(!synth); + ERR_FAIL_NULL(synth); if (!paused) { if (synth->Pause() == S_OK) { paused = true; @@ -221,13 +221,13 @@ void TTS_Windows::pause() { } void TTS_Windows::resume() { - ERR_FAIL_COND(!synth); + ERR_FAIL_NULL(synth); synth->Resume(); paused = false; } void TTS_Windows::stop() { - ERR_FAIL_COND(!synth); + ERR_FAIL_NULL(synth); SPVOICESTATUS status; synth->GetStatus(&status, nullptr); diff --git a/platform/windows/wgl_detect_version.cpp b/platform/windows/wgl_detect_version.cpp new file mode 100644 index 0000000000..264cd525c5 --- /dev/null +++ b/platform/windows/wgl_detect_version.cpp @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* wgl_detect_version.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED) + +#include "wgl_detect_version.h" +#include "os_windows.h" + +#include "core/string/print_string.h" +#include "core/string/ustring.h" + +#include <windows.h> + +#include <dwmapi.h> +#include <stdio.h> +#include <stdlib.h> + +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_VENDOR 0x1F00 +#define WGL_RENDERER 0x1F01 +#define WGL_VERSION 0x1F02 + +#if defined(__GNUC__) +// Workaround GCC warning from -Wcast-function-type. +#define GetProcAddress (void *)GetProcAddress +#endif + +typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXT)(HDC); +typedef BOOL(APIENTRY *PFNWGLDELETECONTEXT)(HGLRC); +typedef BOOL(APIENTRY *PFNWGLMAKECURRENT)(HDC, HGLRC); +typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *); +typedef void *(APIENTRY *PFNWGLGETPROCADDRESS)(LPCSTR); +typedef const char *(APIENTRY *PFNWGLGETSTRINGPROC)(unsigned int); + +int detect_wgl_version() { + int major = 0; + int minor = 0; + + PFNWGLCREATECONTEXT gd_wglCreateContext; + PFNWGLMAKECURRENT gd_wglMakeCurrent; + PFNWGLDELETECONTEXT gd_wglDeleteContext; + PFNWGLGETPROCADDRESS gd_wglGetProcAddress; + + HMODULE module = LoadLibraryW(L"opengl32.dll"); + if (!module) { + return 0; + } + gd_wglCreateContext = (PFNWGLCREATECONTEXT)GetProcAddress(module, "wglCreateContext"); + gd_wglMakeCurrent = (PFNWGLMAKECURRENT)GetProcAddress(module, "wglMakeCurrent"); + gd_wglDeleteContext = (PFNWGLDELETECONTEXT)GetProcAddress(module, "wglDeleteContext"); + gd_wglGetProcAddress = (PFNWGLGETPROCADDRESS)GetProcAddress(module, "wglGetProcAddress"); + if (!gd_wglCreateContext || !gd_wglMakeCurrent || !gd_wglDeleteContext || !gd_wglGetProcAddress) { + return 0; + } + + LPCWSTR class_name = L"EngineWGLDetect"; + HINSTANCE hInstance = static_cast<OS_Windows *>(OS::get_singleton())->get_hinstance(); + WNDCLASSW wc = {}; + + wc.lpfnWndProc = DefWindowProcW; + wc.hInstance = hInstance; + wc.lpszClassName = class_name; + + RegisterClassW(&wc); + + HWND hWnd = CreateWindowExW(WS_EX_APPWINDOW, class_name, L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr); + if (hWnd) { + HDC hDC = GetDC(hWnd); + if (hDC) { + static PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor + 1, + PFD_DRAW_TO_WINDOW | // Format Must Support Window + PFD_SUPPORT_OPENGL | // Format Must Support OpenGL + PFD_DOUBLEBUFFER, + (BYTE)PFD_TYPE_RGBA, + (BYTE)(OS::get_singleton()->is_layered_allowed() ? 32 : 24), + (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, // Color Bits Ignored + (BYTE)(OS::get_singleton()->is_layered_allowed() ? 8 : 0), // Alpha Buffer + (BYTE)0, // Shift Bit Ignored + (BYTE)0, // No Accumulation Buffer + (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, // Accumulation Bits Ignored + (BYTE)24, // 24Bit Z-Buffer (Depth Buffer) + (BYTE)0, // No Stencil Buffer + (BYTE)0, // No Auxiliary Buffer + (BYTE)PFD_MAIN_PLANE, // Main Drawing Layer + (BYTE)0, // Reserved + 0, 0, 0 // Layer Masks Ignored + }; + + int pixel_format = ChoosePixelFormat(hDC, &pfd); + SetPixelFormat(hDC, pixel_format, &pfd); + + HGLRC hRC = gd_wglCreateContext(hDC); + if (hRC) { + if (gd_wglMakeCurrent(hDC, hRC)) { + int attribs[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, 3, + WGL_CONTEXT_MINOR_VERSION_ARB, 3, + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + 0 + }; + + PFNWGLCREATECONTEXTATTRIBSARBPROC gd_wglCreateContextAttribsARB = nullptr; + gd_wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)gd_wglGetProcAddress("wglCreateContextAttribsARB"); + if (gd_wglCreateContextAttribsARB) { + HGLRC new_hRC = gd_wglCreateContextAttribsARB(hDC, 0, attribs); + if (new_hRC) { + if (gd_wglMakeCurrent(hDC, new_hRC)) { + PFNWGLGETSTRINGPROC gd_wglGetString = (PFNWGLGETSTRINGPROC)GetProcAddress(module, "glGetString"); + if (gd_wglGetString) { + const char *prefixes[] = { + "OpenGL ES-CM ", + "OpenGL ES-CL ", + "OpenGL ES ", + "OpenGL SC ", + nullptr + }; + const char *version = (const char *)gd_wglGetString(WGL_VERSION); + if (version) { + const String device_vendor = String::utf8((const char *)gd_wglGetString(WGL_VENDOR)).strip_edges(); + const String device_name = String::utf8((const char *)gd_wglGetString(WGL_RENDERER)).strip_edges(); + for (int i = 0; prefixes[i]; i++) { + size_t length = strlen(prefixes[i]); + if (strncmp(version, prefixes[i], length) == 0) { + version += length; + break; + } + } +#ifdef _MSC_VER + sscanf_s(version, "%d.%d", &major, &minor); +#else + sscanf(version, "%d.%d", &major, &minor); +#endif + print_verbose(vformat("Native OpenGL API detected: %d.%d: %s - %s", major, minor, device_vendor, device_name)); + } + } + } + gd_wglMakeCurrent(nullptr, nullptr); + gd_wglDeleteContext(new_hRC); + } + } + } + gd_wglMakeCurrent(nullptr, nullptr); + gd_wglDeleteContext(hRC); + } + ReleaseDC(hWnd, hDC); + } + DestroyWindow(hWnd); + } + UnregisterClassW(class_name, hInstance); + + return major * 10000 + minor; +} + +#endif // WINDOWS_ENABLED && GLES3_ENABLED diff --git a/platform/windows/wgl_detect_version.h b/platform/windows/wgl_detect_version.h new file mode 100644 index 0000000000..0be2923ba3 --- /dev/null +++ b/platform/windows/wgl_detect_version.h @@ -0,0 +1,40 @@ +/**************************************************************************/ +/* wgl_detect_version.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef WGL_DETECT_VERSION_H +#define WGL_DETECT_VERSION_H + +#if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED) + +int detect_wgl_version(); + +#endif // WINDOWS_ENABLED && GLES3_ENABLED + +#endif // WGL_DETECT_VERSION_H diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp index bb223628a8..6881a75596 100644 --- a/platform/windows/windows_terminal_logger.cpp +++ b/platform/windows/windows_terminal_logger.cpp @@ -81,12 +81,9 @@ void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file return; } -#ifndef UWP_ENABLED HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE); if (!hCon || hCon == INVALID_HANDLE_VALUE) { -#endif StdLogger::log_error(p_function, p_file, p_line, p_code, p_rationale, p_type); -#ifndef UWP_ENABLED } else { CONSOLE_SCREEN_BUFFER_INFO sbi; //original GetConsoleScreenBufferInfo(hCon, &sbi); @@ -159,7 +156,6 @@ void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file SetConsoleTextAttribute(hCon, sbi.wAttributes); } -#endif } WindowsTerminalLogger::~WindowsTerminalLogger() {} |
