summaryrefslogtreecommitdiffstats
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/java/app/AndroidManifest.xml4
-rw-r--r--platform/android/java/app/config.gradle2
-rw-r--r--platform/android/java/editor/build.gradle2
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt12
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt10
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.kt128
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java7
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotIO.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java7
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java49
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt36
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java139
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java18
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt11
-rw-r--r--platform/android/java_godot_lib_jni.cpp60
-rw-r--r--platform/android/java_godot_lib_jni.h1
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.cpp5
-rw-r--r--platform/macos/display_server_macos.mm16
-rw-r--r--platform/macos/godot_main_macos.mm4
-rw-r--r--platform/macos/godot_menu_item.h1
-rw-r--r--platform/macos/godot_menu_item.mm14
-rw-r--r--platform/macos/native_menu_macos.mm37
-rw-r--r--platform/web/audio_driver_web.cpp32
-rw-r--r--platform/web/audio_driver_web.h1
-rw-r--r--platform/web/godot_audio.h1
-rw-r--r--platform/web/js/libs/library_godot_audio.js27
-rw-r--r--platform/web/js/libs/library_godot_input.js1
-rw-r--r--platform/web/js/libs/library_godot_javascript_singleton.js16
-rw-r--r--platform/web/web_main.cpp4
-rw-r--r--platform/windows/display_server_windows.cpp9
-rw-r--r--platform/windows/display_server_windows.h5
-rw-r--r--platform/windows/native_menu_windows.cpp37
-rw-r--r--platform/windows/native_menu_windows.h1
33 files changed, 441 insertions, 258 deletions
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index 4abc6548bf..0cc929d226 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -24,6 +24,10 @@
android:hasFragileUserData="false"
android:requestLegacyExternalStorage="false"
tools:ignore="GoogleAppIndexingWarning" >
+ <profileable
+ android:shell="true"
+ android:enabled="true"
+ tools:targetApi="29" />
<!-- Records the version of the Godot editor used for building -->
<meta-data
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index 01759a1b2f..eb9ad9de05 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -7,7 +7,7 @@ ext.versions = [
targetSdk : 34,
buildTools : '34.0.0',
kotlinVersion : '1.9.20',
- fragmentVersion : '1.6.2',
+ fragmentVersion : '1.7.1',
nexusPublishVersion: '1.3.0',
javaVersion : JavaVersion.VERSION_17,
// Also update 'platform/android/detect.py#get_ndk_version()' when this is updated.
diff --git a/platform/android/java/editor/build.gradle b/platform/android/java/editor/build.gradle
index 55fe2a22fe..37f68d295a 100644
--- a/platform/android/java/editor/build.gradle
+++ b/platform/android/java/editor/build.gradle
@@ -9,7 +9,7 @@ dependencies {
implementation "androidx.fragment:fragment:$versions.fragmentVersion"
implementation project(":lib")
- implementation "androidx.window:window:1.2.0"
+ implementation "androidx.window:window:1.3.0"
implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion"
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
}
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
index 5515347bd6..dad397de61 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
@@ -117,6 +117,10 @@ open class GodotEditor : GodotActivity() {
val longPressEnabled = enableLongPressGestures()
val panScaleEnabled = enablePanAndScaleGestures()
+ val useInputBuffering = useInputBuffering()
+ val useAccumulatedInput = useAccumulatedInput()
+ GodotLib.updateInputDispatchSettings(useAccumulatedInput, useInputBuffering)
+
checkForProjectPermissionsToEnable()
runOnUiThread {
@@ -124,6 +128,7 @@ open class GodotEditor : GodotActivity() {
godotFragment?.godot?.renderView?.inputHandler?.apply {
enableLongPress(longPressEnabled)
enablePanningAndScalingGestures(panScaleEnabled)
+ enableInputDispatchToRenderThread(!useInputBuffering && !useAccumulatedInput)
}
}
}
@@ -275,6 +280,13 @@ open class GodotEditor : GodotActivity() {
java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/touchscreen/enable_pan_and_scale_gestures"))
/**
+ * Use input buffering for the Godot Android editor.
+ */
+ protected open fun useInputBuffering() = java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/editor/android/use_input_buffering"))
+
+ protected open fun useAccumulatedInput() = java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/editor/android/use_accumulated_input"))
+
+ /**
* Whether we should launch the new godot instance in an adjacent window
* @see https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT
*/
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
index 8e4e089211..f50b5577c3 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
@@ -30,6 +30,8 @@
package org.godotengine.editor
+import org.godotengine.godot.GodotLib
+
/**
* Drives the 'run project' window of the Godot Editor.
*/
@@ -39,9 +41,13 @@ class GodotGame : GodotEditor() {
override fun overrideOrientationRequest() = false
- override fun enableLongPressGestures() = false
+ override fun enableLongPressGestures() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_long_press_as_right_click"))
+
+ override fun enablePanAndScaleGestures() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
+
+ override fun useInputBuffering() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_input_buffering"))
- override fun enablePanAndScaleGestures() = false
+ override fun useAccumulatedInput() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_accumulated_input"))
override fun checkForProjectPermissionsToEnable() {
// Nothing to do.. by the time we get here, the project permissions will have already
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
index 290be727ab..c188a97ca5 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
@@ -84,17 +84,20 @@ class Godot(private val context: Context) : SensorEventListener {
private companion object {
private val TAG = Godot::class.java.simpleName
- }
- private val windowManager: WindowManager by lazy {
- requireActivity().getSystemService(Context.WINDOW_SERVICE) as WindowManager
+ // Supported build flavors
+ const val EDITOR_FLAVOR = "editor"
+ const val TEMPLATE_FLAVOR = "template"
}
+
+ private val windowManager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+ private val mSensorManager: SensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
+ private val mClipboard: ClipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
+ private val vibratorService: Vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+
private val pluginRegistry: GodotPluginRegistry by lazy {
GodotPluginRegistry.getPluginRegistry()
}
- private val mSensorManager: SensorManager by lazy {
- requireActivity().getSystemService(Context.SENSOR_SERVICE) as SensorManager
- }
private val mAccelerometer: Sensor? by lazy {
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
}
@@ -107,9 +110,6 @@ class Godot(private val context: Context) : SensorEventListener {
private val mGyroscope: Sensor? by lazy {
mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
}
- private val mClipboard: ClipboardManager by lazy {
- requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
- }
private val uiChangeListener = View.OnSystemUiVisibilityChangeListener { visibility: Int ->
if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
@@ -192,6 +192,8 @@ class Godot(private val context: Context) : SensorEventListener {
return
}
+ Log.v(TAG, "OnCreate: $primaryHost")
+
darkMode = context.resources?.configuration?.uiMode?.and(Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
beginBenchmarkMeasure("Startup", "Godot::onCreate")
@@ -200,6 +202,8 @@ class Godot(private val context: Context) : SensorEventListener {
val activity = requireActivity()
val window = activity.window
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
+
+ Log.v(TAG, "Initializing Godot plugin registry")
GodotPluginRegistry.initializePluginRegistry(this, primaryHost.getHostPlugins(this))
if (io == null) {
io = GodotIO(activity)
@@ -323,13 +327,17 @@ class Godot(private val context: Context) : SensorEventListener {
return false
}
- if (expansionPackPath.isNotEmpty()) {
- commandLine.add("--main-pack")
- commandLine.add(expansionPackPath)
- }
- val activity = requireActivity()
- if (!nativeLayerInitializeCompleted) {
- nativeLayerInitializeCompleted = GodotLib.initialize(
+ Log.v(TAG, "OnInitNativeLayer: $host")
+
+ beginBenchmarkMeasure("Startup", "Godot::onInitNativeLayer")
+ try {
+ if (expansionPackPath.isNotEmpty()) {
+ commandLine.add("--main-pack")
+ commandLine.add(expansionPackPath)
+ }
+ val activity = requireActivity()
+ if (!nativeLayerInitializeCompleted) {
+ nativeLayerInitializeCompleted = GodotLib.initialize(
activity,
this,
activity.assets,
@@ -338,15 +346,20 @@ class Godot(private val context: Context) : SensorEventListener {
directoryAccessHandler,
fileAccessHandler,
useApkExpansion,
- )
- }
+ )
+ Log.v(TAG, "Godot native layer initialization completed: $nativeLayerInitializeCompleted")
+ }
- if (nativeLayerInitializeCompleted && !nativeLayerSetupCompleted) {
- nativeLayerSetupCompleted = GodotLib.setup(commandLine.toTypedArray(), tts)
- if (!nativeLayerSetupCompleted) {
- Log.e(TAG, "Unable to setup the Godot engine! Aborting...")
- alert(R.string.error_engine_setup_message, R.string.text_error_title, this::forceQuit)
+ if (nativeLayerInitializeCompleted && !nativeLayerSetupCompleted) {
+ nativeLayerSetupCompleted = GodotLib.setup(commandLine.toTypedArray(), tts)
+ if (!nativeLayerSetupCompleted) {
+ throw IllegalStateException("Unable to setup the Godot engine! Aborting...")
+ } else {
+ Log.v(TAG, "Godot native layer setup completed")
+ }
}
+ } finally {
+ endBenchmarkMeasure("Startup", "Godot::onInitNativeLayer")
}
return isNativeInitialized()
}
@@ -370,6 +383,9 @@ class Godot(private val context: Context) : SensorEventListener {
throw IllegalStateException("onInitNativeLayer() must be invoked successfully prior to initializing the render view")
}
+ Log.v(TAG, "OnInitRenderView: $host")
+
+ beginBenchmarkMeasure("Startup", "Godot::onInitRenderView")
try {
val activity: Activity = host.activity
containerLayout = providedContainerLayout
@@ -392,8 +408,7 @@ class Godot(private val context: Context) : SensorEventListener {
containerLayout?.addView(editText)
renderView = if (usesVulkan()) {
if (!meetsVulkanRequirements(activity.packageManager)) {
- alert(R.string.error_missing_vulkan_requirements_message, R.string.text_error_title, this::forceQuit)
- return null
+ throw IllegalStateException(activity.getString(R.string.error_missing_vulkan_requirements_message))
}
GodotVulkanRenderView(host, this)
} else {
@@ -482,11 +497,14 @@ class Godot(private val context: Context) : SensorEventListener {
containerLayout?.removeAllViews()
containerLayout = null
}
+
+ endBenchmarkMeasure("Startup", "Godot::onInitRenderView")
}
return containerLayout
}
fun onStart(host: GodotHost) {
+ Log.v(TAG, "OnStart: $host")
if (host != primaryHost) {
return
}
@@ -495,6 +513,7 @@ class Godot(private val context: Context) : SensorEventListener {
}
fun onResume(host: GodotHost) {
+ Log.v(TAG, "OnResume: $host")
if (host != primaryHost) {
return
}
@@ -527,6 +546,7 @@ class Godot(private val context: Context) : SensorEventListener {
}
fun onPause(host: GodotHost) {
+ Log.v(TAG, "OnPause: $host")
if (host != primaryHost) {
return
}
@@ -539,6 +559,7 @@ class Godot(private val context: Context) : SensorEventListener {
}
fun onStop(host: GodotHost) {
+ Log.v(TAG, "OnStop: $host")
if (host != primaryHost) {
return
}
@@ -547,6 +568,7 @@ class Godot(private val context: Context) : SensorEventListener {
}
fun onDestroy(primaryHost: GodotHost) {
+ Log.v(TAG, "OnDestroy: $primaryHost")
if (this.primaryHost != primaryHost) {
return
}
@@ -604,18 +626,29 @@ class Godot(private val context: Context) : SensorEventListener {
* Invoked on the render thread when the Godot setup is complete.
*/
private fun onGodotSetupCompleted() {
- Log.d(TAG, "OnGodotSetupCompleted")
-
- // These properties are defined after Godot setup completion, so we retrieve them here.
- val longPressEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_long_press_as_right_click"))
- val panScaleEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
- val rotaryInputAxis = java.lang.Integer.parseInt(GodotLib.getGlobal("input_devices/pointing/android/rotary_input_scroll_axis"))
-
- runOnUiThread {
- renderView?.inputHandler?.apply {
- enableLongPress(longPressEnabled)
- enablePanningAndScalingGestures(panScaleEnabled)
- setRotaryInputAxis(rotaryInputAxis)
+ Log.v(TAG, "OnGodotSetupCompleted")
+
+ if (!isEditorBuild()) {
+ // These properties are defined after Godot setup completion, so we retrieve them here.
+ val longPressEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_long_press_as_right_click"))
+ val panScaleEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
+ val rotaryInputAxisValue = GodotLib.getGlobal("input_devices/pointing/android/rotary_input_scroll_axis")
+
+ val useInputBuffering = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_input_buffering"))
+ val useAccumulatedInput = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_accumulated_input"))
+ GodotLib.updateInputDispatchSettings(useAccumulatedInput, useInputBuffering)
+
+ runOnUiThread {
+ renderView?.inputHandler?.apply {
+ enableLongPress(longPressEnabled)
+ enablePanningAndScalingGestures(panScaleEnabled)
+ enableInputDispatchToRenderThread(!useInputBuffering && !useAccumulatedInput)
+ try {
+ setRotaryInputAxis(Integer.parseInt(rotaryInputAxisValue))
+ } catch (e: NumberFormatException) {
+ Log.w(TAG, e)
+ }
+ }
}
}
@@ -629,7 +662,7 @@ class Godot(private val context: Context) : SensorEventListener {
* Invoked on the render thread when the Godot main loop has started.
*/
private fun onGodotMainLoopStarted() {
- Log.d(TAG, "OnGodotMainLoopStarted")
+ Log.v(TAG, "OnGodotMainLoopStarted")
for (plugin in pluginRegistry.allPlugins) {
plugin.onGodotMainLoopStarted()
@@ -646,12 +679,7 @@ class Godot(private val context: Context) : SensorEventListener {
decorView.setOnSystemUiVisibilityChangeListener(uiChangeListener)
}
- @Keep
- private fun alert(message: String, title: String) {
- alert(message, title, null)
- }
-
- private fun alert(
+ fun alert(
@StringRes messageResId: Int,
@StringRes titleResId: Int,
okCallback: Runnable?
@@ -660,7 +688,9 @@ class Godot(private val context: Context) : SensorEventListener {
alert(res.getString(messageResId), res.getString(titleResId), okCallback)
}
- private fun alert(message: String, title: String, okCallback: Runnable?) {
+ @JvmOverloads
+ @Keep
+ fun alert(message: String, title: String, okCallback: Runnable? = null) {
val activity: Activity = getActivity() ?: return
runOnUiThread {
val builder = AlertDialog.Builder(activity)
@@ -759,6 +789,11 @@ class Godot(private val context: Context) : SensorEventListener {
return mClipboard.hasPrimaryClip()
}
+ /**
+ * @return true if this is an editor build, false if this is a template build
+ */
+ fun isEditorBuild() = BuildConfig.FLAVOR == EDITOR_FLAVOR
+
fun getClipboard(): String {
val clipData = mClipboard.primaryClip ?: return ""
val text = clipData.getItemAt(0).text ?: return ""
@@ -770,7 +805,7 @@ class Godot(private val context: Context) : SensorEventListener {
mClipboard.setPrimaryClip(clip)
}
- private fun forceQuit() {
+ fun forceQuit() {
forceQuit(0)
}
@@ -881,7 +916,6 @@ class Godot(private val context: Context) : SensorEventListener {
@Keep
private fun vibrate(durationMs: Int, amplitude: Int) {
if (durationMs > 0 && requestPermission("VIBRATE")) {
- val vibratorService = getActivity()?.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator? ?: return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (amplitude <= -1) {
vibratorService.vibrate(
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java
index a323045e1b..1612ddd0b3 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java
@@ -42,6 +42,7 @@ import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.os.Messenger;
+import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -203,6 +204,12 @@ public class GodotFragment extends Fragment implements IDownloaderClient, GodotH
if (godotContainerLayout == null) {
throw new IllegalStateException("Unable to initialize engine render view");
}
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Engine initialization failed", e);
+ final String errorMessage = TextUtils.isEmpty(e.getMessage())
+ ? getString(R.string.error_engine_setup_message)
+ : e.getMessage();
+ godot.alert(errorMessage, getString(R.string.text_error_title), godot::forceQuit);
} catch (IllegalArgumentException ignored) {
final Activity activity = getActivity();
Intent notifierIntent = new Intent(activity, activity.getClass());
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
index 4b51bd778d..219631284a 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
@@ -121,7 +121,7 @@ public class GodotIO {
activity.startActivity(intent);
return 0;
- } catch (ActivityNotFoundException e) {
+ } catch (Exception e) {
Log.e(TAG, "Unable to open uri " + uriString, e);
return 1;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
index d0c3d4a687..37e889daf7 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -240,4 +240,11 @@ public class GodotLib {
* @see GodotRenderer#onActivityPaused()
*/
public static native void onRendererPaused();
+
+ /**
+ * Invoked on the GL thread to update the input dispatch settings
+ * @param useAccumulatedInput True to use accumulated input, false otherwise
+ * @param useInputBuffering True to use input buffering, false otherwise
+ */
+ public static native void updateInputDispatchSettings(boolean useAccumulatedInput, boolean useInputBuffering);
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java
index c316812404..c9421a3257 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java
@@ -1704,15 +1704,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
mHasSurface = true;
mFinishedCreatingEglSurface = false;
sGLThreadManager.notifyAll();
- while (mWaitingForSurface
- && !mFinishedCreatingEglSurface
- && !mExited) {
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
}
}
@@ -1723,13 +1714,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
}
mHasSurface = false;
sGLThreadManager.notifyAll();
- while((!mWaitingForSurface) && (!mExited)) {
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
}
}
@@ -1740,16 +1724,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
}
mRequestPaused = true;
sGLThreadManager.notifyAll();
- while ((! mExited) && (! mPaused)) {
- if (LOG_PAUSE_RESUME) {
- Log.i("Main thread", "onPause waiting for mPaused.");
- }
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
- }
}
}
@@ -1762,16 +1736,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
mRequestRender = true;
mRenderComplete = false;
sGLThreadManager.notifyAll();
- while ((! mExited) && mPaused && (!mRenderComplete)) {
- if (LOG_PAUSE_RESUME) {
- Log.i("Main thread", "onResume waiting for !mPaused.");
- }
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
- }
}
}
@@ -1793,19 +1757,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
}
sGLThreadManager.notifyAll();
-
- // Wait for thread to react to resize and render a frame
- while (! mExited && !mPaused && !mRenderComplete
- && ableToDraw()) {
- if (LOG_SURFACE) {
- Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId());
- }
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
- }
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
index 49b34a5229..4cd3bd8db9 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
@@ -44,7 +44,7 @@ import org.godotengine.godot.GodotLib
* @See https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener
* @See https://developer.android.com/reference/android/view/ScaleGestureDetector.OnScaleGestureListener
*/
-internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureListener {
+internal class GodotGestureHandler(private val inputHandler: GodotInputHandler) : SimpleOnGestureListener(), OnScaleGestureListener {
companion object {
private val TAG = GodotGestureHandler::class.java.simpleName
@@ -65,13 +65,13 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
private var lastDragY: Float = 0.0f
override fun onDown(event: MotionEvent): Boolean {
- GodotInputHandler.handleMotionEvent(event, MotionEvent.ACTION_DOWN, nextDownIsDoubleTap)
+ inputHandler.handleMotionEvent(event, MotionEvent.ACTION_DOWN, nextDownIsDoubleTap)
nextDownIsDoubleTap = false
return true
}
override fun onSingleTapUp(event: MotionEvent): Boolean {
- GodotInputHandler.handleMotionEvent(event)
+ inputHandler.handleMotionEvent(event)
return true
}
@@ -85,10 +85,10 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
}
// Cancel the previous down event
- GodotInputHandler.handleMotionEvent(event, MotionEvent.ACTION_CANCEL)
+ inputHandler.handleMotionEvent(event, MotionEvent.ACTION_CANCEL)
// Turn a context click into a single tap right mouse button click.
- GodotInputHandler.handleMouseEvent(
+ inputHandler.handleMouseEvent(
event,
MotionEvent.ACTION_DOWN,
MotionEvent.BUTTON_SECONDARY,
@@ -104,7 +104,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
if (!hasCapture) {
// Dispatch a mouse relative ACTION_UP event to signal the end of the capture
- GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_UP, true)
+ inputHandler.handleMouseEvent(MotionEvent.ACTION_UP, true)
}
pointerCaptureInProgress = hasCapture
}
@@ -131,9 +131,9 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
if (contextClickInProgress || GodotInputHandler.isMouseEvent(event)) {
// This may be an ACTION_BUTTON_RELEASE event which we don't handle,
// so we convert it to an ACTION_UP event.
- GodotInputHandler.handleMouseEvent(event, MotionEvent.ACTION_UP)
+ inputHandler.handleMouseEvent(event, MotionEvent.ACTION_UP)
} else {
- GodotInputHandler.handleTouchEvent(event)
+ inputHandler.handleTouchEvent(event)
}
pointerCaptureInProgress = false
dragInProgress = false
@@ -148,7 +148,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
private fun onActionMove(event: MotionEvent): Boolean {
if (contextClickInProgress) {
- GodotInputHandler.handleMouseEvent(event, event.actionMasked, MotionEvent.BUTTON_SECONDARY, false)
+ inputHandler.handleMouseEvent(event, event.actionMasked, MotionEvent.BUTTON_SECONDARY, false)
return true
} else if (!scaleInProgress) {
// The 'onScroll' event is triggered with a long delay.
@@ -158,7 +158,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
if (lastDragX != event.getX(0) || lastDragY != event.getY(0)) {
lastDragX = event.getX(0)
lastDragY = event.getY(0)
- GodotInputHandler.handleMotionEvent(event)
+ inputHandler.handleMotionEvent(event)
return true
}
}
@@ -168,9 +168,9 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
override fun onDoubleTapEvent(event: MotionEvent): Boolean {
if (event.actionMasked == MotionEvent.ACTION_UP) {
nextDownIsDoubleTap = false
- GodotInputHandler.handleMotionEvent(event)
+ inputHandler.handleMotionEvent(event)
} else if (event.actionMasked == MotionEvent.ACTION_MOVE && !panningAndScalingEnabled) {
- GodotInputHandler.handleMotionEvent(event)
+ inputHandler.handleMotionEvent(event)
}
return true
@@ -191,7 +191,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
if (dragInProgress || lastDragX != 0.0f || lastDragY != 0.0f) {
if (originEvent != null) {
// Cancel the drag
- GodotInputHandler.handleMotionEvent(originEvent, MotionEvent.ACTION_CANCEL)
+ inputHandler.handleMotionEvent(originEvent, MotionEvent.ACTION_CANCEL)
}
dragInProgress = false
lastDragX = 0.0f
@@ -202,12 +202,12 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
val x = terminusEvent.x
val y = terminusEvent.y
if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled && !pointerCaptureInProgress && !dragInProgress) {
- GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f)
+ inputHandler.handlePanEvent(x, y, distanceX / 5f, distanceY / 5f)
} else if (!scaleInProgress) {
dragInProgress = true
lastDragX = terminusEvent.getX(0)
lastDragY = terminusEvent.getY(0)
- GodotInputHandler.handleMotionEvent(terminusEvent)
+ inputHandler.handleMotionEvent(terminusEvent)
}
return true
}
@@ -218,11 +218,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
}
if (detector.scaleFactor >= 0.8f && detector.scaleFactor != 1f && detector.scaleFactor <= 1.2f) {
- GodotLib.magnify(
- detector.focusX,
- detector.focusY,
- detector.scaleFactor
- )
+ inputHandler.handleMagnifyEvent(detector.focusX, detector.focusY, detector.scaleFactor)
}
return true
}
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 83e76e49c9..889618914d 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
@@ -75,7 +75,9 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
*/
private int lastSeenToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
- private static int rotaryInputAxis = ROTARY_INPUT_VERTICAL_AXIS;
+ private int rotaryInputAxis = ROTARY_INPUT_VERTICAL_AXIS;
+
+ private boolean dispatchInputToRenderThread = false;
public GodotInputHandler(GodotRenderView godotView) {
final Context context = godotView.getView().getContext();
@@ -83,7 +85,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
mInputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
mInputManager.registerInputDeviceListener(this, null);
- this.godotGestureHandler = new GodotGestureHandler();
+ this.godotGestureHandler = new GodotGestureHandler(this);
this.gestureDetector = new GestureDetector(context, godotGestureHandler);
this.gestureDetector.setIsLongpressEnabled(false);
this.scaleGestureDetector = new ScaleGestureDetector(context, godotGestureHandler);
@@ -109,6 +111,22 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
/**
+ * Specifies whether input should be dispatch on the UI thread or on the Render thread.
+ * @param enable true to dispatch input on the Render thread, false to dispatch input on the UI thread
+ */
+ public void enableInputDispatchToRenderThread(boolean enable) {
+ this.dispatchInputToRenderThread = enable;
+ }
+
+ /**
+ * @return true if input must be dispatched from the render thread. If false, input is
+ * dispatched from the UI thread.
+ */
+ private boolean shouldDispatchInputToRenderThread() {
+ return dispatchInputToRenderThread;
+ }
+
+ /**
* On Wear OS devices, sets which axis of the mouse wheel rotary input is mapped to. This is 1 (vertical axis) by default.
*/
public void setRotaryInputAxis(int axis) {
@@ -151,14 +169,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
if (mJoystickIds.indexOfKey(deviceId) >= 0) {
final int button = getGodotButton(keyCode);
final int godotJoyId = mJoystickIds.get(deviceId);
- GodotLib.joybutton(godotJoyId, button, false);
+ handleJoystickButtonEvent(godotJoyId, button, false);
}
} else {
// getKeyCode(): The physical key that was pressed.
final int physical_keycode = event.getKeyCode();
final int unicode = event.getUnicodeChar();
final int key_label = event.getDisplayLabel();
- GodotLib.key(physical_keycode, unicode, key_label, false, event.getRepeatCount() > 0);
+ handleKeyEvent(physical_keycode, unicode, key_label, false, event.getRepeatCount() > 0);
};
return true;
@@ -187,13 +205,13 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
if (mJoystickIds.indexOfKey(deviceId) >= 0) {
final int button = getGodotButton(keyCode);
final int godotJoyId = mJoystickIds.get(deviceId);
- GodotLib.joybutton(godotJoyId, button, true);
+ handleJoystickButtonEvent(godotJoyId, button, true);
}
} else {
final int physical_keycode = event.getKeyCode();
final int unicode = event.getUnicodeChar();
final int key_label = event.getDisplayLabel();
- GodotLib.key(physical_keycode, unicode, key_label, true, event.getRepeatCount() > 0);
+ handleKeyEvent(physical_keycode, unicode, key_label, true, event.getRepeatCount() > 0);
}
return true;
@@ -248,7 +266,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
if (joystick.axesValues.indexOfKey(axis) < 0 || (float)joystick.axesValues.get(axis) != value) {
// save value to prevent repeats
joystick.axesValues.put(axis, value);
- GodotLib.joyaxis(godotJoyId, i, value);
+ handleJoystickAxisEvent(godotJoyId, i, value);
}
}
@@ -258,7 +276,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
if (joystick.hatX != hatX || joystick.hatY != hatY) {
joystick.hatX = hatX;
joystick.hatY = hatY;
- GodotLib.joyhat(godotJoyId, hatX, hatY);
+ handleJoystickHatEvent(godotJoyId, hatX, hatY);
}
}
return true;
@@ -284,10 +302,12 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
int[] deviceIds = mInputManager.getInputDeviceIds();
for (int deviceId : deviceIds) {
InputDevice device = mInputManager.getInputDevice(deviceId);
- if (DEBUG) {
- Log.v(TAG, String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName()));
+ if (device != null) {
+ if (DEBUG) {
+ Log.v(TAG, String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName()));
+ }
+ onInputDeviceAdded(deviceId);
}
- onInputDeviceAdded(deviceId);
}
}
@@ -364,7 +384,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
mJoysticksDevices.put(deviceId, joystick);
- GodotLib.joyconnectionchanged(id, true, joystick.name);
+ handleJoystickConnectionChangedEvent(id, true, joystick.name);
}
@Override
@@ -378,7 +398,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
final int godotJoyId = mJoystickIds.get(deviceId);
mJoystickIds.delete(deviceId);
mJoysticksDevices.delete(deviceId);
- GodotLib.joyconnectionchanged(godotJoyId, false, "");
+ handleJoystickConnectionChangedEvent(godotJoyId, false, "");
}
@Override
@@ -482,15 +502,15 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
}
- static boolean handleMotionEvent(final MotionEvent event) {
+ boolean handleMotionEvent(final MotionEvent event) {
return handleMotionEvent(event, event.getActionMasked());
}
- static boolean handleMotionEvent(final MotionEvent event, int eventActionOverride) {
+ boolean handleMotionEvent(final MotionEvent event, int eventActionOverride) {
return handleMotionEvent(event, eventActionOverride, false);
}
- static boolean handleMotionEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
+ boolean handleMotionEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
if (isMouseEvent(event)) {
return handleMouseEvent(event, eventActionOverride, doubleTap);
}
@@ -523,19 +543,19 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return (float)Math.cos(orientation) * tiltMult;
}
- static boolean handleMouseEvent(final MotionEvent event) {
+ boolean handleMouseEvent(final MotionEvent event) {
return handleMouseEvent(event, event.getActionMasked());
}
- static boolean handleMouseEvent(final MotionEvent event, int eventActionOverride) {
+ boolean handleMouseEvent(final MotionEvent event, int eventActionOverride) {
return handleMouseEvent(event, eventActionOverride, false);
}
- static boolean handleMouseEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
+ boolean handleMouseEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
return handleMouseEvent(event, eventActionOverride, event.getButtonState(), doubleTap);
}
- static boolean handleMouseEvent(final MotionEvent event, int eventActionOverride, int buttonMaskOverride, boolean doubleTap) {
+ boolean handleMouseEvent(final MotionEvent event, int eventActionOverride, int buttonMaskOverride, boolean doubleTap) {
final float x = event.getX();
final float y = event.getY();
@@ -564,11 +584,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return handleMouseEvent(eventActionOverride, buttonMaskOverride, x, y, horizontalFactor, verticalFactor, doubleTap, sourceMouseRelative, pressure, getEventTiltX(event), getEventTiltY(event));
}
- static boolean handleMouseEvent(int eventAction, boolean sourceMouseRelative) {
+ boolean handleMouseEvent(int eventAction, boolean sourceMouseRelative) {
return handleMouseEvent(eventAction, 0, 0f, 0f, 0f, 0f, false, sourceMouseRelative, 1f, 0f, 0f);
}
- static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) {
+ boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) {
// Fix the buttonsMask
switch (eventAction) {
case MotionEvent.ACTION_CANCEL:
@@ -584,6 +604,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
break;
}
+ final int updatedButtonsMask = buttonsMask;
// We don't handle ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE events as they typically
// follow ACTION_DOWN and ACTION_UP events. As such, handling them would result in duplicate
// stream of events to the engine.
@@ -596,22 +617,26 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_SCROLL: {
- GodotLib.dispatchMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY);
+ if (shouldDispatchInputToRenderThread()) {
+ mRenderView.queueOnRenderThread(() -> GodotLib.dispatchMouseEvent(eventAction, updatedButtonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY));
+ } else {
+ GodotLib.dispatchMouseEvent(eventAction, updatedButtonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY);
+ }
return true;
}
}
return false;
}
- static boolean handleTouchEvent(final MotionEvent event) {
+ boolean handleTouchEvent(final MotionEvent event) {
return handleTouchEvent(event, event.getActionMasked());
}
- static boolean handleTouchEvent(final MotionEvent event, int eventActionOverride) {
+ boolean handleTouchEvent(final MotionEvent event, int eventActionOverride) {
return handleTouchEvent(event, eventActionOverride, false);
}
- static boolean handleTouchEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
+ boolean handleTouchEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
final int pointerCount = event.getPointerCount();
if (pointerCount == 0) {
return true;
@@ -636,10 +661,70 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN: {
- GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap);
+ if (shouldDispatchInputToRenderThread()) {
+ mRenderView.queueOnRenderThread(() -> GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap));
+ } else {
+ GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap);
+ }
return true;
}
}
return false;
}
+
+ void handleMagnifyEvent(float x, float y, float factor) {
+ if (shouldDispatchInputToRenderThread()) {
+ mRenderView.queueOnRenderThread(() -> GodotLib.magnify(x, y, factor));
+ } else {
+ GodotLib.magnify(x, y, factor);
+ }
+ }
+
+ void handlePanEvent(float x, float y, float deltaX, float deltaY) {
+ if (shouldDispatchInputToRenderThread()) {
+ mRenderView.queueOnRenderThread(() -> GodotLib.pan(x, y, deltaX, deltaY));
+ } else {
+ GodotLib.pan(x, y, deltaX, deltaY);
+ }
+ }
+
+ private void handleJoystickButtonEvent(int device, int button, boolean pressed) {
+ if (shouldDispatchInputToRenderThread()) {
+ mRenderView.queueOnRenderThread(() -> GodotLib.joybutton(device, button, pressed));
+ } else {
+ GodotLib.joybutton(device, button, pressed);
+ }
+ }
+
+ private void handleJoystickAxisEvent(int device, int axis, float value) {
+ if (shouldDispatchInputToRenderThread()) {
+ mRenderView.queueOnRenderThread(() -> GodotLib.joyaxis(device, axis, value));
+ } else {
+ GodotLib.joyaxis(device, axis, value);
+ }
+ }
+
+ private void handleJoystickHatEvent(int device, int hatX, int hatY) {
+ if (shouldDispatchInputToRenderThread()) {
+ mRenderView.queueOnRenderThread(() -> GodotLib.joyhat(device, hatX, hatY));
+ } else {
+ GodotLib.joyhat(device, hatX, hatY);
+ }
+ }
+
+ private void handleJoystickConnectionChangedEvent(int device, boolean connected, String name) {
+ if (shouldDispatchInputToRenderThread()) {
+ mRenderView.queueOnRenderThread(() -> GodotLib.joyconnectionchanged(device, connected, name));
+ } else {
+ GodotLib.joyconnectionchanged(device, connected, name);
+ }
+ }
+
+ void handleKeyEvent(int physicalKeycode, int unicode, int keyLabel, boolean pressed, boolean echo) {
+ if (shouldDispatchInputToRenderThread()) {
+ mRenderView.queueOnRenderThread(() -> GodotLib.key(physicalKeycode, unicode, keyLabel, pressed, echo));
+ } else {
+ GodotLib.key(physicalKeycode, unicode, keyLabel, pressed, echo);
+ }
+ }
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
index 06b565c30f..e545669970 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
@@ -93,8 +93,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
@Override
public void beforeTextChanged(final CharSequence pCharSequence, final int start, final int count, final int after) {
for (int i = 0; i < count; ++i) {
- GodotLib.key(KeyEvent.KEYCODE_DEL, 0, 0, true, false);
- GodotLib.key(KeyEvent.KEYCODE_DEL, 0, 0, false, false);
+ mRenderView.getInputHandler().handleKeyEvent(KeyEvent.KEYCODE_DEL, 0, 0, true, false);
+ mRenderView.getInputHandler().handleKeyEvent(KeyEvent.KEYCODE_DEL, 0, 0, false, false);
if (mHasSelection) {
mHasSelection = false;
@@ -115,8 +115,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
// Return keys are handled through action events
continue;
}
- GodotLib.key(0, character, 0, true, false);
- GodotLib.key(0, character, 0, false, false);
+ mRenderView.getInputHandler().handleKeyEvent(0, character, 0, true, false);
+ mRenderView.getInputHandler().handleKeyEvent(0, character, 0, false, false);
}
}
@@ -127,18 +127,16 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
if (characters != null) {
for (int i = 0; i < characters.length(); i++) {
final int character = characters.codePointAt(i);
- GodotLib.key(0, character, 0, true, false);
- GodotLib.key(0, character, 0, false, false);
+ mRenderView.getInputHandler().handleKeyEvent(0, character, 0, true, false);
+ mRenderView.getInputHandler().handleKeyEvent(0, character, 0, false, false);
}
}
}
if (pActionID == EditorInfo.IME_ACTION_DONE) {
// Enter key has been pressed
- mRenderView.queueOnRenderThread(() -> {
- GodotLib.key(KeyEvent.KEYCODE_ENTER, 0, 0, true, false);
- GodotLib.key(KeyEvent.KEYCODE_ENTER, 0, 0, false, false);
- });
+ mRenderView.getInputHandler().handleKeyEvent(KeyEvent.KEYCODE_ENTER, 0, 0, true, false);
+ mRenderView.getInputHandler().handleKeyEvent(KeyEvent.KEYCODE_ENTER, 0, 0, false, false);
mRenderView.getView().requestFocus();
return true;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt b/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt
index 69748c0a8d..d39f2309b8 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt
@@ -81,7 +81,8 @@ fun beginBenchmarkMeasure(scope: String, label: String) {
*
* * Note: Only enabled on 'editorDev' build variant.
*/
-fun endBenchmarkMeasure(scope: String, label: String) {
+@JvmOverloads
+fun endBenchmarkMeasure(scope: String, label: String, dumpBenchmark: Boolean = false) {
if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") {
return
}
@@ -93,6 +94,10 @@ fun endBenchmarkMeasure(scope: String, label: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Trace.endAsyncSection("[$scope] $label", 0)
}
+
+ if (dumpBenchmark) {
+ dumpBenchmark()
+ }
}
/**
@@ -102,11 +107,11 @@ fun endBenchmarkMeasure(scope: String, label: String) {
* * Note: Only enabled on 'editorDev' build variant.
*/
@JvmOverloads
-fun dumpBenchmark(fileAccessHandler: FileAccessHandler?, filepath: String? = benchmarkFile) {
+fun dumpBenchmark(fileAccessHandler: FileAccessHandler? = null, filepath: String? = benchmarkFile) {
if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") {
return
}
- if (!useBenchmark) {
+ if (!useBenchmark || benchmarkTracker.isEmpty()) {
return
}
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 40068745d6..87d4281c5a 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -67,6 +67,13 @@ static AndroidInputHandler *input_handler = nullptr;
static GodotJavaWrapper *godot_java = nullptr;
static GodotIOJavaWrapper *godot_io_java = nullptr;
+enum StartupStep {
+ STEP_TERMINATED = -1,
+ STEP_SETUP,
+ STEP_SHOW_LOGO,
+ STEP_STARTED
+};
+
static SafeNumeric<int> step; // Shared between UI and render threads
static Size2 new_size;
@@ -76,7 +83,7 @@ static Vector3 magnetometer;
static Vector3 gyroscope;
static void _terminate(JNIEnv *env, bool p_restart = false) {
- step.set(-1); // Ensure no further steps are attempted and no further events are sent
+ step.set(STEP_TERMINATED); // Ensure no further steps are attempted and no further events are sent
// lets cleanup
// Unregister android plugins
@@ -203,7 +210,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, j
os_android->set_display_size(Size2i(p_width, p_height));
// No need to reset the surface during startup
- if (step.get() > 0) {
+ if (step.get() > STEP_SETUP) {
if (p_surface) {
ANativeWindow *native_window = ANativeWindow_fromSurface(env, p_surface);
os_android->set_native_window(native_window);
@@ -216,7 +223,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, j
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface) {
if (os_android) {
- if (step.get() == 0) {
+ if (step.get() == STEP_SETUP) {
// During startup
if (p_surface) {
ANativeWindow *native_window = ANativeWindow_fromSurface(env, p_surface);
@@ -230,7 +237,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz) {
- if (step.get() == 0) {
+ if (step.get() <= STEP_SETUP) {
return;
}
@@ -244,20 +251,26 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ttsCallback(JNIEnv *e
}
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz) {
- if (step.get() == -1) {
+ if (step.get() == STEP_TERMINATED) {
return true;
}
- if (step.get() == 0) {
+ if (step.get() == STEP_SETUP) {
// 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();
+ Main::setup2(false); // The logo is shown in the next frame otherwise we run into rendering issues
input_handler = new AndroidInputHandler();
step.increment();
return true;
}
- if (step.get() == 1) {
+ if (step.get() == STEP_SHOW_LOGO) {
+ Main::setup_boot_logo();
+ step.increment();
+ return true;
+ }
+
+ if (step.get() == STEP_STARTED) {
if (Main::start() != EXIT_SUCCESS) {
return true; // should exit instead and print the error
}
@@ -283,7 +296,7 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env,
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative, jfloat p_pressure, jfloat p_tilt_x, jfloat p_tilt_y) {
- if (step.get() <= 0) {
+ if (step.get() <= STEP_SETUP) {
return;
}
@@ -292,7 +305,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JN
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray position, jboolean p_double_tap) {
- if (step.get() <= 0) {
+ if (step.get() <= STEP_SETUP) {
return;
}
@@ -313,7 +326,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JN
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor) {
- if (step.get() <= 0) {
+ if (step.get() <= STEP_SETUP) {
return;
}
input_handler->process_magnify(Point2(p_x, p_y), p_factor);
@@ -321,7 +334,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env,
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y) {
- if (step.get() <= 0) {
+ if (step.get() <= STEP_SETUP) {
return;
}
input_handler->process_pan(Point2(p_x, p_y), Vector2(p_delta_x, p_delta_y));
@@ -329,7 +342,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jcla
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed) {
- if (step.get() <= 0) {
+ if (step.get() <= STEP_SETUP) {
return;
}
@@ -344,7 +357,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value) {
- if (step.get() <= 0) {
+ if (step.get() <= STEP_SETUP) {
return;
}
@@ -359,7 +372,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env,
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y) {
- if (step.get() <= 0) {
+ if (step.get() <= STEP_SETUP) {
return;
}
@@ -396,7 +409,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_physical_keycode, jint p_unicode, jint p_key_label, jboolean p_pressed, jboolean p_echo) {
- if (step.get() <= 0) {
+ if (step.get() <= STEP_SETUP) {
return;
}
input_handler->process_key_event(p_physical_keycode, p_unicode, p_key_label, p_pressed, p_echo);
@@ -419,7 +432,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jclass clazz) {
- if (step.get() <= 0) {
+ if (step.get() <= STEP_SETUP) {
return;
}
@@ -427,7 +440,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env,
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jclass clazz) {
- if (step.get() <= 0) {
+ if (step.get() <= STEP_SETUP) {
return;
}
@@ -516,7 +529,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResu
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz) {
- if (step.get() <= 0) {
+ if (step.get() <= STEP_SETUP) {
return;
}
@@ -528,7 +541,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNI
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz) {
- if (step.get() <= 0) {
+ if (step.get() <= STEP_SETUP) {
return;
}
@@ -536,4 +549,11 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIE
os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_PAUSED);
}
}
+
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_updateInputDispatchSettings(JNIEnv *env, jclass clazz, jboolean p_use_accumulated_input, jboolean p_use_input_buffering) {
+ if (Input::get_singleton()) {
+ Input::get_singleton()->set_use_accumulated_input(p_use_accumulated_input);
+ Input::get_singleton()->set_use_input_buffering(p_use_input_buffering);
+ }
+}
}
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index f32ffc291a..852c475e7e 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -69,6 +69,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResu
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_updateInputDispatchSettings(JNIEnv *env, jclass clazz, jboolean p_use_accumulated_input, jboolean p_use_input_buffering);
}
#endif // JAVA_GODOT_LIB_JNI_H
diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp
index 341cc517e3..7bdc75db29 100644
--- a/platform/linuxbsd/wayland/wayland_thread.cpp
+++ b/platform/linuxbsd/wayland/wayland_thread.cpp
@@ -2049,9 +2049,14 @@ void WaylandThread::_wp_relative_pointer_on_relative_motion(void *data, struct z
PointerData &pd = ss->pointer_data_buffer;
+ WindowState *ws = wl_surface_get_window_state(ss->pointed_surface);
+ ERR_FAIL_NULL(ws);
+
pd.relative_motion.x = wl_fixed_to_double(dx);
pd.relative_motion.y = wl_fixed_to_double(dy);
+ pd.relative_motion *= window_state_get_scale_factor(ws);
+
pd.relative_motion_time = uptime_lo;
}
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index a1a91345ac..da45391995 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -568,23 +568,7 @@ void DisplayServerMacOS::menu_callback(id p_sender) {
}
GodotMenuItem *value = [p_sender representedObject];
-
if (value) {
- if (value->max_states > 0) {
- value->state++;
- if (value->state >= value->max_states) {
- value->state = 0;
- }
- }
-
- if (value->checkable_type == CHECKABLE_TYPE_CHECK_BOX) {
- if ([p_sender state] == NSControlStateValueOff) {
- [p_sender setState:NSControlStateValueOn];
- } else {
- [p_sender setState:NSControlStateValueOff];
- }
- }
-
if (value->callback.is_valid()) {
MenuCall mc;
mc.tag = value->meta;
diff --git a/platform/macos/godot_main_macos.mm b/platform/macos/godot_main_macos.mm
index 942c351ac0..eebaed0eaf 100644
--- a/platform/macos/godot_main_macos.mm
+++ b/platform/macos/godot_main_macos.mm
@@ -41,8 +41,8 @@
int main(int argc, char **argv) {
#if defined(VULKAN_ENABLED)
- // MoltenVK - enable full component swizzling support.
- setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
+ setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1); // MoltenVK - enable full component swizzling support.
+ setenv("MVK_CONFIG_SWAPCHAIN_MIN_MAG_FILTER_USE_NEAREST", "0", 1); // MoltenVK - use linear surface scaling. TODO: remove when full DPI scaling is implemented.
#endif
#if defined(SANITIZERS_ENABLED)
diff --git a/platform/macos/godot_menu_item.h b/platform/macos/godot_menu_item.h
index b6e2d41c08..e1af317259 100644
--- a/platform/macos/godot_menu_item.h
+++ b/platform/macos/godot_menu_item.h
@@ -52,6 +52,7 @@ enum GlobalMenuCheckType {
Callable hover_callback;
Variant meta;
GlobalMenuCheckType checkable_type;
+ bool checked;
int max_states;
int state;
Ref<Image> img;
diff --git a/platform/macos/godot_menu_item.mm b/platform/macos/godot_menu_item.mm
index 30dac9be9b..479542113a 100644
--- a/platform/macos/godot_menu_item.mm
+++ b/platform/macos/godot_menu_item.mm
@@ -31,4 +31,18 @@
#include "godot_menu_item.h"
@implementation GodotMenuItem
+
+- (id)init {
+ self = [super init];
+
+ self->callback = Callable();
+ self->key_callback = Callable();
+ self->checkable_type = GlobalMenuCheckType::CHECKABLE_TYPE_NONE;
+ self->checked = false;
+ self->max_states = 0;
+ self->state = 0;
+
+ return self;
+}
+
@end
diff --git a/platform/macos/native_menu_macos.mm b/platform/macos/native_menu_macos.mm
index 1ae1137ca0..802d58dc26 100644
--- a/platform/macos/native_menu_macos.mm
+++ b/platform/macos/native_menu_macos.mm
@@ -373,12 +373,7 @@ int NativeMenuMacOS::add_submenu_item(const RID &p_rid, const String &p_label, c
menu_item = [md->menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@"" atIndex:p_index];
GodotMenuItem *obj = [[GodotMenuItem alloc] init];
- obj->callback = Callable();
- obj->key_callback = Callable();
obj->meta = p_tag;
- obj->checkable_type = CHECKABLE_TYPE_NONE;
- obj->max_states = 0;
- obj->state = 0;
[menu_item setRepresentedObject:obj];
[md_sub->menu setTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()]];
@@ -417,9 +412,6 @@ int NativeMenuMacOS::add_item(const RID &p_rid, const String &p_label, const Cal
obj->callback = p_callback;
obj->key_callback = p_key_callback;
obj->meta = p_tag;
- obj->checkable_type = CHECKABLE_TYPE_NONE;
- obj->max_states = 0;
- obj->state = 0;
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)];
[menu_item setRepresentedObject:obj];
}
@@ -438,8 +430,6 @@ int NativeMenuMacOS::add_check_item(const RID &p_rid, const String &p_label, con
obj->key_callback = p_key_callback;
obj->meta = p_tag;
obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX;
- obj->max_states = 0;
- obj->state = 0;
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)];
[menu_item setRepresentedObject:obj];
}
@@ -457,9 +447,6 @@ int NativeMenuMacOS::add_icon_item(const RID &p_rid, const Ref<Texture2D> &p_ico
obj->callback = p_callback;
obj->key_callback = p_key_callback;
obj->meta = p_tag;
- obj->checkable_type = CHECKABLE_TYPE_NONE;
- obj->max_states = 0;
- obj->state = 0;
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
if (ds && p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
obj->img = p_icon->get_image();
@@ -489,8 +476,6 @@ int NativeMenuMacOS::add_icon_check_item(const RID &p_rid, const Ref<Texture2D>
obj->key_callback = p_key_callback;
obj->meta = p_tag;
obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX;
- obj->max_states = 0;
- obj->state = 0;
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
if (ds && p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
obj->img = p_icon->get_image();
@@ -520,8 +505,6 @@ int NativeMenuMacOS::add_radio_check_item(const RID &p_rid, const String &p_labe
obj->key_callback = p_key_callback;
obj->meta = p_tag;
obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON;
- obj->max_states = 0;
- obj->state = 0;
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)];
[menu_item setRepresentedObject:obj];
}
@@ -540,8 +523,6 @@ int NativeMenuMacOS::add_icon_radio_check_item(const RID &p_rid, const Ref<Textu
obj->key_callback = p_key_callback;
obj->meta = p_tag;
obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON;
- obj->max_states = 0;
- obj->state = 0;
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
if (ds && p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
obj->img = p_icon->get_image();
@@ -570,7 +551,6 @@ int NativeMenuMacOS::add_multistate_item(const RID &p_rid, const String &p_label
obj->callback = p_callback;
obj->key_callback = p_key_callback;
obj->meta = p_tag;
- obj->checkable_type = CHECKABLE_TYPE_NONE;
obj->max_states = p_max_states;
obj->state = p_default_state;
[menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)];
@@ -640,7 +620,10 @@ bool NativeMenuMacOS::is_item_checked(const RID &p_rid, int p_idx) const {
ERR_FAIL_COND_V(p_idx >= item_start + item_count, false);
const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx];
if (menu_item) {
- return ([menu_item state] == NSControlStateValueOn);
+ const GodotMenuItem *obj = [menu_item representedObject];
+ if (obj) {
+ return obj->checked;
+ }
}
return false;
}
@@ -958,10 +941,14 @@ void NativeMenuMacOS::set_item_checked(const RID &p_rid, int p_idx, bool p_check
ERR_FAIL_COND(p_idx >= item_start + item_count);
NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx];
if (menu_item) {
- if (p_checked) {
- [menu_item setState:NSControlStateValueOn];
- } else {
- [menu_item setState:NSControlStateValueOff];
+ GodotMenuItem *obj = [menu_item representedObject];
+ if (obj) {
+ obj->checked = p_checked;
+ if (p_checked) {
+ [menu_item setState:NSControlStateValueOn];
+ } else {
+ [menu_item setState:NSControlStateValueOff];
+ }
}
}
}
diff --git a/platform/web/audio_driver_web.cpp b/platform/web/audio_driver_web.cpp
index dd986e650c..b24c6cb1fd 100644
--- a/platform/web/audio_driver_web.cpp
+++ b/platform/web/audio_driver_web.cpp
@@ -33,6 +33,8 @@
#include "godot_audio.h"
#include "core/config/project_settings.h"
+#include "core/object/object.h"
+#include "scene/main/node.h"
#include "servers/audio/audio_stream.h"
#include <emscripten.h>
@@ -51,6 +53,33 @@ void AudioDriverWeb::_latency_update_callback(float p_latency) {
AudioDriverWeb::audio_context.output_latency = p_latency;
}
+void AudioDriverWeb::_sample_playback_finished_callback(const char *p_playback_object_id) {
+ const ObjectID playback_id = ObjectID(String::to_int(p_playback_object_id));
+
+ Object *playback_object = ObjectDB::get_instance(playback_id);
+ if (playback_object == nullptr) {
+ return;
+ }
+ Ref<AudioSamplePlayback> playback = Object::cast_to<AudioSamplePlayback>(playback_object);
+ if (playback.is_null()) {
+ return;
+ }
+
+ Object *player_object = ObjectDB::get_instance(playback->player_id);
+ if (player_object == nullptr) {
+ return;
+ }
+ Node *player = Object::cast_to<Node>(player_object);
+ if (player == nullptr) {
+ return;
+ }
+
+ const StringName finished = SNAME("finished");
+ if (player->has_signal(finished)) {
+ player->emit_signal(finished);
+ }
+}
+
void AudioDriverWeb::_audio_driver_process(int p_from, int p_samples) {
int32_t *stream_buffer = reinterpret_cast<int32_t *>(output_rb);
const int max_samples = memarr_len(output_rb);
@@ -132,6 +161,9 @@ Error AudioDriverWeb::init() {
if (!input_rb) {
return ERR_OUT_OF_MEMORY;
}
+
+ godot_audio_sample_set_finished_callback(&_sample_playback_finished_callback);
+
return OK;
}
diff --git a/platform/web/audio_driver_web.h b/platform/web/audio_driver_web.h
index 298ad90fae..46c5ce4de1 100644
--- a/platform/web/audio_driver_web.h
+++ b/platform/web/audio_driver_web.h
@@ -58,6 +58,7 @@ private:
WASM_EXPORT static void _state_change_callback(int p_state);
WASM_EXPORT static void _latency_update_callback(float p_latency);
+ WASM_EXPORT static void _sample_playback_finished_callback(const char *p_playback_object_id);
static AudioDriverWeb *singleton;
diff --git a/platform/web/godot_audio.h b/platform/web/godot_audio.h
index 8bebbcf7de..dd5bec00cf 100644
--- a/platform/web/godot_audio.h
+++ b/platform/web/godot_audio.h
@@ -57,6 +57,7 @@ extern void godot_audio_sample_set_pause(const char *p_playback_object_id, bool
extern int godot_audio_sample_is_active(const char *p_playback_object_id);
extern void godot_audio_sample_update_pitch_scale(const char *p_playback_object_id, float p_pitch_scale);
extern void godot_audio_sample_set_volumes_linear(const char *p_playback_object_id, int *p_buses_buf, int p_buses_size, float *p_volumes_buf, int p_volumes_size);
+extern void godot_audio_sample_set_finished_callback(void (*p_callback)(const char *));
extern void godot_audio_sample_bus_set_count(int p_count);
extern void godot_audio_sample_bus_remove(int p_index);
diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js
index 531dbdaeab..0b16b07261 100644
--- a/platform/web/js/libs/library_godot_audio.js
+++ b/platform/web/js/libs/library_godot_audio.js
@@ -687,9 +687,15 @@ class SampleNode {
}
switch (self.getSample().loopMode) {
- case 'disabled':
+ case 'disabled': {
+ const id = this.id;
self.stop();
- break;
+ if (GodotAudio.sampleFinishedCallback != null) {
+ const idCharPtr = GodotRuntime.allocString(id);
+ GodotAudio.sampleFinishedCallback(idCharPtr);
+ GodotRuntime.free(idCharPtr);
+ }
+ } break;
case 'forward':
case 'backward':
self.restart();
@@ -1090,6 +1096,12 @@ const _GodotAudio = {
busSolo: null,
Bus,
+ /**
+ * Callback to signal that a sample has finished.
+ * @type {(playbackObjectIdPtr: number) => void | null}
+ */
+ sampleFinishedCallback: null,
+
/** @type {AudioContext} */
ctx: null,
input: null,
@@ -1764,6 +1776,17 @@ const _GodotAudio = {
godot_audio_sample_bus_set_mute: function (bus, enable) {
GodotAudio.set_sample_bus_mute(bus, Boolean(enable));
},
+
+ godot_audio_sample_set_finished_callback__proxy: 'sync',
+ godot_audio_sample_set_finished_callback__sig: 'vi',
+ /**
+ * Sets the finished callback
+ * @param {Number} callbackPtr Finished callback pointer
+ * @returns {void}
+ */
+ godot_audio_sample_set_finished_callback: function (callbackPtr) {
+ GodotAudio.sampleFinishedCallback = GodotRuntime.get_func(callbackPtr);
+ },
};
autoAddDeps(_GodotAudio, '$GodotAudio');
diff --git a/platform/web/js/libs/library_godot_input.js b/platform/web/js/libs/library_godot_input.js
index 7ea89d553f..6e3b97023d 100644
--- a/platform/web/js/libs/library_godot_input.js
+++ b/platform/web/js/libs/library_godot_input.js
@@ -112,6 +112,7 @@ const GodotIME = {
ime.style.top = '0px';
ime.style.width = '100%';
ime.style.height = '40px';
+ ime.style.pointerEvents = 'none';
ime.style.display = 'none';
ime.contentEditable = 'true';
diff --git a/platform/web/js/libs/library_godot_javascript_singleton.js b/platform/web/js/libs/library_godot_javascript_singleton.js
index b17fde1544..6bb69bca95 100644
--- a/platform/web/js/libs/library_godot_javascript_singleton.js
+++ b/platform/web/js/libs/library_godot_javascript_singleton.js
@@ -81,11 +81,16 @@ const GodotJSWrapper = {
case 0:
return null;
case 1:
- return !!GodotRuntime.getHeapValue(val, 'i64');
- case 2:
- return GodotRuntime.getHeapValue(val, 'i64');
+ return Boolean(GodotRuntime.getHeapValue(val, 'i64'));
+ case 2: {
+ // `heap_value` may be a bigint.
+ const heap_value = GodotRuntime.getHeapValue(val, 'i64');
+ return heap_value >= Number.MIN_SAFE_INTEGER && heap_value <= Number.MAX_SAFE_INTEGER
+ ? Number(heap_value)
+ : heap_value;
+ }
case 3:
- return GodotRuntime.getHeapValue(val, 'double');
+ return Number(GodotRuntime.getHeapValue(val, 'double'));
case 4:
return GodotRuntime.parseString(GodotRuntime.getHeapValue(val, '*'));
case 24: // OBJECT
@@ -110,6 +115,9 @@ const GodotJSWrapper = {
}
GodotRuntime.setHeapValue(p_exchange, p_val, 'double');
return 3; // FLOAT
+ } else if (type === 'bigint') {
+ GodotRuntime.setHeapValue(p_exchange, p_val, 'i64');
+ return 2; // INT
} else if (type === 'string') {
const c_str = GodotRuntime.allocString(p_val);
GodotRuntime.setHeapValue(p_exchange, c_str, '*');
diff --git a/platform/web/web_main.cpp b/platform/web/web_main.cpp
index 04513f6d57..d0c3bd7c0e 100644
--- a/platform/web/web_main.cpp
+++ b/platform/web/web_main.cpp
@@ -35,6 +35,8 @@
#include "core/config/engine.h"
#include "core/io/resource_loader.h"
#include "main/main.h"
+#include "scene/main/scene_tree.h"
+#include "scene/main/window.h" // SceneTree only forward declares it.
#include <emscripten/emscripten.h>
#include <stdlib.h>
@@ -130,7 +132,7 @@ extern EMSCRIPTEN_KEEPALIVE int godot_web_main(int argc, char *argv[]) {
if (Engine::get_singleton()->is_project_manager_hint() && FileAccess::exists("/tmp/preload.zip")) {
PackedStringArray ps;
ps.push_back("/tmp/preload.zip");
- os->get_main_loop()->emit_signal(SNAME("files_dropped"), ps, -1);
+ SceneTree::get_singleton()->get_root()->emit_signal(SNAME("files_dropped"), ps);
}
#endif
emscripten_set_main_loop(main_loop_callback, -1, false);
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 8d26a705a9..c1b3540f68 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -38,6 +38,7 @@
#include "core/version.h"
#include "drivers/png/png_driver_common.h"
#include "main/main.h"
+#include "scene/resources/texture.h"
#if defined(VULKAN_ENABLED)
#include "rendering_context_driver_vulkan_windows.h"
@@ -3807,9 +3808,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
case WM_ACTIVATE: {
// Activation can happen just after the window has been created, even before the callbacks are set.
// Therefore, it's safer to defer the delivery of the event.
- if (!windows[window_id].activate_timer_id) {
- windows[window_id].activate_timer_id = SetTimer(windows[window_id].hWnd, 1, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
- }
+ // It's important to set an nIDEvent different from the SetTimer for move_timer_id because
+ // if the same nIDEvent is passed, the timer is replaced and the same timer_id is returned.
+ windows[window_id].activate_timer_id = SetTimer(windows[window_id].hWnd, DisplayServerWindows::TIMER_ID_WINDOW_ACTIVATION, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
windows[window_id].activate_state = GET_WM_ACTIVATE_STATE(wParam, lParam);
return 0;
} break;
@@ -4727,7 +4728,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
case WM_ENTERSIZEMOVE: {
Input::get_singleton()->release_pressed_events();
- windows[window_id].move_timer_id = SetTimer(windows[window_id].hWnd, 1, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
+ windows[window_id].move_timer_id = SetTimer(windows[window_id].hWnd, DisplayServerWindows::TIMER_ID_MOVE_REDRAW, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
} break;
case WM_EXITSIZEMOVE: {
KillTimer(windows[window_id].hWnd, windows[window_id].move_timer_id);
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 382f18c239..c2f4de7d81 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -332,6 +332,11 @@ class DisplayServerWindows : public DisplayServer {
String tablet_driver;
Vector<String> tablet_drivers;
+ enum TimerID {
+ TIMER_ID_MOVE_REDRAW = 1,
+ TIMER_ID_WINDOW_ACTIVATION = 2,
+ };
+
enum {
KEY_EVENT_BUFFER_SIZE = 512
};
diff --git a/platform/windows/native_menu_windows.cpp b/platform/windows/native_menu_windows.cpp
index d9dc28e9d9..fde55918e4 100644
--- a/platform/windows/native_menu_windows.cpp
+++ b/platform/windows/native_menu_windows.cpp
@@ -81,22 +81,6 @@ void NativeMenuWindows::_menu_activate(HMENU p_menu, int p_index) const {
if (GetMenuItemInfoW(md->menu, p_index, true, &item)) {
MenuItemData *item_data = (MenuItemData *)item.dwItemData;
if (item_data) {
- if (item_data->max_states > 0) {
- item_data->state++;
- if (item_data->state >= item_data->max_states) {
- item_data->state = 0;
- }
- }
-
- if (item_data->checkable_type == CHECKABLE_TYPE_CHECK_BOX) {
- if ((item.fState & MFS_CHECKED) == MFS_CHECKED) {
- item.fState &= ~MFS_CHECKED;
- } else {
- item.fState |= MFS_CHECKED;
- }
- SetMenuItemInfoW(md->menu, p_index, true, &item);
- }
-
if (item_data->callback.is_valid()) {
Variant ret;
Callable::CallError ce;
@@ -619,9 +603,12 @@ bool NativeMenuWindows::is_item_checked(const RID &p_rid, int p_idx) const {
MENUITEMINFOW item;
ZeroMemory(&item, sizeof(item));
item.cbSize = sizeof(item);
- item.fMask = MIIM_STATE;
+ item.fMask = MIIM_STATE | MIIM_DATA;
if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
- return (item.fState & MFS_CHECKED) == MFS_CHECKED;
+ MenuItemData *item_data = (MenuItemData *)item.dwItemData;
+ if (item_data) {
+ return item_data->checked;
+ }
}
return false;
}
@@ -861,12 +848,16 @@ void NativeMenuWindows::set_item_checked(const RID &p_rid, int p_idx, bool p_che
MENUITEMINFOW item;
ZeroMemory(&item, sizeof(item));
item.cbSize = sizeof(item);
- item.fMask = MIIM_STATE;
+ item.fMask = MIIM_STATE | MIIM_DATA;
if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
- if (p_checked) {
- item.fState |= MFS_CHECKED;
- } else {
- item.fState &= ~MFS_CHECKED;
+ MenuItemData *item_data = (MenuItemData *)item.dwItemData;
+ if (item_data) {
+ item_data->checked = p_checked;
+ if (p_checked) {
+ item.fState |= MFS_CHECKED;
+ } else {
+ item.fState &= ~MFS_CHECKED;
+ }
}
SetMenuItemInfoW(md->menu, p_idx, true, &item);
}
diff --git a/platform/windows/native_menu_windows.h b/platform/windows/native_menu_windows.h
index 5c4aaa52c8..235a4b332a 100644
--- a/platform/windows/native_menu_windows.h
+++ b/platform/windows/native_menu_windows.h
@@ -51,6 +51,7 @@ class NativeMenuWindows : public NativeMenu {
Callable callback;
Variant meta;
GlobalMenuCheckType checkable_type;
+ bool checked = false;
int max_states = 0;
int state = 0;
Ref<Image> img;