diff options
Diffstat (limited to 'platform')
52 files changed, 484 insertions, 465 deletions
diff --git a/platform/android/README.md b/platform/android/README.md new file mode 100644 index 0000000000..343e588553 --- /dev/null +++ b/platform/android/README.md @@ -0,0 +1,14 @@ +# Android platform port + +This folder contains the Java and C++ (JNI) code for the Android platform port, +using [Gradle](https://gradle.org/) as a build system. + +## Artwork license + +[`logo.png`](logo.png) and [`run_icon.png`](run_icon.png) are licensed under +[Creative Commons Attribution 3.0 Unported](https://developer.android.com/distribute/marketing-tools/brand-guidelines#android_robot) +per the Android logo usage guidelines: + +> The Android robot is reproduced or modified from work created and shared by +> Google and used according to terms described in the Creative Commons 3.0 +> Attribution License. diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp index b9004c4989..e03375e8d9 100644 --- a/platform/android/android_input_handler.cpp +++ b/platform/android/android_input_handler.cpp @@ -377,7 +377,7 @@ MouseButton AndroidInputHandler::_android_button_mask_to_godot_button_mask(int a if (android_button_mask & AMOTION_EVENT_BUTTON_BACK) { godot_button_mask |= MOUSE_BUTTON_MASK_XBUTTON1; } - if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) { + if (android_button_mask & AMOTION_EVENT_BUTTON_FORWARD) { godot_button_mask |= MOUSE_BUTTON_MASK_XBUTTON2; } diff --git a/platform/android/detect.py b/platform/android/detect.py index 7a993e9ca6..61ccad9ac3 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -93,7 +93,7 @@ def configure(env): install_ndk_if_needed(env) # Workaround for MinGW. See: - # http://www.scons.org/wiki/LongCmdLinesOnWin32 + # https://www.scons.org/wiki/LongCmdLinesOnWin32 if os.name == "nt": import subprocess diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp index 0bae090702..0eeee8215d 100644 --- a/platform/android/dir_access_jandroid.cpp +++ b/platform/android/dir_access_jandroid.cpp @@ -161,7 +161,7 @@ bool DirAccessJAndroid::dir_exists(String p_dir) { if (current_dir == "") sd = p_dir; else { - if (p_dir.is_rel_path()) + if (p_dir.is_relative_path()) sd = current_dir.plus_file(p_dir); else sd = fix_path(p_dir); diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index fc86abb6f1..8df61831c2 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -48,6 +48,8 @@ void register_android_exporter() { EDITOR_DEF("export/android/shutdown_adb_on_exit", true); + EDITOR_DEF("export/android/one_click_deploy_clear_previous_install", false); + Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>(memnew(EditorExportPlatformAndroid)); EditorExport::get_singleton()->add_export_platform(exporter); } diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 17ee173855..e5422a28af 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -717,6 +717,10 @@ Error EditorExportPlatformAndroid::copy_gradle_so(void *p_userdata, const Shared return OK; } +bool EditorExportPlatformAndroid::_has_storage_permission(const Vector<String> &p_permissions) { + return p_permissions.find("android.permission.READ_EXTERNAL_STORAGE") != -1 || p_permissions.find("android.permission.WRITE_EXTERNAL_STORAGE") != -1; +} + void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) { const char **aperms = android_perms; while (*aperms) { @@ -763,12 +767,17 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres Vector<String> perms; _get_permissions(p_preset, p_give_internet, perms); for (int i = 0; i < perms.size(); i++) { - manifest_text += vformat(" <uses-permission android:name=\"%s\" />\n", perms.get(i)); + String permission = perms.get(i); + if (permission == "android.permission.WRITE_EXTERNAL_STORAGE" || permission == "android.permission.READ_EXTERNAL_STORAGE") { + manifest_text += vformat(" <uses-permission android:name=\"%s\" android:maxSdkVersion=\"29\" />\n", permission); + } else { + manifest_text += vformat(" <uses-permission android:name=\"%s\" />\n", permission); + } } manifest_text += _get_xr_features_tag(p_preset); manifest_text += _get_instrumentation_tag(p_preset); - manifest_text += _get_application_tag(p_preset); + manifest_text += _get_application_tag(p_preset, _has_storage_permission(perms)); manifest_text += "</manifest>\n"; String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release")); @@ -824,6 +833,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p Vector<String> perms; // Write permissions into the perms variable. _get_permissions(p_preset, p_give_internet, perms); + bool has_storage_permission = _has_storage_permission(perms); while (ofs < (uint32_t)p_manifest.size()) { uint32_t chunk = decode_uint32(&p_manifest[ofs]); @@ -913,6 +923,10 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p } } + if (tname == "application" && attrname == "requestLegacyExternalStorage") { + encode_uint32(has_storage_permission ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); + } + if (tname == "application" && attrname == "allowBackup") { encode_uint32(backup_allowed, &p_manifest.write[iofs + 16]); } @@ -963,6 +977,11 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p Vector<int> feature_versions; if (xr_mode_index == 1 /* XRMode.OVR */) { + // Set degrees of freedom + feature_names.push_back("android.hardware.vr.headtracking"); + feature_required_list.push_back(true); + feature_versions.push_back(1); + // Check for hand tracking int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required if (hand_tracking_index > 0) { @@ -1632,8 +1651,6 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname")); @@ -1780,7 +1797,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, int rv; String output; - bool remove_prev = p_preset->get("one_click_deploy/clear_previous_install"); + bool remove_prev = EDITOR_GET("export/android/one_click_deploy_clear_previous_install"); String version_name = p_preset->get("version/name"); String package_name = p_preset->get("package/unique_name"); @@ -2597,7 +2614,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP String export_filename = p_path.get_file(); String export_path = p_path.get_base_dir(); - if (export_path.is_rel_path()) { + if (export_path.is_relative_path()) { export_path = OS::get_singleton()->get_resource_dir().plus_file(export_path); } export_path = ProjectSettings::get_singleton()->globalize_path(export_path).simplify_path(); @@ -2925,7 +2942,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP void EditorExportPlatformAndroid::get_platform_features(List<String> *r_features) { r_features->push_back("mobile"); - r_features->push_back("Android"); + r_features->push_back("android"); } void EditorExportPlatformAndroid::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) { diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h index 909428c2fe..b061ee4e04 100644 --- a/platform/android/export/export_plugin.h +++ b/platform/android/export/export_plugin.h @@ -134,6 +134,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { static Error copy_gradle_so(void *p_userdata, const SharedObject &p_so); + bool _has_storage_permission(const Vector<String> &p_permissions); + void _get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions); void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug); diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp index 76512226bf..6fbdf73cd0 100644 --- a/platform/android/export/gradle_export_util.cpp +++ b/platform/android/export/gradle_export_util.cpp @@ -197,6 +197,8 @@ String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) { String manifest_xr_features; bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1; if (uses_xr) { + manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vr.headtracking\" android:required=\"true\" android:version=\"1\" />\n"; + int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required if (hand_tracking_index == 1) { manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n"; @@ -228,25 +230,29 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) { "tools:replace=\"android:screenOrientation\" " "android:screenOrientation=\"%s\">\n", orientation); - if (!uses_xr) { + if (uses_xr) { + manifest_activity_text += " <meta-data tools:node=\"replace\" android:name=\"com.oculus.vr.focusaware\" android:value=\"true\" />\n"; + } else { manifest_activity_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.vr.focusaware\" />\n"; } manifest_activity_text += " </activity>\n"; return manifest_activity_text; } -String _get_application_tag(const Ref<EditorExportPreset> &p_preset) { +String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_storage_permission) { String manifest_application_text = vformat( " <application android:label=\"@string/godot_project_name_string\"\n" " android:allowBackup=\"%s\"\n" " android:icon=\"@mipmap/icon\"\n" " android:isGame=\"%s\"\n" " android:hasFragileUserData=\"%s\"\n" - " tools:replace=\"android:allowBackup,android:isGame,android:hasFragileUserData\"\n" + " android:requestLegacyExternalStorage=\"%s\"\n" + " tools:replace=\"android:allowBackup,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n" " tools:ignore=\"GoogleAppIndexingWarning\">\n\n", bool_to_string(p_preset->get("user_data_backup/allow")), bool_to_string(p_preset->get("package/classify_as_game")), - bool_to_string(p_preset->get("package/retain_data_on_uninstall"))); + bool_to_string(p_preset->get("package/retain_data_on_uninstall")), + bool_to_string(p_has_storage_permission)); manifest_application_text += _get_activity_tag(p_preset); manifest_application_text += " </application>\n"; diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h index 44e9a1727d..8a93c25d79 100644 --- a/platform/android/export/gradle_export_util.h +++ b/platform/android/export/gradle_export_util.h @@ -81,6 +81,6 @@ String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset); String _get_activity_tag(const Ref<EditorExportPreset> &p_preset); -String _get_application_tag(const Ref<EditorExportPreset> &p_preset); +String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_storage_permission); #endif //GODOT_GRADLE_EXPORT_UTIL_H diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml index 467a0dc3c0..d7bf6cef30 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -22,6 +22,7 @@ android:icon="@mipmap/icon" android:isGame="true" android:hasFragileUserData="false" + android:requestLegacyExternalStorage="false" tools:ignore="GoogleAppIndexingWarning" > <!-- Records the version of the Godot editor used for building --> @@ -48,6 +49,10 @@ <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> + + <!-- Enable access to OpenXR on Oculus mobile devices, no-op on other Android + platforms. --> + <category android:name="com.oculus.intent.category.VR" /> </intent-filter> </activity> diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index 18e07c3762..9640887399 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -34,9 +34,8 @@ allprojects { } dependencies { - implementation libraries.supportCoreUtils implementation libraries.kotlinStdLib - implementation libraries.v4Support + implementation libraries.androidxFragment if (rootProject.findProject(":lib")) { implementation project(":lib") diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index 81fc87b7ef..fcee54e493 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -1,12 +1,11 @@ ext.versions = [ - androidGradlePlugin: '4.2.1', - compileSdk : 29, - minSdk : 18, - targetSdk : 29, + androidGradlePlugin: '4.2.2', + compileSdk : 30, + minSdk : 19, + targetSdk : 30, buildTools : '30.0.3', - supportCoreUtils : '1.0.0', kotlinVersion : '1.5.10', - v4Support : '1.0.0', + fragmentVersion : '1.3.6', javaVersion : 1.8, ndkVersion : '21.4.7075529' // Also update 'platform/android/detect.py#get_project_ndk_version()' when this is updated. @@ -14,10 +13,9 @@ ext.versions = [ ext.libraries = [ androidGradlePlugin: "com.android.tools.build:gradle:$versions.androidGradlePlugin", - supportCoreUtils : "androidx.legacy:legacy-support-core-utils:$versions.supportCoreUtils", kotlinGradlePlugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlinVersion", kotlinStdLib : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlinVersion", - v4Support : "androidx.legacy:legacy-support-v4:$versions.v4Support" + androidxFragment : "androidx.fragment:fragment:$versions.fragmentVersion", ] ext.getExportPackageName = { -> diff --git a/platform/android/java/app/gradle.properties b/platform/android/java/app/gradle.properties index 19587bd81f..0ad8e611ca 100644 --- a/platform/android/java/app/gradle.properties +++ b/platform/android/java/app/gradle.properties @@ -4,7 +4,7 @@ # where otherwise specified. # For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html +# https://www.gradle.org/docs/current/userguide/build_environment.html android.enableJetifier=true android.useAndroidX=true @@ -15,7 +15,7 @@ org.gradle.jvmargs=-Xmx4536m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# https://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true org.gradle.warning.mode=all diff --git a/platform/android/java/gradle.properties b/platform/android/java/gradle.properties index b51a19a005..5cd94e85d9 100644 --- a/platform/android/java/gradle.properties +++ b/platform/android/java/gradle.properties @@ -7,7 +7,7 @@ # any settings specified in this file. # For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html +# https://www.gradle.org/docs/current/userguide/build_environment.html android.enableJetifier=true android.useAndroidX=true @@ -18,7 +18,7 @@ org.gradle.jvmargs=-Xmx4536m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# https://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true org.gradle.warning.mode=all diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle index 663ba73d40..fbed4ed078 100644 --- a/platform/android/java/lib/build.gradle +++ b/platform/android/java/lib/build.gradle @@ -2,9 +2,8 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' dependencies { - implementation libraries.supportCoreUtils implementation libraries.kotlinStdLib - implementation libraries.v4Support + implementation libraries.androidxFragment } def pathToRootDir = "../../../../" diff --git a/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Helpers.java b/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Helpers.java index 2a72c9818d..9aa65fd786 100644 --- a/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Helpers.java +++ b/platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Helpers.java @@ -54,7 +54,7 @@ public class Helpers { /* * Parse the Content-Disposition HTTP Header. The format of the header is defined here: - * http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html This header provides a filename for + * https://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html This header provides a filename for * content that is going to be downloaded to the file system. We only support the attachment * type. */ diff --git a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java index ad7048cbf3..3600706c7c 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java +++ b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java @@ -30,6 +30,7 @@ package org.godotengine.godot; +import android.content.ComponentName; import android.content.Intent; import android.os.Bundle; import android.util.Log; @@ -86,6 +87,26 @@ public abstract class FullScreenGodotApp extends FragmentActivity implements God } @Override + public final void onGodotRestartRequested(Godot instance) { + if (instance == godotFragment) { + // HACK: + // + // Currently it's very hard to properly deinitialize Godot on Android to restart the game + // from scratch. Therefore, we need to kill the whole app process and relaunch it. + // + // Restarting only the activity, wouldn't be enough unless it did proper cleanup (including + // releasing and reloading native libs or resetting their state somehow and clearing statics). + // + // Using instrumentation is a way of making the whole app process restart, because Android + // will kill any process of the same package which was already running. + // + Bundle args = new Bundle(); + args.putParcelable("intent", getIntent()); + startInstrumentation(new ComponentName(this, GodotInstrumentation.class), null, args); + } + } + + @Override public void onNewIntent(Intent intent) { super.onNewIntent(intent); if (godotFragment != null) { diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 76751a886c..70bc73b9ad 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -49,7 +49,6 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.ComponentName; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; @@ -68,15 +67,12 @@ import android.os.Environment; import android.os.Messenger; import android.os.VibrationEffect; import android.os.Vibrator; -import android.provider.Settings.Secure; import android.view.Display; -import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Surface; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; -import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; import android.widget.Button; @@ -299,7 +295,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { View pluginView = plugin.onMainCreate(activity); if (pluginView != null) { - containerLayout.addView(pluginView); + if (plugin.shouldBeOnTop()) { + containerLayout.addView(pluginView); + } else { + containerLayout.addView(pluginView, 0); + } } } } @@ -321,7 +321,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC @SuppressLint("MissingPermission") @Keep private void vibrate(int durationMs) { - if (requestPermission("VIBRATE")) { + if (durationMs > 0 && requestPermission("VIBRATE")) { Vibrator v = (Vibrator)getContext().getSystemService(Context.VIBRATOR_SERVICE); if (v != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -335,22 +335,8 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC } public void restart() { - // HACK: - // - // Currently it's very hard to properly deinitialize Godot on Android to restart the game - // from scratch. Therefore, we need to kill the whole app process and relaunch it. - // - // Restarting only the activity, wouldn't be enough unless it did proper cleanup (including - // releasing and reloading native libs or resetting their state somehow and clearing statics). - // - // Using instrumentation is a way of making the whole app process restart, because Android - // will kill any process of the same package which was already running. - // - final Activity activity = getActivity(); - if (activity != null) { - Bundle args = new Bundle(); - args.putParcelable("intent", mCurrentIntent); - activity.startInstrumentation(new ComponentName(activity, GodotInstrumentation.class), null, args); + if (godotHost != null) { + godotHost.onGodotRestartRequested(this); } } @@ -471,7 +457,6 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC final Activity activity = getActivity(); io = new GodotIO(activity); - io.unique_id = Secure.getString(activity.getContentResolver(), Secure.ANDROID_ID); GodotLib.io = io; netUtils = new GodotNetUtils(activity); mSensorManager = (SensorManager)activity.getSystemService(Context.SENSOR_SERVICE); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java index 58e982c569..7b22895994 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java @@ -58,4 +58,10 @@ public interface GodotHost { * Invoked on the UI thread as the last step of the Godot instance clean up phase. */ default void onGodotForceQuit(Godot instance) {} + + /** + * Invoked on the GL thread when the Godot instance wants to be restarted. It's up to the host + * to perform the appropriate action(s). + */ + default void onGodotRestartRequested(Godot instance) {} } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java index 66882e8e72..d85d88ec6c 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java @@ -30,15 +30,19 @@ package org.godotengine.godot; -import org.godotengine.godot.input.*; +import org.godotengine.godot.input.GodotEditText; import android.app.Activity; -import android.content.*; +import android.content.ActivityNotFoundException; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.AssetManager; import android.graphics.Point; import android.net.Uri; -import android.os.*; +import android.os.Build; +import android.os.Environment; +import android.provider.Settings; +import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseArray; @@ -47,14 +51,16 @@ import android.view.DisplayCutout; import android.view.WindowInsets; import java.io.IOException; -import java.io.InputStream; import java.util.Locale; // Wrapper for native library public class GodotIO { - AssetManager am; - final Activity activity; + private static final String TAG = GodotIO.class.getSimpleName(); + + private final AssetManager am; + private final Activity activity; + private final String uniqueId; GodotEditText edit; final int SCREEN_LANDSCAPE = 0; @@ -66,167 +72,6 @@ public class GodotIO { final int SCREEN_SENSOR = 6; ///////////////////////// - /// FILES - ///////////////////////// - - public int last_file_id = 1; - - static class AssetData { - public boolean eof = false; - public String path; - public InputStream is; - public int len; - public int pos; - } - - SparseArray<AssetData> streams; - - public int file_open(String path, boolean write) { - //System.out.printf("file_open: Attempt to Open %s\n",path); - - //Log.v("MyApp", "TRYING TO OPEN FILE: " + path); - if (write) - return -1; - - AssetData ad = new AssetData(); - - try { - ad.is = am.open(path); - - } catch (Exception e) { - //System.out.printf("Exception on file_open: %s\n",path); - return -1; - } - - try { - ad.len = ad.is.available(); - } catch (Exception e) { - System.out.printf("Exception availabling on file_open: %s\n", path); - return -1; - } - - ad.path = path; - ad.pos = 0; - ++last_file_id; - streams.put(last_file_id, ad); - - return last_file_id; - } - public int file_get_size(int id) { - if (streams.get(id) == null) { - System.out.printf("file_get_size: Invalid file id: %d\n", id); - return -1; - } - - return streams.get(id).len; - } - public void file_seek(int id, int bytes) { - if (streams.get(id) == null) { - System.out.printf("file_get_size: Invalid file id: %d\n", id); - return; - } - //seek sucks - AssetData ad = streams.get(id); - if (bytes > ad.len) - bytes = ad.len; - if (bytes < 0) - bytes = 0; - - try { - if (bytes > (int)ad.pos) { - int todo = bytes - (int)ad.pos; - while (todo > 0) { - todo -= ad.is.skip(todo); - } - ad.pos = bytes; - } else if (bytes < (int)ad.pos) { - ad.is = am.open(ad.path); - - ad.pos = bytes; - int todo = bytes; - while (todo > 0) { - todo -= ad.is.skip(todo); - } - } - - ad.eof = false; - } catch (IOException e) { - System.out.printf("Exception on file_seek: %s\n", e); - return; - } - } - - public int file_tell(int id) { - if (streams.get(id) == null) { - System.out.printf("file_read: Can't tell eof for invalid file id: %d\n", id); - return 0; - } - - AssetData ad = streams.get(id); - return ad.pos; - } - public boolean file_eof(int id) { - if (streams.get(id) == null) { - System.out.printf("file_read: Can't check eof for invalid file id: %d\n", id); - return false; - } - - AssetData ad = streams.get(id); - return ad.eof; - } - - public byte[] file_read(int id, int bytes) { - if (streams.get(id) == null) { - System.out.printf("file_read: Can't read invalid file id: %d\n", id); - return new byte[0]; - } - - AssetData ad = streams.get(id); - - if (ad.pos + bytes > ad.len) { - bytes = ad.len - ad.pos; - ad.eof = true; - } - - if (bytes == 0) { - return new byte[0]; - } - - byte[] buf1 = new byte[bytes]; - int r = 0; - try { - r = ad.is.read(buf1); - } catch (IOException e) { - System.out.printf("Exception on file_read: %s\n", e); - return new byte[bytes]; - } - - if (r == 0) { - return new byte[0]; - } - - ad.pos += r; - - if (r < bytes) { - byte[] buf2 = new byte[r]; - for (int i = 0; i < r; i++) - buf2[i] = buf1[i]; - return buf2; - } else { - return buf1; - } - } - - public void file_close(int id) { - if (streams.get(id) == null) { - System.out.printf("file_close: Can't close invalid file id: %d\n", id); - return; - } - - streams.remove(id); - } - - ///////////////////////// /// DIRECTORIES ///////////////////////// @@ -236,9 +81,9 @@ public class GodotIO { public String path; } - public int last_dir_id = 1; + private int last_dir_id = 1; - SparseArray<AssetDir> dirs; + private final SparseArray<AssetDir> dirs; public int dir_open(String path) { AssetDir ad = new AssetDir(); @@ -257,7 +102,6 @@ public class GodotIO { return -1; } - //System.out.printf("Opened dir: %s\n",path); ++last_dir_id; dirs.put(last_dir_id, ad); @@ -320,9 +164,14 @@ public class GodotIO { GodotIO(Activity p_activity) { am = p_activity.getAssets(); activity = p_activity; - //streams = new HashMap<Integer, AssetData>(); - streams = new SparseArray<>(); dirs = new SparseArray<>(); + String androidId = Settings.Secure.getString(activity.getContentResolver(), + Settings.Secure.ANDROID_ID); + if (androidId == null) { + androidId = ""; + } + + uniqueId = androidId; } ///////////////////////// @@ -331,7 +180,6 @@ public class GodotIO { public int openURI(String p_uri) { try { - Log.v("MyApp", "TRYING TO OPEN URI: " + p_uri); String path = p_uri; String type = ""; if (path.startsWith("/")) { @@ -357,12 +205,12 @@ public class GodotIO { } } - public String getDataDir() { - return activity.getFilesDir().getAbsolutePath(); + public String getCacheDir() { + return activity.getCacheDir().getAbsolutePath(); } - public String getExternalDataDir() { - return activity.getExternalFilesDir(null).getAbsolutePath(); + public String getDataDir() { + return activity.getFilesDir().getAbsolutePath(); } public String getLocale() { @@ -456,51 +304,58 @@ public class GodotIO { public static final int SYSTEM_DIR_PICTURES = 6; public static final int SYSTEM_DIR_RINGTONES = 7; - public String getSystemDir(int idx) { - String what = ""; + public String getSystemDir(int idx, boolean shared_storage) { + String what; switch (idx) { - case SYSTEM_DIR_DESKTOP: { - //what=Environment.DIRECTORY_DOCUMENTS; - what = Environment.DIRECTORY_DOWNLOADS; + case SYSTEM_DIR_DESKTOP: + default: { + what = null; // This leads to the app specific external root directory. } break; + case SYSTEM_DIR_DCIM: { what = Environment.DIRECTORY_DCIM; - } break; + case SYSTEM_DIR_DOCUMENTS: { - what = Environment.DIRECTORY_DOWNLOADS; - //what=Environment.DIRECTORY_DOCUMENTS; + what = Environment.DIRECTORY_DOCUMENTS; } break; + case SYSTEM_DIR_DOWNLOADS: { what = Environment.DIRECTORY_DOWNLOADS; - } break; + case SYSTEM_DIR_MOVIES: { what = Environment.DIRECTORY_MOVIES; - } break; + case SYSTEM_DIR_MUSIC: { what = Environment.DIRECTORY_MUSIC; } break; + case SYSTEM_DIR_PICTURES: { what = Environment.DIRECTORY_PICTURES; } break; + case SYSTEM_DIR_RINGTONES: { what = Environment.DIRECTORY_RINGTONES; - } break; } - if (what.equals("")) - return ""; - return Environment.getExternalStoragePublicDirectory(what).getAbsolutePath(); + if (shared_storage) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + Log.w(TAG, "Shared storage access is limited on Android 10 and higher."); + } + if (TextUtils.isEmpty(what)) { + return Environment.getExternalStorageDirectory().getAbsolutePath(); + } else { + return Environment.getExternalStoragePublicDirectory(what).getAbsolutePath(); + } + } else { + return activity.getExternalFilesDir(what).getAbsolutePath(); + } } - protected static final String PREFS_FILE = "device_id.xml"; - protected static final String PREFS_DEVICE_ID = "device_id"; - - public static String unique_id = ""; public String getUniqueID() { - return unique_id; + return uniqueId; } } 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 2dc8359615..4536c21ed3 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 @@ -191,6 +191,9 @@ public abstract class GodotPlugin { * The plugin can return a non-null {@link View} layout in order to add it 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. + * * @see Activity#onCreate(Bundle) * @return the plugin's view to be included; null if no views should be included. */ @@ -311,6 +314,17 @@ public abstract class GodotPlugin { } /** + * Returns whether the plugin's {@link View} returned in onMainCreate() should be placed on + * top of the main Godot view. + * + * 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. + */ + public boolean shouldBeOnTop() { + return true; + } + + /** * Runs the specified action on the UI thread. If the current thread is the UI * thread, then the action is executed immediately. If the current thread is * not the UI thread, the action is posted to the event queue of the UI thread. diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp index 5e99135498..5cd2c382d2 100644 --- a/platform/android/java_godot_io_wrapper.cpp +++ b/platform/android/java_godot_io_wrapper.cpp @@ -48,8 +48,8 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc } _open_URI = p_env->GetMethodID(cls, "openURI", "(Ljava/lang/String;)I"); + _get_cache_dir = p_env->GetMethodID(cls, "getCacheDir", "()Ljava/lang/String;"); _get_data_dir = p_env->GetMethodID(cls, "getDataDir", "()Ljava/lang/String;"); - _get_external_data_dir = p_env->GetMethodID(cls, "getExternalDataDir", "()Ljava/lang/String;"); _get_locale = p_env->GetMethodID(cls, "getLocale", "()Ljava/lang/String;"); _get_model = p_env->GetMethodID(cls, "getModel", "()Ljava/lang/String;"); _get_screen_DPI = p_env->GetMethodID(cls, "getScreenDPI", "()I"); @@ -59,7 +59,7 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc _hide_keyboard = p_env->GetMethodID(cls, "hideKeyboard", "()V"); _set_screen_orientation = p_env->GetMethodID(cls, "setScreenOrientation", "(I)V"); _get_screen_orientation = p_env->GetMethodID(cls, "getScreenOrientation", "()I"); - _get_system_dir = p_env->GetMethodID(cls, "getSystemDir", "(I)Ljava/lang/String;"); + _get_system_dir = p_env->GetMethodID(cls, "getSystemDir", "(IZ)Ljava/lang/String;"); } } @@ -82,22 +82,22 @@ Error GodotIOJavaWrapper::open_uri(const String &p_uri) { } } -String GodotIOJavaWrapper::get_user_data_dir() { - if (_get_data_dir) { +String GodotIOJavaWrapper::get_cache_dir() { + if (_get_cache_dir) { JNIEnv *env = get_jni_env(); ERR_FAIL_COND_V(env == nullptr, String()); - jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_data_dir); + jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_cache_dir); return jstring_to_string(s, env); } else { return String(); } } -String GodotIOJavaWrapper::get_external_data_dir() { - if (_get_external_data_dir) { +String GodotIOJavaWrapper::get_user_data_dir() { + if (_get_data_dir) { JNIEnv *env = get_jni_env(); ERR_FAIL_COND_V(env == nullptr, String()); - jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_external_data_dir); + jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_data_dir); return jstring_to_string(s, env); } else { return String(); @@ -200,11 +200,11 @@ int GodotIOJavaWrapper::get_screen_orientation() { } } -String GodotIOJavaWrapper::get_system_dir(int p_dir) { +String GodotIOJavaWrapper::get_system_dir(int p_dir, bool p_shared_storage) { if (_get_system_dir) { JNIEnv *env = get_jni_env(); ERR_FAIL_COND_V(env == nullptr, String(".")); - jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir); + jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir, p_shared_storage); return jstring_to_string(s, env); } else { return String("."); diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h index e4c0a4b2c7..8f6d7f813f 100644 --- a/platform/android/java_godot_io_wrapper.h +++ b/platform/android/java_godot_io_wrapper.h @@ -46,8 +46,8 @@ private: jclass cls; jmethodID _open_URI = 0; + jmethodID _get_cache_dir = 0; jmethodID _get_data_dir = 0; - jmethodID _get_external_data_dir = 0; jmethodID _get_locale = 0; jmethodID _get_model = 0; jmethodID _get_screen_DPI = 0; @@ -66,8 +66,8 @@ public: jobject get_instance(); Error open_uri(const String &p_uri); + String get_cache_dir(); String get_user_data_dir(); - String get_external_data_dir(); String get_locale(); String get_model(); int get_screen_dpi(); @@ -80,7 +80,7 @@ public: void set_vk_height(int p_height); void set_screen_orientation(int p_orient); int get_screen_orientation(); - String get_system_dir(int p_dir); + String get_system_dir(int p_dir, bool p_shared_storage); }; #endif /* !JAVA_GODOT_IO_WRAPPER_H */ diff --git a/platform/android/logo.png b/platform/android/logo.png Binary files differindex f44d360a25..9c8be93646 100644 --- a/platform/android/logo.png +++ b/platform/android/logo.png diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index c2e12442b3..21fb31d991 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -213,6 +213,10 @@ String OS_Android::get_model_name() const { return OS_Unix::get_model_name(); } +String OS_Android::get_data_path() const { + return get_user_data_dir(); +} + String OS_Android::get_user_data_dir() const { if (data_dir_cache != String()) return data_dir_cache; @@ -225,11 +229,11 @@ String OS_Android::get_user_data_dir() const { return "."; } -String OS_Android::get_external_data_dir() const { - String data_dir = godot_io_java->get_external_data_dir(); - if (data_dir != "") { - data_dir = _remove_symlink(data_dir); - return data_dir; +String OS_Android::get_cache_path() const { + String cache_dir = godot_io_java->get_cache_dir(); + if (cache_dir != "") { + cache_dir = _remove_symlink(cache_dir); + return cache_dir; } return "."; } @@ -242,8 +246,8 @@ String OS_Android::get_unique_id() const { return OS::get_unique_id(); } -String OS_Android::get_system_dir(SystemDir p_dir) const { - return godot_io_java->get_system_dir(p_dir); +String OS_Android::get_system_dir(SystemDir p_dir, bool p_shared_storage) const { + return godot_io_java->get_system_dir(p_dir, p_shared_storage); } void OS_Android::set_display_size(const Size2i &p_size) { diff --git a/platform/android/os_android.h b/platform/android/os_android.h index a5b995a775..c938297821 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -110,14 +110,15 @@ public: virtual Error shell_open(String p_uri) override; virtual String get_user_data_dir() const override; - virtual String get_external_data_dir() const override; + virtual String get_data_path() const override; + virtual String get_cache_path() const override; virtual String get_resource_dir() const override; virtual String get_locale() const override; virtual String get_model_name() const override; virtual String get_unique_id() const override; - virtual String get_system_dir(SystemDir p_dir) const override; + virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override; void vibrate_handheld(int p_duration_ms) override; diff --git a/platform/iphone/export/export_plugin.h b/platform/iphone/export/export_plugin.h index 8d3af6e057..359f855d86 100644 --- a/platform/iphone/export/export_plugin.h +++ b/platform/iphone/export/export_plugin.h @@ -204,7 +204,7 @@ public: virtual void get_platform_features(List<String> *r_features) override { r_features->push_back("mobile"); - r_features->push_back("iOS"); + r_features->push_back("ios"); } virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { diff --git a/platform/javascript/README.md b/platform/javascript/README.md new file mode 100644 index 0000000000..f181bea9e0 --- /dev/null +++ b/platform/javascript/README.md @@ -0,0 +1,15 @@ +# HTML5 platform port + +This folder contains the C++ and JavaScript code for the HTML5/WebAssembly platform port, +compiled using [Emscripten](https://emscripten.org/). + +It also contains a ESLint linting setup (see [`package.json`](package.json)). + +See also [`misc/dist/html`](/misc/dist/html) folder for files used by this platform +such as the HTML5 shell. + +## Artwork license + +[`logo.png`](logo.png) and [`run_icon.png`](run_icon.png) are licensed under +[Creative Commons Attribution 3.0 Unported](https://www.w3.org/html/logo/faq.html#how-licenced) +per the HTML5 logo usage guidelines. diff --git a/platform/javascript/api/javascript_tools_editor_plugin.cpp b/platform/javascript/api/javascript_tools_editor_plugin.cpp index 54f541f607..c50195639c 100644 --- a/platform/javascript/api/javascript_tools_editor_plugin.cpp +++ b/platform/javascript/api/javascript_tools_editor_plugin.cpp @@ -35,6 +35,7 @@ #include "core/config/project_settings.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" +#include "core/os/time.h" #include "editor/editor_node.h" #include <emscripten/emscripten.h> @@ -58,23 +59,40 @@ JavaScriptToolsEditorPlugin::JavaScriptToolsEditorPlugin(EditorNode *p_editor) { void JavaScriptToolsEditorPlugin::_download_zip(Variant p_v) { if (!Engine::get_singleton() || !Engine::get_singleton()->is_editor_hint()) { - WARN_PRINT("Project download is only available in Editor mode"); + ERR_PRINT("Downloading the project as a ZIP archive is only available in Editor mode."); return; } String resource_path = ProjectSettings::get_singleton()->get_resource_path(); FileAccess *src_f; zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - zipFile zip = zipOpen2("/tmp/project.zip", APPEND_STATUS_CREATE, nullptr, &io); - String base_path = resource_path.substr(0, resource_path.rfind("/")) + "/"; + + // Name the downloded ZIP file to contain the project name and download date for easier organization. + // Replace characters not allowed (or risky) in Windows file names with safe characters. + // In the project name, all invalid characters become an empty string so that a name + // like "Platformer 2: Godette's Revenge" becomes "platformer_2-_godette-s_revenge". + const String project_name_safe = + GLOBAL_GET("application/config/name").to_lower().replace(" ", "_"); + const String datetime_safe = + Time::get_singleton()->get_datetime_string_from_system(false, true).replace(" ", "_"); + const String output_name = OS::get_singleton()->get_safe_dir_name(vformat("%s_%s.zip")); + const String output_path = String("/tmp").plus_file(output_name); + + zipFile zip = zipOpen2(output_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io); + const String base_path = resource_path.substr(0, resource_path.rfind("/")) + "/"; _zip_recursive(resource_path, base_path, zip); zipClose(zip, nullptr); - FileAccess *f = FileAccess::open("/tmp/project.zip", FileAccess::READ); - ERR_FAIL_COND_MSG(!f, "Unable to create zip file"); + FileAccess *f = FileAccess::open(output_path, FileAccess::READ); + ERR_FAIL_COND_MSG(!f, "Unable to create ZIP file."); Vector<uint8_t> buf; buf.resize(f->get_length()); f->get_buffer(buf.ptrw(), buf.size()); - godot_js_os_download_buffer(buf.ptr(), buf.size(), "project.zip", "application/zip"); + godot_js_os_download_buffer(buf.ptr(), buf.size(), output_name.utf8().get_data(), "application/zip"); + + f->close(); + memdelete(f); + // Remove the temporary file since it was sent to the user's native filesystem as a download. + DirAccess::remove_file_or_error(output_path); } void JavaScriptToolsEditorPlugin::_zip_file(String p_path, String p_base_path, zipFile p_zip) { @@ -108,7 +126,7 @@ void JavaScriptToolsEditorPlugin::_zip_file(String p_path, String p_base_path, z void JavaScriptToolsEditorPlugin::_zip_recursive(String p_path, String p_base_path, zipFile p_zip) { DirAccess *dir = DirAccess::open(p_path); if (!dir) { - WARN_PRINT("Unable to open dir for zipping: " + p_path); + WARN_PRINT("Unable to open directory for zipping: " + p_path); return; } dir->list_dir_begin(); diff --git a/platform/javascript/emscripten_helpers.py b/platform/javascript/emscripten_helpers.py index ab98838e20..4dad2d5204 100644 --- a/platform/javascript/emscripten_helpers.py +++ b/platform/javascript/emscripten_helpers.py @@ -24,7 +24,10 @@ def get_build_version(): v = "%d.%d" % (version.major, version.minor) if version.patch > 0: v += ".%d" % version.patch - v += ".%s.%s" % (version.status, name) + status = version.status + if os.getenv("GODOT_VERSION_STATUS") != None: + status = str(os.getenv("GODOT_VERSION_STATUS")) + v += ".%s.%s" % (status, name) return v diff --git a/platform/javascript/export/export_plugin.h b/platform/javascript/export/export_plugin.h index 736edfe3a8..bdd1235259 100644 --- a/platform/javascript/export/export_plugin.h +++ b/platform/javascript/export/export_plugin.h @@ -134,7 +134,7 @@ public: virtual void get_platform_features(List<String> *r_features) override { r_features->push_back("web"); - r_features->push_back(get_os_name()); + r_features->push_back(get_os_name().to_lower()); } virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { diff --git a/platform/javascript/js/libs/library_godot_os.js b/platform/javascript/js/libs/library_godot_os.js index 5aa750757c..99e7ee8b5f 100644 --- a/platform/javascript/js/libs/library_godot_os.js +++ b/platform/javascript/js/libs/library_godot_os.js @@ -106,7 +106,7 @@ autoAddDeps(GodotConfig, '$GodotConfig'); mergeInto(LibraryManager.library, GodotConfig); const GodotFS = { - $GodotFS__deps: ['$FS', '$IDBFS', '$GodotRuntime'], + $GodotFS__deps: ['$ERRNO_CODES', '$FS', '$IDBFS', '$GodotRuntime'], $GodotFS__postset: [ 'Module["initFS"] = GodotFS.init;', 'Module["copyToFS"] = GodotFS.copy_to_fs;', diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 76102d941b..95c5909d50 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -137,12 +137,12 @@ int OS_JavaScript::get_processor_count() const { } bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) { - if (p_feature == "HTML5" || p_feature == "web") { + if (p_feature == "html5" || p_feature == "web") { return true; } #ifdef JAVASCRIPT_EVAL_ENABLED - if (p_feature == "JavaScript") { + if (p_feature == "javascript") { return true; } #endif diff --git a/platform/linuxbsd/README.md b/platform/linuxbsd/README.md new file mode 100644 index 0000000000..0d3fb37be5 --- /dev/null +++ b/platform/linuxbsd/README.md @@ -0,0 +1,11 @@ +# Linux/*BSD platform port + +This folder contains the C++ code for the Linux/*BSD platform port. + +## Artwork license + +[`logo.png`](logo.png) is derived from the [Linux logo](https://isc.tamu.edu/~lewing/linux/): + +> Permission to use and/or modify this image is granted provided you acknowledge me + <lewing@isc.tamu.edu> and [The GIMP](https://isc.tamu.edu/~lewing/gimp/) + if someone asks. diff --git a/platform/linuxbsd/crash_handler_linuxbsd.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp index ea0222cb19..0e98af71fa 100644 --- a/platform/linuxbsd/crash_handler_linuxbsd.cpp +++ b/platform/linuxbsd/crash_handler_linuxbsd.cpp @@ -32,6 +32,8 @@ #include "core/config/project_settings.h" #include "core/os/os.h" +#include "core/version.h" +#include "core/version_hash.gen.h" #include "main/main.h" #ifdef DEBUG_ENABLED @@ -61,12 +63,19 @@ static void handle_crash(int sig) { } // Dump the backtrace to stderr with a message to the user + fprintf(stderr, "\n================================================================\n"); fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig); if (OS::get_singleton()->get_main_loop()) { OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH); } + // Print the engine version just before, so that people are reminded to include the version in backtrace reports. + if (String(VERSION_HASH).length() != 0) { + fprintf(stderr, "Engine version: " VERSION_FULL_NAME " (" VERSION_HASH ")\n"); + } else { + fprintf(stderr, "Engine version: " VERSION_FULL_NAME "\n"); + } fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data()); char **strings = backtrace_symbols(bt_buffer, size); if (strings) { @@ -115,6 +124,7 @@ static void handle_crash(int sig) { free(strings); } fprintf(stderr, "-- END OF BACKTRACE --\n"); + fprintf(stderr, "================================================================\n"); // Abort to pass the error to the OS abort(); diff --git a/platform/linuxbsd/export/export.cpp b/platform/linuxbsd/export/export.cpp index 5c6be2d7d4..965a38ef4e 100644 --- a/platform/linuxbsd/export/export.cpp +++ b/platform/linuxbsd/export/export.cpp @@ -53,7 +53,7 @@ void register_linuxbsd_exporter() { platform->set_debug_32("linux_x11_32_debug"); platform->set_release_64("linux_x11_64_release"); platform->set_debug_64("linux_x11_64_debug"); - platform->set_os_name("X11"); + platform->set_os_name("LinuxBSD"); platform->set_chmod_flags(0755); platform->set_fixup_embedded_pck_func(&fixup_embedded_pck); diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp index 08630be8b0..2c9801f512 100644 --- a/platform/linuxbsd/os_linuxbsd.cpp +++ b/platform/linuxbsd/os_linuxbsd.cpp @@ -273,7 +273,7 @@ String OS_LinuxBSD::get_cache_path() const { } } -String OS_LinuxBSD::get_system_dir(SystemDir p_dir) const { +String OS_LinuxBSD::get_system_dir(SystemDir p_dir, bool p_shared_storage) const { String xdgparam; switch (p_dir) { diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h index 1e06587322..35c80e3f9b 100644 --- a/platform/linuxbsd/os_linuxbsd.h +++ b/platform/linuxbsd/os_linuxbsd.h @@ -84,7 +84,7 @@ public: virtual String get_data_path() const override; virtual String get_cache_path() const override; - virtual String get_system_dir(SystemDir p_dir) const override; + virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override; virtual Error shell_open(String p_uri) override; diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm index 0f128d504f..31228b10b4 100644 --- a/platform/osx/crash_handler_osx.mm +++ b/platform/osx/crash_handler_osx.mm @@ -32,6 +32,8 @@ #include "core/config/project_settings.h" #include "core/os/os.h" +#include "core/version.h" +#include "core/version_hash.gen.h" #include "main/main.h" #include <string.h> @@ -85,11 +87,18 @@ static void handle_crash(int sig) { } // Dump the backtrace to stderr with a message to the user + fprintf(stderr, "\n================================================================\n"); fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig); if (OS::get_singleton()->get_main_loop()) OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH); + // Print the engine version just before, so that people are reminded to include the version in backtrace reports. + if (String(VERSION_HASH).length() != 0) { + fprintf(stderr, "Engine version: " VERSION_FULL_NAME " (" VERSION_HASH ")\n"); + } else { + fprintf(stderr, "Engine version: " VERSION_FULL_NAME "\n"); + } fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data()); char **strings = backtrace_symbols(bt_buffer, size); if (strings) { @@ -148,6 +157,7 @@ static void handle_crash(int sig) { free(strings); } fprintf(stderr, "-- END OF BACKTRACE --\n"); + fprintf(stderr, "================================================================\n"); // Abort to pass the error to the OS abort(); diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm index 43b7d7c1e0..f037d75fbd 100644 --- a/platform/osx/display_server_osx.mm +++ b/platform/osx/display_server_osx.mm @@ -190,6 +190,8 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) { [wd.window_object setContentMinSize:NSMakeSize(0, 0)]; [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)]; + // Force window resize event. + [self windowDidResize:notification]; } - (void)windowDidExitFullScreen:(NSNotification *)notification { @@ -217,6 +219,8 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) { if (wd.on_top) { [wd.window_object setLevel:NSFloatingWindowLevel]; } + // Force window resize event. + [self windowDidResize:notification]; } - (void)windowDidChangeBackingProperties:(NSNotification *)notification { diff --git a/platform/osx/export/export_plugin.cpp b/platform/osx/export/export_plugin.cpp index 5b959d6da4..54a3104482 100644 --- a/platform/osx/export/export_plugin.cpp +++ b/platform/osx/export/export_plugin.cpp @@ -931,7 +931,7 @@ void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String zipfi.tmz_date.tm_hour = time.hour; zipfi.tmz_date.tm_mday = date.day; zipfi.tmz_date.tm_min = time.minute; - zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/ + zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/ zipfi.tmz_date.tm_sec = time.second; zipfi.tmz_date.tm_year = date.year; zipfi.dosDate = 0; @@ -976,7 +976,7 @@ void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String zipfi.tmz_date.tm_hour = time.hour; zipfi.tmz_date.tm_mday = date.day; zipfi.tmz_date.tm_min = time.minute; - zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/ + zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/ zipfi.tmz_date.tm_sec = time.second; zipfi.tmz_date.tm_year = date.year; zipfi.dosDate = 0; diff --git a/platform/osx/export/export_plugin.h b/platform/osx/export/export_plugin.h index cd85ce2aad..ca5086622e 100644 --- a/platform/osx/export/export_plugin.h +++ b/platform/osx/export/export_plugin.h @@ -115,7 +115,7 @@ public: virtual void get_platform_features(List<String> *r_features) override { r_features->push_back("pc"); r_features->push_back("s3tc"); - r_features->push_back("macOS"); + r_features->push_back("macos"); } virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp index d778271350..e67d2b0e91 100644 --- a/platform/osx/joypad_osx.cpp +++ b/platform/osx/joypad_osx.cpp @@ -143,6 +143,8 @@ void joypad::add_hid_element(IOHIDElementRef p_element) { switch (usage) { case kHIDUsage_Sim_Rudder: case kHIDUsage_Sim_Throttle: + case kHIDUsage_Sim_Accelerator: + case kHIDUsage_Sim_Brake: if (!has_element(cookie, &axis_elements)) { list = &axis_elements; } @@ -332,6 +334,13 @@ bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) { p_joy->add_hid_elements(array); CFRelease(array); } + // Xbox controller hat values start at 1 rather than 0. + p_joy->offset_hat = vendor == 0x45e && + (product_id == 0x0b05 || + product_id == 0x02e0 || + product_id == 0x02fd || + product_id == 0x0b13); + return true; } @@ -388,13 +397,16 @@ bool joypad::check_ff_features() { return false; } -static int process_hat_value(int p_min, int p_max, int p_value) { +static int process_hat_value(int p_min, int p_max, int p_value, bool p_offset_hat) { int range = (p_max - p_min + 1); int value = p_value - p_min; int hat_value = HatMask::HAT_MASK_CENTER; if (range == 4) { value *= 2; } + if (p_offset_hat) { + value -= 1; + } switch (value) { case 0: @@ -468,7 +480,7 @@ void JoypadOSX::process_joypads() { for (int j = 0; j < joy.hat_elements.size(); j++) { rec_element &elem = joy.hat_elements.write[j]; int value = joy.get_hid_element_state(&elem); - int hat_value = process_hat_value(elem.min, elem.max, value); + int hat_value = process_hat_value(elem.min, elem.max, value, joy.offset_hat); input->joy_hat(joy.id, (HatMask)hat_value); } diff --git a/platform/osx/joypad_osx.h b/platform/osx/joypad_osx.h index bf7e8949df..c060c3d523 100644 --- a/platform/osx/joypad_osx.h +++ b/platform/osx/joypad_osx.h @@ -64,6 +64,7 @@ struct joypad { Vector<rec_element> hat_elements; int id = 0; + bool offset_hat = false; io_service_t ffservice = 0; /* Interface for force feedback, 0 = no ff */ FFCONSTANTFORCE ff_constant_force; diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 37d30add78..df41ccd892 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -84,7 +84,7 @@ public: virtual String get_bundle_resource_dir() const override; virtual String get_godot_dir_name() const override; - virtual String get_system_dir(SystemDir p_dir) const override; + virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override; Error shell_open(String p_uri) override; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index c458a0264a..c6e35fee83 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -395,7 +395,7 @@ String OS_OSX::get_godot_dir_name() const { return String(VERSION_SHORT_NAME).capitalize(); } -String OS_OSX::get_system_dir(SystemDir p_dir) const { +String OS_OSX::get_system_dir(SystemDir p_dir, bool p_shared_storage) const { NSSearchPathDirectory id; bool found = true; diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp index 5bd00b1549..a54b85a803 100644 --- a/platform/uwp/export/export_plugin.cpp +++ b/platform/uwp/export/export_plugin.cpp @@ -485,7 +485,7 @@ Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_p void EditorExportPlatformUWP::get_platform_features(List<String> *r_features) { r_features->push_back("pc"); - r_features->push_back("UWP"); + r_features->push_back("uwp"); } void EditorExportPlatformUWP::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) { diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp index e2d507eddd..1b4dae207f 100644 --- a/platform/windows/crash_handler_windows.cpp +++ b/platform/windows/crash_handler_windows.cpp @@ -32,6 +32,8 @@ #include "core/config/project_settings.h" #include "core/os/os.h" +#include "core/version.h" +#include "core/version_hash.gen.h" #include "main/main.h" #ifdef CRASH_HANDLER_EXCEPTION @@ -127,6 +129,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { return EXCEPTION_CONTINUE_SEARCH; } + fprintf(stderr, "\n================================================================\n"); fprintf(stderr, "%s: Program crashed\n", __FUNCTION__); if (OS::get_singleton()->get_main_loop()) @@ -175,6 +178,12 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { msg = proj_settings->get("debug/settings/crash_handler/message"); } + // Print the engine version just before, so that people are reminded to include the version in backtrace reports. + if (String(VERSION_HASH).length() != 0) { + fprintf(stderr, "Engine version: " VERSION_FULL_NAME " (" VERSION_HASH ")\n"); + } else { + fprintf(stderr, "Engine version: " VERSION_FULL_NAME "\n"); + } fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data()); int n = 0; @@ -200,6 +209,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { } while (frame.AddrReturn.Offset != 0 && n < 256); fprintf(stderr, "-- END OF BACKTRACE --\n"); + fprintf(stderr, "================================================================\n"); SymCleanup(process); diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 9154f7749e..3961480d23 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -321,7 +321,7 @@ def configure_msvc(env, manual_msvc_config): def configure_mingw(env): # Workaround for MinGW. See: - # http://www.scons.org/wiki/LongCmdLinesOnWin32 + # https://www.scons.org/wiki/LongCmdLinesOnWin32 env.use_windows_spawn_fix() ## Build type diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index b6489e7a95..1723026849 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -80,7 +80,7 @@ String DisplayServerWindows::get_name() const { } void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) { - if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN) { + if (windows.has(MAIN_WINDOW_ID) && (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN)) { // Mouse is grabbed (captured or confined). WindowData &wd = windows[MAIN_WINDOW_ID]; @@ -118,8 +118,10 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) { void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) { _THREAD_SAFE_METHOD_ - if (mouse_mode == p_mode) + if (mouse_mode == p_mode) { + // Already in the same mode; do nothing. return; + } mouse_mode = p_mode; @@ -134,7 +136,7 @@ void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) { _THREAD_SAFE_METHOD_ if (!windows.has(last_focused_window)) { - return; //no window focused? + return; // No focused window? } if (mouse_mode == MOUSE_MODE_CAPTURED) { @@ -154,7 +156,6 @@ Point2i DisplayServerWindows::mouse_get_position() const { POINT p; GetCursorPos(&p); return Point2i(p.x, p.y); - //return Point2(old_x, old_y); } MouseButton DisplayServerWindows::mouse_get_button_state() const { @@ -165,12 +166,12 @@ void DisplayServerWindows::clipboard_set(const String &p_text) { _THREAD_SAFE_METHOD_ if (!windows.has(last_focused_window)) { - return; //no window focused? + return; // No focused window? } - // Convert LF line endings to CRLF in clipboard content - // Otherwise, line endings won't be visible when pasted in other software - String text = p_text.replace("\r\n", "\n").replace("\n", "\r\n"); // avoid \r\r\n + // Convert LF line endings to CRLF in clipboard content. + // Otherwise, line endings won't be visible when pasted in other software. + String text = p_text.replace("\r\n", "\n").replace("\n", "\r\n"); // Avoid \r\r\n. if (!OpenClipboard(windows[last_focused_window].hWnd)) { ERR_FAIL_MSG("Unable to open clipboard."); @@ -187,7 +188,7 @@ void DisplayServerWindows::clipboard_set(const String &p_text) { SetClipboardData(CF_UNICODETEXT, mem); - // set the CF_TEXT version (not needed?) + // 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."); @@ -206,7 +207,7 @@ String DisplayServerWindows::clipboard_get() const { _THREAD_SAFE_METHOD_ if (!windows.has(last_focused_window)) { - return String(); //no window focused? + return String(); // No focused window? } String ret; @@ -498,16 +499,18 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod } void DisplayServerWindows::show_window(WindowID p_id) { + ERR_FAIL_COND(!windows.has(p_id)); + WindowData &wd = windows[p_id]; if (p_id != MAIN_WINDOW_ID) { _update_window_style(p_id); } - ShowWindow(wd.hWnd, wd.no_focus ? SW_SHOWNOACTIVATE : SW_SHOW); // Show The Window + ShowWindow(wd.hWnd, wd.no_focus ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window. if (!wd.no_focus) { - SetForegroundWindow(wd.hWnd); // Slightly Higher Priority - SetFocus(wd.hWnd); // Sets Keyboard Focus To + SetForegroundWindow(wd.hWnd); // Slightly higher priority. + SetFocus(wd.hWnd); // Set keyboard focus. } } @@ -606,6 +609,8 @@ void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p } void DisplayServerWindows::_update_window_mouse_passthrough(WindowID p_window) { + ERR_FAIL_COND(!windows.has(p_window)); + if (windows[p_window].mpath.size() == 0) { SetWindowRgn(windows[p_window].hWnd, nullptr, TRUE); } else { @@ -664,16 +669,11 @@ Point2i DisplayServerWindows::window_get_position(WindowID p_window) const { ClientToScreen(wd.hWnd, &point); return Point2i(point.x, point.y); - -#if 0 - //do not use this method, as it includes windows decorations - RECT r; - GetWindowRect(wd.hWnd, &r); - return Point2(r.left, r.top); -#endif } void DisplayServerWindows::_update_real_mouse_position(WindowID p_window) { + ERR_FAIL_COND(!windows.has(p_window)); + POINT mouse_pos; if (GetCursorPos(&mouse_pos) && ScreenToClient(windows[p_window].hWnd, &mouse_pos)) { if (mouse_pos.x > 0 && mouse_pos.y > 0 && mouse_pos.x <= windows[p_window].width && mouse_pos.y <= windows[p_window].height) { @@ -691,14 +691,9 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window ERR_FAIL_COND(!windows.has(p_window)); WindowData &wd = windows[p_window]; - if (wd.fullscreen) + if (wd.fullscreen) { return; -#if 0 - //wrong needs to account properly for decorations - RECT r; - GetWindowRect(wd.hWnd, &r); - MoveWindow(wd.hWnd, p_position.x, p_position.y, r.right - r.left, r.bottom - r.top, TRUE); -#else + } RECT rc; rc.left = p_position.x; @@ -711,8 +706,8 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window AdjustWindowRectEx(&rc, style, false, exStyle); MoveWindow(wd.hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE); -#endif - // Don't let the mouse leave the window when moved + + // Don't let the mouse leave the window when moved. if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { RECT rect; GetClientRect(wd.hWnd, &rect); @@ -729,16 +724,15 @@ void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_pa _THREAD_SAFE_METHOD_ ERR_FAIL_COND(p_window == p_parent); - ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd_window = windows[p_window]; ERR_FAIL_COND(wd_window.transient_parent == p_parent); - ERR_FAIL_COND_MSG(wd_window.always_on_top, "Windows with the 'on top' can't become transient."); if (p_parent == INVALID_WINDOW_ID) { - //remove transient + // Remove transient. ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID); ERR_FAIL_COND(!windows.has(wd_window.transient_parent)); @@ -838,7 +832,7 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo MoveWindow(wd.hWnd, rect.left, rect.top, w, h, TRUE); - // Don't let the mouse leave the window when resizing to a smaller resolution + // Don't let the mouse leave the window when resizing to a smaller resolution. if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { RECT crect; GetClientRect(wd.hWnd, &crect); @@ -854,12 +848,13 @@ Size2i DisplayServerWindows::window_get_size(WindowID p_window) const { ERR_FAIL_COND_V(!windows.has(p_window), Size2i()); const WindowData &wd = windows[p_window]; + // GetClientRect() returns a zero rect for a minimized window, so we need to get the size in another way. if (wd.minimized) { return Size2(wd.width, wd.height); } RECT r; - if (GetClientRect(wd.hWnd, &r)) { // Only area inside of window border + if (GetClientRect(wd.hWnd, &r)) { // Retrieves area inside of window border, including decoration. return Size2(r.right - r.left, r.bottom - r.top); } return Size2(); @@ -872,13 +867,17 @@ Size2i DisplayServerWindows::window_get_real_size(WindowID p_window) const { const WindowData &wd = windows[p_window]; RECT r; - if (GetWindowRect(wd.hWnd, &r)) { // Includes area of the window border + if (GetWindowRect(wd.hWnd, &r)) { // Retrieves area inside of window border, including decoration. return Size2(r.right - r.left, r.bottom - r.top); } return Size2(); } void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscreen, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) { + // Windows docs for window styles: + // https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles + // https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles + r_style = 0; r_style_ex = WS_EX_WINDOWEDGE; if (p_main_window) { @@ -886,10 +885,7 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre } if (p_fullscreen || p_borderless) { - r_style |= WS_POPUP; - //if (p_borderless) { - // r_style_ex |= WS_EX_TOOLWINDOW; - //} + r_style |= WS_POPUP; // p_borderless was WS_EX_TOOLWINDOW in the past. } else { if (p_resizable) { if (p_maximized) { @@ -1025,7 +1021,7 @@ bool DisplayServerWindows::window_is_maximize_allowed(WindowID p_window) const { // FIXME: Implement this, or confirm that it should always be true. - return true; //no idea + return true; } void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) { @@ -1170,8 +1166,9 @@ void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowI void DisplayServerWindows::console_set_visible(bool p_enabled) { _THREAD_SAFE_METHOD_ - if (console_visible == p_enabled) + if (console_visible == p_enabled) { return; + } ShowWindow(GetConsoleWindow(), p_enabled ? SW_SHOW : SW_HIDE); console_visible = p_enabled; } @@ -1185,8 +1182,9 @@ void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) { ERR_FAIL_INDEX(p_shape, CURSOR_MAX); - if (cursor_shape == p_shape) + if (cursor_shape == p_shape) { return; + } if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) { cursor_shape = p_shape; @@ -1196,7 +1194,7 @@ void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) { static const LPCTSTR win_cursors[CURSOR_MAX] = { IDC_ARROW, IDC_IBEAM, - IDC_HAND, //finger + IDC_HAND, // Finger. IDC_CROSS, IDC_WAIT, IDC_APPSTARTING, @@ -1227,49 +1225,49 @@ DisplayServer::CursorShape DisplayServerWindows::cursor_get_shape() const { } void DisplayServerWindows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap) { - // Get the system display DC + // Get the system display DC. HDC hDC = GetDC(nullptr); - // Create helper DC + // Create helper DC. HDC hMainDC = CreateCompatibleDC(hDC); HDC hAndMaskDC = CreateCompatibleDC(hDC); HDC hXorMaskDC = CreateCompatibleDC(hDC); - // Get the dimensions of the source bitmap + // Get the dimensions of the source bitmap. BITMAP bm; GetObject(hSourceBitmap, sizeof(BITMAP), &bm); - // Create the mask bitmaps - hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color - hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color + // Create the mask bitmaps. + hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // Color. + hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // Color. - // Release the system display DC + // Release the system display DC. ReleaseDC(nullptr, hDC); - // Select the bitmaps to helper DC + // Select the bitmaps to helper DC. HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap); HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap); HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap); // Assign the monochrome AND mask bitmap pixels so that the pixels of the source bitmap - // with 'clrTransparent' will be white pixels of the monochrome bitmap + // with 'clrTransparent' will be white pixels of the monochrome bitmap. SetBkColor(hMainDC, clrTransparent); BitBlt(hAndMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCCOPY); // Assign the color XOR mask bitmap pixels so that the pixels of the source bitmap // with 'clrTransparent' will be black and rest the pixels same as corresponding - // pixels of the source bitmap + // pixels of the source bitmap. SetBkColor(hXorMaskDC, RGB(0, 0, 0)); SetTextColor(hXorMaskDC, RGB(255, 255, 255)); BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hAndMaskDC, 0, 0, SRCCOPY); BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCAND); - // Deselect bitmaps from the helper DC + // Deselect bitmaps from the helper DC. SelectObject(hMainDC, hOldMainBitmap); SelectObject(hAndMaskDC, hOldAndMaskBitmap); SelectObject(hXorMaskDC, hOldXorMaskBitmap); - // Delete the helper DC + // Delete the helper DC. DeleteDC(hXorMaskDC); DeleteDC(hAndMaskDC); DeleteDC(hMainDC); @@ -1326,7 +1324,7 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh UINT image_size = texture_size.width * texture_size.height; - // Create the BITMAP with alpha channel + // Create the BITMAP with alpha channel. COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size); for (UINT index = 0; index < image_size; index++) { @@ -1341,11 +1339,11 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh *(buffer + index) = image->get_pixel(column_index, row_index).to_argb32(); } - // Using 4 channels, so 4 * 8 bits + // Using 4 channels, so 4 * 8 bits. HBITMAP bitmap = CreateBitmap(texture_size.width, texture_size.height, 1, 4 * 8, buffer); COLORREF clrTransparent = -1; - // Create the AND and XOR masks for the bitmap + // Create the AND and XOR masks for the bitmap. HBITMAP hAndMask = nullptr; HBITMAP hXorMask = nullptr; @@ -1357,7 +1355,7 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh return; } - // Finally, create the icon + // Finally, create the icon. ICONINFO iconinfo; iconinfo.fIcon = FALSE; iconinfo.xHotspot = p_hotspot.x; @@ -1365,8 +1363,9 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh iconinfo.hbmMask = hAndMask; iconinfo.hbmColor = hXorMask; - if (cursors[p_shape]) + if (cursors[p_shape]) { DestroyIcon(cursors[p_shape]); + } cursors[p_shape] = CreateIconIndirect(&iconinfo); @@ -1392,7 +1391,7 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh memfree(buffer); DeleteObject(bitmap); } else { - // Reset to default system cursor + // Reset to default system cursor. if (cursors[p_shape]) { DestroyIcon(cursors[p_shape]); cursors[p_shape] = nullptr; @@ -1574,9 +1573,9 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { icon_dir = (ICONDIR *)memrealloc(icon_dir, 3 * sizeof(WORD) + icon_dir->idCount * sizeof(ICONDIRENTRY)); f->get_buffer((uint8_t *)&icon_dir->idEntries[0], icon_dir->idCount * sizeof(ICONDIRENTRY)); - int small_icon_index = -1; // Select 16x16 with largest color count + int small_icon_index = -1; // Select 16x16 with largest color count. int small_icon_cc = 0; - int big_icon_index = -1; // Select largest + int big_icon_index = -1; // Select largest. int big_icon_width = 16; int big_icon_cc = 0; @@ -1606,7 +1605,7 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { small_icon_cc = big_icon_cc; } - // Read the big icon + // Read the big icon. DWORD bytecount_big = icon_dir->idEntries[big_icon_index].dwBytesInRes; Vector<uint8_t> data_big; data_big.resize(bytecount_big); @@ -1616,7 +1615,7 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { HICON icon_big = CreateIconFromResource((PBYTE)&data_big.write[0], bytecount_big, TRUE, 0x00030000); ERR_FAIL_COND_MSG(!icon_big, "Could not create " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + "."); - // Read the small icon + // Read the small icon. DWORD bytecount_small = icon_dir->idEntries[small_icon_index].dwBytesInRes; Vector<uint8_t> data_small; data_small.resize(bytecount_small); @@ -1626,7 +1625,7 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { HICON icon_small = CreateIconFromResource((PBYTE)&data_small.write[0], bytecount_small, TRUE, 0x00030000); ERR_FAIL_COND_MSG(!icon_small, "Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + "."); - // Online tradition says to be sure last error is cleared and set the small icon first + // Online tradition says to be sure last error is cleared and set the small icon first. int err = 0; SetLastError(err); @@ -1647,12 +1646,13 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) { ERR_FAIL_COND(!p_icon.is_valid()); Ref<Image> icon = p_icon->duplicate(); - if (icon->get_format() != Image::FORMAT_RGBA8) + if (icon->get_format() != Image::FORMAT_RGBA8) { icon->convert(Image::FORMAT_RGBA8); + } int w = icon->get_width(); int h = icon->get_height(); - /* Create temporary bitmap buffer */ + // Create temporary bitmap buffer. int icon_len = 40 + h * w * 4; Vector<BYTE> v; v.resize(icon_len); @@ -1686,10 +1686,10 @@ void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) { HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000); - /* Set the icon for the window */ + // Set the icon for the window. SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon); - /* Set the icon in the task manager (should we do this?) */ + // Set the icon in the task manager (should we do this?). SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon); } @@ -1717,13 +1717,13 @@ void DisplayServerWindows::set_context(Context p_context) { // Keeping the name suggested by Microsoft, but this macro really answers: // Is this mouse event emulated from touch or pen input? #define IsPenEvent(dw) (((dw)&SIGNATURE_MASK) == MI_WP_SIGNATURE) -// This one tells whether the event comes from touchscreen (and not from pen) +// This one tells whether the event comes from touchscreen (and not from pen). #define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw)&0x80)) void DisplayServerWindows::_touch_event(WindowID p_window, bool p_pressed, float p_x, float p_y, int idx) { - // Defensive - if (touch_state.has(idx) == p_pressed) + if (touch_state.has(idx) == p_pressed) { return; + } if (p_pressed) { touch_state.insert(idx, Vector2(p_x, p_y)); @@ -1743,12 +1743,13 @@ void DisplayServerWindows::_touch_event(WindowID p_window, bool p_pressed, float void DisplayServerWindows::_drag_event(WindowID p_window, float p_x, float p_y, int idx) { Map<int, Vector2>::Element *curr = touch_state.find(idx); - // Defensive - if (!curr) + if (!curr) { return; + } - if (curr->get() == Vector2(p_x, p_y)) + if (curr->get() == Vector2(p_x, p_y)) { return; + } Ref<InputEventScreenDrag> event; event.instantiate(); @@ -1790,7 +1791,7 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) Ref<InputEventFromWindow> event_from_window = p_event; if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) { - //send to a window + // Send to a single window. if (!windows.has(event_from_window->get_window_id())) { in_dispatch_input_event = false; ERR_FAIL_MSG("DisplayServerWindows: Invalid window id in input event."); @@ -1802,7 +1803,7 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) } callable.call((const Variant **)&evp, 1, ret, ce); } else { - //send to all windows + // Send to all windows. for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { Callable callable = E->get().input_event_callback; if (callable.is_null()) { @@ -1815,6 +1816,9 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) in_dispatch_input_event = false; } +// Our default window procedure to handle processing of window-related system messages/events. +// Also known as DefProc or DefWindowProc. +// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-procedures LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (drop_events) { if (user_proc) { @@ -1827,6 +1831,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA WindowID window_id = INVALID_WINDOW_ID; bool window_created = false; + // Check whether window exists. for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { if (E->get().hWnd == hWnd) { window_id = E->key(); @@ -1835,19 +1840,19 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } + // Window doesn't exist or creation in progress, don't handle messages yet. if (!window_created) { - // Window creation in progress. window_id = window_id_counter; ERR_FAIL_COND_V(!windows.has(window_id), 0); } - switch (uMsg) // Check For Windows Messages - { + // Process window messages. + switch (uMsg) { case WM_SETFOCUS: { windows[window_id].window_has_focus = true; last_focused_window = window_id; - // Restore mouse mode + // Restore mouse mode. _set_mouse_mode_impl(mouse_mode); if (!app_focused) { @@ -1856,16 +1861,15 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } app_focused = true; } - break; - } + } break; case WM_KILLFOCUS: { windows[window_id].window_has_focus = false; last_focused_window = window_id; - // Release capture unconditionally because it can be set due to dragging, in addition to captured mode + // Release capture unconditionally because it can be set due to dragging, in addition to captured mode. ReleaseCapture(); - // Release every touch to avoid sticky points + // Release every touch to avoid sticky points. for (Map<int, Vector2>::Element *E = touch_state.front(); E; E = E->next()) { _touch_event(window_id, false, E->get().x, E->get().y, E->key()); } @@ -1883,10 +1887,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } app_focused = false; } - - break; - } - case WM_ACTIVATE: { // Watch For Window Activate Message + } break; + case WM_ACTIVATE: { // Watch for window activate message. if (!windows[window_id].window_focused) { _process_activate_event(window_id, wParam, lParam); } else { @@ -1896,11 +1898,13 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA // Run a timer to prevent event catching warning if the focused window is closing. windows[window_id].focus_timer_id = SetTimer(windows[window_id].hWnd, 2, USER_TIMER_MINIMUM, (TIMERPROC) nullptr); } - return 0; // Return To The Message Loop - } + return 0; // Return to the message loop. + } break; case WM_GETMINMAXINFO: { if (windows[window_id].resizable && !windows[window_id].fullscreen) { - Size2 decor = window_get_size(window_id) - window_get_real_size(window_id); // Size of window decorations + // Size of window decorations. + Size2 decor = window_get_real_size(window_id) - window_get_size(window_id); + MINMAXINFO *min_max_info = (MINMAXINFO *)lParam; if (windows[window_id].min_size != Size2()) { min_max_info->ptMinTrackSize.x = windows[window_id].min_size.x + decor.x; @@ -1911,37 +1915,31 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA min_max_info->ptMaxTrackSize.y = windows[window_id].max_size.y + decor.y; } return 0; - } else { - break; } - } - case WM_PAINT: - + } break; + case WM_PAINT: { Main::force_redraw(); - break; - - case WM_SYSCOMMAND: // Intercept System Commands + } break; + case WM_SYSCOMMAND: // Intercept system commands. { - switch (wParam) // Check System Calls + switch (wParam) // Check system calls. { - case SC_SCREENSAVE: // Screensaver Trying To Start? - case SC_MONITORPOWER: // Monitor Trying To Enter Powersave? - return 0; // Prevent From Happening + case SC_SCREENSAVE: // Screensaver trying to start? + case SC_MONITORPOWER: // Monitor trying to enter powersave? + return 0; // Prevent from happening. case SC_KEYMENU: if ((lParam >> 16) <= 0) return 0; } - break; // Exit - } - - case WM_CLOSE: // Did We Receive A Close Message? + } break; + case WM_CLOSE: // Did we receive a close message? { if (windows[window_id].focus_timer_id != 0U) { KillTimer(windows[window_id].hWnd, windows[window_id].focus_timer_id); } _send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST); - return 0; // Jump Back + return 0; // Jump back. } case WM_MOUSELEAVE: { old_invalid = true; @@ -1983,7 +1981,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA Point2i c(windows[window_id].width / 2, windows[window_id].height / 2); - // centering just so it works as before + // Centering just so it works as before. POINT pos = { (int)c.x, (int)c.y }; ClientToScreen(windows[window_id].hWnd, &pos); SetCursorPos(pos.x, pos.y); @@ -2006,7 +2004,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA (double(raw->data.mouse.lLastX) - 65536.0 / (nScreenWidth)) * nScreenWidth / 65536.0 + nScreenLeft, (double(raw->data.mouse.lLastY) - 65536.0 / (nScreenHeight)) * nScreenHeight / 65536.0 + nScreenTop); - POINT coords; //client coords + POINT coords; // Client coords. coords.x = abs_pos.x; coords.y = abs_pos.y; @@ -2015,14 +2013,11 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_relative(Vector2(coords.x - old_x, coords.y - old_y)); old_x = coords.x; old_y = coords.y; - - /*Input.mi.dx = (int)((((double)(pos.x)-nScreenLeft) * 65536) / nScreenWidth + 65536 / (nScreenWidth)); - Input.mi.dy = (int)((((double)(pos.y)-nScreenTop) * 65536) / nScreenHeight + 65536 / (nScreenHeight)); - */ } - if (windows[window_id].window_has_focus && mm->get_relative() != Vector2()) + if (windows[window_id].window_has_focus && mm->get_relative() != Vector2()) { Input::get_singleton()->parse_input_event(mm); + } } delete[] lpb; } break; @@ -2167,7 +2162,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } if (Input::get_singleton()->is_emulating_mouse_from_touch()) { - // Universal translation enabled; ignore OS translation + // Universal translation enabled; ignore OS translation. LPARAM extra = GetMessageExtraInfo(); if (IsTouchEvent(extra)) { break; @@ -2175,7 +2170,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } if (outside) { - //mouse enter + // Mouse enter. if (mouse_mode != MOUSE_MODE_CAPTURED) { _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); @@ -2186,7 +2181,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA cursor_set_shape(c); outside = false; - //Once-Off notification, must call again.... + // Once-off notification, must call again. TRACKMOUSEEVENT tme; tme.cbSize = sizeof(TRACKMOUSEEVENT); tme.dwFlags = TME_LEAVE; @@ -2219,7 +2214,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_button_mask(last_button_state); - POINT coords; //client coords + POINT coords; // Client coords. coords.x = GET_X_LPARAM(lParam); coords.y = GET_Y_LPARAM(lParam); @@ -2261,7 +2256,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA Input::get_singleton()->parse_input_event(mm); } - return 0; //Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event + return 0; // Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event. } break; case WM_MOUSEMOVE: { if (windows[window_id].block_mm) { @@ -2273,7 +2268,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } if (Input::get_singleton()->is_emulating_mouse_from_touch()) { - // Universal translation enabled; ignore OS translation + // Universal translation enabled; ignore OS translation. LPARAM extra = GetMessageExtraInfo(); if (IsTouchEvent(extra)) { break; @@ -2281,7 +2276,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } if (outside) { - //mouse enter + // Mouse enter. if (mouse_mode != MOUSE_MODE_CAPTURED) { _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); @@ -2292,7 +2287,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA cursor_set_shape(c); outside = false; - //Once-Off notification, must call again.... + // Once-off notification, must call again. TRACKMOUSEEVENT tme; tme.cbSize = sizeof(TRACKMOUSEEVENT); tme.dwFlags = TME_LEAVE; @@ -2370,7 +2365,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA case WM_LBUTTONDOWN: case WM_LBUTTONUP: if (Input::get_singleton()->is_emulating_mouse_from_touch()) { - // Universal translation enabled; ignore OS translations for left button + // Universal translation enabled; ignore OS translations for left button. LPARAM extra = GetMessageExtraInfo(); if (IsTouchEvent(extra)) { break; @@ -2494,7 +2489,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mb->set_ctrl_pressed((wParam & MK_CONTROL) != 0); mb->set_shift_pressed((wParam & MK_SHIFT) != 0); mb->set_alt_pressed(alt_mem); - //mb->is_alt_pressed()=(wParam&MK_MENU)!=0; + // mb->is_alt_pressed()=(wParam&MK_MENU)!=0; if (mb->is_pressed()) { last_button_state |= MouseButton(1 << (mb->get_button_index() - 1)); } else { @@ -2521,7 +2516,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } } else { - // for reasons unknown to mankind, wheel comes in screen coordinates + // For reasons unknown to mankind, wheel comes in screen coordinates. POINT coords; coords.x = mb->get_position().x; coords.y = mb->get_position().y; @@ -2535,7 +2530,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA Input::get_singleton()->parse_input_event(mb); if (mb->is_pressed() && mb->get_button_index() > 3 && mb->get_button_index() < 8) { - //send release for mouse wheel + // Send release for mouse wheel. Ref<InputEventMouseButton> mbd = mb->duplicate(); mbd->set_window_id(window_id); last_button_state &= (MouseButton) ~(1 << (mbd->get_button_index() - 1)); @@ -2545,7 +2540,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } break; - case WM_MOVE: { if (!IsIconic(windows[window_id].hWnd)) { int x = int16_t(LOWORD(lParam)); @@ -2561,12 +2555,14 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } } break; - case WM_SIZE: { - // Ignore size when a SIZE_MINIMIZED event is triggered + // Ignore window size change when a SIZE_MINIMIZED event is triggered. if (wParam != SIZE_MINIMIZED) { + // The new width and height of the client area. int window_w = LOWORD(lParam); int window_h = HIWORD(lParam); + + // Set new value to the size if it isn't preserved. if (window_w > 0 && window_h > 0 && !windows[window_id].preserve_window_size) { windows[window_id].width = window_w; windows[window_id].height = window_h; @@ -2577,29 +2573,38 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } #endif - } else { + } else { // If the size is preserved. windows[window_id].preserve_window_size = false; + + // Restore the old size. window_set_size(Size2(windows[window_id].width, windows[window_id].height), window_id); } - } else { + } else { // When the window has been minimized, preserve its size. windows[window_id].preserve_window_size = true; } + // Call windows rect change callback. if (!windows[window_id].rect_changed_callback.is_null()) { Variant size = Rect2i(windows[window_id].last_pos.x, windows[window_id].last_pos.y, windows[window_id].width, windows[window_id].height); - Variant *sizep = &size; + Variant *size_ptr = &size; Variant ret; Callable::CallError ce; - windows[window_id].rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce); + windows[window_id].rect_changed_callback.call((const Variant **)&size_ptr, 1, ret, ce); } + // The window has been maximized. if (wParam == SIZE_MAXIMIZED) { windows[window_id].maximized = true; windows[window_id].minimized = false; - } else if (wParam == SIZE_MINIMIZED) { + } + // The window has been minimized. + else if (wParam == SIZE_MINIMIZED) { windows[window_id].maximized = false; windows[window_id].minimized = true; - } else if (wParam == SIZE_RESTORED) { + windows[window_id].preserve_window_size = false; + } + // The window has been resized, but neither the SIZE_MINIMIZED nor SIZE_MAXIMIZED value applies. + else if (wParam == SIZE_RESTORED) { windows[window_id].maximized = false; windows[window_id].minimized = false; } @@ -2626,9 +2631,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA ZeroMemory(dib_data, dib_size.x * dib_size.y * 4); } #endif - //return 0; // Jump Back } break; - case WM_ENTERSIZEMOVE: { Input::get_singleton()->release_pressed_events(); windows[window_id].move_timer_id = SetTimer(windows[window_id].hWnd, 1, USER_TIMER_MINIMUM, (TIMERPROC) nullptr); @@ -2648,7 +2651,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA windows[window_id].focus_timer_id = 0U; } } break; - case WM_SYSKEYDOWN: case WM_SYSKEYUP: case WM_KEYUP: @@ -2700,7 +2702,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA case WM_INPUTLANGCHANGEREQUEST: { // FIXME: Do something? } break; - case WM_TOUCH: { BOOL bHandled = FALSE; UINT cInputs = LOWORD(wParam); @@ -2714,7 +2715,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA TOUCH_COORD_TO_PIXEL(ti.y), }; ScreenToClient(hWnd, &touch_pos); - //do something with each touch input entry + // Do something with each touch input entry. if (ti.dwFlags & TOUCHEVENTF_MOVE) { _drag_event(window_id, touch_pos.x, touch_pos.y, ti.dwID); } else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) { @@ -2723,11 +2724,11 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } bHandled = TRUE; } else { - /* handle the error here */ + // TODO: Handle the error here. } memdelete_arr(pInputs); } else { - /* handle the error here, probably out of memory */ + // TODO: Handle the error here, probably out of memory. } if (bHandled) { CloseTouchInputHandle((HTOUCHINPUT)lParam); @@ -2735,14 +2736,13 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA }; } break; - case WM_DEVICECHANGE: { joypad->probe_joypads(); } break; case WM_SETCURSOR: { if (LOWORD(lParam) == HTCLIENT) { if (windows[window_id].window_has_focus && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN)) { - //Hide the cursor + // Hide the cursor. if (hCursor == nullptr) { hCursor = SetCursor(nullptr); } else { @@ -2757,7 +2757,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } } - } break; case WM_DROPFILES: { HDROP hDropInfo = (HDROP)wParam; @@ -2781,9 +2780,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA Callable::CallError ce; windows[window_id].drop_files_callback.call((const Variant **)&vp, 1, ret, ce); } - } break; - default: { if (user_proc) { return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam); @@ -2809,7 +2806,7 @@ void DisplayServerWindows::_process_activate_event(WindowID p_window_id, WPARAM alt_mem = false; control_mem = false; shift_mem = false; - } else { // WM_INACTIVE + } else { // WM_INACTIVE. Input::get_singleton()->release_pressed_events(); _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_OUT); windows[p_window_id].window_focused = false; @@ -2826,7 +2823,7 @@ void DisplayServerWindows::_process_key_events() { KeyEvent &ke = key_event_buffer[i]; switch (ke.uMsg) { case WM_CHAR: { - // extended keys should only be processed as WM_KEYDOWN message. + // Extended keys should only be processed as WM_KEYDOWN message. if (!KeyMappingWindows::is_extended_key(ke.wParam) && ((i == 0 && ke.uMsg == WM_CHAR) || (i > 0 && key_event_buffer[i - 1].uMsg == WM_CHAR))) { static char32_t prev_wc = 0; char32_t unicode = ke.wParam; @@ -2867,9 +2864,9 @@ void DisplayServerWindows::_process_key_events() { k->set_unicode(0); Input::get_singleton()->parse_input_event(k); + } else { + // Do nothing. } - - //do nothing } break; case WM_KEYUP: case WM_KEYDOWN: { @@ -2885,7 +2882,7 @@ void DisplayServerWindows::_process_key_events() { k->set_pressed(ke.uMsg == WM_KEYDOWN); if ((ke.lParam & (1 << 24)) && (ke.wParam == VK_RETURN)) { - // Special case for Numpad Enter key + // Special case for Numpad Enter key. k->set_keycode(KEY_KP_ENTER); } else { k->set_keycode((Key)KeyMappingWindows::get_keysym(ke.wParam)); @@ -3031,8 +3028,8 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, if (p_mode != WINDOW_MODE_FULLSCREEN) { wd.pre_fs_valid = true; } -#ifdef VULKAN_ENABLED +#ifdef VULKAN_ENABLED if (rendering_driver == "vulkan") { if (context_vulkan->window_create(id, p_vsync_mode, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) == -1) { memdelete(context_vulkan); @@ -3087,7 +3084,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, wd.last_pressure_update = 0; wd.last_tilt = Vector2(); - // IME + // IME. wd.im_himc = ImmGetContext(wd.hWnd); ImmReleaseContext(wd.hWnd, wd.im_himc); @@ -3102,7 +3099,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, return id; } -// WinTab API +// WinTab API. bool DisplayServerWindows::wintab_available = false; WTOpenPtr DisplayServerWindows::wintab_WTOpen = nullptr; WTClosePtr DisplayServerWindows::wintab_WTClose = nullptr; @@ -3110,7 +3107,7 @@ WTInfoPtr DisplayServerWindows::wintab_WTInfo = nullptr; WTPacketPtr DisplayServerWindows::wintab_WTPacket = nullptr; WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr; -// Windows Ink API +// Windows Ink API. bool DisplayServerWindows::winink_available = false; GetPointerTypePtr DisplayServerWindows::win8p_GetPointerType = nullptr; GetPointerPenInfoPtr DisplayServerWindows::win8p_GetPointerPenInfo = nullptr; @@ -3173,7 +3170,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win outside = true; - //Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink. + // Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink. HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll"); if (wintab_lib) { wintab_WTOpen = (WTOpenPtr)GetProcAddress(wintab_lib, "WTOpenW"); @@ -3189,7 +3186,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win tablet_drivers.push_back("wintab"); } - //Note: Windows Ink API for pen input, available on Windows 8+ only. + // Note: Windows Ink API for pen input, available on Windows 8+ only. HMODULE user32_lib = LoadLibraryW(L"user32.dll"); if (user32_lib) { win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType"); @@ -3222,7 +3219,6 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win wc.lpfnWndProc = (WNDPROC)::WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; - //wc.hInstance = hInstance; wc.hInstance = hInstance ? hInstance : GetModuleHandle(nullptr); wc.hIcon = LoadIcon(nullptr, IDI_WINLOGO); wc.hCursor = nullptr; //LoadCursor(nullptr, IDC_ARROW); @@ -3246,7 +3242,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win Rid[0].hwndTarget = 0; if (RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) { - //registration failed. + // Registration failed. use_raw_input = false; } @@ -3263,6 +3259,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win } } #endif + #if defined(OPENGL_ENABLED) if (rendering_driver_index == VIDEO_DRIVER_GLES2) { context_gles2 = memnew(ContextGL_Windows(hWnd, false)); @@ -3285,6 +3282,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win } } #endif + Point2i window_position( (screen_get_size(0).width - p_resolution.width) / 2, (screen_get_size(0).height - p_resolution.height) / 2); @@ -3313,7 +3311,6 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win //set_ime_active(false); if (!OS::get_singleton()->is_in_low_processor_usage_mode()) { - //SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); DWORD index = 0; HANDLE handle = AvSetMmThreadCharacteristics("Games", &index); @@ -3321,7 +3318,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win AvSetMmThreadPriority(handle, AVRT_PRIORITY_CRITICAL); // This is needed to make sure that background work does not starve the main thread. - // This is only setting priority of this thread, not the whole process. + // This is only setting the priority of this thread, not the whole process. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); } diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 2c8afaf7de..2a0a509d43 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -681,7 +681,7 @@ String OS_Windows::get_godot_dir_name() const { return String(VERSION_SHORT_NAME).capitalize(); } -String OS_Windows::get_system_dir(SystemDir p_dir) const { +String OS_Windows::get_system_dir(SystemDir p_dir, bool p_shared_storage) const { KNOWNFOLDERID id; switch (p_dir) { diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index ea0c263b78..c4a2eda8f4 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -150,7 +150,7 @@ public: virtual String get_cache_path() const override; virtual String get_godot_dir_name() const override; - virtual String get_system_dir(SystemDir p_dir) const override; + virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override; virtual String get_user_data_dir() const override; virtual String get_unique_id() const override; |