diff options
Diffstat (limited to 'platform')
16 files changed, 272 insertions, 81 deletions
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/lib/src/org/godotengine/godot/FullScreenGodotApp.java b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java index 4e67402c63..b966cda846 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java +++ b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java @@ -44,7 +44,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 +64,8 @@ public abstract class FullScreenGodotApp extends FragmentActivity { public void onNewIntent(Intent intent) { if (godotFragment != null) { godotFragment.onNewIntent(intent); + } else { + super.onNewIntent(intent); } } 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..6da47bb0fe 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,6 +180,22 @@ 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; + } + + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (result_callback != null) { result_callback.callback(requestCode, resultCode, data); @@ -201,6 +219,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 +240,10 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { plugin.onGodotMainLoopStarted(); } + + if (godotHost != null) { + godotHost.onGodotMainLoopStarted(); + } } /** @@ -301,7 +337,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 +392,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 +524,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 +580,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_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_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 759980373a..ff1bbec40d 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... @@ -120,11 +121,21 @@ GodotJavaViewWrapper *GodotJavaWrapper::get_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(); - 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,24 +143,26 @@ void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) { if (p_env == nullptr) { p_env = get_jni_env(); } + 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(); - 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(); - p_env->CallVoidMethod(godot_instance, _finish); + p_env->CallVoidMethod(godot_instance, _finish); + } } void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) { 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/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..0b2145a92b 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), ] @@ -306,6 +307,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..e29faf4f3c 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)); @@ -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 { diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp index f46a0dbe2e..b8679a87fe 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; } 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); |