diff options
Diffstat (limited to 'platform')
60 files changed, 552 insertions, 622 deletions
diff --git a/platform/android/api/jni_singleton.h b/platform/android/api/jni_singleton.h index 49c0104e67..965eaabf81 100644 --- a/platform/android/api/jni_singleton.h +++ b/platform/android/api/jni_singleton.h @@ -83,7 +83,7 @@ public: v = (jvalue *)alloca(sizeof(jvalue) * p_argcount); } - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); int res = env->PushLocalFrame(16); diff --git a/platform/android/audio_driver_jandroid.cpp b/platform/android/audio_driver_jandroid.cpp index 825132ac8c..ee28959adc 100644 --- a/platform/android/audio_driver_jandroid.cpp +++ b/platform/android/audio_driver_jandroid.cpp @@ -72,7 +72,7 @@ Error AudioDriverAndroid::init() { // __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device"); - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); int mix_rate = GLOBAL_GET("audio/mix_rate"); int latency = GLOBAL_GET("audio/output_latency"); @@ -98,7 +98,7 @@ void AudioDriverAndroid::start() { } void AudioDriverAndroid::setup(jobject p_io) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); io = p_io; jclass c = env->GetObjectClass(io); @@ -162,7 +162,7 @@ void AudioDriverAndroid::unlock() { } void AudioDriverAndroid::finish() { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); env->CallVoidMethod(io, _quit); if (audioBuffer) { @@ -175,7 +175,7 @@ void AudioDriverAndroid::finish() { } void AudioDriverAndroid::set_pause(bool p_pause) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); env->CallVoidMethod(io, _pause, p_pause); } diff --git a/platform/android/detect.py b/platform/android/detect.py index 650606ff8b..0e696024a9 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -13,7 +13,7 @@ def get_name(): def can_build(): - return "ANDROID_NDK_ROOT" in os.environ + return ("ANDROID_NDK_ROOT" in os.environ) or ("ANDROID_SDK_ROOT" in os.environ) or ("ANDROID_HOME" in os.environ) def get_platform(platform): @@ -24,13 +24,36 @@ def get_opts(): from SCons.Variables import BoolVariable, EnumVariable return [ - ("ANDROID_NDK_ROOT", "Path to the Android NDK", os.environ.get("ANDROID_NDK_ROOT", 0)), + ("ANDROID_NDK_ROOT", "Path to the Android NDK", get_android_ndk_root()), + ("ANDROID_SDK_ROOT", "Path to the Android SDK", get_android_sdk_root()), ("ndk_platform", 'Target platform (android-<api>, e.g. "android-24")', "android-24"), EnumVariable("android_arch", "Target architecture", "armv7", ("armv7", "arm64v8", "x86", "x86_64")), BoolVariable("android_neon", "Enable NEON support (armv7 only)", True), ] +# Return the ANDROID_SDK_ROOT environment variable. +# While ANDROID_HOME has been deprecated, it's used as a fallback for backward +# compatibility purposes. +def get_android_sdk_root(): + if "ANDROID_SDK_ROOT" in os.environ: + return os.environ.get("ANDROID_SDK_ROOT", 0) + else: + return os.environ.get("ANDROID_HOME", 0) + + +# Return the ANDROID_NDK_ROOT environment variable. +# If the env variable is already defined, we use it with the expectation that +# the user knows what they're doing (e.g: testing a new NDK version). +# Otherwise, we generate one for this build using the ANDROID_SDK_ROOT env +# variable and the project ndk version. +def get_android_ndk_root(): + if "ANDROID_NDK_ROOT" in os.environ: + return os.environ.get("ANDROID_NDK_ROOT", 0) + else: + return get_android_sdk_root() + "/ndk/" + get_project_ndk_version() + + def get_flags(): return [ ("tools", False), @@ -47,7 +70,31 @@ def create(env): return env.Clone(tools=tools) +# Check if ANDROID_NDK_ROOT is valid. +# If not, install the ndk using ANDROID_SDK_ROOT and sdkmanager. +def install_ndk_if_needed(env): + print("Checking for Android NDK...") + env_ndk_version = get_env_ndk_version(env["ANDROID_NDK_ROOT"]) + if env_ndk_version is None: + # Reinstall the ndk and update ANDROID_NDK_ROOT. + print("Installing Android NDK...") + if env["ANDROID_SDK_ROOT"] is None: + raise Exception("Invalid ANDROID_SDK_ROOT environment variable.") + + import subprocess + + extension = ".bat" if os.name == "nt" else "" + sdkmanager_path = env["ANDROID_SDK_ROOT"] + "/cmdline-tools/latest/bin/sdkmanager" + extension + ndk_download_args = "ndk;" + get_project_ndk_version() + subprocess.check_call([sdkmanager_path, ndk_download_args]) + + env["ANDROID_NDK_ROOT"] = env["ANDROID_SDK_ROOT"] + "/ndk/" + get_project_ndk_version() + print("ANDROID_NDK_ROOT: " + env["ANDROID_NDK_ROOT"]) + + def configure(env): + install_ndk_if_needed(env) + # Workaround for MinGW. See: # http://www.scons.org/wiki/LongCmdLinesOnWin32 if os.name == "nt": @@ -270,7 +317,7 @@ def configure(env): # Link flags - ndk_version = get_ndk_version(env["ANDROID_NDK_ROOT"]) + ndk_version = get_env_ndk_version(env["ANDROID_NDK_ROOT"]) if ndk_version != None and LooseVersion(ndk_version) >= LooseVersion("17.1.4828580"): env.Append(LINKFLAGS=["-Wl,--exclude-libs,libgcc.a", "-Wl,--exclude-libs,libatomic.a", "-nostdlib++"]) else: @@ -323,8 +370,14 @@ def configure(env): env.Append(LIBS=["OpenSLES", "EGL", "GLESv2", "vulkan", "android", "log", "z", "dl"]) +# Return the project NDK version. +# This is kept in sync with the value in 'platform/android/java/app/config.gradle'. +def get_project_ndk_version(): + return "21.3.6528147" + + # Return NDK version string in source.properties (adapted from the Chromium project). -def get_ndk_version(path): +def get_env_ndk_version(path): if path is None: return None prop_file_path = os.path.join(path, "source.properties") diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp index 78685991a8..f8ac29c738 100644 --- a/platform/android/dir_access_jandroid.cpp +++ b/platform/android/dir_access_jandroid.cpp @@ -47,7 +47,7 @@ DirAccess *DirAccessJAndroid::create_fs() { Error DirAccessJAndroid::list_dir_begin() { list_dir_end(); - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); jstring js = env->NewStringUTF(current_dir.utf8().get_data()); int res = env->CallIntMethod(io, _dir_open, js); @@ -62,7 +62,7 @@ Error DirAccessJAndroid::list_dir_begin() { String DirAccessJAndroid::get_next() { ERR_FAIL_COND_V(id == 0, ""); - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); jstring str = (jstring)env->CallObjectMethod(io, _dir_next, id); if (!str) return ""; @@ -73,7 +73,7 @@ String DirAccessJAndroid::get_next() { } bool DirAccessJAndroid::current_is_dir() const { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); return env->CallBooleanMethod(io, _dir_is_dir, id); } @@ -86,7 +86,7 @@ void DirAccessJAndroid::list_dir_end() { if (id == 0) return; - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); env->CallVoidMethod(io, _dir_close, id); id = 0; } @@ -100,7 +100,7 @@ String DirAccessJAndroid::get_drive(int p_drive) { } Error DirAccessJAndroid::change_dir(String p_dir) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); if (p_dir == "" || p_dir == "." || (p_dir == ".." && current_dir == "")) return OK; @@ -154,7 +154,7 @@ bool DirAccessJAndroid::file_exists(String p_file) { } bool DirAccessJAndroid::dir_exists(String p_dir) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); String sd; @@ -207,7 +207,7 @@ size_t DirAccessJAndroid::get_space_left() { } void DirAccessJAndroid::setup(jobject p_io) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); io = p_io; jclass c = env->GetObjectClass(io); diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 62720e8249..a963c5a741 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -269,7 +269,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { Vector<Device> devices; volatile bool devices_changed; Mutex device_lock; - Thread *check_for_changes_thread; + Thread check_for_changes_thread; volatile bool quit_request; static void _check_for_changes_poll_thread(void *ud) { @@ -2848,13 +2848,12 @@ public: devices_changed = true; plugins_changed = true; quit_request = false; - check_for_changes_thread = Thread::create(_check_for_changes_poll_thread, this); + check_for_changes_thread.start(_check_for_changes_poll_thread, this); } ~EditorExportPlatformAndroid() { quit_request = true; - Thread::wait_to_finish(check_for_changes_thread); - memdelete(check_for_changes_thread); + check_for_changes_thread.wait_to_finish(); } }; diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index 53d11fda5b..814cc30613 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -98,6 +98,8 @@ android { disable 'MissingTranslation', 'UnusedResources' } + ndkVersion versions.ndkVersion + packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index 80cf6f7ede..8d3aa8a6b0 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -7,7 +7,8 @@ ext.versions = [ supportCoreUtils : '1.0.0', kotlinVersion : '1.4.10', v4Support : '1.0.0', - javaVersion : 1.8 + javaVersion : 1.8, + ndkVersion : '21.3.6528147' // Also update 'platform/android/detect.py#get_project_ndk_version()' when this is updated. ] diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle index 89ce3d15e6..6260cadffb 100644 --- a/platform/android/java/lib/build.gradle +++ b/platform/android/java/lib/build.gradle @@ -68,7 +68,7 @@ android { File sconsExecutableFile = null def sconsName = "scons" def sconsExts = (org.gradle.internal.os.OperatingSystem.current().isWindows() - ? [".bat", ".exe"] + ? [".bat", ".cmd", ".ps1", ".exe"] : [""]) logger.lifecycle("Looking for $sconsName executable path") for (ext in sconsExts) { diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index 3368363ce7..435b8b325f 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -38,6 +38,8 @@ import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener; import android.os.Build; import android.util.Log; +import android.util.SparseArray; +import android.util.SparseIntArray; import android.view.InputDevice; import android.view.InputDevice.MotionRange; import android.view.KeyEvent; @@ -46,17 +48,24 @@ import android.view.MotionEvent; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; /** * Handles input related events for the {@link GodotRenderView} view. */ public class GodotInputHandler implements InputDeviceListener { - private final ArrayList<Joystick> mJoysticksDevices = new ArrayList<Joystick>(); - private final GodotRenderView mRenderView; private final InputManagerCompat mInputManager; + private final String tag = this.getClass().getSimpleName(); + + private final SparseIntArray mJoystickIds = new SparseIntArray(4); + private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<Joystick>(4); + public GodotInputHandler(GodotRenderView godotView) { mRenderView = godotView; mInputManager = InputManagerCompat.Factory.getInputManager(mRenderView.getView().getContext()); @@ -82,19 +91,20 @@ public class GodotInputHandler implements InputDeviceListener { if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { return false; - }; + } int source = event.getSource(); if (isKeyEvent_GameDevice(source)) { - final int button = getGodotButton(keyCode); - final int device_id = findJoystickDevice(event.getDeviceId()); - // Check if the device exists - if (device_id > -1) { + final int deviceId = event.getDeviceId(); + if (mJoystickIds.indexOfKey(deviceId) >= 0) { + final int button = getGodotButton(keyCode); + final int godotJoyId = mJoystickIds.get(deviceId); + queueEvent(new Runnable() { @Override public void run() { - GodotLib.joybutton(device_id, button, false); + GodotLib.joybutton(godotJoyId, button, false); } }); } @@ -107,7 +117,7 @@ public class GodotInputHandler implements InputDeviceListener { GodotLib.key(keyCode, scanCode, chr, false); } }); - }; + } return true; } @@ -122,24 +132,25 @@ public class GodotInputHandler implements InputDeviceListener { if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { return false; - }; + } int source = event.getSource(); //Log.e(TAG, String.format("Key down! source %d, device %d, joystick %d, %d, %d", event.getDeviceId(), source, (source & InputDevice.SOURCE_JOYSTICK), (source & InputDevice.SOURCE_DPAD), (source & InputDevice.SOURCE_GAMEPAD))); + final int deviceId = event.getDeviceId(); + // Check if source is a game device and that the device is a registered gamepad if (isKeyEvent_GameDevice(source)) { if (event.getRepeatCount() > 0) // ignore key echo return true; - final int button = getGodotButton(keyCode); - final int device_id = findJoystickDevice(event.getDeviceId()); + if (mJoystickIds.indexOfKey(deviceId) >= 0) { + final int button = getGodotButton(keyCode); + final int godotJoyId = mJoystickIds.get(deviceId); - // Check if the device exists - if (device_id > -1) { queueEvent(new Runnable() { @Override public void run() { - GodotLib.joybutton(device_id, button, true); + GodotLib.joybutton(godotJoyId, button, true); } }); } @@ -152,7 +163,7 @@ public class GodotInputHandler implements InputDeviceListener { GodotLib.key(keyCode, scanCode, chr, true); } }); - }; + } return true; } @@ -203,38 +214,52 @@ public class GodotInputHandler implements InputDeviceListener { } public boolean onGenericMotionEvent(MotionEvent event) { - if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) { - final int device_id = findJoystickDevice(event.getDeviceId()); - + if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getAction() == MotionEvent.ACTION_MOVE) { // Check if the device exists - if (device_id > -1) { - Joystick joy = mJoysticksDevices.get(device_id); - - for (int i = 0; i < joy.axes.size(); i++) { - InputDevice.MotionRange range = joy.axes.get(i); - final float value = (event.getAxisValue(range.getAxis()) - range.getMin()) / range.getRange() * 2.0f - 1.0f; - final int idx = i; - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyaxis(device_id, idx, value); - } - }); + final int deviceId = event.getDeviceId(); + if (mJoystickIds.indexOfKey(deviceId) >= 0) { + final int godotJoyId = mJoystickIds.get(deviceId); + Joystick joystick = mJoysticksDevices.get(deviceId); + + for (int i = 0; i < joystick.axes.size(); i++) { + final int axis = joystick.axes.get(i); + final float value = event.getAxisValue(axis); + /** + * As all axes are polled for each event, only fire an axis event if the value has actually changed. + * Prevents flooding Godot with repeated events. + */ + if (joystick.axesValues.indexOfKey(axis) < 0 || (float)joystick.axesValues.get(axis) != value) { + // save value to prevent repeats + joystick.axesValues.put(axis, value); + final int godotAxisIdx = i; + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyaxis(godotJoyId, godotAxisIdx, value); + //Log.i(tag, "GodotLib.joyaxis("+godotJoyId+", "+godotAxisIdx+", "+value+");"); + } + }); + } } - for (int i = 0; i < joy.hats.size(); i += 2) { - final int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis())); - final int hatY = Math.round(event.getAxisValue(joy.hats.get(i + 1).getAxis())); - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyhat(device_id, hatX, hatY); - } - }); + if (joystick.hasAxisHat) { + final int hatX = Math.round(event.getAxisValue(MotionEvent.AXIS_HAT_X)); + final int hatY = Math.round(event.getAxisValue(MotionEvent.AXIS_HAT_Y)); + if (joystick.hatX != hatX || joystick.hatY != hatY) { + joystick.hatX = hatX; + joystick.hatY = hatY; + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyhat(godotJoyId, hatX, hatY); + //Log.i(tag, "GodotLib.joyhat("+godotJoyId+", "+hatX+", "+hatY+");"); + } + }); + } } return true; } - } else if ((event.getSource() & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS) { + } else if (event.isFromSource(InputDevice.SOURCE_STYLUS)) { final float x = event.getX(); final float y = event.getY(); final int type = event.getAction(); @@ -245,6 +270,7 @@ public class GodotInputHandler implements InputDeviceListener { } }); return true; + } else if (event.isFromSource(InputDevice.SOURCE_MOUSE) || event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return handleMouseEvent(event); @@ -266,67 +292,98 @@ public class GodotInputHandler implements InputDeviceListener { } } + private int assignJoystickIdNumber(int deviceId) { + int godotJoyId = 0; + while (mJoystickIds.indexOfValue(godotJoyId) >= 0) { + godotJoyId++; + } + mJoystickIds.put(deviceId, godotJoyId); + return godotJoyId; + } + @Override public void onInputDeviceAdded(int deviceId) { - int id = findJoystickDevice(deviceId); - // Check if the device has not been already added - if (id < 0) { - InputDevice device = mInputManager.getInputDevice(deviceId); - //device can be null if deviceId is not found - if (device != null) { - int sources = device.getSources(); - if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) || - ((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) { - id = mJoysticksDevices.size(); - - Joystick joy = new Joystick(); - joy.device_id = deviceId; - joy.name = device.getName(); - joy.axes = new ArrayList<InputDevice.MotionRange>(); - joy.hats = new ArrayList<InputDevice.MotionRange>(); - - List<InputDevice.MotionRange> ranges = device.getMotionRanges(); - Collections.sort(ranges, new RangeComparator()); - - for (InputDevice.MotionRange range : ranges) { - if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { - joy.hats.add(range); - } else { - joy.axes.add(range); - } - } - mJoysticksDevices.add(joy); + if (mJoystickIds.indexOfKey(deviceId) >= 0) { + return; + } + + InputDevice device = mInputManager.getInputDevice(deviceId); + //device can be null if deviceId is not found + if (device == null) { + return; + } + + int sources = device.getSources(); + + // Device may not be a joystick or gamepad + if ((sources & InputDevice.SOURCE_GAMEPAD) != InputDevice.SOURCE_GAMEPAD && + (sources & InputDevice.SOURCE_JOYSTICK) != InputDevice.SOURCE_JOYSTICK) { + return; + } + + // Assign first available number. Re-use numbers where possible. + final int id = assignJoystickIdNumber(deviceId); + + final Joystick joystick = new Joystick(); + joystick.device_id = deviceId; + joystick.name = device.getName(); + + //Helps with creating new joypad mappings. + Log.i(tag, "=== New Input Device: " + joystick.name); - final int device_id = id; - final String name = joy.name; - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyconnectionchanged(device_id, true, name); - } - }); + Set<Integer> already = new HashSet<Integer>(); + for (InputDevice.MotionRange range : device.getMotionRanges()) { + boolean isJoystick = range.isFromSource(InputDevice.SOURCE_JOYSTICK); + boolean isGamepad = range.isFromSource(InputDevice.SOURCE_GAMEPAD); + //Log.i(tag, "axis: "+range.getAxis()+ ", isJoystick: "+isJoystick+", isGamepad: "+isGamepad); + if (!isJoystick && !isGamepad) { + continue; + } + final int axis = range.getAxis(); + if (axis == MotionEvent.AXIS_HAT_X || axis == MotionEvent.AXIS_HAT_Y) { + joystick.hasAxisHat = true; + } else { + if (!already.contains(axis)) { + already.add(axis); + joystick.axes.add(axis); + } else { + Log.w(tag, " - DUPLICATE AXIS VALUE IN LIST: " + axis); } } } + Collections.sort(joystick.axes); + for (int idx = 0; idx < joystick.axes.size(); idx++) { + //Helps with creating new joypad mappings. + Log.i(tag, " - Mapping Android axis " + joystick.axes.get(idx) + " to Godot axis " + idx); + } + mJoysticksDevices.put(deviceId, joystick); + + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyconnectionchanged(id, true, joystick.name); + } + }); } @Override public void onInputDeviceRemoved(int deviceId) { - final int device_id = findJoystickDevice(deviceId); - - // Check if the evice has not been already removed - if (device_id > -1) { - mJoysticksDevices.remove(device_id); - - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyconnectionchanged(device_id, false, ""); - } - }); + // Check if the device has not been already removed + if (mJoystickIds.indexOfKey(deviceId) < 0) { + return; } + final int godotJoyId = mJoystickIds.get(deviceId); + mJoystickIds.delete(deviceId); + mJoysticksDevices.delete(deviceId); + + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyconnectionchanged(godotJoyId, false, ""); + } + }); } @Override @@ -407,16 +464,6 @@ public class GodotInputHandler implements InputDeviceListener { return button; } - private int findJoystickDevice(int device_id) { - for (int i = 0; i < mJoysticksDevices.size(); i++) { - if (mJoysticksDevices.get(i).device_id == device_id) { - return i; - } - } - - return -1; - } - private boolean handleMouseEvent(final MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_HOVER_ENTER: diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java b/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java index 82bd45ee3f..4b7318c718 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java @@ -30,9 +30,10 @@ package org.godotengine.godot.input; -import android.view.InputDevice.MotionRange; +import android.util.SparseArray; import java.util.ArrayList; +import java.util.List; /** * POJO class to represent a Joystick input device. @@ -40,6 +41,12 @@ import java.util.ArrayList; class Joystick { int device_id; String name; - ArrayList<MotionRange> axes; - ArrayList<MotionRange> hats; + List<Integer> axes = new ArrayList<Integer>(); + protected boolean hasAxisHat = false; + /* + * Keep track of values so we can prevent flooding the engine with useless events. + */ + protected final SparseArray axesValues = new SparseArray<Float>(4); + protected int hatX; + protected int hatY; } diff --git a/platform/android/java/nativeSrcsConfigs/build.gradle b/platform/android/java/nativeSrcsConfigs/build.gradle index 65b7bb9dc9..66077060ea 100644 --- a/platform/android/java/nativeSrcsConfigs/build.gradle +++ b/platform/android/java/nativeSrcsConfigs/build.gradle @@ -31,6 +31,8 @@ android { } } + ndkVersion versions.ndkVersion + externalNativeBuild { cmake { path "CMakeLists.txt" diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp index 2b5ca530da..ab03599dc3 100644 --- a/platform/android/java_class_wrapper.cpp +++ b/platform/android/java_class_wrapper.cpp @@ -37,7 +37,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, if (!M) return false; - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); MethodInfo *method = nullptr; for (List<MethodInfo>::Element *E = M->get().front(); E; E = E->next()) { @@ -964,7 +964,7 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { if (class_cache.has(p_class)) return class_cache[p_class]; - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); jclass bclass = env->FindClass(p_class.utf8().get_data()); ERR_FAIL_COND_V(!bclass, Ref<JavaClass>()); @@ -1148,7 +1148,7 @@ JavaClassWrapper *JavaClassWrapper::singleton = nullptr; JavaClassWrapper::JavaClassWrapper(jobject p_activity) { singleton = this; - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); 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 8f8275826d..4ee4427aa0 100644 --- a/platform/android/java_godot_io_wrapper.cpp +++ b/platform/android/java_godot_io_wrapper.cpp @@ -34,7 +34,7 @@ // JNIEnv is only valid within the thread it belongs to, in a multi threading environment // we can't cache it. // For GodotIO we call all access methods from our thread and we thus get a valid JNIEnv -// from ThreadAndroid. +// from get_jni_env(). GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instance) { godot_io_instance = p_env->NewGlobalRef(p_godot_io_instance); @@ -72,7 +72,7 @@ jobject GodotIOJavaWrapper::get_instance() { Error GodotIOJavaWrapper::open_uri(const String &p_uri) { if (_open_URI) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); jstring jStr = env->NewStringUTF(p_uri.utf8().get_data()); return env->CallIntMethod(godot_io_instance, _open_URI, jStr) ? ERR_CANT_OPEN : OK; } else { @@ -82,7 +82,7 @@ Error GodotIOJavaWrapper::open_uri(const String &p_uri) { String GodotIOJavaWrapper::get_user_data_dir() { if (_get_data_dir) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_data_dir); return jstring_to_string(s, env); } else { @@ -92,7 +92,7 @@ String GodotIOJavaWrapper::get_user_data_dir() { String GodotIOJavaWrapper::get_locale() { if (_get_locale) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_locale); return jstring_to_string(s, env); } else { @@ -102,7 +102,7 @@ String GodotIOJavaWrapper::get_locale() { String GodotIOJavaWrapper::get_model() { if (_get_model) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_model); return jstring_to_string(s, env); } else { @@ -112,7 +112,7 @@ String GodotIOJavaWrapper::get_model() { int GodotIOJavaWrapper::get_screen_dpi() { if (_get_screen_DPI) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); return env->CallIntMethod(godot_io_instance, _get_screen_DPI); } else { return 160; @@ -121,7 +121,7 @@ int GodotIOJavaWrapper::get_screen_dpi() { void GodotIOJavaWrapper::screen_get_usable_rect(int (&p_rect_xywh)[4]) { if (_screen_get_usable_rect) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); 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); @@ -134,7 +134,7 @@ void GodotIOJavaWrapper::screen_get_usable_rect(int (&p_rect_xywh)[4]) { String GodotIOJavaWrapper::get_unique_id() { if (_get_unique_id) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_unique_id); return jstring_to_string(s, env); } else { @@ -148,7 +148,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 = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); 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); } @@ -156,21 +156,21 @@ void GodotIOJavaWrapper::show_vk(const String &p_existing, bool p_multiline, int void GodotIOJavaWrapper::hide_vk() { if (_hide_keyboard) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); env->CallVoidMethod(godot_io_instance, _hide_keyboard); } } void GodotIOJavaWrapper::set_screen_orientation(int p_orient) { if (_set_screen_orientation) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); env->CallVoidMethod(godot_io_instance, _set_screen_orientation, p_orient); } } int GodotIOJavaWrapper::get_screen_orientation() { if (_get_screen_orientation) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); return env->CallIntMethod(godot_io_instance, _get_screen_orientation); } else { return 0; @@ -179,7 +179,7 @@ int GodotIOJavaWrapper::get_screen_orientation() { String GodotIOJavaWrapper::get_system_dir(int p_dir) { if (_get_system_dir) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); 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 e3a4ce63ef..bb22162879 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -87,7 +87,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en godot_java = new GodotJavaWrapper(env, activity, godot_instance); godot_io_java = new GodotIOJavaWrapper(env, godot_java->get_member_object("io", "Lorg/godotengine/godot/GodotIO;", env)); - ThreadAndroid::make_default(jvm); + init_thread_jandroid(jvm, env); jobject amgr = env->NewGlobalRef(p_asset_manager); @@ -119,7 +119,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline) { - ThreadAndroid::setup_thread(); + setup_android_thread(); const char **cmdline = nullptr; jstring *j_cmdline = nullptr; @@ -206,7 +206,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl return; if (step == 0) { - // Since Godot is initialized on the UI thread, _main_thread_id was set to that thread's id, + // Since Godot is initialized on the UI thread, main_thread_id was set to that thread's id, // but for Godot purposes, the main thread is the one running the game loop Main::setup2(Thread::get_caller_id()); ++step; @@ -382,7 +382,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jclass clazz) { - ThreadAndroid::setup_thread(); + setup_android_thread(); AudioDriverAndroid::thread_func(env); } diff --git a/platform/android/java_godot_view_wrapper.cpp b/platform/android/java_godot_view_wrapper.cpp index cb26c7b8c5..5b638300ef 100644 --- a/platform/android/java_godot_view_wrapper.cpp +++ b/platform/android/java_godot_view_wrapper.cpp @@ -33,7 +33,7 @@ #include "thread_jandroid.h" GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); _godot_view = env->NewGlobalRef(godot_view); @@ -47,20 +47,20 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) { void GodotJavaViewWrapper::request_pointer_capture() { if (_request_pointer_capture != 0) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); env->CallVoidMethod(_godot_view, _request_pointer_capture); } } void GodotJavaViewWrapper::release_pointer_capture() { if (_request_pointer_capture != 0) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); env->CallVoidMethod(_godot_view, _release_pointer_capture); } } GodotJavaViewWrapper::~GodotJavaViewWrapper() { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); env->DeleteGlobalRef(_godot_view); env->DeleteGlobalRef(_cls); } diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index c4e7f272d3..759980373a 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -33,7 +33,7 @@ // JNIEnv is only valid within the thread it belongs to, in a multi threading environment // we can't cache it. // For Godot we call most access methods from our thread and we thus get a valid JNIEnv -// from ThreadAndroid. For one or two we expect to pass the environment +// from get_jni_env(). For one or two we expect to pass the environment // TODO we could probably create a base class for this... @@ -91,7 +91,7 @@ jobject GodotJavaWrapper::get_activity() { jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env) { if (godot_class) { if (p_env == nullptr) - p_env = ThreadAndroid::get_env(); + p_env = get_jni_env(); jfieldID fid = p_env->GetStaticFieldID(godot_class, p_name, p_class); return p_env->GetStaticObjectField(godot_class, fid); @@ -102,7 +102,7 @@ jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_cl jobject GodotJavaWrapper::get_class_loader() { if (_get_class_loader) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); return env->CallObjectMethod(activity, _get_class_loader); } else { return nullptr; @@ -113,7 +113,7 @@ GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() { if (_godot_view != nullptr) { return _godot_view; } - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); 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; @@ -122,7 +122,7 @@ GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() { void GodotJavaWrapper::on_video_init(JNIEnv *p_env) { if (_on_video_init) if (p_env == nullptr) - p_env = ThreadAndroid::get_env(); + p_env = get_jni_env(); p_env->CallVoidMethod(godot_instance, _on_video_init); } @@ -130,7 +130,7 @@ void GodotJavaWrapper::on_video_init(JNIEnv *p_env) { void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) { if (_on_godot_main_loop_started) { if (p_env == nullptr) { - p_env = ThreadAndroid::get_env(); + p_env = get_jni_env(); } } p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started); @@ -139,7 +139,7 @@ void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) { void GodotJavaWrapper::restart(JNIEnv *p_env) { if (_restart) if (p_env == nullptr) - p_env = ThreadAndroid::get_env(); + p_env = get_jni_env(); p_env->CallVoidMethod(godot_instance, _restart); } @@ -147,21 +147,21 @@ void GodotJavaWrapper::restart(JNIEnv *p_env) { void GodotJavaWrapper::force_quit(JNIEnv *p_env) { if (_finish) if (p_env == nullptr) - p_env = ThreadAndroid::get_env(); + p_env = get_jni_env(); p_env->CallVoidMethod(godot_instance, _finish); } void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) { if (_set_keep_screen_on) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); env->CallVoidMethod(godot_instance, _set_keep_screen_on, p_enabled); } } void GodotJavaWrapper::alert(const String &p_message, const String &p_title) { if (_alert) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); 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); @@ -169,7 +169,7 @@ void GodotJavaWrapper::alert(const String &p_message, const String &p_title) { } int GodotJavaWrapper::get_gles_version_code() { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); if (_get_GLES_version_code) { return env->CallIntMethod(godot_instance, _get_GLES_version_code); } @@ -183,7 +183,7 @@ bool GodotJavaWrapper::has_get_clipboard() { String GodotJavaWrapper::get_clipboard() { if (_get_clipboard) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard); return jstring_to_string(s, env); } else { @@ -193,7 +193,7 @@ String GodotJavaWrapper::get_clipboard() { String GodotJavaWrapper::get_input_fallback_mapping() { if (_get_input_fallback_mapping) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); jstring fallback_mapping = (jstring)env->CallObjectMethod(godot_instance, _get_input_fallback_mapping); return jstring_to_string(fallback_mapping, env); } else { @@ -207,7 +207,7 @@ bool GodotJavaWrapper::has_set_clipboard() { void GodotJavaWrapper::set_clipboard(const String &p_text) { if (_set_clipboard) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); jstring jStr = env->NewStringUTF(p_text.utf8().get_data()); env->CallVoidMethod(godot_instance, _set_clipboard, jStr); } @@ -215,7 +215,7 @@ void GodotJavaWrapper::set_clipboard(const String &p_text) { bool GodotJavaWrapper::request_permission(const String &p_name) { if (_request_permission) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); jstring jStrName = env->NewStringUTF(p_name.utf8().get_data()); return env->CallBooleanMethod(godot_instance, _request_permission, jStrName); } else { @@ -225,7 +225,7 @@ bool GodotJavaWrapper::request_permission(const String &p_name) { bool GodotJavaWrapper::request_permissions() { if (_request_permissions) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); return env->CallBooleanMethod(godot_instance, _request_permissions); } else { return false; @@ -235,7 +235,7 @@ bool GodotJavaWrapper::request_permissions() { Vector<String> GodotJavaWrapper::get_granted_permissions() const { Vector<String> permissions_list; if (_get_granted_permissions) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); jobject permissions_object = env->CallObjectMethod(godot_instance, _get_granted_permissions); jobjectArray *arr = reinterpret_cast<jobjectArray *>(&permissions_object); @@ -253,14 +253,14 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const { void GodotJavaWrapper::init_input_devices() { if (_init_input_devices) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); env->CallVoidMethod(godot_instance, _init_input_devices); } } jobject GodotJavaWrapper::get_surface() { if (_get_surface) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); return env->CallObjectMethod(godot_instance, _get_surface); } else { return nullptr; @@ -269,7 +269,7 @@ jobject GodotJavaWrapper::get_surface() { bool GodotJavaWrapper::is_activity_resumed() { if (_is_activity_resumed) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); return env->CallBooleanMethod(godot_instance, _is_activity_resumed); } else { return false; @@ -278,7 +278,7 @@ bool GodotJavaWrapper::is_activity_resumed() { void GodotJavaWrapper::vibrate(int p_duration_ms) { if (_vibrate) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms); } } diff --git a/platform/android/net_socket_android.cpp b/platform/android/net_socket_android.cpp index ba7b3d3775..ddc2368793 100644 --- a/platform/android/net_socket_android.cpp +++ b/platform/android/net_socket_android.cpp @@ -38,7 +38,7 @@ jmethodID NetSocketAndroid::_multicast_lock_acquire = 0; jmethodID NetSocketAndroid::_multicast_lock_release = 0; void NetSocketAndroid::setup(jobject p_net_utils) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); net_utils = env->NewGlobalRef(p_net_utils); @@ -51,14 +51,14 @@ void NetSocketAndroid::setup(jobject p_net_utils) { void NetSocketAndroid::multicast_lock_acquire() { if (_multicast_lock_acquire) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); env->CallVoidMethod(net_utils, _multicast_lock_acquire); } } void NetSocketAndroid::multicast_lock_release() { if (_multicast_lock_release) { - JNIEnv *env = ThreadAndroid::get_env(); + JNIEnv *env = get_jni_env(); env->CallVoidMethod(net_utils, _multicast_lock_release); } } diff --git a/platform/android/string_android.h b/platform/android/string_android.h index 25c6f749d4..3721315d3f 100644 --- a/platform/android/string_android.h +++ b/platform/android/string_android.h @@ -37,14 +37,14 @@ /** * Converts JNI jstring to Godot String. * @param source Source JNI string. If null an empty string is returned. - * @param env JNI environment instance. If null obtained by ThreadAndroid::get_env(). + * @param env JNI environment instance. If null obtained by get_jni_env(). * @return Godot string instance. */ static inline String jstring_to_string(jstring source, JNIEnv *env = nullptr) { String result; if (source) { if (!env) { - env = ThreadAndroid::get_env(); + env = get_jni_env(); } const char *const source_utf8 = env->GetStringUTFChars(source, nullptr); if (source_utf8) { diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp index cb3527067f..afcc7294f2 100644 --- a/platform/android/thread_jandroid.cpp +++ b/platform/android/thread_jandroid.cpp @@ -30,116 +30,29 @@ #include "thread_jandroid.h" -#include "core/object/script_language.h" -#include "core/os/memory.h" -#include "core/templates/safe_refcount.h" +#include "core/os/thread.h" -static void _thread_id_key_destr_callback(void *p_value) { - memdelete(static_cast<Thread::ID *>(p_value)); -} - -static pthread_key_t _create_thread_id_key() { - pthread_key_t key; - pthread_key_create(&key, &_thread_id_key_destr_callback); - return key; -} - -pthread_key_t ThreadAndroid::thread_id_key = _create_thread_id_key(); -Thread::ID ThreadAndroid::next_thread_id = 0; - -Thread::ID ThreadAndroid::get_id() const { - return id; -} - -Thread *ThreadAndroid::create_thread_jandroid() { - return memnew(ThreadAndroid); -} - -void *ThreadAndroid::thread_callback(void *userdata) { - ThreadAndroid *t = reinterpret_cast<ThreadAndroid *>(userdata); - setup_thread(); - ScriptServer::thread_enter(); //scripts may need to attach a stack - t->id = atomic_increment(&next_thread_id); - pthread_setspecific(thread_id_key, (void *)memnew(ID(t->id))); - t->callback(t->user); - ScriptServer::thread_exit(); - return nullptr; -} - -Thread *ThreadAndroid::create_func_jandroid(ThreadCreateCallback p_callback, void *p_user, const Settings &) { - ThreadAndroid *tr = memnew(ThreadAndroid); - tr->callback = p_callback; - tr->user = p_user; - pthread_attr_init(&tr->pthread_attr); - pthread_attr_setdetachstate(&tr->pthread_attr, PTHREAD_CREATE_JOINABLE); - - pthread_create(&tr->pthread, &tr->pthread_attr, thread_callback, tr); - - return tr; -} +static JavaVM *java_vm = nullptr; +static thread_local JNIEnv *env = nullptr; -Thread::ID ThreadAndroid::get_thread_id_func_jandroid() { - void *value = pthread_getspecific(thread_id_key); - - if (value) - return *static_cast<ID *>(value); - - ID new_id = atomic_increment(&next_thread_id); - pthread_setspecific(thread_id_key, (void *)memnew(ID(new_id))); - return new_id; -} - -void ThreadAndroid::wait_to_finish_func_jandroid(Thread *p_thread) { - ThreadAndroid *tp = static_cast<ThreadAndroid *>(p_thread); - ERR_FAIL_COND(!tp); - ERR_FAIL_COND(tp->pthread == 0); - - pthread_join(tp->pthread, nullptr); - tp->pthread = 0; -} - -void ThreadAndroid::_thread_destroyed(void *value) { - /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */ - JNIEnv *env = (JNIEnv *)value; - if (env != nullptr) { - java_vm->DetachCurrentThread(); - pthread_setspecific(jvm_key, nullptr); - } -} - -pthread_key_t ThreadAndroid::jvm_key; -JavaVM *ThreadAndroid::java_vm = nullptr; - -void ThreadAndroid::setup_thread() { - if (pthread_getspecific(jvm_key)) - return; //already setup - JNIEnv *env; +static void init_thread() { java_vm->AttachCurrentThread(&env, nullptr); - pthread_setspecific(jvm_key, (void *)env); } -void ThreadAndroid::make_default(JavaVM *p_java_vm) { - java_vm = p_java_vm; - create_func = create_func_jandroid; - get_thread_id_func = get_thread_id_func_jandroid; - wait_to_finish_func = wait_to_finish_func_jandroid; - pthread_key_create(&jvm_key, _thread_destroyed); - setup_thread(); +static void term_thread() { + java_vm->DetachCurrentThread(); } -JNIEnv *ThreadAndroid::get_env() { - if (!pthread_getspecific(jvm_key)) { - setup_thread(); - } - - JNIEnv *env = nullptr; - java_vm->AttachCurrentThread(&env, nullptr); - return env; +void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env) { + java_vm = p_jvm; + env = p_env; + Thread::_set_platform_funcs(nullptr, nullptr, &init_thread, &term_thread); } -ThreadAndroid::ThreadAndroid() { - pthread = 0; +void setup_android_thread() { + init_thread(); } -ThreadAndroid::~ThreadAndroid() { +JNIEnv *get_jni_env() { + return env; } diff --git a/platform/android/thread_jandroid.h b/platform/android/thread_jandroid.h index c37e2d740e..ff13ae911f 100644 --- a/platform/android/thread_jandroid.h +++ b/platform/android/thread_jandroid.h @@ -28,46 +28,14 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef THREAD_POSIX_H -#define THREAD_POSIX_H +#ifndef THREAD_JANDROID_H +#define THREAD_JANDROID_H -#include "core/os/thread.h" #include <jni.h> -#include <pthread.h> -#include <sys/types.h> -class ThreadAndroid : public Thread { - static pthread_key_t thread_id_key; - static ID next_thread_id; +void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env); - pthread_t pthread; - pthread_attr_t pthread_attr; - ThreadCreateCallback callback; - void *user; - ID id; - - static Thread *create_thread_jandroid(); - - static void *thread_callback(void *userdata); - - static Thread *create_func_jandroid(ThreadCreateCallback p_callback, void *, const Settings &); - static ID get_thread_id_func_jandroid(); - static void wait_to_finish_func_jandroid(Thread *p_thread); - - static void _thread_destroyed(void *value); - ThreadAndroid(); - - static pthread_key_t jvm_key; - static JavaVM *java_vm; - -public: - virtual ID get_id() const; - - static void make_default(JavaVM *p_java_vm); - static void setup_thread(); - static JNIEnv *get_env(); - - ~ThreadAndroid(); -}; +void setup_android_thread(); +JNIEnv *get_jni_env(); #endif diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py index ad4af9ba6a..17796beb6f 100644 --- a/platform/iphone/detect.py +++ b/platform/iphone/detect.py @@ -34,6 +34,7 @@ def get_opts(): " validation layers)", False, ), + BoolVariable("ios_simulator", "Build for iOS Simulator", False), BoolVariable("ios_exceptions", "Enable exceptions", False), ("ios_triple", "Triple for ios toolchain", ""), ] @@ -107,8 +108,17 @@ def configure(env): ## Compile flags - if env["arch"] == "x86" or env["arch"] == "x86_64": + if env["ios_simulator"]: detect_darwin_sdk_path("iphonesimulator", env) + env.Append(CCFLAGS=["-mios-simulator-version-min=13.0"]) + env.Append(LINKFLAGS=["-mios-simulator-version-min=13.0"]) + env.extra_suffix = ".simulator" + env.extra_suffix + else: + detect_darwin_sdk_path("iphone", env) + env.Append(CCFLAGS=["-miphoneos-version-min=11.0"]) + env.Append(LINKFLAGS=["-miphoneos-version-min=11.0"]) + + if env["arch"] == "x86" or env["arch"] == "x86_64": env["ENV"]["MACOSX_DEPLOYMENT_TARGET"] = "10.9" arch_flag = "i386" if env["arch"] == "x86" else env["arch"] env.Append( @@ -116,11 +126,10 @@ def configure(env): "-fobjc-arc -arch " + arch_flag + " -fobjc-abi-version=2 -fobjc-legacy-dispatch -fmessage-length=0 -fpascal-strings -fblocks" - " -fasm-blocks -isysroot $IPHONESDK -mios-simulator-version-min=13.0" + " -fasm-blocks -isysroot $IPHONESDK" ).split() ) elif env["arch"] == "arm": - detect_darwin_sdk_path("iphone", env) env.Append( CCFLAGS=( "-fobjc-arc -arch armv7 -fmessage-length=0 -fno-strict-aliasing" @@ -128,16 +137,15 @@ def configure(env): " -fpascal-strings -fblocks -isysroot $IPHONESDK -fvisibility=hidden -mthumb" ' "-DIBOutlet=__attribute__((iboutlet))"' ' "-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection(ClassName)))"' - ' "-DIBAction=void)__attribute__((ibaction)" -miphoneos-version-min=11.0 -MMD -MT dependencies'.split() + ' "-DIBAction=void)__attribute__((ibaction)" -MMD -MT dependencies'.split() ) ) elif env["arch"] == "arm64": - detect_darwin_sdk_path("iphone", env) env.Append( CCFLAGS=( "-fobjc-arc -arch arm64 -fmessage-length=0 -fno-strict-aliasing" " -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits" - " -fpascal-strings -fblocks -fvisibility=hidden -MMD -MT dependencies -miphoneos-version-min=11.0" + " -fpascal-strings -fblocks -fvisibility=hidden -MMD -MT dependencies" " -isysroot $IPHONESDK".split() ) ) @@ -162,7 +170,6 @@ def configure(env): LINKFLAGS=[ "-arch", arch_flag, - "-mios-simulator-version-min=13.0", "-isysroot", "$IPHONESDK", "-Xlinker", @@ -173,46 +180,14 @@ def configure(env): ] ) elif env["arch"] == "arm": - env.Append(LINKFLAGS=["-arch", "armv7", "-Wl,-dead_strip", "-miphoneos-version-min=11.0"]) + env.Append(LINKFLAGS=["-arch", "armv7", "-Wl,-dead_strip"]) if env["arch"] == "arm64": - env.Append(LINKFLAGS=["-arch", "arm64", "-Wl,-dead_strip", "-miphoneos-version-min=11.0"]) + env.Append(LINKFLAGS=["-arch", "arm64", "-Wl,-dead_strip"]) env.Append( LINKFLAGS=[ "-isysroot", "$IPHONESDK", - "-framework", - "AudioToolbox", - "-framework", - "AVFoundation", - "-framework", - "CoreAudio", - "-framework", - "CoreGraphics", - "-framework", - "CoreMedia", - "-framework", - "CoreVideo", - "-framework", - "CoreMotion", - "-framework", - "Foundation", - "-framework", - "GameController", - "-framework", - "MediaPlayer", - "-framework", - "Metal", - "-framework", - "QuartzCore", - "-framework", - "Security", - "-framework", - "SystemConfiguration", - "-framework", - "UIKit", - "-framework", - "ARKit", ] ) diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index d0c0ef7a4b..91cecdd704 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -57,7 +57,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { // Plugins volatile bool plugins_changed; - Thread *check_for_changes_thread; + Thread check_for_changes_thread; volatile bool quit_request; Mutex plugins_lock; Vector<PluginConfigIOS> plugins; @@ -1351,6 +1351,8 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> Vector<String> added_embedded_dependenciy_names; HashMap<String, String> plist_values; + Set<String> plugin_linker_flags; + Error err; for (int i = 0; i < enabled_plugins.size(); i++) { @@ -1417,6 +1419,13 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> p_config_data.capabilities.push_back(capability); } + // Linker flags + // Checking duplicates + for (int j = 0; j < plugin.linker_flags.size(); j++) { + String linker_flag = plugin.linker_flags[j]; + plugin_linker_flags.insert(linker_flag); + } + // Plist // Using hash map container to remove duplicates const String *K = nullptr; @@ -1497,6 +1506,27 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> p_config_data.cpp_code += plugin_cpp_code.format(plugin_format, "$_"); } + + // Update Linker Flag Values + { + String result_linker_flags = " "; + for (Set<String>::Element *E = plugin_linker_flags.front(); E; E = E->next()) { + const String &flag = E->get(); + + if (flag.length() == 0) { + continue; + } + + if (result_linker_flags.length() > 0) { + result_linker_flags += ' '; + } + + result_linker_flags += flag; + } + result_linker_flags = result_linker_flags.replace("\"", "\\\""); + p_config_data.linker_flags += result_linker_flags; + } + return OK; } @@ -1570,9 +1600,9 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p return ERR_SKIP; } - String library_to_use = "libgodot.iphone." + String(p_debug ? "debug" : "release") + ".fat.a"; + String library_to_use = "libgodot.iphone." + String(p_debug ? "debug" : "release") + ".xcframework"; - print_line("Static library: " + library_to_use); + print_line("Static framework: " + library_to_use); String pkg_name; if (p_preset->get("application/name") != "") { pkg_name = p_preset->get("application/name"); // app_name @@ -1658,7 +1688,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p if (files_to_parse.has(file)) { _fix_config_file(p_preset, data, config_data, p_debug); } else if (file.begins_with("libgodot.iphone")) { - if (file != library_to_use) { + if (!file.begins_with(library_to_use) || file.ends_with(String("/empty"))) { ret = unzGoToNextFile(src_pkg_zip); continue; //ignore! } @@ -1666,7 +1696,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p #if defined(OSX_ENABLED) || defined(X11_ENABLED) is_execute = true; #endif - file = "godot_ios.a"; + file = file.replace(library_to_use, binary_name + ".xcframework"); } if (file == project_file) { @@ -1940,13 +1970,12 @@ EditorExportPlatformIOS::EditorExportPlatformIOS() { plugins_changed = true; quit_request = false; - check_for_changes_thread = Thread::create(_check_for_changes_poll_thread, this); + check_for_changes_thread.start(_check_for_changes_poll_thread, this); } EditorExportPlatformIOS::~EditorExportPlatformIOS() { quit_request = true; - Thread::wait_to_finish(check_for_changes_thread); - memdelete(check_for_changes_thread); + check_for_changes_thread.wait_to_finish(); } void register_iphone_exporter() { diff --git a/platform/iphone/godot_app_delegate.m b/platform/iphone/godot_app_delegate.m index 7b9cf7893c..3ce9bffc79 100644 --- a/platform/iphone/godot_app_delegate.m +++ b/platform/iphone/godot_app_delegate.m @@ -302,37 +302,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil; // MARK: Remote Notification -- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { - for (ApplicationDelegateService *service in services) { - if (![service respondsToSelector:_cmd]) { - continue; - } - - [service application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; - } -} - -- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { - for (ApplicationDelegateService *service in services) { - if (![service respondsToSelector:_cmd]) { - continue; - } - - [service application:application didFailToRegisterForRemoteNotificationsWithError:error]; - } -} - -- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { - for (ApplicationDelegateService *service in services) { - if (![service respondsToSelector:_cmd]) { - continue; - } - - [service application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; - } - - completionHandler(UIBackgroundFetchResultNoData); -} +// Moved to the iOS Plugin // MARK: User Activity and Handling Quick Actions diff --git a/platform/iphone/godot_view.h b/platform/iphone/godot_view.h index 29960c47a8..265f826173 100644 --- a/platform/iphone/godot_view.h +++ b/platform/iphone/godot_view.h @@ -32,12 +32,20 @@ class String; +@class GodotView; @protocol DisplayLayer; @protocol GodotViewRendererProtocol; +@protocol GodotViewDelegate + +- (BOOL)godotViewFinishedSetup:(GodotView *)view; + +@end + @interface GodotView : UIView @property(assign, nonatomic) id<GodotViewRendererProtocol> renderer; +@property(assign, nonatomic) id<GodotViewDelegate> delegate; @property(assign, readonly, nonatomic) BOOL isActive; diff --git a/platform/iphone/godot_view.mm b/platform/iphone/godot_view.mm index bf073ae295..887297848e 100644 --- a/platform/iphone/godot_view.mm +++ b/platform/iphone/godot_view.mm @@ -120,6 +120,7 @@ static const int max_touches = 8; [self stopRendering]; self.renderer = nil; + self.delegate = nil; if (self.renderingLayer) { [self.renderingLayer removeFromSuperlayer]; @@ -241,6 +242,14 @@ static const int max_touches = 8; return; } + if (self.delegate) { + BOOL delegateFinishedSetup = [self.delegate godotViewFinishedSetup:self]; + + if (!delegateFinishedSetup) { + return; + } + } + [self handleMotion]; [self.renderer renderOnView:self]; } diff --git a/platform/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm index 625cbf178e..51c4da2960 100644 --- a/platform/iphone/os_iphone.mm +++ b/platform/iphone/os_iphone.mm @@ -144,8 +144,6 @@ void OSIPhone::deinitialize_modules() { } void OSIPhone::set_main_loop(MainLoop *p_main_loop) { - godot_ios_plugins_initialize(); - main_loop = p_main_loop; if (main_loop) { @@ -179,6 +177,8 @@ bool OSIPhone::iterate() { } void OSIPhone::start() { + godot_ios_plugins_initialize(); + Main::start(); if (joypad_iphone) { diff --git a/platform/iphone/plugin/godot_plugin_config.h b/platform/iphone/plugin/godot_plugin_config.h index 72fab13600..f4e30c8349 100644 --- a/platform/iphone/plugin/godot_plugin_config.h +++ b/platform/iphone/plugin/godot_plugin_config.h @@ -66,6 +66,7 @@ struct PluginConfigIOS { inline static const char *DEPENDENCIES_SYSTEM_KEY = "system"; inline static const char *DEPENDENCIES_CAPABILITIES_KEY = "capabilities"; inline static const char *DEPENDENCIES_FILES_KEY = "files"; + inline static const char *DEPENDENCIES_LINKER_FLAGS = "linker_flags"; inline static const char *PLIST_SECTION = "plist"; @@ -89,6 +90,8 @@ struct PluginConfigIOS { Vector<String> files_to_copy; Vector<String> capabilities; + Vector<String> linker_flags; + // Optional plist section // Supports only string types for now HashMap<String, String> plist; @@ -260,6 +263,8 @@ static inline PluginConfigIOS load_plugin_config(Ref<ConfigFile> config_file, co plugin_config.files_to_copy = resolve_local_dependencies(config_base_dir, files); plugin_config.capabilities = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_CAPABILITIES_KEY, Vector<String>()); + + plugin_config.linker_flags = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_LINKER_FLAGS, Vector<String>()); } if (config_file->has_section(PluginConfigIOS::PLIST_SECTION)) { diff --git a/platform/iphone/view_controller.mm b/platform/iphone/view_controller.mm index c41aa13bb7..6cef244567 100644 --- a/platform/iphone/view_controller.mm +++ b/platform/iphone/view_controller.mm @@ -40,12 +40,14 @@ #import <AVFoundation/AVFoundation.h> #import <GameController/GameController.h> -@interface ViewController () +@interface ViewController () <GodotViewDelegate> @property(strong, nonatomic) GodotViewRenderer *renderer; @property(strong, nonatomic) GodotNativeVideoView *videoView; @property(strong, nonatomic) GodotKeyboardInputView *keyboardView; +@property(strong, nonatomic) UIView *godotLoadingOverlay; + @end @implementation ViewController @@ -62,6 +64,7 @@ self.view = view; view.renderer = self.renderer; + view.delegate = self; } - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { @@ -97,6 +100,7 @@ [super viewDidLoad]; [self observeKeyboard]; + [self displayLoadingOverlay]; if (@available(iOS 11.0, *)) { [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; @@ -121,6 +125,31 @@ object:nil]; } +- (void)displayLoadingOverlay { + NSBundle *bundle = [NSBundle mainBundle]; + NSString *storyboardName = @"Launch Screen"; + + if ([bundle pathForResource:storyboardName ofType:@"storyboardc"] == nil) { + return; + } + + UIStoryboard *launchStoryboard = [UIStoryboard storyboardWithName:storyboardName bundle:bundle]; + + UIViewController *controller = [launchStoryboard instantiateInitialViewController]; + self.godotLoadingOverlay = controller.view; + self.godotLoadingOverlay.frame = self.view.bounds; + self.godotLoadingOverlay.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + + [self.view addSubview:self.godotLoadingOverlay]; +} + +- (BOOL)godotViewFinishedSetup:(GodotView *)view { + [self.godotLoadingOverlay removeFromSuperview]; + self.godotLoadingOverlay = nil; + + return YES; +} + - (void)dealloc { [self.videoView stopVideo]; @@ -130,6 +159,11 @@ self.renderer = nil; + if (self.godotLoadingOverlay) { + [self.godotLoadingOverlay removeFromSuperview]; + self.godotLoadingOverlay = nil; + } + [[NSNotificationCenter defaultCenter] removeObserver:self]; } diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index b0302a5f88..11a45d2811 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -42,8 +42,6 @@ if env["gdnative_enabled"]: sys_env["LIBS"] = [] # We use IDBFS. Since Emscripten 1.39.1 it needs to be linked explicitly. sys_env.Append(LIBS=["idbfs.js"]) - # JS prepended to the module code loading the side library. - sys_env.Append(LINKFLAGS=["--pre-js", sys_env.File("js/dynlink.pre.js")]) # Configure it as a main module (dynamic linking support). sys_env.Append(CCFLAGS=["-s", "MAIN_MODULE=1"]) sys_env.Append(LINKFLAGS=["-s", "MAIN_MODULE=1"]) @@ -53,7 +51,6 @@ if env["gdnative_enabled"]: sys_env["ENV"]["EMCC_FORCE_STDLIBS"] = "libc,libc++,libc++abi" # The main emscripten runtime, with exported standard libraries. sys = sys_env.Program(build_targets, ["javascript_runtime.cpp"]) - sys_env.Depends(sys, "js/dynlink.pre.js") # The side library, containing all Godot code. wasm_env = env.Clone() @@ -97,7 +94,13 @@ out_files = [ zip_dir.File(binary_name + ".html"), zip_dir.File(binary_name + ".audio.worklet.js"), ] -html_file = "#misc/dist/html/editor.html" if env["tools"] else "#misc/dist/html/full-size.html" +html_file = "#misc/dist/html/full-size.html" +if env["tools"]: + subst_dict = {"\$GODOT_VERSION": env.GetBuildVersion()} + html_file = env.Substfile( + target="#bin/godot${PROGSUFFIX}.html", source="#misc/dist/html/editor.html", SUBST_DICT=subst_dict + ) + in_files = [js_wrapped, build[1], html_file, "#platform/javascript/js/libs/audio.worklet.js"] if env["gdnative_enabled"]: in_files.append(build[2]) # Runtime diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp index 6395fdf721..f7cc9e6540 100644 --- a/platform/javascript/audio_driver_javascript.cpp +++ b/platform/javascript/audio_driver_javascript.cpp @@ -267,7 +267,7 @@ int AudioDriverJavaScript::WorkletNode::create(int p_buffer_size, int p_channels void AudioDriverJavaScript::WorkletNode::start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) { godot_audio_worklet_start(p_in_buf, p_in_buf_size, p_out_buf, p_out_buf_size, state); - thread = Thread::create(_audio_thread_func, this); + thread.start(_audio_thread_func, this); } void AudioDriverJavaScript::WorkletNode::lock() { @@ -280,8 +280,6 @@ void AudioDriverJavaScript::WorkletNode::unlock() { void AudioDriverJavaScript::WorkletNode::finish() { quit = true; // Ask thread to quit. - Thread::wait_to_finish(thread); - memdelete(thread); - thread = nullptr; + thread.wait_to_finish(); } #endif diff --git a/platform/javascript/audio_driver_javascript.h b/platform/javascript/audio_driver_javascript.h index d55ec261a4..393693640f 100644 --- a/platform/javascript/audio_driver_javascript.h +++ b/platform/javascript/audio_driver_javascript.h @@ -59,7 +59,7 @@ public: STATE_MAX, }; Mutex mutex; - Thread *thread = nullptr; + Thread thread; bool quit = false; int32_t state[STATE_MAX] = { 0 }; diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index 0d57f8aad1..653d18f791 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -1,7 +1,14 @@ import os import sys -from emscripten_helpers import run_closure_compiler, create_engine_file, add_js_libraries, add_js_pre, add_js_externs +from emscripten_helpers import ( + run_closure_compiler, + create_engine_file, + add_js_libraries, + add_js_pre, + add_js_externs, + get_build_version, +) from methods import get_compiler_version from SCons.Util import WhereIs @@ -50,12 +57,13 @@ def get_flags(): def configure(env): - if not isinstance(env["initial_memory"], int): + try: + env["initial_memory"] = int(env["initial_memory"]) + except Exception: print("Initial memory must be a valid integer") sys.exit(255) ## Build type - if env["target"] == "release": # Use -Os to prioritize optimizing for reduced file size. This is # particularly valuable for the web platform because it directly @@ -139,6 +147,9 @@ def configure(env): env.AddMethod(add_js_pre, "AddJSPre") env.AddMethod(add_js_externs, "AddJSExterns") + # Add method for getting build version string. + env.AddMethod(get_build_version, "GetBuildVersion") + # Add method that joins/compiles our Engine files. env.AddMethod(create_engine_file, "CreateEngineFile") diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp index 915e8eeacf..b54ac5230a 100644 --- a/platform/javascript/display_server_javascript.cpp +++ b/platform/javascript/display_server_javascript.cpp @@ -93,7 +93,7 @@ EM_BOOL DisplayServerJavaScript::fullscreen_change_callback(int p_event_type, co DisplayServerJavaScript *display = get_singleton(); // Empty ID is canvas. String target_id = String::utf8(p_event->id); - if (target_id.is_empty() || target_id == String::utf8(display->canvas_id)) { + if (target_id.is_empty() || target_id == String::utf8(&(display->canvas_id[1]))) { // This event property is the only reliable data on // browser fullscreen state. if (p_event->isFullscreen) { @@ -455,7 +455,7 @@ DisplayServer::MouseMode DisplayServerJavaScript::mouse_get_mode() const { EmscriptenPointerlockChangeEvent ev; emscripten_get_pointerlock_status(&ev); - return (ev.isActive && String::utf8(ev.id) == String::utf8(canvas_id)) ? MOUSE_MODE_CAPTURED : MOUSE_MODE_VISIBLE; + return (ev.isActive && String::utf8(ev.id) == String::utf8(&canvas_id[1])) ? MOUSE_MODE_CAPTURED : MOUSE_MODE_VISIBLE; } // Wheel @@ -703,6 +703,9 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive // Ensure the canvas ID. godot_js_config_canvas_id_get(canvas_id, 256); + // Handle contextmenu, webglcontextlost + godot_js_display_setup_canvas(); + // Check if it's windows. swap_cancel_ok = godot_js_display_is_swap_ok_cancel() == 1; diff --git a/platform/javascript/emscripten_helpers.py b/platform/javascript/emscripten_helpers.py index 8b8c492e22..d08555916b 100644 --- a/platform/javascript/emscripten_helpers.py +++ b/platform/javascript/emscripten_helpers.py @@ -15,6 +15,15 @@ def run_closure_compiler(target, source, env, for_signature): return " ".join(cmd) +def get_build_version(env): + import 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) + + def create_engine_file(env, target, source, externs): if env["use_closure_compiler"]: return env.BuildJS(target, source, JSEXTERNS=externs) diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index 48ccc1f87a..f5d8a68994 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -135,6 +135,7 @@ public: s += "Access-Control-Allow-Origin: *\r\n"; s += "Cross-Origin-Opener-Policy: same-origin\r\n"; s += "Cross-Origin-Embedder-Policy: require-corp\r\n"; + s += "Cache-Control: no-store, max-age=0\r\n"; s += "\r\n"; CharString cs = s.utf8(); Error err = connection->put_data((const uint8_t *)cs.get_data(), cs.size() - 1); @@ -213,7 +214,7 @@ class EditorExportPlatformJavaScript : public EditorExportPlatform { Ref<EditorHTTPServer> server; bool server_quit = false; Mutex server_lock; - Thread *server_thread = nullptr; + Thread server_thread; enum ExportMode { EXPORT_MODE_NORMAL = 0, @@ -681,7 +682,7 @@ void EditorExportPlatformJavaScript::_server_thread_poll(void *data) { EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() { server.instance(); - server_thread = Thread::create(_server_thread_poll, this); + server_thread.start(_server_thread_poll, this); Ref<Image> img = memnew(Image(_javascript_logo)); logo.instance(); @@ -702,8 +703,7 @@ EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() { EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() { server->stop(); server_quit = true; - Thread::wait_to_finish(server_thread); - memdelete(server_thread); + server_thread.wait_to_finish(); } void register_javascript_exporter() { diff --git a/platform/javascript/godot_js.h b/platform/javascript/godot_js.h index 0006848756..e8f41d4cae 100644 --- a/platform/javascript/godot_js.h +++ b/platform/javascript/godot_js.h @@ -86,6 +86,7 @@ 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(); #ifdef __cplusplus } #endif diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp index c8c48dd582..1136ea299d 100644 --- a/platform/javascript/http_client_javascript.cpp +++ b/platform/javascript/http_client_javascript.cpp @@ -104,7 +104,11 @@ Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector Error err = prepare_request(p_method, p_url, p_headers); if (err != OK) return err; - godot_xhr_send_data(xhr_id, p_body.ptr(), p_body.size()); + if (p_body.is_empty()) { + godot_xhr_send(xhr_id, nullptr, 0); + } else { + godot_xhr_send(xhr_id, p_body.ptr(), p_body.size()); + } return OK; } @@ -112,7 +116,12 @@ Error HTTPClient::request(Method p_method, const String &p_url, const Vector<Str Error err = prepare_request(p_method, p_url, p_headers); if (err != OK) return err; - godot_xhr_send_string(xhr_id, p_body.utf8().get_data()); + if (p_body.is_empty()) { + godot_xhr_send(xhr_id, nullptr, 0); + } else { + const CharString cs = p_body.utf8(); + godot_xhr_send(xhr_id, cs.get_data(), cs.length()); + } return OK; } diff --git a/platform/javascript/http_request.h b/platform/javascript/http_request.h index d32b2f265e..a9d6faade2 100644 --- a/platform/javascript/http_request.h +++ b/platform/javascript/http_request.h @@ -53,9 +53,7 @@ extern int godot_xhr_open(int p_xhr_id, const char *p_method, const char *p_url, extern void godot_xhr_set_request_header(int p_xhr_id, const char *p_header, const char *p_value); -extern void godot_xhr_send_null(int p_xhr_id); -extern void godot_xhr_send_string(int p_xhr_id, const char *p_data); -extern void godot_xhr_send_data(int p_xhr_id, const void *p_data, int p_len); +extern void godot_xhr_send(int p_xhr_id, const void *p_data, int p_len); extern void godot_xhr_abort(int p_xhr_id); /* this is an HTTPClient::ResponseCode, not ::Status */ diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp index 0b8af70b13..0fe95b0a8f 100644 --- a/platform/javascript/javascript_main.cpp +++ b/platform/javascript/javascript_main.cpp @@ -88,6 +88,13 @@ extern EMSCRIPTEN_KEEPALIVE int godot_js_main(int argc, char *argv[]) { Main::start(); os->get_main_loop()->initialize(); +#ifdef TOOLS_ENABLED + if (Main::is_project_manager() && FileAccess::exists("/tmp/preload.zip")) { + PackedStringArray ps; + ps.push_back("/tmp/preload.zip"); + os->get_main_loop()->emit_signal("files_dropped", ps, -1); + } +#endif emscripten_set_main_loop(main_loop_callback, -1, false); // Immediately run the first iteration. // We are inside an animation frame, we want to immediately draw on the newly setup canvas. diff --git a/platform/javascript/js/dynlink.pre.js b/platform/javascript/js/dynlink.pre.js deleted file mode 100644 index 34bc371ea9..0000000000 --- a/platform/javascript/js/dynlink.pre.js +++ /dev/null @@ -1 +0,0 @@ -Module['dynamicLibraries'] = [Module['thisProgram'] + '.side.wasm'].concat(Module['dynamicLibraries'] ? Module['dynamicLibraries'] : []); diff --git a/platform/javascript/js/engine/engine.js b/platform/javascript/js/engine/engine.js index 4b8a7dde69..321221323c 100644 --- a/platform/javascript/js/engine/engine.js +++ b/platform/javascript/js/engine/engine.js @@ -62,7 +62,7 @@ const Engine = (function () { // Emscripten configuration. config['thisProgram'] = me.executableName; config['noExitRuntime'] = true; - config['dynamicLibraries'] = me.gdnativeLibs; + config['dynamicLibraries'] = [`${me.executableName}.side.wasm`].concat(me.gdnativeLibs); Godot(config).then(function (module) { module['initFS'](me.persistentPaths).then(function (fs_err) { me.rtenv = module; @@ -107,17 +107,6 @@ const Engine = (function () { me.canvas.tabIndex = 0; } - // Disable right-click context menu. - me.canvas.addEventListener('contextmenu', function (ev) { - ev.preventDefault(); - }, false); - - // Until context restoration is implemented warn the user of context loss. - me.canvas.addEventListener('webglcontextlost', function (ev) { - alert('WebGL context lost, please reload the page'); // eslint-disable-line no-alert - ev.preventDefault(); - }, false); - // Browser locale, or custom one if defined. let locale = me.customLocale; if (!locale) { diff --git a/platform/javascript/js/libs/library_godot_display.js b/platform/javascript/js/libs/library_godot_display.js index 2977b7c122..7b085aebc8 100644 --- a/platform/javascript/js/libs/library_godot_display.js +++ b/platform/javascript/js/libs/library_godot_display.js @@ -619,6 +619,18 @@ const GodotDisplay = { GodotDisplayListeners.add(canvas, 'drop', GodotDisplayDragDrop.handler(dropFiles)); }, + godot_js_display_setup_canvas__sig: 'v', + godot_js_display_setup_canvas: function () { + const canvas = GodotConfig.canvas; + GodotDisplayListeners.add(canvas, 'contextmenu', function (ev) { + ev.preventDefault(); + }, false); + GodotDisplayListeners.add(canvas, 'webglcontextlost', function (ev) { + alert('WebGL context lost, please reload the page'); // eslint-disable-line no-alert + ev.preventDefault(); + }, false); + }, + /* * Gamepads */ diff --git a/platform/javascript/js/libs/library_godot_http_request.js b/platform/javascript/js/libs/library_godot_http_request.js index 930d3209f8..7dc2a2df29 100644 --- a/platform/javascript/js/libs/library_godot_http_request.js +++ b/platform/javascript/js/libs/library_godot_http_request.js @@ -82,31 +82,13 @@ const GodotHTTPRequest = { GodotHTTPRequest.requests[xhrId].setRequestHeader(GodotRuntime.parseString(header), GodotRuntime.parseString(value)); }, - godot_xhr_send_null__sig: 'vi', - godot_xhr_send_null: function (xhrId) { - GodotHTTPRequest.requests[xhrId].send(); - }, - - godot_xhr_send_string__sig: 'vii', - godot_xhr_send_string: function (xhrId, strPtr) { - if (!strPtr) { - GodotRuntime.error('Failed to send string per XHR: null pointer'); - return; - } - GodotHTTPRequest.requests[xhrId].send(GodotRuntime.parseString(strPtr)); - }, - - godot_xhr_send_data__sig: 'viii', - godot_xhr_send_data: function (xhrId, ptr, len) { - if (!ptr) { - GodotRuntime.error('Failed to send data per XHR: null pointer'); - return; - } - if (len < 0) { - GodotRuntime.error('Failed to send data per XHR: buffer length less than 0'); - return; + godot_xhr_send__sig: 'viii', + godot_xhr_send: function (xhrId, p_ptr, p_len) { + let data = null; + if (p_ptr && p_len) { + data = GodotRuntime.heapCopy(HEAP8, p_ptr, p_len); } - GodotHTTPRequest.requests[xhrId].send(HEAPU8.subarray(ptr, ptr + len)); + GodotHTTPRequest.requests[xhrId].send(data); }, godot_xhr_abort__sig: 'vi', diff --git a/platform/linuxbsd/SCsub b/platform/linuxbsd/SCsub index 6e43ffcedb..ddc698a55b 100644 --- a/platform/linuxbsd/SCsub +++ b/platform/linuxbsd/SCsub @@ -18,5 +18,5 @@ common_x11 = [ prog = env.add_program("#bin/godot", ["godot_linuxbsd.cpp"] + common_x11) -if env["debug_symbols"] == "yes" and env["separate_debug_symbols"]: +if env["debug_symbols"] and env["separate_debug_symbols"]: env.AddPostAction(prog, run_in_subprocess(platform_linuxbsd_builders.make_debug_linuxbsd)) diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index a819731328..13d1fe3237 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -64,15 +64,15 @@ def get_opts(): BoolVariable("use_llvm", "Use the LLVM compiler", False), BoolVariable("use_lld", "Use the LLD linker", False), BoolVariable("use_thinlto", "Use ThinLTO", False), - BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", False), + BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", True), BoolVariable("use_coverage", "Test Godot coverage", False), BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False), BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False), BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN))", False), BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False), BoolVariable("pulseaudio", "Detect and use PulseAudio", True), - BoolVariable("udev", "Use udev for gamepad connection callbacks", False), - EnumVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", "yes", ("yes", "no")), + BoolVariable("udev", "Use udev for gamepad connection callbacks", True), + BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True), BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False), BoolVariable("touch", "Enable touch events", True), BoolVariable("execinfo", "Use libexecinfo on systems where glibc is not available", False), @@ -92,7 +92,7 @@ def configure(env): else: # optimize for size env.Prepend(CCFLAGS=["-Os"]) - if env["debug_symbols"] == "yes": + if env["debug_symbols"]: env.Prepend(CCFLAGS=["-g2"]) elif env["target"] == "release_debug": @@ -102,7 +102,7 @@ def configure(env): env.Prepend(CCFLAGS=["-Os"]) env.Prepend(CPPDEFINES=["DEBUG_ENABLED"]) - if env["debug_symbols"] == "yes": + if env["debug_symbols"]: env.Prepend(CCFLAGS=["-g2"]) elif env["target"] == "debug": @@ -390,4 +390,7 @@ def configure(env): # Link those statically for portability if env["use_static_cpp"]: - env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"]) + # Workaround for GH-31743, Ubuntu 18.04 i386 crashes when it's used. + # That doesn't make any sense but it's likely a Ubuntu bug? + if is64 or env["bits"] == "64": + env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"]) diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index 00b90923de..53baf17858 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -3360,7 +3360,7 @@ void DisplayServerX11::process_events() { Vector<String> files = String((char *)p.data).split("\n", false); for (int i = 0; i < files.size(); i++) { - files.write[i] = files[i].replace("file://", "").http_unescape().strip_edges(); + files.write[i] = files[i].replace("file://", "").uri_decode().strip_edges(); } if (!windows[window_id].drop_files_callback.is_null()) { @@ -4266,7 +4266,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode } } - events_thread = Thread::create(_poll_events_thread, this); + events_thread.start(_poll_events_thread, this); _update_real_mouse_position(windows[MAIN_WINDOW_ID]); @@ -4280,9 +4280,7 @@ DisplayServerX11::~DisplayServerX11() { _clipboard_transfer_ownership(XInternAtom(x11_display, "CLIPBOARD", 0), x11_main_window); events_thread_done = true; - Thread::wait_to_finish(events_thread); - memdelete(events_thread); - events_thread = nullptr; + events_thread.wait_to_finish(); //destroy all windows for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h index 7784ba82b5..906710f933 100644 --- a/platform/linuxbsd/display_server_x11.h +++ b/platform/linuxbsd/display_server_x11.h @@ -252,7 +252,7 @@ class DisplayServerX11 : public DisplayServer { void _dispatch_input_event(const Ref<InputEvent> &p_event); mutable Mutex events_mutex; - Thread *events_thread = nullptr; + Thread events_thread; bool events_thread_done = false; LocalVector<XEvent> polled_events; static void _poll_events_thread(void *ud); diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp index 291ca49585..4e96e6d687 100644 --- a/platform/linuxbsd/joypad_linux.cpp +++ b/platform/linuxbsd/joypad_linux.cpp @@ -32,6 +32,7 @@ #include "joypad_linux.h" +#include <dirent.h> #include <errno.h> #include <fcntl.h> #include <linux/input.h> @@ -73,13 +74,12 @@ void JoypadLinux::Joypad::reset() { JoypadLinux::JoypadLinux(Input *in) { exit_udev = false; input = in; - joy_thread = Thread::create(joy_thread_func, this); + joy_thread.start(joy_thread_func, this); } JoypadLinux::~JoypadLinux() { exit_udev = true; - Thread::wait_to_finish(joy_thread); - memdelete(joy_thread); + joy_thread.wait_to_finish(); close_joypad(); } @@ -183,13 +183,23 @@ void JoypadLinux::monitor_joypads() { { MutexLock lock(joy_mutex); - for (int i = 0; i < 32; i++) { + DIR *input_directory; + input_directory = opendir("/dev/input"); + if (input_directory) { + struct dirent *current; char fname[64]; - sprintf(fname, "/dev/input/event%d", i); - if (attached_devices.find(fname) == -1) { - open_joypad(fname); + + while ((current = readdir(input_directory)) != NULL) { + if (strncmp(current->d_name, "event", 5) != 0) { + continue; + } + sprintf(fname, "/dev/input/%.*s", 16, current->d_name); + if (attached_devices.find(fname) == -1) { + open_joypad(fname); + } } } + closedir(input_directory); } usleep(1000000); // 1s } diff --git a/platform/linuxbsd/joypad_linux.h b/platform/linuxbsd/joypad_linux.h index 20d30b510c..bf343b7ceb 100644 --- a/platform/linuxbsd/joypad_linux.h +++ b/platform/linuxbsd/joypad_linux.h @@ -72,7 +72,7 @@ private: bool exit_udev; Mutex joy_mutex; - Thread *joy_thread; + Thread joy_thread; Input *input; Joypad joypads[JOYPADS_MAX]; Vector<String> attached_devices; diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp index 44b3930d6c..09e1f9461c 100644 --- a/platform/linuxbsd/os_linuxbsd.cpp +++ b/platform/linuxbsd/os_linuxbsd.cpp @@ -410,7 +410,7 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) { OS::Time time = OS::get_singleton()->get_time(false); String timestamp = vformat("%04d-%02d-%02dT%02d:%02d:", date.year, date.month, date.day, time.hour, time.min); timestamp = vformat("%s%02d", timestamp, time.sec); // vformat only supports up to 6 arguments. - String trash_info = "[Trash Info]\nPath=" + p_path.http_escape() + "\nDeletionDate=" + timestamp + "\n"; + String trash_info = "[Trash Info]\nPath=" + p_path.uri_encode() + "\nDeletionDate=" + timestamp + "\n"; { Error err; FileAccess *file = FileAccess::open(trash_path + "/info/" + file_name + ".trashinfo", FileAccess::WRITE, &err); diff --git a/platform/osx/SCsub b/platform/osx/SCsub index aa95a89444..46c13d8550 100644 --- a/platform/osx/SCsub +++ b/platform/osx/SCsub @@ -18,5 +18,5 @@ files = [ prog = env.add_program("#bin/godot", files) -if env["debug_symbols"] == "yes" and env["separate_debug_symbols"]: +if env["debug_symbols"] and env["separate_debug_symbols"]: env.AddPostAction(prog, run_in_subprocess(platform_osx_builders.make_debug_osx)) diff --git a/platform/osx/detect.py b/platform/osx/detect.py index 466f68d269..acea00c5ac 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -31,7 +31,7 @@ def get_opts(): False, ), EnumVariable("macports_clang", "Build using Clang from MacPorts", "no", ("no", "5.0", "devel")), - EnumVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", "yes", ("yes", "no")), + BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True), BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False), BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False), BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False), @@ -54,7 +54,7 @@ def configure(env): if env["arch"] != "arm64": env.Prepend(CCFLAGS=["-msse2"]) - if env["debug_symbols"] == "yes": + if env["debug_symbols"]: env.Prepend(CCFLAGS=["-g2"]) elif env["target"] == "release_debug": @@ -63,7 +63,7 @@ def configure(env): else: # optimize for size env.Prepend(CCFLAGS=["-Os"]) env.Prepend(CPPDEFINES=["DEBUG_ENABLED"]) - if env["debug_symbols"] == "yes": + if env["debug_symbols"]: env.Prepend(CCFLAGS=["-g2"]) elif env["target"] == "debug": diff --git a/platform/server/detect.py b/platform/server/detect.py index db503584d3..1c3fa990fe 100644 --- a/platform/server/detect.py +++ b/platform/server/detect.py @@ -32,13 +32,13 @@ def get_opts(): return [ BoolVariable("use_llvm", "Use the LLVM compiler", False), - BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", False), + BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", True), BoolVariable("use_coverage", "Test Godot coverage", False), BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False), BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False), BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN))", False), BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False), - EnumVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", "yes", ("yes", "no")), + BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True), BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False), BoolVariable("execinfo", "Use libexecinfo on systems where glibc is not available", False), ] @@ -58,7 +58,7 @@ def configure(env): else: # optimize for size env.Prepend(CCFLAGS=["-Os"]) - if env["debug_symbols"] == "yes": + if env["debug_symbols"]: env.Prepend(CCFLAGS=["-g2"]) elif env["target"] == "release_debug": @@ -68,7 +68,7 @@ def configure(env): env.Prepend(CCFLAGS=["-Os"]) env.Prepend(CPPDEFINES=["DEBUG_ENABLED"]) - if env["debug_symbols"] == "yes": + if env["debug_symbols"]: env.Prepend(CCFLAGS=["-g2"]) elif env["target"] == "debug": diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index f43c4ecdaf..fa97948395 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -44,8 +44,6 @@ #include "platform/windows/windows_terminal_logger.h" #include "servers/audio_server.h" #include "servers/rendering/rendering_server_default.h" -#include "servers/rendering/rendering_server_wrap_mt.h" -#include "thread_uwp.h" #include <ppltasks.h> #include <wrl.h> @@ -130,8 +128,6 @@ void OS_UWP::initialize_core() { //RedirectIOToConsole(); - ThreadUWP::make_default(); - FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES); FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA); FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM); diff --git a/platform/uwp/thread_uwp.cpp b/platform/uwp/thread_uwp.cpp deleted file mode 100644 index 364c414375..0000000000 --- a/platform/uwp/thread_uwp.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/*************************************************************************/ -/* thread_uwp.cpp */ -/*************************************************************************/ -/* 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. */ -/*************************************************************************/ - -#include "thread_uwp.h" - -#include "core/os/memory.h" - -Thread *ThreadUWP::create_func_uwp(ThreadCreateCallback p_callback, void *p_user, const Settings &) { - ThreadUWP *thread = memnew(ThreadUWP); - - std::thread new_thread(p_callback, p_user); - std::swap(thread->thread, new_thread); - - return thread; -}; - -Thread::ID ThreadUWP::get_thread_id_func_uwp() { - return std::hash<std::thread::id>()(std::this_thread::get_id()); -}; - -void ThreadUWP::wait_to_finish_func_uwp(Thread *p_thread) { - ThreadUWP *tp = static_cast<ThreadUWP *>(p_thread); - tp->thread.join(); -}; - -Thread::ID ThreadUWP::get_id() const { - return std::hash<std::thread::id>()(thread.get_id()); -}; - -void ThreadUWP::make_default() { - create_func = create_func_uwp; - get_thread_id_func = get_thread_id_func_uwp; - wait_to_finish_func = wait_to_finish_func_uwp; -}; diff --git a/platform/uwp/thread_uwp.h b/platform/uwp/thread_uwp.h deleted file mode 100644 index 0bfc71d2e0..0000000000 --- a/platform/uwp/thread_uwp.h +++ /dev/null @@ -1,59 +0,0 @@ -/*************************************************************************/ -/* thread_uwp.h */ -/*************************************************************************/ -/* 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. */ -/*************************************************************************/ - -#ifndef THREAD_UWP_H -#define THREAD_UWP_H - -#ifdef UWP_ENABLED - -#include "core/os/thread.h" - -#include <thread> - -class ThreadUWP : public Thread { - std::thread thread; - - static Thread *create_func_uwp(ThreadCreateCallback p_callback, void *, const Settings &); - static ID get_thread_id_func_uwp(); - static void wait_to_finish_func_uwp(Thread *p_thread); - - ThreadUWP() {} - -public: - virtual ID get_id() const; - - static void make_default(); - - ~ThreadUWP() {} -}; - -#endif // UWP_ENABLED - -#endif // THREAD_UWP_H diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 0c9aa77803..47d8e14680 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -32,5 +32,5 @@ if env["vsproj"]: env.vs_srcs += ["platform/windows/" + str(x)] if not os.getenv("VCINSTALLDIR"): - if env["debug_symbols"] == "yes" and env["separate_debug_symbols"]: + if env["debug_symbols"] and env["separate_debug_symbols"]: env.AddPostAction(prog, run_in_subprocess(platform_windows_builders.make_debug_mingw)) diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 5216fca2ca..f26dea8d35 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -64,7 +64,7 @@ def get_opts(): # XP support dropped after EOL due to missing API for IPv6 and other issues # Vista support dropped after EOL due to GH-10243 ("target_win_version", "Targeted Windows version, >= 0x0601 (Windows 7)", "0x0601"), - EnumVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", "yes", ("yes", "no")), + BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True), EnumVariable("windows_subsystem", "Windows subsystem", "default", ("default", "console", "gui")), BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False), ("msvc_version", "MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.", None), @@ -204,12 +204,12 @@ def configure_msvc(env, manual_msvc_config): env.Append(LINKFLAGS=["/OPT:REF"]) elif env["target"] == "debug": - env.AppendUnique(CCFLAGS=["/Z7", "/Od", "/EHsc"]) + env.AppendUnique(CCFLAGS=["/Zi", "/FS", "/Od", "/EHsc"]) env.AppendUnique(CPPDEFINES=["DEBUG_ENABLED"]) env.Append(LINKFLAGS=["/DEBUG"]) - if env["debug_symbols"] == "yes": - env.AppendUnique(CCFLAGS=["/Z7"]) + if env["debug_symbols"]: + env.AppendUnique(CCFLAGS=["/Zi", "/FS"]) env.AppendUnique(LINKFLAGS=["/DEBUG"]) if env["windows_subsystem"] == "gui": @@ -224,6 +224,7 @@ def configure_msvc(env, manual_msvc_config): env.AppendUnique(CCFLAGS=["/MT"]) else: env.AppendUnique(CCFLAGS=["/MD"]) + env.AppendUnique(CCFLAGS=["/Gd", "/GR", "/nologo"]) # Force to use Unicode encoding env.AppendUnique(CCFLAGS=["/utf-8"]) @@ -339,13 +340,13 @@ def configure_mingw(env): else: # optimize for size env.Prepend(CCFLAGS=["-Os"]) - if env["debug_symbols"] == "yes": + if env["debug_symbols"]: env.Prepend(CCFLAGS=["-g2"]) elif env["target"] == "release_debug": env.Append(CCFLAGS=["-O2"]) env.Append(CPPDEFINES=["DEBUG_ENABLED"]) - if env["debug_symbols"] == "yes": + if env["debug_symbols"]: env.Prepend(CCFLAGS=["-g2"]) if env["optimize"] == "speed": # optimize for speed (default) env.Append(CCFLAGS=["-O2"]) diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 14e7d395d3..21dfc4ae2c 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -1784,7 +1784,10 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) Ref<InputEventFromWindow> event_from_window = p_event; if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) { //send to a window - ERR_FAIL_COND(!windows.has(event_from_window->get_window_id())); + if (!windows.has(event_from_window->get_window_id())) { + in_dispatch_input_event = false; + ERR_FAIL_MSG("DisplayServerWindows: Invalid window id in input event."); + } Callable callable = windows[event_from_window->get_window_id()].input_event_callback; if (callable.is_null()) { in_dispatch_input_event = false; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 3c38c715c1..2dd9ed4bd1 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -39,14 +39,12 @@ #include "core/version_generated.gen.h" #include "drivers/windows/dir_access_windows.h" #include "drivers/windows/file_access_windows.h" -#include "drivers/windows/thread_windows.h" #include "joypad_windows.h" #include "lang_table.h" #include "main/main.h" #include "platform/windows/display_server_windows.h" #include "servers/audio_server.h" #include "servers/rendering/rendering_server_default.h" -#include "servers/rendering/rendering_server_wrap_mt.h" #include "windows_terminal_logger.h" #include <avrt.h> @@ -176,8 +174,6 @@ void OS_Windows::initialize() { //RedirectIOToConsole(); - ThreadWindows::make_default(); - FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES); FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA); FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM); |