diff options
Diffstat (limited to 'platform')
62 files changed, 898 insertions, 334 deletions
diff --git a/platform/android/detect.py b/platform/android/detect.py index 5f0fcc9b77..996b6dcf41 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -197,12 +197,11 @@ def configure(env): if env["optimize"] == "speed": # optimize for speed (default) env.Append(LINKFLAGS=["-O2"]) env.Append(CCFLAGS=["-O2", "-fomit-frame-pointer"]) - env.Append(CPPDEFINES=["NDEBUG"]) - else: # optimize for size + elif env["optimize"] == "size": # optimize for size env.Append(CCFLAGS=["-Os"]) - env.Append(CPPDEFINES=["NDEBUG"]) env.Append(LINKFLAGS=["-Os"]) + env.Append(CPPDEFINES=["NDEBUG"]) if can_vectorize: env.Append(CCFLAGS=["-ftree-vectorize"]) if env["target"] == "release_debug": diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index 5f7e5eaa83..dd001baba9 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -477,7 +477,7 @@ void DisplayServerAndroid::process_joy_event(DisplayServerAndroid::JoypadEvent p Input::get_singleton()->joy_button(p_event.device, p_event.index, p_event.pressed); break; case JOY_EVENT_AXIS: - Input::JoyAxis value; + Input::JoyAxisValue value; value.min = -1; value.value = p_event.value; Input::get_singleton()->joy_axis(p_event.device, p_event.index, value); @@ -741,15 +741,15 @@ void DisplayServerAndroid::process_mouse_event(int input_device, int event_actio ev->set_pressed(true); buttons_state = event_buttons_mask; if (event_vertical_factor > 0) { - _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_UP, event_vertical_factor); + _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_UP, event_vertical_factor); } else if (event_vertical_factor < 0) { - _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_DOWN, -event_vertical_factor); + _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_DOWN, -event_vertical_factor); } if (event_horizontal_factor > 0) { - _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_RIGHT, event_horizontal_factor); + _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_RIGHT, event_horizontal_factor); } else if (event_horizontal_factor < 0) { - _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_LEFT, -event_horizontal_factor); + _wheel_button_click(event_buttons_mask, ev, MOUSE_BUTTON_WHEEL_LEFT, -event_horizontal_factor); } } break; } @@ -784,16 +784,16 @@ void DisplayServerAndroid::process_double_tap(int event_android_button_mask, Poi int DisplayServerAndroid::_button_index_from_mask(int button_mask) { switch (button_mask) { - case BUTTON_MASK_LEFT: - return BUTTON_LEFT; - case BUTTON_MASK_RIGHT: - return BUTTON_RIGHT; - case BUTTON_MASK_MIDDLE: - return BUTTON_MIDDLE; - case BUTTON_MASK_XBUTTON1: - return BUTTON_XBUTTON1; - case BUTTON_MASK_XBUTTON2: - return BUTTON_XBUTTON2; + case MOUSE_BUTTON_MASK_LEFT: + return MOUSE_BUTTON_LEFT; + case MOUSE_BUTTON_MASK_RIGHT: + return MOUSE_BUTTON_RIGHT; + case MOUSE_BUTTON_MASK_MIDDLE: + return MOUSE_BUTTON_MIDDLE; + case MOUSE_BUTTON_MASK_XBUTTON1: + return MOUSE_BUTTON_XBUTTON1; + case MOUSE_BUTTON_MASK_XBUTTON2: + return MOUSE_BUTTON_XBUTTON2; default: return 0; } @@ -854,19 +854,19 @@ int DisplayServerAndroid::mouse_get_button_state() const { int DisplayServerAndroid::_android_button_mask_to_godot_button_mask(int android_button_mask) { int godot_button_mask = 0; if (android_button_mask & AMOTION_EVENT_BUTTON_PRIMARY) { - godot_button_mask |= BUTTON_MASK_LEFT; + godot_button_mask |= MOUSE_BUTTON_MASK_LEFT; } if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) { - godot_button_mask |= BUTTON_MASK_RIGHT; + godot_button_mask |= MOUSE_BUTTON_MASK_RIGHT; } if (android_button_mask & AMOTION_EVENT_BUTTON_TERTIARY) { - godot_button_mask |= BUTTON_MASK_MIDDLE; + godot_button_mask |= MOUSE_BUTTON_MASK_MIDDLE; } if (android_button_mask & AMOTION_EVENT_BUTTON_BACK) { - godot_button_mask |= BUTTON_MASK_XBUTTON1; + godot_button_mask |= MOUSE_BUTTON_MASK_XBUTTON1; } if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) { - godot_button_mask |= BUTTON_MASK_XBUTTON2; + godot_button_mask |= MOUSE_BUTTON_MASK_XBUTTON2; } return godot_button_mask; diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 326e513261..5f9f420b59 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -2262,8 +2262,9 @@ public: CharString command_line_argument = command_line_strings[i].utf8(); int base = r_command_line_flags.size(); int length = command_line_argument.length(); - if (length == 0) + if (length == 0) { continue; + } r_command_line_flags.resize(base + 4 + length); encode_uint32(length, &r_command_line_flags.write[base]); copymem(&r_command_line_flags.write[base + 4], command_line_argument.ptr(), length); @@ -2484,7 +2485,7 @@ public: _clear_assets_directory(); if (!apk_expansion) { print_verbose("Exporting project files.."); - err = export_project_files(p_preset, rename_and_store_file_in_gradle_project, NULL, ignore_so_file); + err = export_project_files(p_preset, rename_and_store_file_in_gradle_project, nullptr, ignore_so_file); if (err != OK) { EditorNode::add_io_error("Could not export project files to gradle project\n"); return err; @@ -2615,10 +2616,11 @@ public: } // This is the start of the Legacy build system print_verbose("Starting legacy build system.."); - if (p_debug) + if (p_debug) { src_apk = p_preset->get("custom_template/debug"); - else + } else { src_apk = p_preset->get("custom_template/release"); + } src_apk = src_apk.strip_edges(); if (src_apk == "") { if (p_debug) { @@ -2813,11 +2815,11 @@ public: zipOpenNewFileInZip(unaligned_apk, "assets/_cl_", &zipfi, - NULL, + nullptr, 0, - NULL, + nullptr, 0, - NULL, + nullptr, 0, // No compress (little size gain and potentially slower startup) Z_DEFAULT_COMPRESSION); zipWriteInFileInZip(unaligned_apk, command_line_flags.ptr(), command_line_flags.size()); diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h index 097a2391ee..40b8e90c6f 100644 --- a/platform/android/export/gradle_export_util.h +++ b/platform/android/export/gradle_export_util.h @@ -47,20 +47,21 @@ const String godot_project_name_xml_string = R"(<?xml version="1.0" encoding="ut DisplayServer::ScreenOrientation _get_screen_orientation() { String orientation_settings = ProjectSettings::get_singleton()->get("display/window/handheld/orientation"); DisplayServer::ScreenOrientation screen_orientation; - if (orientation_settings == "portrait") + if (orientation_settings == "portrait") { screen_orientation = DisplayServer::SCREEN_PORTRAIT; - else if (orientation_settings == "reverse_landscape") + } else if (orientation_settings == "reverse_landscape") { screen_orientation = DisplayServer::SCREEN_REVERSE_LANDSCAPE; - else if (orientation_settings == "reverse_portrait") + } else if (orientation_settings == "reverse_portrait") { screen_orientation = DisplayServer::SCREEN_REVERSE_PORTRAIT; - else if (orientation_settings == "sensor_landscape") + } else if (orientation_settings == "sensor_landscape") { screen_orientation = DisplayServer::SCREEN_SENSOR_LANDSCAPE; - else if (orientation_settings == "sensor_portrait") + } else if (orientation_settings == "sensor_portrait") { screen_orientation = DisplayServer::SCREEN_SENSOR_PORTRAIT; - else if (orientation_settings == "sensor") + } else if (orientation_settings == "sensor") { screen_orientation = DisplayServer::SCREEN_SENSOR; - else + } else { screen_orientation = DisplayServer::SCREEN_LANDSCAPE; + } return screen_orientation; } diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp index 165d5da3ae..e288c16777 100644 --- a/platform/android/file_access_android.cpp +++ b/platform/android/file_access_android.cpp @@ -114,6 +114,9 @@ uint8_t FileAccessAndroid::get_8() const { } int FileAccessAndroid::get_buffer(uint8_t *p_dst, int p_length) const { + ERR_FAIL_COND_V(!p_dst, -1); + ERR_FAIL_COND_V(p_length < 0, -1); + off_t r = AAsset_read(a, p_dst, p_length); if (pos + p_length > len) { diff --git a/platform/android/java/app/assets/.gitignore b/platform/android/java/app/assets/.gitignore new file mode 100644 index 0000000000..d6b7ef32c8 --- /dev/null +++ b/platform/android/java/app/assets/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index 934c4bf441..f103f22db2 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -77,7 +77,7 @@ android { defaultConfig { // The default ignore pattern for the 'assets' directory includes hidden files and directories which are used by Godot projects. aaptOptions { - ignoreAssetsPattern "!.svn:!.git:!.ds_store:!*.scc:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~" + ignoreAssetsPattern "!.svn:!.git:!.gitignore:!.ds_store:!*.scc:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~" } ndk { @@ -106,8 +106,10 @@ android { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' - // Should be uncommented for development purpose within Android Studio - // doNotStrip '**/*.so' + // 'doNotStrip' is enabled for development within Android Studio + if (shouldNotStrip()) { + doNotStrip '**/*.so' + } } signingConfigs { diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index 585e517631..c0ae4007d2 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -209,10 +209,19 @@ ext.getReleaseKeyAlias = { -> return keyAlias } +ext.isAndroidStudio = { -> + def sysProps = System.getProperties() + return sysProps != null && sysProps['idea.platform.prefix'] != null +} + ext.shouldZipAlign = { -> String zipAlignFlag = project.hasProperty("perform_zipalign") ? project.property("perform_zipalign") : "" if (zipAlignFlag == null || zipAlignFlag.isEmpty()) { - zipAlignFlag = "false" + if (isAndroidStudio()) { + zipAlignFlag = "true" + } else { + zipAlignFlag = "false" + } } return Boolean.parseBoolean(zipAlignFlag) } @@ -220,7 +229,15 @@ ext.shouldZipAlign = { -> ext.shouldSign = { -> String signFlag = project.hasProperty("perform_signing") ? project.property("perform_signing") : "" if (signFlag == null || signFlag.isEmpty()) { - signFlag = "false" + if (isAndroidStudio()) { + signFlag = "true" + } else { + signFlag = "false" + } } return Boolean.parseBoolean(signFlag) } + +ext.shouldNotStrip = { -> + return isAndroidStudio() +} diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle index 6fc9a11a08..663ba73d40 100644 --- a/platform/android/java/lib/build.gradle +++ b/platform/android/java/lib/build.gradle @@ -36,8 +36,10 @@ android { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' - // Should be uncommented for development purpose within Android Studio - // doNotStrip '**/*.so' + // 'doNotStrip' is enabled for development within Android Studio + if (shouldNotStrip()) { + doNotStrip '**/*.so' + } } sourceSets { 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 4e67402c63..ec2ace4821 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java +++ b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java @@ -34,6 +34,7 @@ import android.content.Intent; import android.os.Bundle; import android.view.KeyEvent; +import androidx.annotation.CallSuper; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; @@ -44,7 +45,7 @@ import androidx.fragment.app.FragmentActivity; * It's also a reference implementation for how to setup and use the {@link Godot} fragment * within an Android app. */ -public abstract class FullScreenGodotApp extends FragmentActivity { +public abstract class FullScreenGodotApp extends FragmentActivity implements GodotHost { @Nullable private Godot godotFragment; @@ -64,6 +65,28 @@ public abstract class FullScreenGodotApp extends FragmentActivity { public void onNewIntent(Intent intent) { if (godotFragment != null) { godotFragment.onNewIntent(intent); + } else { + super.onNewIntent(intent); + } + } + + @CallSuper + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (godotFragment != null) { + godotFragment.onActivityResult(requestCode, resultCode, data); + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } + + @CallSuper + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + if (godotFragment != null) { + godotFragment.onRequestPermissionsResult(requestCode, permissions, grantResults); + } else { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } 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 0891904dff..0c16214c8a 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -103,6 +103,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.security.MessageDigest; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -131,6 +132,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC private boolean activityResumed; private int mState; + private GodotHost godotHost; private GodotPluginRegistry pluginRegistry; static private Intent mCurrentIntent; @@ -178,7 +180,25 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC public ResultCallback result_callback; @Override + public void onAttach(Context context) { + super.onAttach(context); + if (getParentFragment() instanceof GodotHost) { + godotHost = (GodotHost)getParentFragment(); + } else if (getActivity() instanceof GodotHost) { + godotHost = (GodotHost)getActivity(); + } + } + + @Override + public void onDetach() { + super.onDetach(); + godotHost = null; + } + + @CallSuper + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); if (result_callback != null) { result_callback.callback(requestCode, resultCode, data); result_callback = null; @@ -189,8 +209,10 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC } } + @CallSuper @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { plugin.onMainRequestPermissionsResult(requestCode, permissions, grantResults); } @@ -201,6 +223,20 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC }; /** + * Invoked on the render thread when the Godot setup is complete. + */ + @CallSuper + protected void onGodotSetupCompleted() { + for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { + plugin.onGodotSetupCompleted(); + } + + if (godotHost != null) { + godotHost.onGodotSetupCompleted(); + } + } + + /** * Invoked on the render thread when the Godot main loop has started. */ @CallSuper @@ -208,6 +244,10 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { plugin.onGodotMainLoopStarted(); } + + if (godotHost != null) { + godotHost.onGodotMainLoopStarted(); + } } /** @@ -228,7 +268,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC GodotLib.setup(command_line); - final String videoDriver = GodotLib.getGlobal("rendering/quality/driver/driver_name"); + final String videoDriver = GodotLib.getGlobal("rendering/driver/driver_name"); if (videoDriver.equals("Vulkan")) { mRenderView = new GodotVulkanRenderView(activity, this); } else { @@ -301,7 +341,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { v.vibrate(VibrationEffect.createOneShot(durationMs, VibrationEffect.DEFAULT_AMPLITUDE)); } else { - //deprecated in API 26 + // deprecated in API 26 v.vibrate(durationMs); } } @@ -356,6 +396,21 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC @CallSuper protected String[] getCommandLine() { + String[] original = parseCommandLine(); + String[] updated; + List<String> hostCommandLine = godotHost != null ? godotHost.getCommandLine() : null; + if (hostCommandLine == null || hostCommandLine.isEmpty()) { + updated = original; + } else { + updated = Arrays.copyOf(original, original.length + hostCommandLine.size()); + for (int i = 0; i < hostCommandLine.size(); i++) { + updated[original.length + i] = hostCommandLine.get(i); + } + } + return updated; + } + + private String[] parseCommandLine() { InputStream is; try { is = getActivity().getAssets().open("_cl_"); @@ -473,7 +528,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC mClipboard = (ClipboardManager)activity.getSystemService(Context.CLIPBOARD_SERVICE); pluginRegistry = GodotPluginRegistry.initializePluginRegistry(this); - //check for apk expansion API + // check for apk expansion API boolean md5mismatch = false; command_line = getCommandLine(); String main_pack_md5 = null; @@ -529,9 +584,9 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC command_line = new_args.toArray(new String[new_args.size()]); } if (use_apk_expansion && main_pack_md5 != null && main_pack_key != null) { - //check that environment is ok! + // check that environment is ok! if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - //show popup and die + // show popup and die } // Build the full path to the app's expansion files diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java new file mode 100644 index 0000000000..317fd13535 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* GodotHost.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +package org.godotengine.godot; + +import java.util.Collections; +import java.util.List; + +/** + * Denotate a component (e.g: Activity, Fragment) that hosts the {@link Godot} fragment. + */ +public interface GodotHost { + /** + * Provides a set of command line parameters to setup the engine. + */ + default List<String> getCommandLine() { + return Collections.emptyList(); + } + + /** + * Invoked on the render thread when the Godot setup is complete. + */ + default void onGodotSetupCompleted() {} + + /** + * Invoked on the render thread when the Godot main loop has started. + */ + default void onGodotMainLoopStarted() {} +} 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 993f0e9127..6c8a3d4219 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 @@ -47,6 +47,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -109,31 +110,9 @@ public abstract class GodotPlugin { * This method is invoked on the render thread. */ public final void onRegisterPluginWithGodotNative() { - registeredSignals.putAll(registerPluginWithGodotNative(this, new GodotPluginInfoProvider() { - @NonNull - @Override - public String getPluginName() { - return GodotPlugin.this.getPluginName(); - } - - @NonNull - @Override - public List<String> getPluginMethods() { - return GodotPlugin.this.getPluginMethods(); - } - - @NonNull - @Override - public Set<SignalInfo> getPluginSignals() { - return GodotPlugin.this.getPluginSignals(); - } - - @NonNull - @Override - public Set<String> getPluginGDNativeLibrariesPaths() { - return GodotPlugin.this.getPluginGDNativeLibrariesPaths(); - } - })); + registeredSignals.putAll( + registerPluginWithGodotNative(this, getPluginName(), getPluginMethods(), getPluginSignals(), + getPluginGDNativeLibrariesPaths())); } /** @@ -141,23 +120,41 @@ public abstract class GodotPlugin { * * This method must be invoked on the render thread. */ - public static Map<String, SignalInfo> registerPluginWithGodotNative(Object pluginObject, GodotPluginInfoProvider pluginInfoProvider) { - nativeRegisterSingleton(pluginInfoProvider.getPluginName(), pluginObject); + public static void registerPluginWithGodotNative(Object pluginObject, + GodotPluginInfoProvider pluginInfoProvider) { + registerPluginWithGodotNative(pluginObject, pluginInfoProvider.getPluginName(), + Collections.emptyList(), pluginInfoProvider.getPluginSignals(), + pluginInfoProvider.getPluginGDNativeLibrariesPaths()); + + // Notify that registration is complete. + pluginInfoProvider.onPluginRegistered(); + } + private static Map<String, SignalInfo> registerPluginWithGodotNative(Object pluginObject, + String pluginName, List<String> pluginMethods, Set<SignalInfo> pluginSignals, + Set<String> pluginGDNativeLibrariesPaths) { + nativeRegisterSingleton(pluginName, pluginObject); + + Set<Method> filteredMethods = new HashSet<>(); Class clazz = pluginObject.getClass(); + Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { - boolean found = false; - - for (String s : pluginInfoProvider.getPluginMethods()) { - if (s.equals(method.getName())) { - found = true; - break; + // Check if the method is annotated with {@link UsedByGodot}. + if (method.getAnnotation(UsedByGodot.class) != null) { + filteredMethods.add(method); + } else { + // For backward compatibility, process the methods from the given <pluginMethods> argument. + for (String methodName : pluginMethods) { + if (methodName.equals(method.getName())) { + filteredMethods.add(method); + break; + } } } - if (!found) - continue; + } + for (Method method : filteredMethods) { List<String> ptr = new ArrayList<>(); Class[] paramTypes = method.getParameterTypes(); @@ -168,21 +165,20 @@ public abstract class GodotPlugin { String[] pt = new String[ptr.size()]; ptr.toArray(pt); - nativeRegisterMethod(pluginInfoProvider.getPluginName(), method.getName(), method.getReturnType().getName(), pt); + nativeRegisterMethod(pluginName, method.getName(), method.getReturnType().getName(), pt); } // Register the signals for this plugin. Map<String, SignalInfo> registeredSignals = new HashMap<>(); - for (SignalInfo signalInfo : pluginInfoProvider.getPluginSignals()) { + for (SignalInfo signalInfo : pluginSignals) { String signalName = signalInfo.getName(); - nativeRegisterSignal(pluginInfoProvider.getPluginName(), signalName, signalInfo.getParamTypesNames()); + nativeRegisterSignal(pluginName, signalName, signalInfo.getParamTypesNames()); registeredSignals.put(signalName, signalInfo); } // Get the list of gdnative libraries to register. - Set<String> gdnativeLibrariesPaths = pluginInfoProvider.getPluginGDNativeLibrariesPaths(); - if (!gdnativeLibrariesPaths.isEmpty()) { - nativeRegisterGDNativeLibraries(gdnativeLibrariesPaths.toArray(new String[0])); + if (!pluginGDNativeLibrariesPaths.isEmpty()) { + nativeRegisterGDNativeLibraries(pluginGDNativeLibrariesPaths.toArray(new String[0])); } return registeredSignals; @@ -236,6 +232,11 @@ public abstract class GodotPlugin { public boolean onMainBackPressed() { return false; } /** + * Invoked on the render thread when the Godot setup is complete. + */ + public void onGodotSetupCompleted() {} + + /** * Invoked on the render thread when the Godot main loop has started. */ public void onGodotMainLoopStarted() {} @@ -282,8 +283,11 @@ public abstract class GodotPlugin { /** * Returns the list of methods to be exposed to Godot. + * + * @deprecated Used the {@link UsedByGodot} annotation instead. */ @NonNull + @Deprecated public List<String> getPluginMethods() { return Collections.emptyList(); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java index c3084b036e..09366384c2 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java @@ -32,7 +32,7 @@ package org.godotengine.godot.plugin; import androidx.annotation.NonNull; -import java.util.List; +import java.util.Collections; import java.util.Set; /** @@ -46,16 +46,12 @@ public interface GodotPluginInfoProvider { String getPluginName(); /** - * Returns the list of methods to be exposed to Godot. - */ - @NonNull - List<String> getPluginMethods(); - - /** * Returns the list of signals to be exposed to Godot. */ @NonNull - Set<SignalInfo> getPluginSignals(); + default Set<SignalInfo> getPluginSignals() { + return Collections.emptySet(); + } /** * Returns the paths for the plugin's gdnative libraries (if any). @@ -63,5 +59,14 @@ public interface GodotPluginInfoProvider { * The paths must be relative to the 'assets' directory and point to a '*.gdnlib' file. */ @NonNull - Set<String> getPluginGDNativeLibrariesPaths(); + default Set<String> getPluginGDNativeLibrariesPaths() { + return Collections.emptySet(); + } + + /** + * This is invoked on the render thread when the plugin described by this instance has been + * registered. + */ + default void onPluginRegistered() { + } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/UsedByGodot.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/UsedByGodot.java new file mode 100644 index 0000000000..04c091d944 --- /dev/null +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/UsedByGodot.java @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* UsedByGodot.java */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +package org.godotengine.godot.plugin; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to indicate a method is being invoked from the Godot game logic. + * + * At runtime, annotated plugin methods are detected and automatically registered. + */ +@Target({ ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface UsedByGodot {} diff --git a/platform/android/java/nativeSrcsConfigs/README.md b/platform/android/java/nativeSrcsConfigs/README.md index e48505ccda..9d884415cc 100644 --- a/platform/android/java/nativeSrcsConfigs/README.md +++ b/platform/android/java/nativeSrcsConfigs/README.md @@ -1,4 +1,4 @@ ## Native sources configs -This is a non functional Android library used to provide Android Studio editor support to the Godot project native files. +This is a non-functional Android library used to provide Android Studio editor support to the Godot project native files. Nothing else should be added to this library. diff --git a/platform/android/java/nativeSrcsConfigs/build.gradle b/platform/android/java/nativeSrcsConfigs/build.gradle index 66077060ea..158bb2b98e 100644 --- a/platform/android/java/nativeSrcsConfigs/build.gradle +++ b/platform/android/java/nativeSrcsConfigs/build.gradle @@ -20,9 +20,6 @@ android { packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' - - // Should be uncommented for development purpose within Android Studio - // doNotStrip '**/*.so' } sourceSets { diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp index ab03599dc3..f49b0e843a 100644 --- a/platform/android/java_class_wrapper.cpp +++ b/platform/android/java_class_wrapper.cpp @@ -38,6 +38,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, return false; JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, false); MethodInfo *method = nullptr; for (List<MethodInfo>::Element *E = M->get().front(); E; E = E->next()) { @@ -965,6 +966,7 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { return class_cache[p_class]; JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, Ref<JavaClass>()); jclass bclass = env->FindClass(p_class.utf8().get_data()); ERR_FAIL_COND_V(!bclass, Ref<JavaClass>()); @@ -1149,6 +1151,7 @@ JavaClassWrapper::JavaClassWrapper(jobject p_activity) { singleton = this; JNIEnv *env = get_jni_env(); + ERR_FAIL_COND(env == nullptr); jclass activityClass = env->FindClass("android/app/Activity"); jmethodID getClassLoader = env->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;"); diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp index 41201db32b..ec3b6f8ac0 100644 --- a/platform/android/java_godot_io_wrapper.cpp +++ b/platform/android/java_godot_io_wrapper.cpp @@ -73,6 +73,7 @@ jobject GodotIOJavaWrapper::get_instance() { Error GodotIOJavaWrapper::open_uri(const String &p_uri) { if (_open_URI) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, ERR_UNAVAILABLE); jstring jStr = env->NewStringUTF(p_uri.utf8().get_data()); return env->CallIntMethod(godot_io_instance, _open_URI, jStr) ? ERR_CANT_OPEN : OK; } else { @@ -83,6 +84,7 @@ Error GodotIOJavaWrapper::open_uri(const String &p_uri) { 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_data_dir); return jstring_to_string(s, env); } else { @@ -93,6 +95,7 @@ String GodotIOJavaWrapper::get_user_data_dir() { String GodotIOJavaWrapper::get_locale() { if (_get_locale) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, String()); jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_locale); return jstring_to_string(s, env); } else { @@ -103,6 +106,7 @@ String GodotIOJavaWrapper::get_locale() { String GodotIOJavaWrapper::get_model() { if (_get_model) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, String()); jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_model); return jstring_to_string(s, env); } else { @@ -113,6 +117,7 @@ String GodotIOJavaWrapper::get_model() { int GodotIOJavaWrapper::get_screen_dpi() { if (_get_screen_DPI) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, 160); return env->CallIntMethod(godot_io_instance, _get_screen_DPI); } else { return 160; @@ -122,6 +127,7 @@ int GodotIOJavaWrapper::get_screen_dpi() { void GodotIOJavaWrapper::screen_get_usable_rect(int (&p_rect_xywh)[4]) { if (_screen_get_usable_rect) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND(env == nullptr); jintArray returnArray = (jintArray)env->CallObjectMethod(godot_io_instance, _screen_get_usable_rect); ERR_FAIL_COND(env->GetArrayLength(returnArray) != 4); jint *arrayBody = env->GetIntArrayElements(returnArray, JNI_FALSE); @@ -135,6 +141,7 @@ void GodotIOJavaWrapper::screen_get_usable_rect(int (&p_rect_xywh)[4]) { String GodotIOJavaWrapper::get_unique_id() { if (_get_unique_id) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, String()); jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_unique_id); return jstring_to_string(s, env); } else { @@ -149,6 +156,7 @@ bool GodotIOJavaWrapper::has_vk() { void GodotIOJavaWrapper::show_vk(const String &p_existing, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) { if (_show_keyboard) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND(env == nullptr); jstring jStr = env->NewStringUTF(p_existing.utf8().get_data()); env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_multiline, p_max_input_length, p_cursor_start, p_cursor_end); } @@ -157,6 +165,7 @@ void GodotIOJavaWrapper::show_vk(const String &p_existing, bool p_multiline, int void GodotIOJavaWrapper::hide_vk() { if (_hide_keyboard) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND(env == nullptr); env->CallVoidMethod(godot_io_instance, _hide_keyboard); } } @@ -164,6 +173,7 @@ void GodotIOJavaWrapper::hide_vk() { void GodotIOJavaWrapper::set_screen_orientation(int p_orient) { if (_set_screen_orientation) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND(env == nullptr); env->CallVoidMethod(godot_io_instance, _set_screen_orientation, p_orient); } } @@ -171,6 +181,7 @@ void GodotIOJavaWrapper::set_screen_orientation(int p_orient) { int GodotIOJavaWrapper::get_screen_orientation() { if (_get_screen_orientation) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, 0); return env->CallIntMethod(godot_io_instance, _get_screen_orientation); } else { return 0; @@ -180,6 +191,7 @@ int GodotIOJavaWrapper::get_screen_orientation() { String GodotIOJavaWrapper::get_system_dir(int p_dir) { 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); return jstring_to_string(s, env); } else { diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index bb22162879..0c342dc280 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -127,9 +127,11 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc if (p_cmdline) { cmdlen = env->GetArrayLength(p_cmdline); if (cmdlen) { - cmdline = (const char **)malloc((cmdlen + 1) * sizeof(const char *)); + cmdline = (const char **)memalloc((cmdlen + 1) * sizeof(const char *)); + ERR_FAIL_NULL_MSG(cmdline, "Out of memory."); cmdline[cmdlen] = nullptr; - j_cmdline = (jstring *)malloc(cmdlen * sizeof(jstring)); + j_cmdline = (jstring *)memalloc(cmdlen * sizeof(jstring)); + ERR_FAIL_NULL_MSG(j_cmdline, "Out of memory."); for (int i = 0; i < cmdlen; i++) { jstring string = (jstring)env->GetObjectArrayElement(p_cmdline, i); @@ -147,13 +149,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc for (int i = 0; i < cmdlen; ++i) { env->ReleaseStringUTFChars(j_cmdline[i], cmdline[i]); } - free(j_cmdline); + memfree(j_cmdline); } - free(cmdline); + memfree(cmdline); } if (err != OK) { - return; //should exit instead and print the error + return; // should exit instead and print the error } java_class_wrapper = memnew(JavaClassWrapper(godot_java->get_activity())); @@ -215,9 +217,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl if (step == 1) { if (!Main::start()) { - return; //should exit instead and print the error + return; // should exit instead and print the error } + godot_java->on_godot_setup_completed(env); os_android->main_loop_begin(); godot_java->on_godot_main_loop_started(env); ++step; diff --git a/platform/android/java_godot_view_wrapper.cpp b/platform/android/java_godot_view_wrapper.cpp index 5b638300ef..6b5e44f371 100644 --- a/platform/android/java_godot_view_wrapper.cpp +++ b/platform/android/java_godot_view_wrapper.cpp @@ -34,6 +34,7 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND(env == nullptr); _godot_view = env->NewGlobalRef(godot_view); @@ -48,6 +49,8 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) { void GodotJavaViewWrapper::request_pointer_capture() { if (_request_pointer_capture != 0) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND(env == nullptr); + env->CallVoidMethod(_godot_view, _request_pointer_capture); } } @@ -55,12 +58,16 @@ void GodotJavaViewWrapper::request_pointer_capture() { void GodotJavaViewWrapper::release_pointer_capture() { if (_request_pointer_capture != 0) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND(env == nullptr); + env->CallVoidMethod(_godot_view, _release_pointer_capture); } } GodotJavaViewWrapper::~GodotJavaViewWrapper() { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND(env == nullptr); + env->DeleteGlobalRef(_godot_view); env->DeleteGlobalRef(_cls); } diff --git a/platform/android/java_godot_view_wrapper.h b/platform/android/java_godot_view_wrapper.h index 548c278292..bfb4369fb8 100644 --- a/platform/android/java_godot_view_wrapper.h +++ b/platform/android/java_godot_view_wrapper.h @@ -34,6 +34,8 @@ #include <android/log.h> #include <jni.h> +#include "string_android.h" + // Class that makes functions in java/src/org/godotengine/godot/GodotView.java callable from C++ class GodotJavaViewWrapper { private: diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 759980373a..bfd93345f3 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -74,6 +74,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ _is_activity_resumed = p_env->GetMethodID(godot_class, "isActivityResumed", "()Z"); _vibrate = p_env->GetMethodID(godot_class, "vibrate", "(I)V"); _get_input_fallback_mapping = p_env->GetMethodID(godot_class, "getInputFallbackMapping", "()Ljava/lang/String;"); + _on_godot_setup_completed = p_env->GetMethodID(godot_class, "onGodotSetupCompleted", "()V"); _on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V"); // get some Activity method pointers... @@ -93,6 +94,8 @@ jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_cl if (p_env == nullptr) p_env = get_jni_env(); + ERR_FAIL_COND_V(p_env == nullptr, nullptr); + jfieldID fid = p_env->GetStaticFieldID(godot_class, p_name, p_class); return p_env->GetStaticObjectField(godot_class, fid); } else { @@ -103,6 +106,8 @@ jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_cl jobject GodotJavaWrapper::get_class_loader() { if (_get_class_loader) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, nullptr); + return env->CallObjectMethod(activity, _get_class_loader); } else { return nullptr; @@ -114,17 +119,30 @@ GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() { return _godot_view; } JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, nullptr); + jmethodID godot_view_getter = env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;"); _godot_view = new GodotJavaViewWrapper(env->CallObjectMethod(godot_instance, godot_view_getter)); return _godot_view; } void GodotJavaWrapper::on_video_init(JNIEnv *p_env) { - if (_on_video_init) + if (_on_video_init) { if (p_env == nullptr) p_env = get_jni_env(); + ERR_FAIL_COND(p_env == nullptr); - p_env->CallVoidMethod(godot_instance, _on_video_init); + p_env->CallVoidMethod(godot_instance, _on_video_init); + } +} + +void GodotJavaWrapper::on_godot_setup_completed(JNIEnv *p_env) { + if (_on_godot_setup_completed) { + if (p_env == nullptr) { + p_env = get_jni_env(); + } + p_env->CallVoidMethod(godot_instance, _on_godot_setup_completed); + } } void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) { @@ -132,29 +150,36 @@ void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) { if (p_env == nullptr) { p_env = get_jni_env(); } + ERR_FAIL_COND(p_env == nullptr); + p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started); } - p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started); } void GodotJavaWrapper::restart(JNIEnv *p_env) { - if (_restart) + if (_restart) { if (p_env == nullptr) p_env = get_jni_env(); + ERR_FAIL_COND(p_env == nullptr); - p_env->CallVoidMethod(godot_instance, _restart); + p_env->CallVoidMethod(godot_instance, _restart); + } } void GodotJavaWrapper::force_quit(JNIEnv *p_env) { - if (_finish) + if (_finish) { if (p_env == nullptr) p_env = get_jni_env(); + ERR_FAIL_COND(p_env == nullptr); - p_env->CallVoidMethod(godot_instance, _finish); + p_env->CallVoidMethod(godot_instance, _finish); + } } void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) { if (_set_keep_screen_on) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND(env == nullptr); + env->CallVoidMethod(godot_instance, _set_keep_screen_on, p_enabled); } } @@ -162,6 +187,8 @@ void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) { void GodotJavaWrapper::alert(const String &p_message, const String &p_title) { if (_alert) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND(env == nullptr); + jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data()); jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data()); env->CallVoidMethod(godot_instance, _alert, jStrMessage, jStrTitle); @@ -170,6 +197,8 @@ void GodotJavaWrapper::alert(const String &p_message, const String &p_title) { int GodotJavaWrapper::get_gles_version_code() { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, 0); + if (_get_GLES_version_code) { return env->CallIntMethod(godot_instance, _get_GLES_version_code); } @@ -184,6 +213,8 @@ bool GodotJavaWrapper::has_get_clipboard() { String GodotJavaWrapper::get_clipboard() { if (_get_clipboard) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, String()); + jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard); return jstring_to_string(s, env); } else { @@ -194,6 +225,8 @@ String GodotJavaWrapper::get_clipboard() { String GodotJavaWrapper::get_input_fallback_mapping() { if (_get_input_fallback_mapping) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, String()); + jstring fallback_mapping = (jstring)env->CallObjectMethod(godot_instance, _get_input_fallback_mapping); return jstring_to_string(fallback_mapping, env); } else { @@ -208,6 +241,8 @@ bool GodotJavaWrapper::has_set_clipboard() { void GodotJavaWrapper::set_clipboard(const String &p_text) { if (_set_clipboard) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND(env == nullptr); + jstring jStr = env->NewStringUTF(p_text.utf8().get_data()); env->CallVoidMethod(godot_instance, _set_clipboard, jStr); } @@ -216,6 +251,8 @@ void GodotJavaWrapper::set_clipboard(const String &p_text) { bool GodotJavaWrapper::request_permission(const String &p_name) { if (_request_permission) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, false); + jstring jStrName = env->NewStringUTF(p_name.utf8().get_data()); return env->CallBooleanMethod(godot_instance, _request_permission, jStrName); } else { @@ -226,6 +263,8 @@ bool GodotJavaWrapper::request_permission(const String &p_name) { bool GodotJavaWrapper::request_permissions() { if (_request_permissions) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, false); + return env->CallBooleanMethod(godot_instance, _request_permissions); } else { return false; @@ -236,6 +275,8 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const { Vector<String> permissions_list; if (_get_granted_permissions) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, permissions_list); + jobject permissions_object = env->CallObjectMethod(godot_instance, _get_granted_permissions); jobjectArray *arr = reinterpret_cast<jobjectArray *>(&permissions_object); @@ -254,6 +295,8 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const { void GodotJavaWrapper::init_input_devices() { if (_init_input_devices) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND(env == nullptr); + env->CallVoidMethod(godot_instance, _init_input_devices); } } @@ -261,6 +304,8 @@ void GodotJavaWrapper::init_input_devices() { jobject GodotJavaWrapper::get_surface() { if (_get_surface) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, nullptr); + return env->CallObjectMethod(godot_instance, _get_surface); } else { return nullptr; @@ -270,6 +315,8 @@ jobject GodotJavaWrapper::get_surface() { bool GodotJavaWrapper::is_activity_resumed() { if (_is_activity_resumed) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND_V(env == nullptr, false); + return env->CallBooleanMethod(godot_instance, _is_activity_resumed); } else { return false; @@ -279,6 +326,8 @@ bool GodotJavaWrapper::is_activity_resumed() { void GodotJavaWrapper::vibrate(int p_duration_ms) { if (_vibrate) { JNIEnv *env = get_jni_env(); + ERR_FAIL_COND(env == nullptr); + env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms); } } diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index 99a60dffa1..0e20747a16 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -66,6 +66,7 @@ private: jmethodID _is_activity_resumed = 0; jmethodID _vibrate = 0; jmethodID _get_input_fallback_mapping = 0; + jmethodID _on_godot_setup_completed = 0; jmethodID _on_godot_main_loop_started = 0; jmethodID _get_class_loader = 0; @@ -80,6 +81,7 @@ public: GodotJavaViewWrapper *get_godot_view(); void on_video_init(JNIEnv *p_env = nullptr); + void on_godot_setup_completed(JNIEnv *p_env = nullptr); void on_godot_main_loop_started(JNIEnv *p_env = nullptr); void restart(JNIEnv *p_env = nullptr); void force_quit(JNIEnv *p_env = nullptr); diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp index afcc7294f2..ba379c8d43 100644 --- a/platform/android/thread_jandroid.cpp +++ b/platform/android/thread_jandroid.cpp @@ -30,17 +30,34 @@ #include "thread_jandroid.h" +#include <android/log.h> + #include "core/os/thread.h" static JavaVM *java_vm = nullptr; static thread_local JNIEnv *env = nullptr; +// The logic here need to improve, init_thread/term_tread are designed to work with Thread::callback +// Calling init_thread from setup_android_thread and get_jni_env to setup an env we're keeping and not detaching +// could cause issues on app termination. +// +// We should be making sure that any thread started calls a nice cleanup function when it's done, +// especially now that we use many more threads. + static void init_thread() { + if (env) { + // thread never detached! just keep using... + return; + } + java_vm->AttachCurrentThread(&env, nullptr); } static void term_thread() { java_vm->DetachCurrentThread(); + + // this is no longer valid, must called init_thread to re-establish + env = nullptr; } void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env) { @@ -50,9 +67,17 @@ void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env) { } void setup_android_thread() { - init_thread(); + if (!env) { + // !BAS! see remarks above + init_thread(); + } } JNIEnv *get_jni_env() { + if (!env) { + // !BAS! see remarks above + init_thread(); + } + return env; } diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py index 17796beb6f..cf358e0878 100644 --- a/platform/iphone/detect.py +++ b/platform/iphone/detect.py @@ -54,7 +54,7 @@ def configure(env): if env["optimize"] == "speed": # optimize for speed (default) env.Append(CCFLAGS=["-O2", "-ftree-vectorize", "-fomit-frame-pointer"]) env.Append(LINKFLAGS=["-O2"]) - else: # optimize for size + elif env["optimize"] == "size": # optimize for size env.Append(CCFLAGS=["-Os", "-ftree-vectorize"]) env.Append(LINKFLAGS=["-Os"]) diff --git a/platform/iphone/godot_view.mm b/platform/iphone/godot_view.mm index 887297848e..468fa2928a 100644 --- a/platform/iphone/godot_view.mm +++ b/platform/iphone/godot_view.mm @@ -39,6 +39,7 @@ #import <CoreMotion/CoreMotion.h> static const int max_touches = 8; +static const float earth_gravity = 9.80665; @interface GodotView () { UITouch *godot_touches[max_touches]; @@ -402,10 +403,19 @@ static const int max_touches = 8; // https://developer.apple.com/reference/coremotion/cmmotionmanager?language=objc // Apple splits our accelerometer date into a gravity and user movement - // component. We add them back together + // component. We add them back together. CMAcceleration gravity = self.motionManager.deviceMotion.gravity; CMAcceleration acceleration = self.motionManager.deviceMotion.userAcceleration; + // To be consistent with Android we convert the unit of measurement from g (Earth's gravity) + // to m/s^2. + gravity.x *= earth_gravity; + gravity.y *= earth_gravity; + gravity.z *= earth_gravity; + acceleration.x *= earth_gravity; + acceleration.y *= earth_gravity; + acceleration.z *= earth_gravity; + ///@TODO We don't seem to be getting data here, is my device broken or /// is this code incorrect? CMMagneticField magnetic = self.motionManager.deviceMotion.magneticField.field; diff --git a/platform/iphone/joypad_iphone.mm b/platform/iphone/joypad_iphone.mm index a0f0eee5d3..45842b38aa 100644 --- a/platform/iphone/joypad_iphone.mm +++ b/platform/iphone/joypad_iphone.mm @@ -287,7 +287,7 @@ void JoypadIPhone::start_processing() { gamepad.dpad.right.isPressed); }; - Input::JoyAxis jx; + Input::JoyAxisValue jx; jx.min = -1; if (element == gamepad.leftThumbstick) { jx.value = gamepad.leftThumbstick.xAxis.value; diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index e80ef374ec..ac8d8de7e0 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -64,21 +64,21 @@ def configure(env): sys.exit(255) ## Build type - if env["target"] == "release": + if env["target"].startswith("release"): # Use -Os to prioritize optimizing for reduced file size. This is # particularly valuable for the web platform because it directly # decreases download time. # -Os reduces file size by around 5 MiB over -O3. -Oz only saves about # 100 KiB over -Os, which does not justify the negative impact on # run-time performance. - env.Append(CCFLAGS=["-Os"]) - env.Append(LINKFLAGS=["-Os"]) - elif env["target"] == "release_debug": - env.Append(CCFLAGS=["-Os"]) - env.Append(LINKFLAGS=["-Os"]) - env.Append(CPPDEFINES=["DEBUG_ENABLED"]) - # Retain function names for backtraces at the cost of file size. - env.Append(LINKFLAGS=["--profiling-funcs"]) + if env["optimize"] != "none": + env.Append(CCFLAGS=["-Os"]) + env.Append(LINKFLAGS=["-Os"]) + + if env["target"] == "release_debug": + env.Append(CPPDEFINES=["DEBUG_ENABLED"]) + # Retain function names for backtraces at the cost of file size. + env.Append(LINKFLAGS=["--profiling-funcs"]) else: # "debug" env.Append(CPPDEFINES=["DEBUG_ENABLED"]) env.Append(CCFLAGS=["-O1", "-g"]) diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp index a605f22e16..fa6f5c1e9e 100644 --- a/platform/javascript/display_server_javascript.cpp +++ b/platform/javascript/display_server_javascript.cpp @@ -189,19 +189,19 @@ EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const E switch (p_event->button) { case DOM_BUTTON_LEFT: - ev->set_button_index(BUTTON_LEFT); + ev->set_button_index(MOUSE_BUTTON_LEFT); break; case DOM_BUTTON_MIDDLE: - ev->set_button_index(BUTTON_MIDDLE); + ev->set_button_index(MOUSE_BUTTON_MIDDLE); break; case DOM_BUTTON_RIGHT: - ev->set_button_index(BUTTON_RIGHT); + ev->set_button_index(MOUSE_BUTTON_RIGHT); break; case DOM_BUTTON_XBUTTON1: - ev->set_button_index(BUTTON_XBUTTON1); + ev->set_button_index(MOUSE_BUTTON_XBUTTON1); break; case DOM_BUTTON_XBUTTON2: - ev->set_button_index(BUTTON_XBUTTON2); + ev->set_button_index(MOUSE_BUTTON_XBUTTON2); break; default: return false; @@ -341,7 +341,7 @@ void DisplayServerJavaScript::cursor_set_custom_image(const RES &p_cursor, Curso Rect2 atlas_rect; if (texture.is_valid()) { - image = texture->get_data(); + image = texture->get_image(); } if (!image.is_valid() && atlas_texture.is_valid()) { @@ -364,7 +364,7 @@ void DisplayServerJavaScript::cursor_set_custom_image(const RES &p_cursor, Curso ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); - image = texture->get_data(); + image = texture->get_image(); ERR_FAIL_COND(!image.is_valid()); @@ -461,13 +461,13 @@ EM_BOOL DisplayServerJavaScript::wheel_callback(int p_event_type, const Emscript ev->set_metakey(input->is_key_pressed(KEY_META)); if (p_event->deltaY < 0) - ev->set_button_index(BUTTON_WHEEL_UP); + ev->set_button_index(MOUSE_BUTTON_WHEEL_UP); else if (p_event->deltaY > 0) - ev->set_button_index(BUTTON_WHEEL_DOWN); + ev->set_button_index(MOUSE_BUTTON_WHEEL_DOWN); else if (p_event->deltaX > 0) - ev->set_button_index(BUTTON_WHEEL_LEFT); + ev->set_button_index(MOUSE_BUTTON_WHEEL_LEFT); else if (p_event->deltaX < 0) - ev->set_button_index(BUTTON_WHEEL_RIGHT); + ev->set_button_index(MOUSE_BUTTON_WHEEL_RIGHT); else return false; @@ -536,6 +536,43 @@ bool DisplayServerJavaScript::screen_is_touchscreen(int p_screen) const { return godot_js_display_touchscreen_is_available(); } +// Virtual Keybaord +void DisplayServerJavaScript::vk_input_text_callback(const char *p_text, int p_cursor) { + DisplayServerJavaScript *ds = DisplayServerJavaScript::get_singleton(); + if (!ds || ds->input_text_callback.is_null()) { + return; + } + // Call input_text + Variant event = String(p_text); + Variant *eventp = &event; + Variant ret; + Callable::CallError ce; + ds->input_text_callback.call((const Variant **)&eventp, 1, ret, ce); + // Insert key right to reach position. + Input *input = Input::get_singleton(); + Ref<InputEventKey> k; + for (int i = 0; i < p_cursor; i++) { + k.instance(); + k->set_pressed(true); + k->set_echo(false); + k->set_keycode(KEY_RIGHT); + input->parse_input_event(k); + k.instance(); + k->set_pressed(false); + k->set_echo(false); + k->set_keycode(KEY_RIGHT); + input->parse_input_event(k); + } +} + +void DisplayServerJavaScript::virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) { + godot_js_display_vk_show(p_existing_text.utf8().get_data(), p_multiline, p_cursor_start, p_cursor_end); +} + +void DisplayServerJavaScript::virtual_keyboard_hide() { + godot_js_display_vk_hide(); +} + // Gamepad void DisplayServerJavaScript::gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid) { Input *input = Input::get_singleton(); @@ -564,7 +601,7 @@ void DisplayServerJavaScript::process_joypads() { // Buttons 6 and 7 in the standard mapping need to be // axis to be handled as JOY_AXIS_TRIGGER by Godot. if (s_standard && (b == 6 || b == 7)) { - Input::JoyAxis joy_axis; + Input::JoyAxisValue joy_axis; joy_axis.min = 0; joy_axis.value = value; int a = b == 6 ? JOY_AXIS_TRIGGER_LEFT : JOY_AXIS_TRIGGER_RIGHT; @@ -574,7 +611,7 @@ void DisplayServerJavaScript::process_joypads() { } } for (int a = 0; a < s_axes_num; a++) { - Input::JoyAxis joy_axis; + Input::JoyAxisValue joy_axis; joy_axis.min = -1; joy_axis.value = s_axes[a]; input->joy_axis(idx, a, joy_axis); @@ -683,7 +720,7 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive godot_js_config_canvas_id_get(canvas_id, 256); // Handle contextmenu, webglcontextlost - godot_js_display_setup_canvas(p_resolution.x, p_resolution.y, p_mode == WINDOW_MODE_FULLSCREEN); + godot_js_display_setup_canvas(p_resolution.x, p_resolution.y, p_mode == WINDOW_MODE_FULLSCREEN, OS::get_singleton()->is_hidpi_allowed() ? 1 : 0); // Check if it's windows. swap_cancel_ok = godot_js_display_is_swap_ok_cancel() == 1; @@ -764,6 +801,7 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive godot_js_display_paste_cb(update_clipboard_callback); godot_js_display_drop_files_cb(drop_files_js_callback); godot_js_display_gamepad_cb(&DisplayServerJavaScript::gamepad_callback); + godot_js_display_vk_cb(&vk_input_text_callback); Input::get_singleton()->set_event_dispatch_function(_dispatch_input_event); } @@ -793,7 +831,8 @@ bool DisplayServerJavaScript::has_feature(Feature p_feature) const { //case FEATURE_WINDOW_TRANSPARENCY: //case FEATURE_KEEP_SCREEN_ON: //case FEATURE_ORIENTATION: - //case FEATURE_VIRTUAL_KEYBOARD: + case FEATURE_VIRTUAL_KEYBOARD: + return godot_js_display_vk_available() != 0; default: return false; } @@ -866,7 +905,7 @@ void DisplayServerJavaScript::window_set_input_event_callback(const Callable &p_ } void DisplayServerJavaScript::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) { - input_text_callback = p_callable; // TODO unused... do I need this? + input_text_callback = p_callable; } void DisplayServerJavaScript::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) { diff --git a/platform/javascript/display_server_javascript.h b/platform/javascript/display_server_javascript.h index 47e25ab2a0..ece38f1a95 100644 --- a/platform/javascript/display_server_javascript.h +++ b/platform/javascript/display_server_javascript.h @@ -75,6 +75,8 @@ private: static EM_BOOL keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data); static EM_BOOL keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data); + static void vk_input_text_callback(const char *p_text, int p_cursor); + static EM_BOOL mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data); static EM_BOOL mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data); @@ -135,6 +137,9 @@ public: int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; + void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), bool p_multiline = false, int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1) override; + void virtual_keyboard_hide() override; + // windows Vector<DisplayServer::WindowID> get_window_list() const override; WindowID get_window_at_screen_position(const Point2i &p_position) const override; diff --git a/platform/javascript/emscripten_helpers.py b/platform/javascript/emscripten_helpers.py index 04fbba8a41..b3b15a1574 100644 --- a/platform/javascript/emscripten_helpers.py +++ b/platform/javascript/emscripten_helpers.py @@ -21,7 +21,11 @@ def get_build_version(): name = "custom_build" if os.getenv("BUILD_NAME") != None: name = os.getenv("BUILD_NAME") - return "%d.%d.%d.%s.%s" % (version.major, version.minor, version.patch, version.status, name) + v = "%d.%d" % (version.major, version.minor) + if version.patch > 0: + v += ".%d" % version.patch + v += ".%s.%s" % (version.status, name) + return v def create_engine_file(env, target, source, externs): diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index 46d0458ca1..3f04bedea2 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -297,6 +297,7 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re } Dictionary config; config["canvasResizePolicy"] = p_preset->get("html/canvas_resize_policy"); + config["experimentalVK"] = p_preset->get("html/experimental_virtual_keyboard"); config["gdnativeLibs"] = libs; config["executable"] = p_name; config["args"] = args; @@ -352,6 +353,7 @@ void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_op r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "*.html"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "html/canvas_resize_policy", PROPERTY_HINT_ENUM, "None,Project,Adaptive"), 2)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/experimental_virtual_keyboard"), false)); } String EditorExportPlatformJavaScript::get_name() const { @@ -548,7 +550,7 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese if (f) { file_sizes[pck_path.get_file()] = (uint64_t)f->get_len(); memdelete(f); - f = NULL; + f = nullptr; } _fix_html(html, p_preset, p_path.get_file().get_basename(), p_debug, p_flags, shared_objects, file_sizes); f = FileAccess::open(p_path, FileAccess::WRITE); diff --git a/platform/javascript/godot_js.h b/platform/javascript/godot_js.h index 5aa8677a54..8927a83cb3 100644 --- a/platform/javascript/godot_js.h +++ b/platform/javascript/godot_js.h @@ -71,6 +71,7 @@ extern int godot_js_display_fullscreen_exit(); extern void godot_js_display_compute_position(int p_x, int p_y, int32_t *r_x, int32_t *r_y); extern void godot_js_display_window_title_set(const char *p_text); extern void godot_js_display_window_icon_set(const uint8_t *p_ptr, int p_len); +extern int godot_js_display_has_webgl(int p_version); // Display clipboard extern int godot_js_display_clipboard_set(const char *p_text); @@ -92,7 +93,14 @@ extern int godot_js_display_gamepad_sample_get(int p_idx, float r_btns[16], int3 extern void godot_js_display_notification_cb(void (*p_callback)(int p_notification), int p_enter, int p_exit, int p_in, int p_out); extern void godot_js_display_paste_cb(void (*p_callback)(const char *p_text)); extern void godot_js_display_drop_files_cb(void (*p_callback)(char **p_filev, int p_filec)); -extern void godot_js_display_setup_canvas(int p_width, int p_height, int p_fullscreen); +extern void godot_js_display_setup_canvas(int p_width, int p_height, int p_fullscreen, int p_hidpi); + +// Display Virtual Keyboard +extern int godot_js_display_vk_available(); +extern void godot_js_display_vk_cb(void (*p_input)(const char *p_text, int p_cursor)); +extern void godot_js_display_vk_show(const char *p_text, int p_multiline, int p_start, int p_end); +extern void godot_js_display_vk_hide(); + #ifdef __cplusplus } #endif diff --git a/platform/javascript/js/engine/config.js b/platform/javascript/js/engine/config.js index 82ff273ecf..6072782875 100644 --- a/platform/javascript/js/engine/config.js +++ b/platform/javascript/js/engine/config.js @@ -91,12 +91,25 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused- */ args: [], /** + * When enabled, this will turn on experimental virtual keyboard support on mobile. + * + * @memberof EngineConfig + * @type {boolean} + * @default + */ + experimentalVK: false, + /** * @ignore * @type {Array.<string>} */ persistentPaths: ['/userfs'], /** * @ignore + * @type {boolean} + */ + persistentDrops: false, + /** + * @ignore * @type {Array.<string>} */ gdnativeLibs: [], @@ -223,6 +236,8 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused- this.locale = parse('locale', this.locale); this.canvasResizePolicy = parse('canvasResizePolicy', this.canvasResizePolicy); this.persistentPaths = parse('persistentPaths', this.persistentPaths); + this.persistentDrops = parse('persistentDrops', this.persistentDrops); + this.experimentalVK = parse('experimentalVK', this.experimentalVK); this.gdnativeLibs = parse('gdnativeLibs', this.gdnativeLibs); this.fileSizes = parse('fileSizes', this.fileSizes); this.args = parse('args', this.args); @@ -307,6 +322,8 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused- 'canvas': this.canvas, 'canvasResizePolicy': this.canvasResizePolicy, 'locale': locale, + 'persistentDrops': this.persistentDrops, + 'virtualKeyboard': this.experimentalVK, 'onExecute': this.onExecute, 'onExit': function (p_code) { cleanup(); // We always need to call the cleanup callback to free memory. diff --git a/platform/javascript/js/engine/engine.js b/platform/javascript/js/engine/engine.js index 19a0552c8c..17a8df9e29 100644 --- a/platform/javascript/js/engine/engine.js +++ b/platform/javascript/js/engine/engine.js @@ -101,19 +101,23 @@ const Engine = (function () { } const me = this; function doInit(promise) { - return promise.then(function (response) { - return Godot(me.config.getModuleConfig(loadPath, response.clone())); - }).then(function (module) { - const paths = me.config.persistentPaths; - return module['initFS'](paths).then(function (err) { - return Promise.resolve(module); + // Care! Promise chaining is bogus with old emscripten versions. + // This caused a regression with the Mono build (which uses an older emscripten version). + // Make sure to test that when refactoring. + return new Promise(function (resolve, reject) { + promise.then(function (response) { + const cloned = new Response(response.clone().body, { 'headers': [['content-type', 'application/wasm']] }); + Godot(me.config.getModuleConfig(loadPath, cloned)).then(function (module) { + const paths = me.config.persistentPaths; + module['initFS'](paths).then(function (err) { + me.rtenv = module; + if (me.config.unloadAfterInit) { + Engine.unload(); + } + resolve(); + }); + }); }); - }).then(function (module) { - me.rtenv = module; - if (me.config.unloadAfterInit) { - Engine.unload(); - } - return Promise.resolve(); }); } preloader.setProgressFunc(this.config.onProgress); diff --git a/platform/javascript/js/engine/preloader.js b/platform/javascript/js/engine/preloader.js index 3535fdb361..564c68d264 100644 --- a/platform/javascript/js/engine/preloader.js +++ b/platform/javascript/js/engine/preloader.js @@ -1,22 +1,5 @@ const Preloader = /** @constructor */ function () { // eslint-disable-line no-unused-vars function getTrackedResponse(response, load_status) { - let clen = 0; - let compressed = false; - response.headers.forEach(function (value, header) { - const h = header.toLowerCase().trim(); - // We can't accurately compute compressed stream length. - if (h === 'content-encoding') { - compressed = true; - } else if (h === 'content-length') { - const length = parseInt(value, 10); - if (!Number.isNaN(length) && length > 0) { - clen = length; - } - } - }); - if (!compressed && clen) { - load_status.total = clen; - } function onloadprogress(reader, controller) { return reader.read().then(function (result) { if (load_status.done) { diff --git a/platform/javascript/js/libs/library_godot_display.js b/platform/javascript/js/libs/library_godot_display.js index c72b6b3691..91cab5eacc 100644 --- a/platform/javascript/js/libs/library_godot_display.js +++ b/platform/javascript/js/libs/library_godot_display.js @@ -192,33 +192,45 @@ const GodotDisplayDragDrop = { GodotDisplayDragDrop.promises = []; GodotDisplayDragDrop.pending_files = []; callback(drops); - const dirs = [DROP.substr(0, DROP.length - 1)]; - // Remove temporary files - files.forEach(function (file) { - FS.unlink(file); - let dir = file.replace(DROP, ''); - let idx = dir.lastIndexOf('/'); - while (idx > 0) { - dir = dir.substr(0, idx); - if (dirs.indexOf(DROP + dir) === -1) { - dirs.push(DROP + dir); - } - idx = dir.lastIndexOf('/'); - } - }); - // Remove dirs. - dirs.sort(function (a, b) { - const al = (a.match(/\//g) || []).length; - const bl = (b.match(/\//g) || []).length; - if (al > bl) { - return -1; - } else if (al < bl) { - return 1; + if (GodotConfig.persistent_drops) { + // Delay removal at exit. + GodotOS.atexit(function (resolve, reject) { + GodotDisplayDragDrop.remove_drop(files, DROP); + resolve(); + }); + } else { + GodotDisplayDragDrop.remove_drop(files, DROP); + } + }); + }, + + remove_drop: function (files, drop_path) { + const dirs = [drop_path.substr(0, drop_path.length - 1)]; + // Remove temporary files + files.forEach(function (file) { + FS.unlink(file); + let dir = file.replace(drop_path, ''); + let idx = dir.lastIndexOf('/'); + while (idx > 0) { + dir = dir.substr(0, idx); + if (dirs.indexOf(drop_path + dir) === -1) { + dirs.push(drop_path + dir); } - return 0; - }).forEach(function (dir) { - FS.rmdir(dir); - }); + idx = dir.lastIndexOf('/'); + } + }); + // Remove dirs. + dirs.sort(function (a, b) { + const al = (a.match(/\//g) || []).length; + const bl = (b.match(/\//g) || []).length; + if (al > bl) { + return -1; + } else if (al < bl) { + return 1; + } + return 0; + }).forEach(function (dir) { + FS.rmdir(dir); }); }, @@ -231,6 +243,105 @@ const GodotDisplayDragDrop = { }; mergeInto(LibraryManager.library, GodotDisplayDragDrop); +const GodotDisplayVK = { + + $GodotDisplayVK__deps: ['$GodotRuntime', '$GodotConfig', '$GodotDisplayListeners'], + $GodotDisplayVK__postset: 'GodotOS.atexit(function(resolve, reject) { GodotDisplayVK.clear(); resolve(); });', + $GodotDisplayVK: { + textinput: null, + textarea: null, + + available: function () { + return GodotConfig.virtual_keyboard && 'ontouchstart' in window; + }, + + init: function (input_cb) { + function create(what) { + const elem = document.createElement(what); + elem.style.display = 'none'; + elem.style.position = 'absolute'; + elem.style.zIndex = '-1'; + elem.style.background = 'transparent'; + elem.style.padding = '0px'; + elem.style.margin = '0px'; + elem.style.overflow = 'hidden'; + elem.style.width = '0px'; + elem.style.height = '0px'; + elem.style.border = '0px'; + elem.style.outline = 'none'; + elem.readonly = true; + elem.disabled = true; + GodotDisplayListeners.add(elem, 'input', function (evt) { + const c_str = GodotRuntime.allocString(elem.value); + input_cb(c_str, elem.selectionEnd); + GodotRuntime.free(c_str); + }, false); + GodotDisplayListeners.add(elem, 'blur', function (evt) { + elem.style.display = 'none'; + elem.readonly = true; + elem.disabled = true; + }, false); + GodotConfig.canvas.insertAdjacentElement('beforebegin', elem); + return elem; + } + GodotDisplayVK.textinput = create('input'); + GodotDisplayVK.textarea = create('textarea'); + GodotDisplayVK.updateSize(); + }, + show: function (text, multiline, start, end) { + if (!GodotDisplayVK.textinput || !GodotDisplayVK.textarea) { + return; + } + if (GodotDisplayVK.textinput.style.display !== '' || GodotDisplayVK.textarea.style.display !== '') { + GodotDisplayVK.hide(); + } + GodotDisplayVK.updateSize(); + const elem = multiline ? GodotDisplayVK.textarea : GodotDisplayVK.textinput; + elem.readonly = false; + elem.disabled = false; + elem.value = text; + elem.style.display = 'block'; + elem.focus(); + elem.setSelectionRange(start, end); + }, + hide: function () { + if (!GodotDisplayVK.textinput || !GodotDisplayVK.textarea) { + return; + } + [GodotDisplayVK.textinput, GodotDisplayVK.textarea].forEach(function (elem) { + elem.blur(); + elem.style.display = 'none'; + elem.value = ''; + }); + }, + updateSize: function () { + if (!GodotDisplayVK.textinput || !GodotDisplayVK.textarea) { + return; + } + const rect = GodotConfig.canvas.getBoundingClientRect(); + function update(elem) { + elem.style.left = `${rect.left}px`; + elem.style.top = `${rect.top}px`; + elem.style.width = `${rect.width}px`; + elem.style.height = `${rect.height}px`; + } + update(GodotDisplayVK.textinput); + update(GodotDisplayVK.textarea); + }, + clear: function () { + if (GodotDisplayVK.textinput) { + GodotDisplayVK.textinput.remove(); + GodotDisplayVK.textinput = null; + } + if (GodotDisplayVK.textarea) { + GodotDisplayVK.textarea.remove(); + GodotDisplayVK.textarea = null; + } + }, + }, +}; +mergeInto(LibraryManager.library, GodotDisplayVK); + /* * Display server cursor helper. * Keeps track of cursor status and custom shapes. @@ -400,6 +511,10 @@ const GodotDisplayScreen = { $GodotDisplayScreen__deps: ['$GodotConfig', '$GodotOS', '$GL', 'emscripten_webgl_get_current_context'], $GodotDisplayScreen: { desired_size: [0, 0], + hidpi: true, + getPixelRatio: function () { + return GodotDisplayScreen.hidpi ? window.devicePixelRatio || 1 : 1; + }, isFullscreen: function () { const elem = document.fullscreenElement || document.mozFullscreenElement || document.webkitFullscreenElement || document.msFullscreenElement; @@ -477,7 +592,7 @@ const GodotDisplayScreen = { } return 0; } - const scale = window.devicePixelRatio || 1; + const scale = GodotDisplayScreen.getPixelRatio(); if (isFullscreen || wantsFullWindow) { // We need to match screen size. width = window.innerWidth * scale; @@ -507,7 +622,7 @@ mergeInto(LibraryManager.library, GodotDisplayScreen); * Exposes all the functions needed by DisplayServer implementation. */ const GodotDisplay = { - $GodotDisplay__deps: ['$GodotConfig', '$GodotRuntime', '$GodotDisplayCursor', '$GodotDisplayListeners', '$GodotDisplayDragDrop', '$GodotDisplayGamepads', '$GodotDisplayScreen'], + $GodotDisplay__deps: ['$GodotConfig', '$GodotRuntime', '$GodotDisplayCursor', '$GodotDisplayListeners', '$GodotDisplayDragDrop', '$GodotDisplayGamepads', '$GodotDisplayScreen', '$GodotDisplayVK'], $GodotDisplay: { window_icon: '', findDPI: function () { @@ -555,7 +670,7 @@ const GodotDisplay = { godot_js_display_pixel_ratio_get__sig: 'f', godot_js_display_pixel_ratio_get: function () { - return window.devicePixelRatio || 1; + return GodotDisplayScreen.getPixelRatio(); }, godot_js_display_fullscreen_request__sig: 'i', @@ -576,12 +691,16 @@ const GodotDisplay = { godot_js_display_size_update__sig: 'i', godot_js_display_size_update: function () { - return GodotDisplayScreen.updateSize(); + const updated = GodotDisplayScreen.updateSize(); + if (updated) { + GodotDisplayVK.updateSize(); + } + return updated; }, godot_js_display_screen_size_get__sig: 'vii', godot_js_display_screen_size_get: function (width, height) { - const scale = window.devicePixelRatio || 1; + const scale = GodotDisplayScreen.getPixelRatio(); GodotRuntime.setHeapValue(width, window.screen.width * scale, 'i32'); GodotRuntime.setHeapValue(height, window.screen.height * scale, 'i32'); }, @@ -600,6 +719,17 @@ const GodotDisplay = { GodotRuntime.setHeapValue(r_y, (y - rect.y) * rh, 'i32'); }, + godot_js_display_has_webgl__sig: 'ii', + godot_js_display_has_webgl: function (p_version) { + if (p_version !== 1 && p_version !== 2) { + return false; + } + try { + return !!document.createElement('canvas').getContext(p_version === 2 ? 'webgl2' : 'webgl'); + } catch (e) { /* Not available */ } + return false; + }, + /* * Canvas */ @@ -739,7 +869,7 @@ const GodotDisplay = { const notif = [p_enter, p_exit, p_in, p_out]; ['mouseover', 'mouseleave', 'focus', 'blur'].forEach(function (evt_name, idx) { GodotDisplayListeners.add(canvas, evt_name, function () { - func.bind(null, notif[idx]); + func(notif[idx]); }, true); }); }, @@ -776,8 +906,8 @@ const GodotDisplay = { GodotDisplayListeners.add(canvas, 'drop', GodotDisplayDragDrop.handler(dropFiles)); }, - godot_js_display_setup_canvas__sig: 'viii', - godot_js_display_setup_canvas: function (p_width, p_height, p_fullscreen) { + godot_js_display_setup_canvas__sig: 'viiii', + godot_js_display_setup_canvas: function (p_width, p_height, p_fullscreen, p_hidpi) { const canvas = GodotConfig.canvas; GodotDisplayListeners.add(canvas, 'contextmenu', function (ev) { ev.preventDefault(); @@ -786,6 +916,7 @@ const GodotDisplay = { alert('WebGL context lost, please reload the page'); // eslint-disable-line no-alert ev.preventDefault(); }, false); + GodotDisplayScreen.hidpi = !!p_hidpi; switch (GodotConfig.canvas_resize_policy) { case 0: // None GodotDisplayScreen.desired_size = [canvas.width, canvas.height]; @@ -800,12 +931,42 @@ const GodotDisplay = { canvas.style.left = 0; break; } + GodotDisplayScreen.updateSize(); if (p_fullscreen) { GodotDisplayScreen.requestFullscreen(); } }, /* + * Virtual Keyboard + */ + godot_js_display_vk_show__sig: 'viiii', + godot_js_display_vk_show: function (p_text, p_multiline, p_start, p_end) { + const text = GodotRuntime.parseString(p_text); + const start = p_start > 0 ? p_start : 0; + const end = p_end > 0 ? p_end : start; + GodotDisplayVK.show(text, p_multiline, start, end); + }, + + godot_js_display_vk_hide__sig: 'v', + godot_js_display_vk_hide: function () { + GodotDisplayVK.hide(); + }, + + godot_js_display_vk_available__sig: 'i', + godot_js_display_vk_available: function () { + return GodotDisplayVK.available(); + }, + + godot_js_display_vk_cb__sig: 'vi', + godot_js_display_vk_cb: function (p_input_cb) { + const input_cb = GodotRuntime.get_func(p_input_cb); + if (GodotDisplayVK.available()) { + GodotDisplayVK.init(input_cb); + } + }, + + /* * Gamepads */ godot_js_display_gamepad_cb__sig: 'vi', diff --git a/platform/javascript/js/libs/library_godot_fetch.js b/platform/javascript/js/libs/library_godot_fetch.js index 4ae6a23593..de5ae2b1ae 100644 --- a/platform/javascript/js/libs/library_godot_fetch.js +++ b/platform/javascript/js/libs/library_godot_fetch.js @@ -49,25 +49,14 @@ const GodotFetch = { if (!obj) { return; } - let size = -1; - let compressed = false; let chunked = false; response.headers.forEach(function (value, header) { const v = value.toLowerCase().trim(); const h = header.toLowerCase().trim(); - if (h === 'content-encoding') { - compressed = true; - size = -1; - } else if (h === 'content-length') { - const len = Number.parseInt(value, 10); - if (!Number.isNaN(len) && !compressed) { - size = len; - } - } else if (h === 'transfer-encoding' && v === 'chunked') { + if (h === 'transfer-encoding' && v === 'chunked') { chunked = true; } }); - obj.bodySize = size; obj.status = response.status; obj.response = response; obj.reader = response.body.getReader(); diff --git a/platform/javascript/js/libs/library_godot_os.js b/platform/javascript/js/libs/library_godot_os.js index 0f189b013c..1d9f889bce 100644 --- a/platform/javascript/js/libs/library_godot_os.js +++ b/platform/javascript/js/libs/library_godot_os.js @@ -59,6 +59,8 @@ const GodotConfig = { canvas: null, locale: 'en', canvas_resize_policy: 2, // Adaptive + virtual_keyboard: false, + persistent_drops: false, on_execute: null, on_exit: null, @@ -66,6 +68,8 @@ const GodotConfig = { GodotConfig.canvas_resize_policy = p_opts['canvasResizePolicy']; GodotConfig.canvas = p_opts['canvas']; GodotConfig.locale = p_opts['locale'] || GodotConfig.locale; + GodotConfig.virtual_keyboard = p_opts['virtualKeyboard']; + GodotConfig.persistent_drops = !!p_opts['persistentDrops']; GodotConfig.on_execute = p_opts['onExecute']; GodotConfig.on_exit = p_opts['onExit']; }, @@ -77,6 +81,8 @@ const GodotConfig = { GodotConfig.canvas = null; GodotConfig.locale = 'en'; GodotConfig.canvas_resize_policy = 2; + GodotConfig.virtual_keyboard = false; + GodotConfig.persistent_drops = false; GodotConfig.on_execute = null; GodotConfig.on_exit = null; }, diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index 09d185ae2b..6b527c6fb5 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -90,7 +90,7 @@ def configure(env): if env["target"] == "release": if env["optimize"] == "speed": # optimize for speed (default) env.Prepend(CCFLAGS=["-O3"]) - else: # optimize for size + elif env["optimize"] == "size": # optimize for size env.Prepend(CCFLAGS=["-Os"]) if env["debug_symbols"]: @@ -99,7 +99,7 @@ def configure(env): elif env["target"] == "release_debug": if env["optimize"] == "speed": # optimize for speed (default) env.Prepend(CCFLAGS=["-O2"]) - else: # optimize for size + elif env["optimize"] == "size": # optimize for size env.Prepend(CCFLAGS=["-Os"]) env.Prepend(CPPDEFINES=["DEBUG_ENABLED"]) diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index 3bc859e17d..8dc1b41702 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -1917,7 +1917,7 @@ void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape Rect2i atlas_rect; if (texture.is_valid()) { - image = texture->get_data(); + image = texture->get_image(); } if (!image.is_valid() && atlas_texture.is_valid()) { @@ -1940,7 +1940,7 @@ void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); - image = texture->get_data(); + image = texture->get_image(); ERR_FAIL_COND(!image.is_valid()); @@ -2012,7 +2012,7 @@ int DisplayServerX11::keyboard_get_layout_count() const { XkbGetNames(x11_display, XkbSymbolsNameMask, kbd); const Atom *groups = kbd->names->groups; - if (kbd->ctrls != NULL) { + if (kbd->ctrls != nullptr) { _group_count = kbd->ctrls->num_groups; } else { while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) { @@ -2046,7 +2046,7 @@ String DisplayServerX11::keyboard_get_layout_language(int p_index) const { int _group_count = 0; const Atom *groups = kbd->names->groups; - if (kbd->ctrls != NULL) { + if (kbd->ctrls != nullptr) { _group_count = kbd->ctrls->num_groups; } else { while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) { @@ -2085,7 +2085,7 @@ String DisplayServerX11::keyboard_get_layout_name(int p_index) const { int _group_count = 0; const Atom *groups = kbd->names->groups; - if (kbd->ctrls != NULL) { + if (kbd->ctrls != nullptr) { _group_count = kbd->ctrls->num_groups; } else { while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) { @@ -2684,7 +2684,7 @@ bool DisplayServerX11::_wait_for_events() const { tv.tv_sec = 1; // Wait for next event or timeout. - int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv); + int num_ready_fds = select(x11_fd + 1, &in_fds, nullptr, nullptr, &tv); if (num_ready_fds > 0) { // Event received. @@ -3298,7 +3298,7 @@ void DisplayServerX11::process_events() { if (xi.pressure_supported) { mm->set_pressure(xi.pressure); } else { - mm->set_pressure((mouse_get_button_state() & (1 << (BUTTON_LEFT - 1))) ? 1.0f : 0.0f); + mm->set_pressure((mouse_get_button_state() & (1 << (MOUSE_BUTTON_LEFT - 1))) ? 1.0f : 0.0f); } mm->set_tilt(xi.tilt); diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp index 471259e50f..e8f4352dff 100644 --- a/platform/linuxbsd/joypad_linux.cpp +++ b/platform/linuxbsd/joypad_linux.cpp @@ -62,7 +62,7 @@ void JoypadLinux::Joypad::reset() { dpad = 0; fd = -1; - Input::JoyAxis jx; + Input::JoyAxisValue jx; jx.min = -1; jx.value = 0.0f; for (int i = 0; i < MAX_ABS; i++) { @@ -178,17 +178,18 @@ void JoypadLinux::monitor_joypads(udev *p_udev) { select() ensured that this will not block. */ dev = udev_monitor_receive_device(mon); - if (dev && udev_device_get_devnode(dev) != 0) { + if (dev && udev_device_get_devnode(dev) != nullptr) { MutexLock lock(joy_mutex); String action = udev_device_get_action(dev); const char *devnode = udev_device_get_devnode(dev); if (devnode) { String devnode_str = devnode; if (devnode_str.find(ignore_str) == -1) { - if (action == "add") + if (action == "add") { open_joypad(devnode); - else if (String(action) == "remove") + } else if (String(action) == "remove") { close_joypad(get_joy_from_path(devnode)); + } } } @@ -212,7 +213,7 @@ void JoypadLinux::monitor_joypads() { struct dirent *current; char fname[64]; - while ((current = readdir(input_directory)) != NULL) { + while ((current = readdir(input_directory)) != nullptr) { if (strncmp(current->d_name, "event", 5) != 0) { continue; } @@ -428,10 +429,10 @@ void JoypadLinux::joypad_vibration_stop(int p_id, uint64_t p_timestamp) { joy.ff_effect_timestamp = p_timestamp; } -Input::JoyAxis JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const { +Input::JoyAxisValue JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const { int min = p_abs->minimum; int max = p_abs->maximum; - Input::JoyAxis jx; + Input::JoyAxisValue jx; if (min < 0) { jx.min = -1; @@ -513,7 +514,7 @@ void JoypadLinux::process_joypads() { return; } if (joy->abs_map[ev.code] != -1 && joy->abs_info[ev.code]) { - Input::JoyAxis value = axis_correct(joy->abs_info[ev.code], ev.value); + Input::JoyAxisValue value = axis_correct(joy->abs_info[ev.code], ev.value); joy->curr_axis[joy->abs_map[ev.code]] = value; } break; diff --git a/platform/linuxbsd/joypad_linux.h b/platform/linuxbsd/joypad_linux.h index b0d0db047b..177d7a51ce 100644 --- a/platform/linuxbsd/joypad_linux.h +++ b/platform/linuxbsd/joypad_linux.h @@ -53,7 +53,7 @@ private: }; struct Joypad { - Input::JoyAxis curr_axis[MAX_ABS]; + Input::JoyAxisValue curr_axis[MAX_ABS]; int key_map[MAX_KEY]; int abs_map[MAX_ABS]; int dpad = 0; @@ -97,7 +97,7 @@ private: void joypad_vibration_start(int p_id, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp); void joypad_vibration_stop(int p_id, uint64_t p_timestamp); - Input::JoyAxis axis_correct(const input_absinfo *p_abs, int p_value) const; + Input::JoyAxisValue axis_correct(const input_absinfo *p_abs, int p_value) const; }; #endif diff --git a/platform/osx/detect.py b/platform/osx/detect.py index c39a4426be..5b320da82f 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -50,7 +50,7 @@ def configure(env): if env["target"] == "release": if env["optimize"] == "speed": # optimize for speed (default) env.Prepend(CCFLAGS=["-O3", "-fomit-frame-pointer", "-ftree-vectorize"]) - else: # optimize for size + elif env["optimize"] == "size": # optimize for size env.Prepend(CCFLAGS=["-Os", "-ftree-vectorize"]) if env["arch"] != "arm64": env.Prepend(CCFLAGS=["-msse2"]) @@ -61,7 +61,7 @@ def configure(env): elif env["target"] == "release_debug": if env["optimize"] == "speed": # optimize for speed (default) env.Prepend(CCFLAGS=["-O2"]) - else: # optimize for size + elif env["optimize"] == "size": # optimize for size env.Prepend(CCFLAGS=["-Os"]) env.Prepend(CPPDEFINES=["DEBUG_ENABLED"]) if env["debug_symbols"]: diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm index ed7d89009f..6b838b6d14 100644 --- a/platform/osx/display_server_osx.mm +++ b/platform/osx/display_server_osx.mm @@ -829,7 +829,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i mb->set_position(pos); mb->set_global_position(pos); mb->set_button_mask(DS_OSX->last_button_state); - if (index == BUTTON_LEFT && pressed) { + if (index == MOUSE_BUTTON_LEFT && pressed) { mb->set_doubleclick([event clickCount] == 2); } @@ -842,10 +842,10 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i if (([event modifierFlags] & NSEventModifierFlagControl)) { wd.mouse_down_control = true; - _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true); + _mouseDownEvent(window_id, event, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MASK_RIGHT, true); } else { wd.mouse_down_control = false; - _mouseDownEvent(window_id, event, BUTTON_LEFT, BUTTON_MASK_LEFT, true); + _mouseDownEvent(window_id, event, MOUSE_BUTTON_LEFT, MOUSE_BUTTON_MASK_LEFT, true); } } @@ -858,9 +858,9 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id]; if (wd.mouse_down_control) { - _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false); + _mouseDownEvent(window_id, event, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MASK_RIGHT, false); } else { - _mouseDownEvent(window_id, event, BUTTON_LEFT, BUTTON_MASK_LEFT, false); + _mouseDownEvent(window_id, event, MOUSE_BUTTON_LEFT, MOUSE_BUTTON_MASK_LEFT, false); } } @@ -946,7 +946,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i } - (void)rightMouseDown:(NSEvent *)event { - _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true); + _mouseDownEvent(window_id, event, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MASK_RIGHT, true); } - (void)rightMouseDragged:(NSEvent *)event { @@ -954,16 +954,16 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i } - (void)rightMouseUp:(NSEvent *)event { - _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false); + _mouseDownEvent(window_id, event, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MASK_RIGHT, false); } - (void)otherMouseDown:(NSEvent *)event { if ((int)[event buttonNumber] == 2) { - _mouseDownEvent(window_id, event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, true); + _mouseDownEvent(window_id, event, MOUSE_BUTTON_MIDDLE, MOUSE_BUTTON_MASK_MIDDLE, true); } else if ((int)[event buttonNumber] == 3) { - _mouseDownEvent(window_id, event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, true); + _mouseDownEvent(window_id, event, MOUSE_BUTTON_XBUTTON1, MOUSE_BUTTON_MASK_XBUTTON1, true); } else if ((int)[event buttonNumber] == 4) { - _mouseDownEvent(window_id, event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, true); + _mouseDownEvent(window_id, event, MOUSE_BUTTON_XBUTTON2, MOUSE_BUTTON_MASK_XBUTTON2, true); } else { return; } @@ -975,11 +975,11 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i - (void)otherMouseUp:(NSEvent *)event { if ((int)[event buttonNumber] == 2) { - _mouseDownEvent(window_id, event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, false); + _mouseDownEvent(window_id, event, MOUSE_BUTTON_MIDDLE, MOUSE_BUTTON_MASK_MIDDLE, false); } else if ((int)[event buttonNumber] == 3) { - _mouseDownEvent(window_id, event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, false); + _mouseDownEvent(window_id, event, MOUSE_BUTTON_XBUTTON1, MOUSE_BUTTON_MASK_XBUTTON1, false); } else if ((int)[event buttonNumber] == 4) { - _mouseDownEvent(window_id, event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, false); + _mouseDownEvent(window_id, event, MOUSE_BUTTON_XBUTTON2, MOUSE_BUTTON_MASK_XBUTTON2, false); } else { return; } @@ -1558,10 +1558,10 @@ inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy sendPanEvent(window_id, deltaX, deltaY, [event modifierFlags]); } else { if (fabs(deltaX)) { - sendScrollEvent(window_id, 0 > deltaX ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_LEFT, fabs(deltaX * 0.3), [event modifierFlags]); + sendScrollEvent(window_id, 0 > deltaX ? MOUSE_BUTTON_WHEEL_RIGHT : MOUSE_BUTTON_WHEEL_LEFT, fabs(deltaX * 0.3), [event modifierFlags]); } if (fabs(deltaY)) { - sendScrollEvent(window_id, 0 < deltaY ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN, fabs(deltaY * 0.3), [event modifierFlags]); + sendScrollEvent(window_id, 0 < deltaY ? MOUSE_BUTTON_WHEEL_UP : MOUSE_BUTTON_WHEEL_DOWN, fabs(deltaY * 0.3), [event modifierFlags]); } } } @@ -3117,7 +3117,7 @@ void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape Rect2 atlas_rect; if (texture.is_valid()) { - image = texture->get_data(); + image = texture->get_image(); } if (!image.is_valid() && atlas_texture.is_valid()) { @@ -3140,7 +3140,7 @@ void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); - image = texture->get_data(); + image = texture->get_image(); ERR_FAIL_COND(!image.is_valid()); diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index f31d8b9b81..aca9471849 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -155,10 +155,11 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), "")); #ifdef OSX_ENABLED - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/replace_existing_signature"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false)); @@ -486,10 +487,18 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese } args.push_back("-s"); - args.push_back(p_preset->get("codesign/identity")); + if (p_preset->get("codesign/identity") == "") { + args.push_back("-"); + } else { + args.push_back(p_preset->get("codesign/identity")); + } args.push_back("-v"); /* provide some more feedback */ + if (p_preset->get("codesign/replace_existing_signature")) { + args.push_back("-f"); + } + args.push_back(p_path); String str; @@ -1055,12 +1064,6 @@ bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset } bool sign_enabled = p_preset->get("codesign/enable"); - if (sign_enabled) { - if (p_preset->get("codesign/identity") == "") { - err += TTR("Codesign: identity not specified.") + "\n"; - valid = false; - } - } bool noto_enabled = p_preset->get("notarization/enable"); if (noto_enabled) { if (!sign_enabled) { diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp index 0b6a0e20a6..b12526915f 100644 --- a/platform/osx/joypad_osx.cpp +++ b/platform/osx/joypad_osx.cpp @@ -433,8 +433,8 @@ void JoypadOSX::poll_joypads() const { } } -static const Input::JoyAxis axis_correct(int p_value, int p_min, int p_max) { - Input::JoyAxis jx; +static const Input::JoyAxisValue axis_correct(int p_value, int p_min, int p_max) { + Input::JoyAxisValue jx; if (p_min < 0) { jx.min = -1; if (p_value < 0) { diff --git a/platform/server/detect.py b/platform/server/detect.py index c799ce03e1..16ddbe1768 100644 --- a/platform/server/detect.py +++ b/platform/server/detect.py @@ -56,7 +56,7 @@ def configure(env): if env["target"] == "release": if env["optimize"] == "speed": # optimize for speed (default) env.Prepend(CCFLAGS=["-O3"]) - else: # optimize for size + elif env["optimize"] == "size": # optimize for size env.Prepend(CCFLAGS=["-Os"]) if env["debug_symbols"]: @@ -65,7 +65,7 @@ def configure(env): elif env["target"] == "release_debug": if env["optimize"] == "speed": # optimize for speed (default) env.Prepend(CCFLAGS=["-O2"]) - else: # optimize for size + elif env["optimize"] == "size": # optimize for size env.Prepend(CCFLAGS=["-Os"]) env.Prepend(CPPDEFINES=["DEBUG_ENABLED"]) diff --git a/platform/uwp/app.cpp b/platform/uwp/app.cpp index dc4238bdd4..b7e4361831 100644 --- a/platform/uwp/app.cpp +++ b/platform/uwp/app.cpp @@ -149,28 +149,28 @@ static int _get_button(Windows::UI::Input::PointerPoint ^ pt) { using namespace Windows::UI::Input; #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP - return BUTTON_LEFT; + return MOUSE_BUTTON_LEFT; #else switch (pt->Properties->PointerUpdateKind) { case PointerUpdateKind::LeftButtonPressed: case PointerUpdateKind::LeftButtonReleased: - return BUTTON_LEFT; + return MOUSE_BUTTON_LEFT; case PointerUpdateKind::RightButtonPressed: case PointerUpdateKind::RightButtonReleased: - return BUTTON_RIGHT; + return MOUSE_BUTTON_RIGHT; case PointerUpdateKind::MiddleButtonPressed: case PointerUpdateKind::MiddleButtonReleased: - return BUTTON_MIDDLE; + return MOUSE_BUTTON_MIDDLE; case PointerUpdateKind::XButton1Pressed: case PointerUpdateKind::XButton1Released: - return BUTTON_WHEEL_UP; + return MOUSE_BUTTON_WHEEL_UP; case PointerUpdateKind::XButton2Pressed: case PointerUpdateKind::XButton2Released: - return BUTTON_WHEEL_DOWN; + return MOUSE_BUTTON_WHEEL_DOWN; default: break; @@ -265,9 +265,9 @@ void App::pointer_event(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Cor if (p_is_wheel) { if (point->Properties->MouseWheelDelta > 0) { - mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_UP); + mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? MOUSE_BUTTON_WHEEL_RIGHT : MOUSE_BUTTON_WHEEL_UP); } else if (point->Properties->MouseWheelDelta < 0) { - mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? BUTTON_WHEEL_LEFT : BUTTON_WHEEL_DOWN); + mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? MOUSE_BUTTON_WHEEL_LEFT : MOUSE_BUTTON_WHEEL_DOWN); } } diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py index fda8fdec66..28922a4f59 100644 --- a/platform/uwp/detect.py +++ b/platform/uwp/detect.py @@ -54,16 +54,19 @@ def configure(env): ## Build type if env["target"] == "release": - env.Append(CCFLAGS=["/O2", "/GL"]) env.Append(CCFLAGS=["/MD"]) - env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS", "/LTCG"]) + env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS"]) + if env["optimize"] != "none": + env.Append(CCFLAGS=["/O2", "/GL"]) + env.Append(LINKFLAGS=["/LTCG"]) elif env["target"] == "release_debug": - env.Append(CCFLAGS=["/O2", "/Zi"]) env.Append(CCFLAGS=["/MD"]) - env.Append(CPPDEFINES=["DEBUG_ENABLED"]) env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"]) env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"]) + env.Append(CPPDEFINES=["DEBUG_ENABLED"]) + if env["optimize"] != "none": + env.Append(CCFLAGS=["/O2", "/Zi"]) elif env["target"] == "debug": env.Append(CCFLAGS=["/Zi"]) diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index 1aad2bfa1a..2a0bc78440 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -855,33 +855,33 @@ class EditorExportPlatformUWP : public EditorExportPlatform { Vector<uint8_t> _get_image_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) { Vector<uint8_t> data; - StreamTexture2D *image = nullptr; + StreamTexture2D *texture = nullptr; if (p_path.find("StoreLogo") != -1) { - image = p_preset->get("images/store_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/store_logo"))); + texture = p_preset->get("images/store_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/store_logo"))); } else if (p_path.find("Square44x44Logo") != -1) { - image = p_preset->get("images/square44x44_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square44x44_logo"))); + texture = p_preset->get("images/square44x44_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square44x44_logo"))); } else if (p_path.find("Square71x71Logo") != -1) { - image = p_preset->get("images/square71x71_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square71x71_logo"))); + texture = p_preset->get("images/square71x71_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square71x71_logo"))); } else if (p_path.find("Square150x150Logo") != -1) { - image = p_preset->get("images/square150x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square150x150_logo"))); + texture = p_preset->get("images/square150x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square150x150_logo"))); } else if (p_path.find("Square310x310Logo") != -1) { - image = p_preset->get("images/square310x310_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square310x310_logo"))); + texture = p_preset->get("images/square310x310_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square310x310_logo"))); } else if (p_path.find("Wide310x150Logo") != -1) { - image = p_preset->get("images/wide310x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/wide310x150_logo"))); + texture = p_preset->get("images/wide310x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/wide310x150_logo"))); } else if (p_path.find("SplashScreen") != -1) { - image = p_preset->get("images/splash_screen").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/splash_screen"))); + texture = p_preset->get("images/splash_screen").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/splash_screen"))); } else { ERR_PRINT("Unable to load logo"); } - if (!image) { + if (!texture) { return data; } String tmp_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("uwp_tmp_logo.png"); - Error err = image->get_data()->save_png(tmp_path); + Error err = texture->get_image()->save_png(tmp_path); if (err != OK) { String err_string = "Couldn't save temp logo file."; diff --git a/platform/uwp/joypad_uwp.cpp b/platform/uwp/joypad_uwp.cpp index 5da90db49d..b419fb4fae 100644 --- a/platform/uwp/joypad_uwp.cpp +++ b/platform/uwp/joypad_uwp.cpp @@ -134,8 +134,8 @@ void JoypadUWP::OnGamepadRemoved(Platform::Object ^ sender, Windows::Gaming::Inp input->joy_connection_changed(idx, false, "Xbox Controller"); } -InputDefault::JoyAxis JoypadUWP::axis_correct(double p_val, bool p_negate, bool p_trigger) const { - InputDefault::JoyAxis jx; +InputDefault::JoyAxisValue JoypadUWP::axis_correct(double p_val, bool p_negate, bool p_trigger) const { + InputDefault::JoyAxisValue jx; jx.min = p_trigger ? 0 : -1; jx.value = (float)(p_negate ? -p_val : p_val); diff --git a/platform/uwp/joypad_uwp.h b/platform/uwp/joypad_uwp.h index 5df4a211ac..d760d9e2fe 100644 --- a/platform/uwp/joypad_uwp.h +++ b/platform/uwp/joypad_uwp.h @@ -73,7 +73,7 @@ private: void OnGamepadAdded(Platform::Object ^ sender, Windows::Gaming::Input::Gamepad ^ value); void OnGamepadRemoved(Platform::Object ^ sender, Windows::Gaming::Input::Gamepad ^ value); - InputDefault::JoyAxis axis_correct(double p_val, bool p_negate = false, bool p_trigger = false) const; + InputDefault::JoyAxisValue axis_correct(double p_val, bool p_negate = false, bool p_trigger = false) const; void joypad_vibration_start(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp); void joypad_vibration_stop(int p_device, uint64_t p_timestamp); }; diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index fa97948395..33992069f9 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -62,6 +62,8 @@ using namespace Windows::Devices::Sensors; using namespace Windows::ApplicationModel::DataTransfer; using namespace concurrency; +static const float earth_gravity = 9.80665; + int OS_UWP::get_video_driver_count() const { return 2; } @@ -372,9 +374,9 @@ void OS_UWP::ManagedType::on_accelerometer_reading_changed(Accelerometer ^ sende AccelerometerReading ^ reading = args->Reading; os->input->set_accelerometer(Vector3( - reading->AccelerationX, - reading->AccelerationY, - reading->AccelerationZ)); + reading->AccelerationX * earth_gravity, + reading->AccelerationY * earth_gravity, + reading->AccelerationZ * earth_gravity)); } void OS_UWP::ManagedType::on_magnetometer_reading_changed(Magnetometer ^ sender, MagnetometerReadingChangedEventArgs ^ args) { diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp index e24e466f88..e2d507eddd 100644 --- a/platform/windows/crash_handler_windows.cpp +++ b/platform/windows/crash_handler_windows.cpp @@ -36,7 +36,7 @@ #ifdef CRASH_HANDLER_EXCEPTION -// Backtrace code code based on: https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app +// Backtrace code based on: https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app #include <algorithm> #include <iterator> diff --git a/platform/windows/detect.py b/platform/windows/detect.py index f26dea8d35..7772ba2dbe 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -72,6 +72,7 @@ def get_opts(): BoolVariable("use_llvm", "Use the LLVM compiler", False), BoolVariable("use_thinlto", "Use ThinLTO", False), BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True), + BoolVariable("use_asan", "Use address sanitizer (ASAN)", False), ] @@ -190,18 +191,20 @@ def configure_msvc(env, manual_msvc_config): if env["target"] == "release": if env["optimize"] == "speed": # optimize for speed (default) env.Append(CCFLAGS=["/O2"]) - else: # optimize for size + env.Append(LINKFLAGS=["/OPT:REF"]) + elif env["optimize"] == "size": # optimize for size env.Append(CCFLAGS=["/O1"]) + env.Append(LINKFLAGS=["/OPT:REF"]) env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"]) - env.Append(LINKFLAGS=["/OPT:REF"]) elif env["target"] == "release_debug": if env["optimize"] == "speed": # optimize for speed (default) env.Append(CCFLAGS=["/O2"]) - else: # optimize for size + env.Append(LINKFLAGS=["/OPT:REF"]) + elif env["optimize"] == "size": # optimize for size env.Append(CCFLAGS=["/O1"]) + env.Append(LINKFLAGS=["/OPT:REF"]) env.AppendUnique(CPPDEFINES=["DEBUG_ENABLED"]) - env.Append(LINKFLAGS=["/OPT:REF"]) elif env["target"] == "debug": env.AppendUnique(CCFLAGS=["/Zi", "/FS", "/Od", "/EHsc"]) @@ -306,6 +309,12 @@ def configure_msvc(env, manual_msvc_config): env.Prepend(CPPPATH=[p for p in os.getenv("INCLUDE").split(";")]) env.Append(LIBPATH=[p for p in os.getenv("LIB").split(";")]) + # Sanitizers + if env["use_asan"]: + env.extra_suffix += ".s" + env.Append(LINKFLAGS=["/INFERASANLIBS"]) + env.Append(CCFLAGS=["/fsanitize=address"]) + # Incremental linking fix env["BUILDERS"]["ProgramOriginal"] = env["BUILDERS"]["Program"] env["BUILDERS"]["Program"] = methods.precious_program diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index b9b78f7bd4..86f20f1dd7 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -1253,12 +1253,12 @@ void DisplayServerWindows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTra HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap); HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap); - // Assign the monochrome AND mask bitmap pixels so that a pixels of the source bitmap + // 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 SetBkColor(hMainDC, clrTransparent); BitBlt(hAndMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCCOPY); - // Assign the color XOR mask bitmap pixels so that a pixels of the source bitmap + // 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 SetBkColor(hXorMaskDC, RGB(0, 0, 0)); @@ -1299,7 +1299,7 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh Rect2 atlas_rect; if (texture.is_valid()) { - image = texture->get_data(); + image = texture->get_image(); } if (!image.is_valid() && atlas_texture.is_valid()) { @@ -1322,7 +1322,7 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); - image = texture->get_data(); + image = texture->get_image(); ERR_FAIL_COND(!image.is_valid()); @@ -2305,7 +2305,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_alt(alt_mem); if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) { - // Note: WinTab sends both WT_PACKET and WM_xBUTTONDOWN/UP/MOUSEMOVE events, use mouse 1/0 pressure only when last_pressure was not update recently. + // Note: WinTab sends both WT_PACKET and WM_xBUTTONDOWN/UP/MOUSEMOVE events, use mouse 1/0 pressure only when last_pressure was not updated recently. if (windows[window_id].last_pressure_update < 10) { windows[window_id].last_pressure_update++; } else { @@ -2431,9 +2431,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA return 0; if (motion > 0) - mb->set_button_index(BUTTON_WHEEL_UP); + mb->set_button_index(MOUSE_BUTTON_WHEEL_UP); else - mb->set_button_index(BUTTON_WHEEL_DOWN); + mb->set_button_index(MOUSE_BUTTON_WHEEL_DOWN); } break; case WM_MOUSEHWHEEL: { @@ -2443,33 +2443,33 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA return 0; if (motion < 0) { - mb->set_button_index(BUTTON_WHEEL_LEFT); + mb->set_button_index(MOUSE_BUTTON_WHEEL_LEFT); mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); } else { - mb->set_button_index(BUTTON_WHEEL_RIGHT); + mb->set_button_index(MOUSE_BUTTON_WHEEL_RIGHT); mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); } } break; case WM_XBUTTONDOWN: { mb->set_pressed(true); if (HIWORD(wParam) == XBUTTON1) - mb->set_button_index(BUTTON_XBUTTON1); + mb->set_button_index(MOUSE_BUTTON_XBUTTON1); else - mb->set_button_index(BUTTON_XBUTTON2); + mb->set_button_index(MOUSE_BUTTON_XBUTTON2); } break; case WM_XBUTTONUP: { mb->set_pressed(false); if (HIWORD(wParam) == XBUTTON1) - mb->set_button_index(BUTTON_XBUTTON1); + mb->set_button_index(MOUSE_BUTTON_XBUTTON1); else - mb->set_button_index(BUTTON_XBUTTON2); + mb->set_button_index(MOUSE_BUTTON_XBUTTON2); } break; case WM_XBUTTONDBLCLK: { mb->set_pressed(true); if (HIWORD(wParam) == XBUTTON1) - mb->set_button_index(BUTTON_XBUTTON1); + mb->set_button_index(MOUSE_BUTTON_XBUTTON1); else - mb->set_button_index(BUTTON_XBUTTON2); + mb->set_button_index(MOUSE_BUTTON_XBUTTON2); mb->set_doubleclick(true); } break; default: { @@ -2566,6 +2566,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA windows[window_id].preserve_window_size = false; window_set_size(Size2(windows[window_id].width, windows[window_id].height), window_id); } + } else { + windows[window_id].preserve_window_size = true; } if (!windows[window_id].rect_changed_callback.is_null()) { diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp index f46a0dbe2e..da36dc1f2b 100644 --- a/platform/windows/joypad_windows.cpp +++ b/platform/windows/joypad_windows.cpp @@ -110,12 +110,11 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) { if (GetRawInputDeviceList(nullptr, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) { return false; } - dev_list = (PRAWINPUTDEVICELIST)malloc(sizeof(RAWINPUTDEVICELIST) * dev_list_count); - if (!dev_list) - return false; + dev_list = (PRAWINPUTDEVICELIST)memalloc(sizeof(RAWINPUTDEVICELIST) * dev_list_count); + ERR_FAIL_NULL_V_MSG(dev_list, false, "Out of memory."); if (GetRawInputDeviceList(dev_list, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) { - free(dev_list); + memfree(dev_list); return false; } for (unsigned int i = 0; i < dev_list_count; i++) { @@ -130,11 +129,11 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) { (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == (LONG)p_guid->Data1) && (GetRawInputDeviceInfoA(dev_list[i].hDevice, RIDI_DEVICENAME, &dev_name, &nameSize) != (UINT)-1) && (strstr(dev_name, "IG_") != nullptr)) { - free(dev_list); + memfree(dev_list); return true; } } - free(dev_list); + memfree(dev_list); return false; } @@ -447,8 +446,8 @@ void JoypadWindows::post_hat(int p_device, DWORD p_dpad) { input->joy_hat(p_device, dpad_val); }; -Input::JoyAxis JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const { - Input::JoyAxis jx; +Input::JoyAxisValue JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const { + Input::JoyAxisValue jx; if (Math::abs(p_val) < MIN_JOY_AXIS) { jx.min = p_trigger ? 0 : -1; jx.value = 0.0f; diff --git a/platform/windows/joypad_windows.h b/platform/windows/joypad_windows.h index 4727b4a14c..757fb54fb3 100644 --- a/platform/windows/joypad_windows.h +++ b/platform/windows/joypad_windows.h @@ -132,7 +132,7 @@ private: void joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp); void joypad_vibration_stop_xinput(int p_device, uint64_t p_timestamp); - Input::JoyAxis axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const; + Input::JoyAxisValue axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const; XInputGetState_t xinput_get_state; XInputSetState_t xinput_set_state; }; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 3280a36e9b..1e9cdd241d 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -334,7 +334,7 @@ OS::TimeZoneInfo OS_Windows::get_time_zone_info() const { } // Bias value returned by GetTimeZoneInformation is inverted of what we expect - // For example on GMT-3 GetTimeZoneInformation return a Bias of 180, so invert the value to get -180 + // For example, on GMT-3 GetTimeZoneInformation return a Bias of 180, so invert the value to get -180 ret.bias = -info.Bias; return ret; } diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp index 56b620a6d9..c1f3827d15 100644 --- a/platform/windows/windows_terminal_logger.cpp +++ b/platform/windows/windows_terminal_logger.cpp @@ -53,7 +53,8 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er if (wlen < 0) return; - wchar_t *wbuf = (wchar_t *)malloc((len + 1) * sizeof(wchar_t)); + wchar_t *wbuf = (wchar_t *)memalloc((len + 1) * sizeof(wchar_t)); + ERR_FAIL_NULL_MSG(wbuf, "Out of memory."); MultiByteToWideChar(CP_UTF8, 0, buf, len, wbuf, wlen); wbuf[wlen] = 0; @@ -62,7 +63,7 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er else wprintf(L"%ls", wbuf); - free(wbuf); + memfree(wbuf); #ifdef DEBUG_ENABLED fflush(stdout); |