summaryrefslogtreecommitdiffstats
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/display_server_android.cpp4
-rw-r--r--platform/android/doc_classes/EditorExportPlatformAndroid.xml2
-rw-r--r--platform/android/export/export_plugin.cpp13
-rw-r--r--platform/android/java/app/config.gradle2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.kt40
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java12
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotHost.java27
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotService.kt4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java24
-rw-r--r--platform/ios/app_delegate.mm33
-rw-r--r--platform/ios/display_server_ios.mm14
-rw-r--r--platform/ios/export/export_plugin.cpp17
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.cpp17
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.h4
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp237
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h3
-rw-r--r--platform/macos/dir_access_macos.h1
-rw-r--r--platform/macos/dir_access_macos.mm17
-rw-r--r--platform/macos/display_server_macos.h1
-rw-r--r--platform/macos/display_server_macos.mm127
-rw-r--r--platform/macos/export/export_plugin.cpp4
-rw-r--r--platform/macos/gl_manager_macos_legacy.mm4
-rw-r--r--platform/macos/godot_menu_delegate.mm42
-rw-r--r--platform/macos/godot_window_delegate.mm9
-rw-r--r--platform/macos/os_macos.mm2
-rw-r--r--platform/web/detect.py21
-rw-r--r--platform/web/display_server_web.cpp228
-rw-r--r--platform/web/display_server_web.h31
-rw-r--r--platform/web/godot_js.h3
-rw-r--r--platform/web/godot_webgl2.h1
-rw-r--r--platform/web/javascript_bridge_singleton.cpp25
-rw-r--r--platform/web/js/engine/config.js4
-rw-r--r--platform/web/js/engine/engine.js4
-rw-r--r--platform/web/js/libs/library_godot_audio.js9
-rw-r--r--platform/web/js/libs/library_godot_display.js40
-rw-r--r--platform/web/js/libs/library_godot_fetch.js7
-rw-r--r--platform/web/js/libs/library_godot_input.js12
-rw-r--r--platform/web/js/libs/library_godot_javascript_singleton.js10
-rw-r--r--platform/web/js/libs/library_godot_os.js13
-rw-r--r--platform/web/js/libs/library_godot_webgl2.js13
-rw-r--r--platform/web/os_web.cpp6
-rw-r--r--platform/web/os_web.h3
-rw-r--r--platform/web/web_main.cpp10
-rw-r--r--platform/windows/detect.py2
-rw-r--r--platform/windows/display_server_windows.cpp24
-rw-r--r--platform/windows/joypad_windows.cpp4
48 files changed, 988 insertions, 148 deletions
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 445a6ea6ea..dd5ab46bd7 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -310,9 +310,9 @@ void DisplayServerAndroid::window_set_drop_files_callback(const Callable &p_call
void DisplayServerAndroid::_window_callback(const Callable &p_callable, const Variant &p_arg, bool p_deferred) const {
if (!p_callable.is_null()) {
if (p_deferred) {
- p_callable.call(p_arg);
- } else {
p_callable.call_deferred(p_arg);
+ } else {
+ p_callable.call(p_arg);
}
}
}
diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
index 0f1da54376..dae968378b 100644
--- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml
+++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
@@ -7,7 +7,7 @@
</description>
<tutorials>
<link title="Exporting for Android">$DOCS_URL/tutorials/export/exporting_for_android.html</link>
- <link title="Custom builds for Android">$DOCS_URL/tutorials/export/android_custom_build.html</link>
+ <link title="Gradle builds for Android">$DOCS_URL/tutorials/export/android_gradle_build.html</link>
<link title="Android plugins documentation index">$DOCS_URL/tutorials/platform/index.html</link>
</tutorials>
<members>
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index aeaa7b9ce7..c3015ec260 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -2238,6 +2238,19 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
#ifdef MODULE_MONO_ENABLED
// Android export is still a work in progress, keep a message as a warning.
err += TTR("Exporting to Android when using C#/.NET is experimental.") + "\n";
+
+ bool unsupported_arch = false;
+ Vector<ABI> enabled_abis = get_enabled_abis(p_preset);
+ for (ABI abi : enabled_abis) {
+ if (abi.arch != "arm64" && abi.arch != "x86_64") {
+ err += vformat(TTR("Android architecture %s not supported in C# projects."), abi.arch) + "\n";
+ unsupported_arch = true;
+ }
+ }
+ if (unsupported_arch) {
+ r_error = err;
+ return false;
+ }
#endif
// Look for export templates (first official, and if defined custom templates).
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index e7c06628c8..a91e7bc7ce 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -9,7 +9,7 @@ ext.versions = [
kotlinVersion : '1.7.0',
fragmentVersion : '1.3.6',
nexusPublishVersion: '1.1.0',
- javaVersion : 11,
+ javaVersion : 17,
// Also update 'platform/android/detect.py#get_ndk_version()' when this is updated.
ndkVersion : '23.2.8568313'
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 f819063a22..857f239e96 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
@@ -84,7 +84,7 @@ class Godot(private val context: Context) : SensorEventListener {
}
private val pluginRegistry: GodotPluginRegistry by lazy {
- GodotPluginRegistry.initializePluginRegistry(this)
+ GodotPluginRegistry.getPluginRegistry()
}
private val mSensorManager: SensorManager by lazy {
requireActivity().getSystemService(Context.SENSOR_SERVICE) as SensorManager
@@ -190,7 +190,7 @@ class Godot(private val context: Context) : SensorEventListener {
val activity = requireActivity()
val window = activity.window
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
- GodotPluginRegistry.initializePluginRegistry(this)
+ GodotPluginRegistry.initializePluginRegistry(this, primaryHost.getHostPlugins(this))
if (io == null) {
io = GodotIO(activity)
}
@@ -250,11 +250,7 @@ class Godot(private val context: Context) : SensorEventListener {
}
i++
}
- if (newArgs.isEmpty()) {
- commandLine = mutableListOf()
- } else {
- commandLine = newArgs
- }
+ commandLine = if (newArgs.isEmpty()) { mutableListOf() } else { newArgs }
if (useApkExpansion && mainPackMd5 != null && mainPackKey != null) {
// Build the full path to the app's expansion files
try {
@@ -392,6 +388,7 @@ class Godot(private val context: Context) : SensorEventListener {
// Fallback to openGl
GodotGLRenderView(host, this, xrMode, useDebugOpengl)
}
+
if (host == primaryHost) {
renderView!!.startRenderer()
}
@@ -575,6 +572,19 @@ 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"))
+
+ runOnUiThread {
+ renderView?.inputHandler?.apply {
+ enableLongPress(longPressEnabled)
+ enablePanningAndScalingGestures(panScaleEnabled)
+ }
+ }
+
for (plugin in pluginRegistry.allPlugins) {
plugin.onGodotSetupCompleted()
}
@@ -585,6 +595,8 @@ 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")
+
for (plugin in pluginRegistry.allPlugins) {
plugin.onGodotMainLoopStarted()
}
@@ -616,7 +628,7 @@ class Godot(private val context: Context) : SensorEventListener {
private fun alert(message: String, title: String, okCallback: Runnable?) {
val activity: Activity = getActivity() ?: return
- runOnUiThread(Runnable {
+ runOnUiThread {
val builder = AlertDialog.Builder(activity)
builder.setMessage(message).setTitle(title)
builder.setPositiveButton(
@@ -627,7 +639,7 @@ class Godot(private val context: Context) : SensorEventListener {
}
val dialog = builder.create()
dialog.show()
- })
+ }
}
/**
@@ -685,9 +697,9 @@ class Godot(private val context: Context) : SensorEventListener {
return false
}
- private fun setKeepScreenOn(p_enabled: Boolean) {
+ private fun setKeepScreenOn(enabled: Boolean) {
runOnUiThread {
- if (p_enabled) {
+ if (enabled) {
getActivity()?.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} else {
getActivity()?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
@@ -835,9 +847,7 @@ class Godot(private val context: Context) : SensorEventListener {
}
}
- override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
- // Do something here if sensor accuracy changes.
- }
+ override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
/**
* Used by the native code (java_godot_wrapper.h) to vibrate the device.
@@ -865,7 +875,7 @@ class Godot(private val context: Context) : SensorEventListener {
private fun getCommandLine(): MutableList<String> {
val original: MutableList<String> = parseCommandLine()
val hostCommandLine = primaryHost?.commandLine
- if (hostCommandLine != null && hostCommandLine.isNotEmpty()) {
+ if (!hostCommandLine.isNullOrEmpty()) {
original.addAll(hostCommandLine)
}
return original
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt b/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt
index 417be7cef4..a60f6e997e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt
@@ -173,6 +173,10 @@ abstract class GodotActivity : FragmentActivity(), GodotHost {
return this
}
+ override fun getGodot(): Godot? {
+ return godotFragment?.godot
+ }
+
/**
* Used to initialize the Godot fragment instance in [onCreate].
*/
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 9a8b10ea3e..120e1722e5 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java
@@ -30,6 +30,7 @@
package org.godotengine.godot;
+import org.godotengine.godot.plugin.GodotPlugin;
import org.godotengine.godot.utils.BenchmarkUtils;
import android.app.Activity;
@@ -65,6 +66,7 @@ import com.google.android.vending.expansion.downloader.IStub;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Set;
/**
* Base fragment for Android apps intending to use Godot for part of the app's UI.
@@ -122,6 +124,7 @@ public class GodotFragment extends Fragment implements IDownloaderClient, GodotH
}
public ResultCallback resultCallback;
+ @Override
public Godot getGodot() {
return godot;
}
@@ -426,4 +429,13 @@ public class GodotFragment extends Fragment implements IDownloaderClient, GodotH
}
return 0;
}
+
+ @Override
+ @CallSuper
+ public Set<GodotPlugin> getHostPlugins(Godot engine) {
+ if (parentHost != null) {
+ return parentHost.getHostPlugins(engine);
+ }
+ return Collections.emptySet();
+ }
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
index e5333085dd..1862b9fa9b 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
@@ -30,39 +30,42 @@
package org.godotengine.godot;
+import org.godotengine.godot.plugin.GodotPlugin;
+
import android.app.Activity;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
/**
* Denotate a component (e.g: Activity, Fragment) that hosts the {@link Godot} engine.
*/
public interface GodotHost {
/**
- * Provides a set of command line parameters to setup the engine.
+ * Provides a set of command line parameters to setup the {@link Godot} engine.
*/
default List<String> getCommandLine() {
return Collections.emptyList();
}
/**
- * Invoked on the render thread when the Godot setup is complete.
+ * Invoked on the render thread when setup of the {@link Godot} engine is complete.
*/
default void onGodotSetupCompleted() {}
/**
- * Invoked on the render thread when the Godot main loop has started.
+ * Invoked on the render thread when the {@link Godot} engine main loop has started.
*/
default void onGodotMainLoopStarted() {}
/**
- * Invoked on the render thread to terminate the given Godot instance.
+ * Invoked on the render thread to terminate the given {@link Godot} engine instance.
*/
default void onGodotForceQuit(Godot instance) {}
/**
- * Invoked on the render thread to terminate the Godot instance with the given id.
+ * Invoked on the render thread to terminate the {@link Godot} engine instance with the given id.
* @param godotInstanceId id of the Godot instance to terminate. See {@code onNewGodotInstanceRequested}
*
* @return true if successful, false otherwise.
@@ -90,7 +93,19 @@ public interface GodotHost {
}
/**
- * Provide access to the Activity hosting the Godot engine.
+ * Provide access to the Activity hosting the {@link Godot} engine.
*/
Activity getActivity();
+
+ /**
+ * Provide access to the hosted {@link Godot} engine.
+ */
+ Godot getGodot();
+
+ /**
+ * Returns a set of {@link GodotPlugin} to be registered with the hosted {@link Godot} engine.
+ */
+ default Set<GodotPlugin> getHostPlugins(Godot engine) {
+ return Collections.emptySet();
+ }
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotService.kt b/platform/android/java/lib/src/org/godotengine/godot/GodotService.kt
index 68cd2c1358..795dc921c7 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotService.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotService.kt
@@ -8,8 +8,10 @@ import android.util.Log
/**
* Godot service responsible for hosting the Godot engine instance.
+ *
+ * Note: Still in development, so it's made private and inaccessible until completed.
*/
-class GodotService : Service() {
+private class GodotService : Service() {
companion object {
private val TAG = GodotService::class.java.simpleName
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 c8b222254e..38c115ad7f 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
@@ -95,7 +95,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
/**
* Enable multi-fingers pan & scale gestures. This is false by default.
- *
+ * <p>
* Note: This may interfere with multi-touch handling / support.
*/
public void enablePanningAndScalingGestures(boolean enable) {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
index d338b72441..711bca02e7 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
@@ -42,8 +42,8 @@ import android.util.Log;
import androidx.annotation.Nullable;
import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -64,9 +64,8 @@ public final class GodotPluginRegistry {
private static GodotPluginRegistry instance;
private final ConcurrentHashMap<String, GodotPlugin> registry;
- private GodotPluginRegistry(Godot godot) {
+ private GodotPluginRegistry() {
registry = new ConcurrentHashMap<>();
- loadPlugins(godot);
}
/**
@@ -93,12 +92,14 @@ public final class GodotPluginRegistry {
* documentation.
*
* @param godot Godot instance
+ * @param runtimePlugins Set of plugins provided at runtime for registration
* @return A singleton instance of {@link GodotPluginRegistry}. This ensures that only one instance
* of each Godot Android plugins is available at runtime.
*/
- public static GodotPluginRegistry initializePluginRegistry(Godot godot) {
+ public static GodotPluginRegistry initializePluginRegistry(Godot godot, Set<GodotPlugin> runtimePlugins) {
if (instance == null) {
- instance = new GodotPluginRegistry(godot);
+ instance = new GodotPluginRegistry();
+ instance.loadPlugins(godot, runtimePlugins);
}
return instance;
@@ -108,7 +109,7 @@ public final class GodotPluginRegistry {
* Return the plugin registry if it's initialized.
* Throws a {@link IllegalStateException} exception if not.
*
- * @throws IllegalStateException if {@link GodotPluginRegistry#initializePluginRegistry(Godot)} has not been called prior to calling this method.
+ * @throws IllegalStateException if {@link GodotPluginRegistry#initializePluginRegistry(Godot, Set)} has not been called prior to calling this method.
*/
public static GodotPluginRegistry getPluginRegistry() throws IllegalStateException {
if (instance == null) {
@@ -118,7 +119,16 @@ public final class GodotPluginRegistry {
return instance;
}
- private void loadPlugins(Godot godot) {
+ private void loadPlugins(Godot godot, Set<GodotPlugin> runtimePlugins) {
+ // Register the runtime plugins
+ if (runtimePlugins != null && !runtimePlugins.isEmpty()) {
+ for (GodotPlugin plugin : runtimePlugins) {
+ Log.i(TAG, "Registering runtime plugin " + plugin.getPluginName());
+ registry.put(plugin.getPluginName(), plugin);
+ }
+ }
+
+ // Register the manifest plugins
try {
final Activity activity = godot.getActivity();
ApplicationInfo appInfo = activity
diff --git a/platform/ios/app_delegate.mm b/platform/ios/app_delegate.mm
index 38846e7508..8a16f8fcc1 100644
--- a/platform/ios/app_delegate.mm
+++ b/platform/ios/app_delegate.mm
@@ -51,6 +51,15 @@ extern void ios_finish();
@implementation AppDelegate
+enum {
+ SESSION_CATEGORY_AMBIENT,
+ SESSION_CATEGORY_MULTI_ROUTE,
+ SESSION_CATEGORY_PLAY_AND_RECORD,
+ SESSION_CATEGORY_PLAYBACK,
+ SESSION_CATEGORY_RECORD,
+ SESSION_CATEGORY_SOLO_AMBIENT
+};
+
static ViewController *mainViewController = nil;
+ (ViewController *)viewController {
@@ -92,8 +101,28 @@ static ViewController *mainViewController = nil;
mainViewController = viewController;
- // prevent to stop music in another background app
- [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
+ int sessionCategorySetting = GLOBAL_GET("audio/general/ios/session_category");
+
+ // Initialize with default Ambient category.
+ AVAudioSessionCategory category = AVAudioSessionCategoryAmbient;
+
+ if (sessionCategorySetting == SESSION_CATEGORY_MULTI_ROUTE) {
+ category = AVAudioSessionCategoryMultiRoute;
+ } else if (sessionCategorySetting == SESSION_CATEGORY_PLAY_AND_RECORD) {
+ category = AVAudioSessionCategoryPlayAndRecord;
+ } else if (sessionCategorySetting == SESSION_CATEGORY_PLAYBACK) {
+ category = AVAudioSessionCategoryPlayback;
+ } else if (sessionCategorySetting == SESSION_CATEGORY_RECORD) {
+ category = AVAudioSessionCategoryRecord;
+ } else if (sessionCategorySetting == SESSION_CATEGORY_SOLO_AMBIENT) {
+ category = AVAudioSessionCategorySoloAmbient;
+ }
+
+ if (GLOBAL_GET("audio/general/ios/mix_with_others")) {
+ [[AVAudioSession sharedInstance] setCategory:category withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
+ } else {
+ [[AVAudioSession sharedInstance] setCategory:category error:nil];
+ }
return YES;
}
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm
index 8508b67f1e..2561c1c095 100644
--- a/platform/ios/display_server_ios.mm
+++ b/platform/ios/display_server_ios.mm
@@ -317,37 +317,37 @@ String DisplayServerIOS::get_name() const {
}
bool DisplayServerIOS::tts_is_speaking() const {
- ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
+ ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return [tts isSpeaking];
}
bool DisplayServerIOS::tts_is_paused() const {
- ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
+ ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return [tts isPaused];
}
TypedArray<Dictionary> DisplayServerIOS::tts_get_voices() const {
- ERR_FAIL_COND_V_MSG(!tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
+ ERR_FAIL_NULL_V_MSG(tts, TypedArray<Dictionary>(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return [tts getVoices];
}
void DisplayServerIOS::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
- ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
+ ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
[tts speak:p_text voice:p_voice volume:p_volume pitch:p_pitch rate:p_rate utterance_id:p_utterance_id interrupt:p_interrupt];
}
void DisplayServerIOS::tts_pause() {
- ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
+ ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
[tts pauseSpeaking];
}
void DisplayServerIOS::tts_resume() {
- ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
+ ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
[tts resumeSpeaking];
}
void DisplayServerIOS::tts_stop() {
- ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
+ ERR_FAIL_NULL_MSG(tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
[tts stopSpeaking];
}
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index a8596c30a6..3cae85ea55 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -178,7 +178,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
@@ -284,7 +284,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
} else if (lines[i].find("$bundle_identifier") != -1) {
strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n";
} else if (lines[i].find("$short_version") != -1) {
- strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n";
+ strnew += lines[i].replace("$short_version", p_preset->get_version("application/short_version")) + "\n";
} else if (lines[i].find("$version") != -1) {
strnew += lines[i].replace("$version", p_preset->get_version("application/version")) + "\n";
} else if (lines[i].find("$signature") != -1) {
@@ -1927,16 +1927,19 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
}
bool EditorExportPlatformIOS::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
-#ifdef MODULE_MONO_ENABLED
- // Don't check for additional errors, as this particular error cannot be resolved.
- r_error += TTR("Exporting to iOS is currently not supported in Godot 4 when using C#/.NET. Use Godot 3 to target iOS with C#/Mono instead.") + "\n";
- r_error += TTR("If this project does not use C#, use a non-C# editor build to export the project.") + "\n";
+#if defined(MODULE_MONO_ENABLED) && !defined(MACOS_ENABLED)
+ // TODO: Remove this restriction when we don't rely on macOS tools to package up the native libraries anymore.
+ r_error += TTR("Exporting to iOS when using C#/.NET is experimental and requires macOS.") + "\n";
return false;
#else
String err;
bool valid = false;
+#if defined(MODULE_MONO_ENABLED)
+ // iOS export is still a work in progress, keep a message as a warning.
+ err += TTR("Exporting to iOS when using C#/.NET is experimental.") + "\n";
+#endif
// Look for export templates (first official, and if defined custom templates).
bool dvalid = exists_export_template("ios.zip", &err);
@@ -1963,7 +1966,7 @@ bool EditorExportPlatformIOS::has_valid_export_configuration(const Ref<EditorExp
}
return valid;
-#endif // !MODULE_MONO_ENABLED
+#endif // !(MODULE_MONO_ENABLED && !MACOS_ENABLED)
}
bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp
index cf4354139a..d9aa98ba70 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.cpp
+++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp
@@ -427,6 +427,17 @@ Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_windo
return OK;
}
+void FreeDesktopPortalDesktop::_file_dialog_callback(const Callable &p_callable, const Variant &p_status, const Variant &p_list, const Variant &p_index) {
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[3] = { &p_status, &p_list, &p_index };
+
+ p_callable.callp(args, 3, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat(RTR("Failed to execute file dialogs callback: %s."), Variant::get_callable_error_text(p_callable, args, 3, ce)));
+ }
+}
+
void FreeDesktopPortalDesktop::_thread_file_dialog_monitor(void *p_ud) {
FreeDesktopPortalDesktop *portal = (FreeDesktopPortalDesktop *)p_ud;
@@ -451,11 +462,7 @@ void FreeDesktopPortalDesktop::_thread_file_dialog_monitor(void *p_ud) {
file_chooser_parse_response(&iter, fd.filter_names, cancel, uris, index);
if (fd.callback.is_valid()) {
- Variant v_status = !cancel;
- Variant v_files = uris;
- Variant v_index = index;
- Variant *v_args[3] = { &v_status, &v_files, &v_index };
- fd.callback.call_deferredp((const Variant **)&v_args, 3);
+ callable_mp(portal, &FreeDesktopPortalDesktop::_file_dialog_callback).call_deferred(fd.callback, !cancel, uris, index);
}
if (fd.prev_focus != DisplayServer::INVALID_WINDOW_ID) {
callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(fd.prev_focus);
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.h b/platform/linuxbsd/freedesktop_portal_desktop.h
index 503c382207..71e9812ea9 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.h
+++ b/platform/linuxbsd/freedesktop_portal_desktop.h
@@ -40,7 +40,7 @@ struct DBusMessage;
struct DBusConnection;
struct DBusMessageIter;
-class FreeDesktopPortalDesktop {
+class FreeDesktopPortalDesktop : public Object {
private:
bool unsupported = false;
@@ -54,6 +54,8 @@ private:
static void append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value);
static bool file_chooser_parse_response(DBusMessageIter *p_iter, const Vector<String> &p_names, bool &r_cancel, Vector<String> &r_urls, int &r_index);
+ void _file_dialog_callback(const Callable &p_callable, const Variant &p_status, const Variant &p_list, const Variant &p_index);
+
struct FileDialogData {
Vector<String> filter_names;
DBusConnection *connection = nullptr;
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 02a5208e11..1660101598 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -39,6 +39,7 @@
#include "core/math/math_funcs.h"
#include "core/string/print_string.h"
#include "core/string/ustring.h"
+#include "drivers/png/png_driver_common.h"
#include "main/main.h"
#include "scene/resources/atlas_texture.h"
@@ -519,7 +520,7 @@ Bool DisplayServerX11::_predicate_clipboard_selection(Display *display, XEvent *
}
Bool DisplayServerX11::_predicate_clipboard_incr(Display *display, XEvent *event, XPointer arg) {
- if (event->type == PropertyNotify && event->xproperty.state == PropertyNewValue) {
+ if (event->type == PropertyNotify && event->xproperty.state == PropertyNewValue && event->xproperty.atom == *(Atom *)arg) {
return True;
} else {
return False;
@@ -593,7 +594,7 @@ String DisplayServerX11::_clipboard_get_impl(Atom p_source, Window x11_window, A
// Non-blocking wait for next event and remove it from the queue.
XEvent ev;
- while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_incr, nullptr)) {
+ while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_incr, (XPointer)&selection)) {
result = XGetWindowProperty(x11_display, x11_window,
selection, // selection type
0, LONG_MAX, // offset - len
@@ -664,6 +665,74 @@ String DisplayServerX11::_clipboard_get_impl(Atom p_source, Window x11_window, A
return ret;
}
+Atom DisplayServerX11::_clipboard_get_image_target(Atom p_source, Window x11_window) const {
+ Atom target = XInternAtom(x11_display, "TARGETS", 0);
+ Atom png = XInternAtom(x11_display, "image/png", 0);
+ Atom *valid_targets = nullptr;
+ unsigned long atom_count = 0;
+
+ Window selection_owner = XGetSelectionOwner(x11_display, p_source);
+ if (selection_owner != None) {
+ // Block events polling while processing selection events.
+ MutexLock mutex_lock(events_mutex);
+
+ Atom selection = XA_PRIMARY;
+ XConvertSelection(x11_display, p_source, target, selection, x11_window, CurrentTime);
+
+ XFlush(x11_display);
+
+ // Blocking wait for predicate to be True and remove the event from the queue.
+ XEvent event;
+ XIfEvent(x11_display, &event, _predicate_clipboard_selection, (XPointer)&x11_window);
+ // Do not get any data, see how much data is there.
+ Atom type;
+ int format, result;
+ unsigned long len, bytes_left, dummy;
+ XGetWindowProperty(x11_display, x11_window,
+ selection, // Tricky..
+ 0, 0, // offset - len
+ 0, // Delete 0==FALSE
+ XA_ATOM, // flag
+ &type, // return type
+ &format, // return format
+ &len, &bytes_left, // data length
+ (unsigned char **)&valid_targets);
+
+ if (valid_targets) {
+ XFree(valid_targets);
+ valid_targets = nullptr;
+ }
+
+ if (type == XA_ATOM && bytes_left > 0) {
+ // Data is ready and can be processed all at once.
+ result = XGetWindowProperty(x11_display, x11_window,
+ selection, 0, bytes_left / 4, 0,
+ XA_ATOM, &type, &format,
+ &len, &dummy, (unsigned char **)&valid_targets);
+ if (result == Success) {
+ atom_count = len;
+ } else {
+ print_verbose("Failed to get selection data.");
+ return None;
+ }
+ } else {
+ return None;
+ }
+ } else {
+ return None;
+ }
+ for (unsigned long i = 0; i < atom_count; i++) {
+ Atom atom = valid_targets[i];
+ if (atom == png) {
+ XFree(valid_targets);
+ return png;
+ }
+ }
+
+ XFree(valid_targets);
+ return None;
+}
+
String DisplayServerX11::_clipboard_get(Atom p_source, Window x11_window) const {
String ret;
Atom utf8_atom = XInternAtom(x11_display, "UTF8_STRING", True);
@@ -702,6 +771,158 @@ String DisplayServerX11::clipboard_get_primary() const {
return ret;
}
+Ref<Image> DisplayServerX11::clipboard_get_image() const {
+ _THREAD_SAFE_METHOD_
+ Atom clipboard = XInternAtom(x11_display, "CLIPBOARD", 0);
+ Window x11_window = windows[MAIN_WINDOW_ID].x11_window;
+ Ref<Image> ret;
+ Atom target = _clipboard_get_image_target(clipboard, x11_window);
+ if (target == None) {
+ return ret;
+ }
+
+ Window selection_owner = XGetSelectionOwner(x11_display, clipboard);
+
+ if (selection_owner != None) {
+ // Block events polling while processing selection events.
+ MutexLock mutex_lock(events_mutex);
+
+ // Identifier for the property the other window
+ // will send the converted data to.
+ Atom transfer_prop = XA_PRIMARY;
+ XConvertSelection(x11_display,
+ clipboard, // source selection
+ target, // format to convert to
+ transfer_prop, // output property
+ x11_window, CurrentTime);
+
+ XFlush(x11_display);
+
+ // Blocking wait for predicate to be True and remove the event from the queue.
+ XEvent event;
+ XIfEvent(x11_display, &event, _predicate_clipboard_selection, (XPointer)&x11_window);
+
+ // Do not get any data, see how much data is there.
+ Atom type;
+ int format, result;
+ unsigned long len, bytes_left, dummy;
+ unsigned char *data;
+ XGetWindowProperty(x11_display, x11_window,
+ transfer_prop, // Property data is transferred through
+ 0, 1, // offset, len (4 so we can get the size if INCR is used)
+ 0, // Delete 0==FALSE
+ AnyPropertyType, // flag
+ &type, // return type
+ &format, // return format
+ &len, &bytes_left, // data length
+ &data);
+
+ if (type == XInternAtom(x11_display, "INCR", 0)) {
+ ERR_FAIL_COND_V_MSG(len != 1, ret, "Incremental transfer initial value was not length.");
+
+ // Data is going to be received incrementally.
+ DEBUG_LOG_X11("INCR selection started.\n");
+
+ LocalVector<uint8_t> incr_data;
+ uint32_t data_size = 0;
+ bool success = false;
+
+ // Initial response is the lower bound of the length of the transferred data.
+ incr_data.resize(*(unsigned long *)data);
+ XFree(data);
+ data = nullptr;
+
+ // Delete INCR property to notify the owner.
+ XDeleteProperty(x11_display, x11_window, transfer_prop);
+
+ // Process events from the queue.
+ bool done = false;
+ while (!done) {
+ if (!_wait_for_events()) {
+ // Error or timeout, abort.
+ break;
+ }
+ // Non-blocking wait for next event and remove it from the queue.
+ XEvent ev;
+ while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_incr, (XPointer)&transfer_prop)) {
+ result = XGetWindowProperty(x11_display, x11_window,
+ transfer_prop, // output property
+ 0, LONG_MAX, // offset - len
+ True, // delete property to notify the owner
+ AnyPropertyType, // flag
+ &type, // return type
+ &format, // return format
+ &len, &bytes_left, // data length
+ &data);
+
+ DEBUG_LOG_X11("PropertyNotify: len=%lu, format=%i\n", len, format);
+
+ if (result == Success) {
+ if (data && (len > 0)) {
+ uint32_t prev_size = incr_data.size();
+ // New chunk, resize to be safe and append data.
+ incr_data.resize(MAX(data_size + len, prev_size));
+ memcpy(incr_data.ptr() + data_size, data, len);
+ data_size += len;
+ } else if (!(format == 0 && len == 0)) {
+ // For unclear reasons the first GetWindowProperty always returns a length and format of 0.
+ // Otherwise, last chunk, process finished.
+ done = true;
+ success = true;
+ }
+ } else {
+ print_verbose("Failed to get selection data chunk.");
+ done = true;
+ }
+
+ if (data) {
+ XFree(data);
+ data = nullptr;
+ }
+
+ if (done) {
+ break;
+ }
+ }
+ }
+
+ if (success && (data_size > 0)) {
+ ret.instantiate();
+ PNGDriverCommon::png_to_image(incr_data.ptr(), incr_data.size(), false, ret);
+ }
+ } else if (bytes_left > 0) {
+ if (data) {
+ XFree(data);
+ data = nullptr;
+ }
+ // Data is ready and can be processed all at once.
+ result = XGetWindowProperty(x11_display, x11_window,
+ transfer_prop, 0, bytes_left + 4, 0,
+ AnyPropertyType, &type, &format,
+ &len, &dummy, &data);
+ if (result == Success) {
+ ret.instantiate();
+ PNGDriverCommon::png_to_image((uint8_t *)data, bytes_left, false, ret);
+ } else {
+ print_verbose("Failed to get selection data.");
+ }
+
+ if (data) {
+ XFree(data);
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool DisplayServerX11::clipboard_has_image() const {
+ Atom target = _clipboard_get_image_target(
+ XInternAtom(x11_display, "CLIPBOARD", 0),
+ windows[MAIN_WINDOW_ID].x11_window);
+ return target != None;
+}
+
Bool DisplayServerX11::_predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg) {
if (event->xany.window == *(Window *)arg) {
return (event->type == SelectionRequest) ||
@@ -2874,7 +3095,7 @@ void DisplayServerX11::cursor_set_custom_image(const Ref<Resource> &p_cursor, Cu
*(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32();
}
- ERR_FAIL_COND(cursor_image->pixels == nullptr);
+ ERR_FAIL_NULL(cursor_image->pixels);
// Save it for a further usage
cursors[p_shape] = XcursorImageLoadCursor(x11_display, cursor_image);
@@ -4085,9 +4306,6 @@ void DisplayServerX11::process_events() {
if (XGetEventData(x11_display, &event.xcookie)) {
if (event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
- int index = event_data->detail;
- Vector2 pos = Vector2(event_data->event_x, event_data->event_y);
-
switch (event_data->evtype) {
case XI_HierarchyChanged:
case XI_DeviceChanged: {
@@ -4204,6 +4422,9 @@ void DisplayServerX11::process_events() {
}
bool is_begin = event_data->evtype == XI_TouchBegin;
+ int index = event_data->detail;
+ Vector2 pos = Vector2(event_data->event_x, event_data->event_y);
+
Ref<InputEventScreenTouch> st;
st.instantiate();
st->set_window_id(window_id);
@@ -4235,6 +4456,10 @@ void DisplayServerX11::process_events() {
if (ime_window_event || ignore_events) {
break;
}
+
+ int index = event_data->detail;
+ Vector2 pos = Vector2(event_data->event_x, event_data->event_y);
+
HashMap<int, Vector2>::Iterator curr_pos_elem = xi.state.find(index);
if (!curr_pos_elem) { // Defensive
break;
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 9706a4aa11..a8d134a6c7 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -304,6 +304,7 @@ class DisplayServerX11 : public DisplayServer {
String _clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const;
String _clipboard_get(Atom p_source, Window x11_window) const;
+ Atom _clipboard_get_image_target(Atom p_source, Window x11_window) const;
void _clipboard_transfer_ownership(Atom p_source, Window x11_window) const;
bool do_mouse_warp = false;
@@ -408,6 +409,8 @@ public:
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
+ virtual Ref<Image> clipboard_get_image() const override;
+ virtual bool clipboard_has_image() const override;
virtual void clipboard_set_primary(const String &p_text) override;
virtual String clipboard_get_primary() const override;
diff --git a/platform/macos/dir_access_macos.h b/platform/macos/dir_access_macos.h
index 64556999a7..167c162200 100644
--- a/platform/macos/dir_access_macos.h
+++ b/platform/macos/dir_access_macos.h
@@ -49,6 +49,7 @@ protected:
virtual String get_drive(int p_drive) override;
virtual bool is_hidden(const String &p_name) override;
+ virtual bool is_case_sensitive(const String &p_path) const override;
};
#endif // UNIX ENABLED
diff --git a/platform/macos/dir_access_macos.mm b/platform/macos/dir_access_macos.mm
index 2413d7bcf3..66d81f2687 100644
--- a/platform/macos/dir_access_macos.mm
+++ b/platform/macos/dir_access_macos.mm
@@ -30,6 +30,8 @@
#include "dir_access_macos.h"
+#include "core/config/project_settings.h"
+
#if defined(UNIX_ENABLED)
#include <errno.h>
@@ -78,4 +80,19 @@ bool DirAccessMacOS::is_hidden(const String &p_name) {
return [hidden boolValue];
}
+bool DirAccessMacOS::is_case_sensitive(const String &p_path) const {
+ String f = p_path;
+ if (!f.is_absolute_path()) {
+ f = get_current_dir().path_join(f);
+ }
+ f = fix_path(f);
+
+ NSURL *url = [NSURL fileURLWithPath:@(f.utf8().get_data())];
+ NSNumber *cs = nil;
+ if (![url getResourceValue:&cs forKey:NSURLVolumeSupportsCaseSensitiveNamesKey error:nil]) {
+ return false;
+ }
+ return [cs boolValue];
+}
+
#endif // UNIX_ENABLED
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 2ca9e493b7..66c89d6cc5 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -119,6 +119,7 @@ public:
bool is_popup = false;
bool mpass = false;
bool focused = false;
+ bool is_visible = true;
Rect2i parent_safe_rect;
};
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 0989e47b19..53db9a5abf 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -118,7 +118,7 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod
WindowData wd;
wd.window_delegate = [[GodotWindowDelegate alloc] init];
- ERR_FAIL_COND_V_MSG(wd.window_delegate == nil, INVALID_WINDOW_ID, "Can't create a window delegate");
+ ERR_FAIL_NULL_V_MSG(wd.window_delegate, INVALID_WINDOW_ID, "Can't create a window delegate");
[wd.window_delegate setWindowID:window_id_counter];
int rq_screen = get_screen_from_rect(p_rect);
@@ -144,11 +144,11 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod
styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable
backing:NSBackingStoreBuffered
defer:NO];
- ERR_FAIL_COND_V_MSG(wd.window_object == nil, INVALID_WINDOW_ID, "Can't create a window");
+ ERR_FAIL_NULL_V_MSG(wd.window_object, INVALID_WINDOW_ID, "Can't create a window");
[wd.window_object setWindowID:window_id_counter];
wd.window_view = [[GodotContentView alloc] init];
- ERR_FAIL_COND_V_MSG(wd.window_view == nil, INVALID_WINDOW_ID, "Can't create a window view");
+ ERR_FAIL_NULL_V_MSG(wd.window_view, INVALID_WINDOW_ID, "Can't create a window view");
[wd.window_view setWindowID:window_id_counter];
[wd.window_view setWantsLayer:TRUE];
@@ -567,7 +567,7 @@ NSImage *DisplayServerMacOS::_convert_to_nsimg(Ref<Image> &p_image) const {
colorSpaceName:NSDeviceRGBColorSpace
bytesPerRow:int(p_image->get_width()) * 4
bitsPerPixel:32];
- ERR_FAIL_COND_V(imgrep == nil, nil);
+ ERR_FAIL_NULL_V(imgrep, nil);
uint8_t *pixels = [imgrep bitmapData];
int len = p_image->get_width() * p_image->get_height();
@@ -583,7 +583,7 @@ NSImage *DisplayServerMacOS::_convert_to_nsimg(Ref<Image> &p_image) const {
}
NSImage *nsimg = [[NSImage alloc] initWithSize:NSMakeSize(p_image->get_width(), p_image->get_height())];
- ERR_FAIL_COND_V(nsimg == nil, nil);
+ ERR_FAIL_NULL_V(nsimg, nil);
[nsimg addRepresentation:imgrep];
return nsimg;
}
@@ -611,7 +611,13 @@ void DisplayServerMacOS::menu_open(NSMenu *p_menu) {
MenuData &md = submenu[submenu_inv[p_menu]];
md.is_open = true;
if (md.open.is_valid()) {
- md.open.call();
+ Variant ret;
+ Callable::CallError ce;
+
+ md.open.callp(nullptr, 0, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat(RTR("Failed to execute menu open callback: %s."), Variant::get_callable_error_text(md.open, nullptr, 0, ce)));
+ }
}
}
}
@@ -621,7 +627,13 @@ void DisplayServerMacOS::menu_close(NSMenu *p_menu) {
MenuData &md = submenu[submenu_inv[p_menu]];
md.is_open = false;
if (md.close.is_valid()) {
- md.close.call();
+ Variant ret;
+ Callable::CallError ce;
+
+ md.close.callp(nullptr, 0, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat(RTR("Failed to execute menu close callback: %s."), Variant::get_callable_error_text(md.close, nullptr, 0, ce)));
+ }
}
}
}
@@ -649,7 +661,7 @@ void DisplayServerMacOS::menu_callback(id p_sender) {
}
}
- if (value->callback != Callable()) {
+ if (value->callback.is_valid()) {
MenuCall mc;
mc.tag = value->meta;
mc.callback = value->callback;
@@ -1574,7 +1586,7 @@ void DisplayServerMacOS::global_menu_set_item_hover_callbacks(const String &p_me
NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
GodotMenuItem *obj = [menu_item representedObject];
- ERR_FAIL_COND(!obj);
+ ERR_FAIL_NULL(obj);
obj->hover_callback = p_callback;
}
}
@@ -1978,20 +1990,27 @@ Error DisplayServerMacOS::dialog_show(String p_title, String p_description, Vect
[window setInformativeText:ns_description];
[window setAlertStyle:NSAlertStyleInformational];
- int button_pressed;
+ Variant button_pressed;
NSInteger ret = [window runModal];
if (ret == NSAlertFirstButtonReturn) {
- button_pressed = 0;
+ button_pressed = int64_t(0);
} else if (ret == NSAlertSecondButtonReturn) {
- button_pressed = 1;
+ button_pressed = int64_t(1);
} else if (ret == NSAlertThirdButtonReturn) {
- button_pressed = 2;
+ button_pressed = int64_t(2);
} else {
- button_pressed = 2 + (ret - NSAlertThirdButtonReturn);
+ button_pressed = int64_t(2 + (ret - NSAlertThirdButtonReturn));
}
if (!p_callback.is_null()) {
- p_callback.call(button_pressed);
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[1] = { &button_pressed };
+
+ p_callback.callp(args, 1, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat(RTR("Failed to execute dialog callback: %s."), Variant::get_callable_error_text(p_callback, args, 1, ce)));
+ }
}
return OK;
@@ -2166,11 +2185,31 @@ Error DisplayServerMacOS::file_dialog_show(const String &p_title, const String &
url.parse_utf8([[[panel URL] path] UTF8String]);
files.push_back(url);
if (!callback.is_null()) {
- callback.call(true, files, [handler getIndex]);
+ Variant v_result = true;
+ Variant v_files = files;
+ Variant v_index = [handler getIndex];
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[3] = { &v_result, &v_files, &v_index };
+
+ callback.callp(args, 3, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat(RTR("Failed to execute file dialog callback: %s."), Variant::get_callable_error_text(callback, args, 3, ce)));
+ }
}
} else {
if (!callback.is_null()) {
- callback.call(false, Vector<String>(), [handler getIndex]);
+ Variant v_result = false;
+ Variant v_files = Vector<String>();
+ Variant v_index = [handler getIndex];
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[3] = { &v_result, &v_files, &v_index };
+
+ callback.callp(args, 3, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat(RTR("Failed to execute file dialogs callback: %s."), Variant::get_callable_error_text(callback, args, 3, ce)));
+ }
}
}
if (prev_focus != INVALID_WINDOW_ID) {
@@ -2231,11 +2270,31 @@ Error DisplayServerMacOS::file_dialog_show(const String &p_title, const String &
files.push_back(url);
}
if (!callback.is_null()) {
- callback.call(true, files, [handler getIndex]);
+ Variant v_result = true;
+ Variant v_files = files;
+ Variant v_index = [handler getIndex];
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[3] = { &v_result, &v_files, &v_index };
+
+ callback.callp(args, 3, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat(RTR("Failed to execute file dialog callback: %s."), Variant::get_callable_error_text(callback, args, 3, ce)));
+ }
}
} else {
if (!callback.is_null()) {
- callback.call(false, Vector<String>(), [handler getIndex]);
+ Variant v_result = false;
+ Variant v_files = Vector<String>();
+ Variant v_index = [handler getIndex];
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[3] = { &v_result, &v_files, &v_index };
+
+ callback.callp(args, 3, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat(RTR("Failed to execute file dialogs callback: %s."), Variant::get_callable_error_text(callback, args, 3, ce)));
+ }
}
}
if (prev_focus != INVALID_WINDOW_ID) {
@@ -2269,7 +2328,15 @@ Error DisplayServerMacOS::dialog_input_text(String p_title, String p_description
ret.parse_utf8([[input stringValue] UTF8String]);
if (!p_callback.is_null()) {
- p_callback.call(ret);
+ Variant v_result = ret;
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[1] = { &v_result };
+
+ p_callback.callp(args, 1, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat(RTR("Failed to execute input dialog callback: %s."), Variant::get_callable_error_text(p_callback, args, 1, ce)));
+ }
}
return OK;
@@ -3562,14 +3629,14 @@ bool DisplayServerMacOS::window_is_focused(WindowID p_window) const {
}
bool DisplayServerMacOS::window_can_draw(WindowID p_window) const {
- return (window_get_mode(p_window) != WINDOW_MODE_MINIMIZED) && [windows[p_window].window_object isOnActiveSpace];
+ return windows[p_window].is_visible;
}
bool DisplayServerMacOS::can_any_window_draw() const {
_THREAD_SAFE_METHOD_
for (const KeyValue<WindowID, WindowData> &E : windows) {
- if ((window_get_mode(E.key) != WINDOW_MODE_MINIMIZED) && [E.value.window_object isOnActiveSpace]) {
+ if (E.value.is_visible) {
return true;
}
}
@@ -3858,7 +3925,7 @@ void DisplayServerMacOS::cursor_set_custom_image(const Ref<Resource> &p_cursor,
bytesPerRow:int(texture_size.width) * 4
bitsPerPixel:32];
- ERR_FAIL_COND(imgrep == nil);
+ ERR_FAIL_NULL(imgrep);
uint8_t *pixels = [imgrep bitmapData];
int len = int(texture_size.width * texture_size.height);
@@ -4020,7 +4087,15 @@ void DisplayServerMacOS::process_events() {
while (List<MenuCall>::Element *call_p = deferred_menu_calls.front()) {
MenuCall call = call_p->get();
deferred_menu_calls.pop_front(); // Remove before call to avoid infinite loop in case callback is using `process_events` (e.g. EditorProgress).
- call.callback.call(call.tag);
+
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[1] = { &call.tag };
+
+ call.callback.callp(args, 1, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat(RTR("Failed to execute menu callback: %s."), Variant::get_callable_error_text(call.callback, args, 1, ce)));
+ }
}
if (!drop_events) {
@@ -4124,7 +4199,7 @@ void DisplayServerMacOS::set_icon(const Ref<Image> &p_icon) {
colorSpaceName:NSDeviceRGBColorSpace
bytesPerRow:img->get_width() * 4
bitsPerPixel:32];
- ERR_FAIL_COND(imgrep == nil);
+ ERR_FAIL_NULL(imgrep);
uint8_t *pixels = [imgrep bitmapData];
int len = img->get_width() * img->get_height();
@@ -4140,7 +4215,7 @@ void DisplayServerMacOS::set_icon(const Ref<Image> &p_icon) {
}
NSImage *nsimg = [[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())];
- ERR_FAIL_COND(nsimg == nil);
+ ERR_FAIL_NULL(nsimg);
[nsimg addRepresentation:imgrep];
[NSApp setApplicationIconImage:nsimg];
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index 804028053d..eb78edd2e7 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -379,7 +379,7 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_category", PROPERTY_HINT_ENUM, "Business,Developer-tools,Education,Entertainment,Finance,Games,Action-games,Adventure-games,Arcade-games,Board-games,Card-games,Casino-games,Dice-games,Educational-games,Family-games,Kids-games,Music-games,Puzzle-games,Racing-games,Role-playing-games,Simulation-games,Sports-games,Strategy-games,Trivia-games,Word-games,Graphics-design,Healthcare-fitness,Lifestyle,Medical,Music,News,Photography,Productivity,Reference,Social-networking,Sports,Travel,Utilities,Video,Weather"), "Games"));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "application/copyright_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()));
@@ -673,7 +673,7 @@ void EditorExportPlatformMacOS::_fix_plist(const Ref<EditorExportPreset> &p_pres
} else if (lines[i].find("$bundle_identifier") != -1) {
strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n";
} else if (lines[i].find("$short_version") != -1) {
- strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n";
+ strnew += lines[i].replace("$short_version", p_preset->get_version("application/short_version")) + "\n";
} else if (lines[i].find("$version") != -1) {
strnew += lines[i].replace("$version", p_preset->get_version("application/version")) + "\n";
} else if (lines[i].find("$signature") != -1) {
diff --git a/platform/macos/gl_manager_macos_legacy.mm b/platform/macos/gl_manager_macos_legacy.mm
index 3e5a96bffd..701de6df78 100644
--- a/platform/macos/gl_manager_macos_legacy.mm
+++ b/platform/macos/gl_manager_macos_legacy.mm
@@ -50,10 +50,10 @@ Error GLManagerLegacy_MacOS::create_context(GLWindow &win) {
};
NSOpenGLPixelFormat *pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
- ERR_FAIL_COND_V(pixel_format == nil, ERR_CANT_CREATE);
+ ERR_FAIL_NULL_V(pixel_format, ERR_CANT_CREATE);
win.context = [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:shared_context];
- ERR_FAIL_COND_V(win.context == nil, ERR_CANT_CREATE);
+ ERR_FAIL_NULL_V(win.context, ERR_CANT_CREATE);
if (shared_context == nullptr) {
shared_context = win.context;
}
diff --git a/platform/macos/godot_menu_delegate.mm b/platform/macos/godot_menu_delegate.mm
index e393a87134..844fd27f01 100644
--- a/platform/macos/godot_menu_delegate.mm
+++ b/platform/macos/godot_menu_delegate.mm
@@ -56,9 +56,16 @@
- (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item {
if (item) {
GodotMenuItem *value = [item representedObject];
- if (value && value->hover_callback != Callable()) {
+ if (value && value->hover_callback.is_valid()) {
// If custom callback is set, use it.
- value->hover_callback.call(value->meta);
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[1] = { &value->meta };
+
+ value->hover_callback.callp(args, 1, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat(RTR("Failed to execute menu hover callback: %s."), Variant::get_callable_error_text(value->hover_callback, args, 1, ce)));
+ }
}
}
}
@@ -73,19 +80,28 @@
if (ev_modifiers == item_modifiers) {
GodotMenuItem *value = [menu_item representedObject];
- if (value->key_callback != Callable()) {
- // If custom callback is set, use it.
- value->key_callback.call(value->meta);
- } else {
- // Otherwise redirect event to the engine.
- if (DisplayServer::get_singleton()) {
- [[[NSApplication sharedApplication] keyWindow] sendEvent:event];
+ if (value) {
+ if (value->key_callback.is_valid()) {
+ // If custom callback is set, use it.
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[1] = { &value->meta };
+
+ value->key_callback.callp(args, 1, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat(RTR("Failed to execute menu key callback: %s."), Variant::get_callable_error_text(value->key_callback, args, 1, ce)));
+ }
+ } else {
+ // Otherwise redirect event to the engine.
+ if (DisplayServer::get_singleton()) {
+ [[[NSApplication sharedApplication] keyWindow] sendEvent:event];
+ }
}
- }
- // Suppress default menu action.
- *target = self;
- *action = @selector(doNothing:);
+ // Suppress default menu action.
+ *target = self;
+ *action = @selector(doNothing:);
+ }
return YES;
}
}
diff --git a/platform/macos/godot_window_delegate.mm b/platform/macos/godot_window_delegate.mm
index 1756f2d676..e359630d1d 100644
--- a/platform/macos/godot_window_delegate.mm
+++ b/platform/macos/godot_window_delegate.mm
@@ -356,4 +356,13 @@
}
}
+- (void)windowDidChangeOcclusionState:(NSNotification *)notification {
+ DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+ DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
+ wd.is_visible = ([wd.window_object occlusionState] & NSWindowOcclusionStateVisible) && [wd.window_object isVisible];
+}
+
@end
diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm
index 1b8ca0134d..29dff683d5 100644
--- a/platform/macos/os_macos.mm
+++ b/platform/macos/os_macos.mm
@@ -819,7 +819,7 @@ OS_MacOS::OS_MacOS() {
[NSApp finishLaunching];
id delegate = [[GodotApplicationDelegate alloc] init];
- ERR_FAIL_COND(!delegate);
+ ERR_FAIL_NULL(delegate);
[NSApp setDelegate:delegate];
pre_wait_observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, &pre_wait_observer_cb, nullptr);
diff --git a/platform/web/detect.py b/platform/web/detect.py
index 043ddca65d..b0044a80ff 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -41,6 +41,11 @@ def get_opts():
"dlink_enabled", "Enable WebAssembly dynamic linking (GDExtension support). Produces bigger binaries", False
),
BoolVariable("use_closure_compiler", "Use closure compiler to minimize JavaScript code", False),
+ BoolVariable(
+ "proxy_to_pthread",
+ "Use Emscripten PROXY_TO_PTHREAD option to run the main application code to a separate thread",
+ False,
+ ),
]
@@ -157,7 +162,7 @@ def configure(env: "Environment"):
env.AddMethod(create_template_zip, "CreateTemplateZip")
# Closure compiler extern and support for ecmascript specs (const, let, etc).
- env["ENV"]["EMCC_CLOSURE_ARGS"] = "--language_in ECMASCRIPT6"
+ env["ENV"]["EMCC_CLOSURE_ARGS"] = "--language_in ECMASCRIPT_2020"
env["CC"] = "emcc"
env["CXX"] = "em++"
@@ -211,6 +216,10 @@ def configure(env: "Environment"):
env.Append(LINKFLAGS=["-Wl,-u,scalbnf"])
if env["dlink_enabled"]:
+ if env["proxy_to_pthread"]:
+ print("GDExtension support requires proxy_to_pthread=no, disabling")
+ env["proxy_to_pthread"] = False
+
if cc_semver < (3, 1, 14):
print("GDExtension support requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver)
sys.exit(255)
@@ -221,6 +230,16 @@ def configure(env: "Environment"):
env.Append(LINKFLAGS=["-fvisibility=hidden"])
env.extra_suffix = ".dlink" + env.extra_suffix
+ # Run the main application in a web worker
+ if env["proxy_to_pthread"]:
+ env.Append(LINKFLAGS=["-s", "PROXY_TO_PTHREAD=1"])
+ env.Append(CPPDEFINES=["PROXY_TO_PTHREAD_ENABLED"])
+ env.Append(LINKFLAGS=["-s", "EXPORTED_RUNTIME_METHODS=['_emscripten_proxy_main']"])
+ # https://github.com/emscripten-core/emscripten/issues/18034#issuecomment-1277561925
+ env.Append(LINKFLAGS=["-s", "TEXTDECODER=0"])
+ # BigInt support to pass object pointers between contexts
+ env.Append(LINKFLAGS=["-s", "WASM_BIGINT"])
+
# Reduce code size by generating less support code (e.g. skip NodeJS support).
env.Append(LINKFLAGS=["-s", "ENVIRONMENT=web,worker"])
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index dcc4ac4bf7..022e044185 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -35,6 +35,7 @@
#include "os_web.h"
#include "core/config/project_settings.h"
+#include "core/object/callable_method_pointer.h"
#include "scene/resources/atlas_texture.h"
#include "servers/rendering/dummy/rasterizer_dummy.h"
@@ -59,13 +60,26 @@ DisplayServerWeb *DisplayServerWeb::get_singleton() {
bool DisplayServerWeb::check_size_force_redraw() {
bool size_changed = godot_js_display_size_update() != 0;
if (size_changed && !rect_changed_callback.is_null()) {
- Variant size = Rect2i(Point2i(), window_get_size()); // TODO use window_get_position if implemented.
+ Size2i window_size = window_get_size();
+ Variant size = Rect2i(Point2i(), window_size); // TODO use window_get_position if implemented.
rect_changed_callback.call(size);
+ emscripten_set_canvas_element_size(canvas_id, window_size.x, window_size.y);
}
return size_changed;
}
void DisplayServerWeb::fullscreen_change_callback(int p_fullscreen) {
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(DisplayServerWeb::_fullscreen_change_callback).bind(p_fullscreen).call_deferred();
+ return;
+ }
+#endif
+
+ _fullscreen_change_callback(p_fullscreen);
+}
+
+void DisplayServerWeb::_fullscreen_change_callback(int p_fullscreen) {
DisplayServerWeb *display = get_singleton();
if (p_fullscreen) {
display->window_mode = WINDOW_MODE_FULLSCREEN;
@@ -75,7 +89,23 @@ void DisplayServerWeb::fullscreen_change_callback(int p_fullscreen) {
}
// Drag and drop callback.
-void DisplayServerWeb::drop_files_js_callback(char **p_filev, int p_filec) {
+void DisplayServerWeb::drop_files_js_callback(const char **p_filev, int p_filec) {
+ Vector<String> files;
+ for (int i = 0; i < p_filec; i++) {
+ files.push_back(String::utf8(p_filev[i]));
+ }
+
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(DisplayServerWeb::_drop_files_js_callback).bind(files).call_deferred();
+ return;
+ }
+#endif
+
+ _drop_files_js_callback(files);
+}
+
+void DisplayServerWeb::_drop_files_js_callback(const Vector<String> &p_files) {
DisplayServerWeb *ds = get_singleton();
if (!ds) {
ERR_FAIL_MSG("Unable to drop files because the DisplayServer is not active");
@@ -83,15 +113,22 @@ void DisplayServerWeb::drop_files_js_callback(char **p_filev, int p_filec) {
if (ds->drop_files_callback.is_null()) {
return;
}
- Vector<String> files;
- for (int i = 0; i < p_filec; i++) {
- files.push_back(String::utf8(p_filev[i]));
- }
- ds->drop_files_callback.call(files);
+ ds->drop_files_callback.call(p_files);
}
// Web quit request callback.
void DisplayServerWeb::request_quit_callback() {
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(DisplayServerWeb::_request_quit_callback).call_deferred();
+ return;
+ }
+#endif
+
+ _request_quit_callback();
+}
+
+void DisplayServerWeb::_request_quit_callback() {
DisplayServerWeb *ds = get_singleton();
if (ds && !ds->window_event_callback.is_null()) {
Variant event = int(DisplayServer::WINDOW_EVENT_CLOSE_REQUEST);
@@ -119,17 +156,32 @@ void DisplayServerWeb::dom2godot_mod(Ref<InputEventWithModifiers> ev, int p_mod,
void DisplayServerWeb::key_callback(int p_pressed, int p_repeat, int p_modifiers) {
DisplayServerWeb *ds = get_singleton();
JSKeyEvent &key_event = ds->key_event;
+
+ const String code = String::utf8(key_event.code);
+ const String key = String::utf8(key_event.key);
+
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(DisplayServerWeb::_key_callback).bind(code, key, p_pressed, p_repeat, p_modifiers).call_deferred();
+ return;
+ }
+#endif
+
+ _key_callback(code, key, p_pressed, p_repeat, p_modifiers);
+}
+
+void DisplayServerWeb::_key_callback(const String &p_key_event_code, const String &p_key_event_key, int p_pressed, int p_repeat, int p_modifiers) {
// Resume audio context after input in case autoplay was denied.
OS_Web::get_singleton()->resume_audio();
char32_t c = 0x00;
- String unicode = String::utf8(key_event.key);
+ String unicode = p_key_event_key;
if (unicode.length() == 1) {
c = unicode[0];
}
- Key keycode = dom_code2godot_scancode(key_event.code, key_event.key, false);
- Key scancode = dom_code2godot_scancode(key_event.code, key_event.key, true);
+ Key keycode = dom_code2godot_scancode(p_key_event_code.utf8().get_data(), p_key_event_key.utf8().get_data(), false);
+ Key scancode = dom_code2godot_scancode(p_key_event_code.utf8().get_data(), p_key_event_key.utf8().get_data(), true);
Ref<InputEventKey> ev;
ev.instantiate();
@@ -150,6 +202,17 @@ void DisplayServerWeb::key_callback(int p_pressed, int p_repeat, int p_modifiers
// Mouse
int DisplayServerWeb::mouse_button_callback(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers) {
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(DisplayServerWeb::_mouse_button_callback).bind(p_pressed, p_button, p_x, p_y, p_modifiers).call_deferred();
+ return true;
+ }
+#endif
+
+ return _mouse_button_callback(p_pressed, p_button, p_x, p_y, p_modifiers);
+}
+
+int DisplayServerWeb::_mouse_button_callback(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers) {
DisplayServerWeb *ds = get_singleton();
Point2 pos(p_x, p_y);
@@ -226,6 +289,17 @@ int DisplayServerWeb::mouse_button_callback(int p_pressed, int p_button, double
}
void DisplayServerWeb::mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers) {
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(DisplayServerWeb::_mouse_move_callback).bind(p_x, p_y, p_rel_x, p_rel_y, p_modifiers).call_deferred();
+ return;
+ }
+#endif
+
+ _mouse_move_callback(p_x, p_y, p_rel_x, p_rel_y, p_modifiers);
+}
+
+void DisplayServerWeb::_mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers) {
BitField<MouseButtonMask> input_mask = Input::get_singleton()->get_mouse_button_mask();
// For motion outside the canvas, only read mouse movement if dragging
// started inside the canvas; imitating desktop app behavior.
@@ -301,9 +375,25 @@ bool DisplayServerWeb::tts_is_paused() const {
}
void DisplayServerWeb::update_voices_callback(int p_size, const char **p_voice) {
- get_singleton()->voices.clear();
+ Vector<String> voices;
for (int i = 0; i < p_size; i++) {
- Vector<String> tokens = String::utf8(p_voice[i]).split(";", true, 2);
+ voices.append(String::utf8(p_voice[i]));
+ }
+
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(DisplayServerWeb::_update_voices_callback).bind(voices).call_deferred();
+ return;
+ }
+#endif
+
+ _update_voices_callback(voices);
+}
+
+void DisplayServerWeb::_update_voices_callback(const Vector<String> &p_voices) {
+ get_singleton()->voices.clear();
+ for (int i = 0; i < p_voices.size(); i++) {
+ Vector<String> tokens = p_voices[i].split(";", true, 2);
if (tokens.size() == 2) {
Dictionary voice_d;
voice_d["name"] = tokens[1];
@@ -334,7 +424,7 @@ void DisplayServerWeb::tts_speak(const String &p_text, const String &p_voice, in
CharString string = p_text.utf8();
utterance_ids[p_utterance_id] = string;
- godot_js_tts_speak(string.get_data(), p_voice.utf8().get_data(), CLAMP(p_volume, 0, 100), CLAMP(p_pitch, 0.f, 2.f), CLAMP(p_rate, 0.1f, 10.f), p_utterance_id, DisplayServerWeb::_js_utterance_callback);
+ godot_js_tts_speak(string.get_data(), p_voice.utf8().get_data(), CLAMP(p_volume, 0, 100), CLAMP(p_pitch, 0.f, 2.f), CLAMP(p_rate, 0.1f, 10.f), p_utterance_id, DisplayServerWeb::js_utterance_callback);
}
void DisplayServerWeb::tts_pause() {
@@ -356,6 +446,17 @@ void DisplayServerWeb::tts_stop() {
godot_js_tts_stop();
}
+void DisplayServerWeb::js_utterance_callback(int p_event, int p_id, int p_pos) {
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(DisplayServerWeb::_js_utterance_callback).bind(p_event, p_id, p_pos).call_deferred();
+ return;
+ }
+#endif
+
+ _js_utterance_callback(p_event, p_id, p_pos);
+}
+
void DisplayServerWeb::_js_utterance_callback(int p_event, int p_id, int p_pos) {
DisplayServerWeb *ds = (DisplayServerWeb *)DisplayServer::get_singleton();
if (ds->utterance_ids.has(p_id)) {
@@ -507,6 +608,17 @@ Point2i DisplayServerWeb::mouse_get_position() const {
// Wheel
int DisplayServerWeb::mouse_wheel_callback(double p_delta_x, double p_delta_y) {
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(DisplayServerWeb::_mouse_wheel_callback).bind(p_delta_x, p_delta_y).call_deferred();
+ return true;
+ }
+#endif
+
+ return _mouse_wheel_callback(p_delta_x, p_delta_y);
+}
+
+int DisplayServerWeb::_mouse_wheel_callback(double p_delta_x, double p_delta_y) {
if (!godot_js_display_canvas_is_focused()) {
if (get_singleton()->cursor_inside_canvas) {
godot_js_display_canvas_focus();
@@ -559,6 +671,17 @@ int DisplayServerWeb::mouse_wheel_callback(double p_delta_x, double p_delta_y) {
// Touch
void DisplayServerWeb::touch_callback(int p_type, int p_count) {
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(DisplayServerWeb::_touch_callback).bind(p_type, p_count).call_deferred();
+ return;
+ }
+#endif
+
+ _touch_callback(p_type, p_count);
+}
+
+void DisplayServerWeb::_touch_callback(int p_type, int p_count) {
DisplayServerWeb *ds = get_singleton();
const JSTouchEvent &touch_event = ds->touch_event;
@@ -603,13 +726,25 @@ bool DisplayServerWeb::is_touchscreen_available() const {
// Virtual Keyboard
void DisplayServerWeb::vk_input_text_callback(const char *p_text, int p_cursor) {
+ String text = p_text;
+
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(DisplayServerWeb::_vk_input_text_callback).bind(text, p_cursor).call_deferred();
+ return;
+ }
+#endif
+
+ _vk_input_text_callback(text, p_cursor);
+}
+
+void DisplayServerWeb::_vk_input_text_callback(const String &p_text, int p_cursor) {
DisplayServerWeb *ds = DisplayServerWeb::get_singleton();
if (!ds || ds->input_text_callback.is_null()) {
return;
}
// Call input_text
- Variant event = String::utf8(p_text);
- ds->input_text_callback.call(event);
+ ds->input_text_callback.call(p_text);
// Insert key right to reach position.
Input *input = Input::get_singleton();
Ref<InputEventKey> k;
@@ -636,14 +771,39 @@ void DisplayServerWeb::virtual_keyboard_hide() {
}
void DisplayServerWeb::window_blur_callback() {
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(DisplayServerWeb::_window_blur_callback).call_deferred();
+ return;
+ }
+#endif
+
+ _window_blur_callback();
+}
+
+void DisplayServerWeb::_window_blur_callback() {
Input::get_singleton()->release_pressed_events();
}
// Gamepad
void DisplayServerWeb::gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid) {
+ String id = p_id;
+ String guid = p_guid;
+
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(DisplayServerWeb::_gamepad_callback).bind(p_index, p_connected, id, guid).call_deferred();
+ return;
+ }
+#endif
+
+ _gamepad_callback(p_index, p_connected, id, guid);
+}
+
+void DisplayServerWeb::_gamepad_callback(int p_index, int p_connected, const String &p_id, const String &p_guid) {
Input *input = Input::get_singleton();
if (p_connected) {
- input->joy_connection_changed(p_index, true, String::utf8(p_id), String::utf8(p_guid));
+ input->joy_connection_changed(p_index, true, p_id, p_guid);
} else {
input->joy_connection_changed(p_index, false, "");
}
@@ -687,7 +847,20 @@ Vector<String> DisplayServerWeb::get_rendering_drivers_func() {
// Clipboard
void DisplayServerWeb::update_clipboard_callback(const char *p_text) {
- get_singleton()->clipboard = String::utf8(p_text);
+ String text = p_text;
+
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(DisplayServerWeb::_update_clipboard_callback).bind(text).call_deferred();
+ return;
+ }
+#endif
+
+ _update_clipboard_callback(text);
+}
+
+void DisplayServerWeb::_update_clipboard_callback(const String &p_text) {
+ get_singleton()->clipboard = p_text;
}
void DisplayServerWeb::clipboard_set(const String &p_text) {
@@ -702,6 +875,17 @@ String DisplayServerWeb::clipboard_get() const {
}
void DisplayServerWeb::send_window_event_callback(int p_notification) {
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(DisplayServerWeb::_send_window_event_callback).bind(p_notification).call_deferred();
+ return;
+ }
+#endif
+
+ _send_window_event_callback(p_notification);
+}
+
+void DisplayServerWeb::_send_window_event_callback(int p_notification) {
DisplayServerWeb *ds = get_singleton();
if (!ds) {
return;
@@ -816,19 +1000,19 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode
godot_js_input_mouse_wheel_cb(&DisplayServerWeb::mouse_wheel_callback);
godot_js_input_touch_cb(&DisplayServerWeb::touch_callback, touch_event.identifier, touch_event.coords);
godot_js_input_key_cb(&DisplayServerWeb::key_callback, key_event.code, key_event.key);
- godot_js_input_paste_cb(update_clipboard_callback);
- godot_js_input_drop_files_cb(drop_files_js_callback);
+ godot_js_input_paste_cb(&DisplayServerWeb::update_clipboard_callback);
+ godot_js_input_drop_files_cb(&DisplayServerWeb::drop_files_js_callback);
godot_js_input_gamepad_cb(&DisplayServerWeb::gamepad_callback);
// JS Display interface (js/libs/library_godot_display.js)
godot_js_display_fullscreen_cb(&DisplayServerWeb::fullscreen_change_callback);
- godot_js_display_window_blur_cb(&window_blur_callback);
- godot_js_display_notification_cb(&send_window_event_callback,
+ godot_js_display_window_blur_cb(&DisplayServerWeb::window_blur_callback);
+ godot_js_display_notification_cb(&DisplayServerWeb::send_window_event_callback,
WINDOW_EVENT_MOUSE_ENTER,
WINDOW_EVENT_MOUSE_EXIT,
WINDOW_EVENT_FOCUS_IN,
WINDOW_EVENT_FOCUS_OUT);
- godot_js_display_vk_cb(&vk_input_text_callback);
+ godot_js_display_vk_cb(&DisplayServerWeb::vk_input_text_callback);
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_event);
}
diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h
index 1653deff80..51c6ab3c0a 100644
--- a/platform/web/display_server_web.h
+++ b/platform/web/display_server_web.h
@@ -91,28 +91,43 @@ private:
// events
WASM_EXPORT static void fullscreen_change_callback(int p_fullscreen);
+ static void _fullscreen_change_callback(int p_fullscreen);
WASM_EXPORT static int mouse_button_callback(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers);
+ static int _mouse_button_callback(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers);
WASM_EXPORT static void mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers);
+ static void _mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers);
WASM_EXPORT static int mouse_wheel_callback(double p_delta_x, double p_delta_y);
+ static int _mouse_wheel_callback(double p_delta_x, double p_delta_y);
WASM_EXPORT static void touch_callback(int p_type, int p_count);
+ static void _touch_callback(int p_type, int p_count);
WASM_EXPORT static void key_callback(int p_pressed, int p_repeat, int p_modifiers);
+ static void _key_callback(const String &p_key_event_code, const String &p_key_event_key, int p_pressed, int p_repeat, int p_modifiers);
WASM_EXPORT static void vk_input_text_callback(const char *p_text, int p_cursor);
+ static void _vk_input_text_callback(const String &p_text, int p_cursor);
WASM_EXPORT static void gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid);
+ static void _gamepad_callback(int p_index, int p_connected, const String &p_id, const String &p_guid);
+ WASM_EXPORT static void js_utterance_callback(int p_event, int p_id, int p_pos);
+ static void _js_utterance_callback(int p_event, int p_id, int p_pos);
+ WASM_EXPORT static void request_quit_callback();
+ static void _request_quit_callback();
+ WASM_EXPORT static void window_blur_callback();
+ static void _window_blur_callback();
+ WASM_EXPORT static void update_voices_callback(int p_size, const char **p_voice);
+ static void _update_voices_callback(const Vector<String> &p_voices);
+ WASM_EXPORT static void update_clipboard_callback(const char *p_text);
+ static void _update_clipboard_callback(const String &p_text);
+ WASM_EXPORT static void send_window_event_callback(int p_notification);
+ static void _send_window_event_callback(int p_notification);
+ WASM_EXPORT static void drop_files_js_callback(const char **p_filev, int p_filec);
+ static void _drop_files_js_callback(const Vector<String> &p_files);
+
void process_joypads();
- WASM_EXPORT static void _js_utterance_callback(int p_event, int p_id, int p_pos);
static Vector<String> get_rendering_drivers_func();
static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error);
static void _dispatch_input_event(const Ref<InputEvent> &p_event);
- WASM_EXPORT static void request_quit_callback();
- WASM_EXPORT static void window_blur_callback();
- WASM_EXPORT static void update_voices_callback(int p_size, const char **p_voice);
- WASM_EXPORT static void update_clipboard_callback(const char *p_text);
- WASM_EXPORT static void send_window_event_callback(int p_notification);
- WASM_EXPORT static void drop_files_js_callback(char **p_filev, int p_filec);
-
protected:
int get_current_video_driver() const;
diff --git a/platform/web/godot_js.h b/platform/web/godot_js.h
index f172148bf9..031e67e486 100644
--- a/platform/web/godot_js.h
+++ b/platform/web/godot_js.h
@@ -38,6 +38,7 @@ extern "C" {
#endif
#include <stddef.h>
+#include <stdint.h>
// Config
extern void godot_js_config_locale_get(char *p_ptr, int p_ptr_max);
@@ -69,7 +70,7 @@ extern int godot_js_input_gamepad_sample();
extern int godot_js_input_gamepad_sample_count();
extern int godot_js_input_gamepad_sample_get(int p_idx, float r_btns[16], int32_t *r_btns_num, float r_axes[10], int32_t *r_axes_num, int32_t *r_standard);
extern void godot_js_input_paste_cb(void (*p_callback)(const char *p_text));
-extern void godot_js_input_drop_files_cb(void (*p_callback)(char **p_filev, int p_filec));
+extern void godot_js_input_drop_files_cb(void (*p_callback)(const char **p_filev, int p_filec));
// TTS
extern int godot_js_tts_is_speaking();
diff --git a/platform/web/godot_webgl2.h b/platform/web/godot_webgl2.h
index d2f46e125e..3ade9e4239 100644
--- a/platform/web/godot_webgl2.h
+++ b/platform/web/godot_webgl2.h
@@ -44,6 +44,7 @@ extern "C" {
#endif
void godot_webgl2_glFramebufferTextureMultiviewOVR(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews);
+void godot_webgl2_glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data);
#define glFramebufferTextureMultiviewOVR godot_webgl2_glFramebufferTextureMultiviewOVR
diff --git a/platform/web/javascript_bridge_singleton.cpp b/platform/web/javascript_bridge_singleton.cpp
index 1bb72456e8..d72ad8331b 100644
--- a/platform/web/javascript_bridge_singleton.cpp
+++ b/platform/web/javascript_bridge_singleton.cpp
@@ -72,7 +72,8 @@ private:
WASM_EXPORT static void _free_lock(void **p_lock, int p_type);
WASM_EXPORT static Variant _js2variant(int p_type, godot_js_wrapper_ex *p_val);
WASM_EXPORT static void *_alloc_variants(int p_size);
- WASM_EXPORT static void _callback(void *p_ref, int p_arg_id, int p_argc);
+ WASM_EXPORT static void callback(void *p_ref, int p_arg_id, int p_argc);
+ static void _callback(const JavaScriptObjectImpl *obj, Variant arg);
protected:
bool _set(const StringName &p_name, const Variant &p_value) override;
@@ -163,7 +164,7 @@ void JavaScriptObjectImpl::_get_property_list(List<PropertyInfo> *p_list) const
}
void JavaScriptObjectImpl::_free_lock(void **p_lock, int p_type) {
- ERR_FAIL_COND_MSG(*p_lock == nullptr, "No lock to free!");
+ ERR_FAIL_NULL_MSG(*p_lock, "No lock to free!");
const Variant::Type type = (Variant::Type)p_type;
switch (type) {
case Variant::STRING: {
@@ -245,9 +246,10 @@ Variant JavaScriptObjectImpl::callp(const StringName &p_method, const Variant **
return _js2variant(type, &exchange);
}
-void JavaScriptObjectImpl::_callback(void *p_ref, int p_args_id, int p_argc) {
+void JavaScriptObjectImpl::callback(void *p_ref, int p_args_id, int p_argc) {
const JavaScriptObjectImpl *obj = (JavaScriptObjectImpl *)p_ref;
ERR_FAIL_COND_MSG(obj->_callable.is_null(), "JavaScript callback failed.");
+
Vector<const Variant *> argp;
Array arg_arr;
for (int i = 0; i < p_argc; i++) {
@@ -256,7 +258,20 @@ void JavaScriptObjectImpl::_callback(void *p_ref, int p_args_id, int p_argc) {
int type = godot_js_wrapper_object_getvar(p_args_id, Variant::INT, &exchange);
arg_arr.push_back(_js2variant(type, &exchange));
}
- obj->_callable.call(arg_arr);
+ Variant arg = arg_arr;
+
+#ifdef PROXY_TO_PTHREAD_ENABLED
+ if (!Thread::is_main_thread()) {
+ callable_mp_static(JavaScriptObjectImpl::_callback).bind(obj, arg).call_deferred();
+ return;
+ }
+#endif
+
+ _callback(obj, arg);
+}
+
+void JavaScriptObjectImpl::_callback(const JavaScriptObjectImpl *obj, Variant arg) {
+ obj->_callable.call(arg);
// Set return value
godot_js_wrapper_ex exchange;
@@ -273,7 +288,7 @@ void JavaScriptObjectImpl::_callback(void *p_ref, int p_args_id, int p_argc) {
Ref<JavaScriptObject> JavaScriptBridge::create_callback(const Callable &p_callable) {
Ref<JavaScriptObjectImpl> out = memnew(JavaScriptObjectImpl);
out->_callable = p_callable;
- out->_js_id = godot_js_wrapper_create_cb(out.ptr(), JavaScriptObjectImpl::_callback);
+ out->_js_id = godot_js_wrapper_create_cb(out.ptr(), JavaScriptObjectImpl::callback);
return out;
}
diff --git a/platform/web/js/engine/config.js b/platform/web/js/engine/config.js
index 6a30c253fb..0b6626968e 100644
--- a/platform/web/js/engine/config.js
+++ b/platform/web/js/engine/config.js
@@ -292,7 +292,9 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
return {};
},
'locateFile': function (path) {
- if (path.endsWith('.worker.js')) {
+ if (!path.startsWith('godot.')) {
+ return path;
+ } else if (path.endsWith('.worker.js')) {
return `${loadPath}.worker.js`;
} else if (path.endsWith('.audio.worklet.js')) {
return `${loadPath}.audio.worklet.js`;
diff --git a/platform/web/js/engine/engine.js b/platform/web/js/engine/engine.js
index fb80bd55e1..3d6720a2fc 100644
--- a/platform/web/js/engine/engine.js
+++ b/platform/web/js/engine/engine.js
@@ -164,6 +164,10 @@ const Engine = (function () {
// Preload GDExtension libraries.
const libs = [];
+ if (me.config.gdextensionLibs.length > 0 && !me.rtenv['loadDynamicLibrary']) {
+ return Promise.reject(new Error('GDExtension libraries are not supported by this engine version. '
+ + 'Enable "Extensions Support" for your export preset and/or build your custom template with "dlink_enabled=yes".'));
+ }
me.config.gdextensionLibs.forEach(function (lib) {
libs.push(me.rtenv['loadDynamicLibrary'](lib, { 'loadAsync': true }));
});
diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js
index cc86c81096..b54c5cac85 100644
--- a/platform/web/js/libs/library_godot_audio.js
+++ b/platform/web/js/libs/library_godot_audio.js
@@ -159,16 +159,19 @@ const GodotAudio = {
return 1;
},
+ godot_audio_has_worklet__proxy: 'sync',
godot_audio_has_worklet__sig: 'i',
godot_audio_has_worklet: function () {
return (GodotAudio.ctx && GodotAudio.ctx.audioWorklet) ? 1 : 0;
},
+ godot_audio_has_script_processor__proxy: 'sync',
godot_audio_has_script_processor__sig: 'i',
godot_audio_has_script_processor: function () {
return (GodotAudio.ctx && GodotAudio.ctx.createScriptProcessor) ? 1 : 0;
},
+ godot_audio_init__proxy: 'sync',
godot_audio_init__sig: 'iiiii',
godot_audio_init: function (p_mix_rate, p_latency, p_state_change, p_latency_update) {
const statechange = GodotRuntime.get_func(p_state_change);
@@ -179,6 +182,7 @@ const GodotAudio = {
return channels;
},
+ godot_audio_resume__proxy: 'sync',
godot_audio_resume__sig: 'v',
godot_audio_resume: function () {
if (GodotAudio.ctx && GodotAudio.ctx.state !== 'running') {
@@ -358,6 +362,7 @@ const GodotAudioWorklet = {
},
},
+ godot_audio_worklet_create__proxy: 'sync',
godot_audio_worklet_create__sig: 'ii',
godot_audio_worklet_create: function (channels) {
try {
@@ -369,6 +374,7 @@ const GodotAudioWorklet = {
return 0;
},
+ godot_audio_worklet_start__proxy: 'sync',
godot_audio_worklet_start__sig: 'viiiii',
godot_audio_worklet_start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, p_state) {
const out_buffer = GodotRuntime.heapSub(HEAPF32, p_out_buf, p_out_size);
@@ -377,6 +383,7 @@ const GodotAudioWorklet = {
GodotAudioWorklet.start(in_buffer, out_buffer, state);
},
+ godot_audio_worklet_start_no_threads__proxy: 'sync',
godot_audio_worklet_start_no_threads__sig: 'viiiiii',
godot_audio_worklet_start_no_threads: function (p_out_buf, p_out_size, p_out_callback, p_in_buf, p_in_size, p_in_callback) {
const out_callback = GodotRuntime.get_func(p_out_callback);
@@ -465,6 +472,7 @@ const GodotAudioScript = {
},
},
+ godot_audio_script_create__proxy: 'sync',
godot_audio_script_create__sig: 'iii',
godot_audio_script_create: function (buffer_length, channel_count) {
const buf_len = GodotRuntime.getHeapValue(buffer_length, 'i32');
@@ -478,6 +486,7 @@ const GodotAudioScript = {
return 0;
},
+ godot_audio_script_start__proxy: 'sync',
godot_audio_script_start__sig: 'viiiii',
godot_audio_script_start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, p_cb) {
const onprocess = GodotRuntime.get_func(p_cb);
diff --git a/platform/web/js/libs/library_godot_display.js b/platform/web/js/libs/library_godot_display.js
index c60e6899f2..99fc429d8f 100644
--- a/platform/web/js/libs/library_godot_display.js
+++ b/platform/web/js/libs/library_godot_display.js
@@ -345,6 +345,7 @@ const GodotDisplay = {
},
},
+ godot_js_display_is_swap_ok_cancel__proxy: 'sync',
godot_js_display_is_swap_ok_cancel__sig: 'i',
godot_js_display_is_swap_ok_cancel: function () {
const win = (['Windows', 'Win64', 'Win32', 'WinCE']);
@@ -355,16 +356,19 @@ const GodotDisplay = {
return 0;
},
+ godot_js_tts_is_speaking__proxy: 'sync',
godot_js_tts_is_speaking__sig: 'i',
godot_js_tts_is_speaking: function () {
return window.speechSynthesis.speaking;
},
+ godot_js_tts_is_paused__proxy: 'sync',
godot_js_tts_is_paused__sig: 'i',
godot_js_tts_is_paused: function () {
return window.speechSynthesis.paused;
},
+ godot_js_tts_get_voices__proxy: 'sync',
godot_js_tts_get_voices__sig: 'vi',
godot_js_tts_get_voices: function (p_callback) {
const func = GodotRuntime.get_func(p_callback);
@@ -382,6 +386,7 @@ const GodotDisplay = {
}
},
+ godot_js_tts_speak__proxy: 'sync',
godot_js_tts_speak__sig: 'viiiffii',
godot_js_tts_speak: function (p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_callback) {
const func = GodotRuntime.get_func(p_callback);
@@ -424,53 +429,63 @@ const GodotDisplay = {
window.speechSynthesis.speak(utterance);
},
+ godot_js_tts_pause__proxy: 'sync',
godot_js_tts_pause__sig: 'v',
godot_js_tts_pause: function () {
window.speechSynthesis.pause();
},
+ godot_js_tts_resume__proxy: 'sync',
godot_js_tts_resume__sig: 'v',
godot_js_tts_resume: function () {
window.speechSynthesis.resume();
},
+ godot_js_tts_stop__proxy: 'sync',
godot_js_tts_stop__sig: 'v',
godot_js_tts_stop: function () {
window.speechSynthesis.cancel();
window.speechSynthesis.resume();
},
+ godot_js_display_alert__proxy: 'sync',
godot_js_display_alert__sig: 'vi',
godot_js_display_alert: function (p_text) {
window.alert(GodotRuntime.parseString(p_text)); // eslint-disable-line no-alert
},
+ godot_js_display_screen_dpi_get__proxy: 'sync',
godot_js_display_screen_dpi_get__sig: 'i',
godot_js_display_screen_dpi_get: function () {
return GodotDisplay.getDPI();
},
+ godot_js_display_pixel_ratio_get__proxy: 'sync',
godot_js_display_pixel_ratio_get__sig: 'f',
godot_js_display_pixel_ratio_get: function () {
return GodotDisplayScreen.getPixelRatio();
},
+ godot_js_display_fullscreen_request__proxy: 'sync',
godot_js_display_fullscreen_request__sig: 'i',
godot_js_display_fullscreen_request: function () {
return GodotDisplayScreen.requestFullscreen();
},
+ godot_js_display_fullscreen_exit__proxy: 'sync',
godot_js_display_fullscreen_exit__sig: 'i',
godot_js_display_fullscreen_exit: function () {
return GodotDisplayScreen.exitFullscreen();
},
+ godot_js_display_desired_size_set__proxy: 'sync',
godot_js_display_desired_size_set__sig: 'vii',
godot_js_display_desired_size_set: function (width, height) {
GodotDisplayScreen.desired_size = [width, height];
GodotDisplayScreen.updateSize();
},
+ godot_js_display_size_update__proxy: 'sync',
godot_js_display_size_update__sig: 'i',
godot_js_display_size_update: function () {
const updated = GodotDisplayScreen.updateSize();
@@ -480,6 +495,7 @@ const GodotDisplay = {
return updated;
},
+ godot_js_display_screen_size_get__proxy: 'sync',
godot_js_display_screen_size_get__sig: 'vii',
godot_js_display_screen_size_get: function (width, height) {
const scale = GodotDisplayScreen.getPixelRatio();
@@ -487,12 +503,14 @@ const GodotDisplay = {
GodotRuntime.setHeapValue(height, window.screen.height * scale, 'i32');
},
+ godot_js_display_window_size_get__proxy: 'sync',
godot_js_display_window_size_get__sig: 'vii',
godot_js_display_window_size_get: function (p_width, p_height) {
GodotRuntime.setHeapValue(p_width, GodotConfig.canvas.width, 'i32');
GodotRuntime.setHeapValue(p_height, GodotConfig.canvas.height, 'i32');
},
+ godot_js_display_has_webgl__proxy: 'sync',
godot_js_display_has_webgl__sig: 'ii',
godot_js_display_has_webgl: function (p_version) {
if (p_version !== 1 && p_version !== 2) {
@@ -507,11 +525,13 @@ const GodotDisplay = {
/*
* Canvas
*/
+ godot_js_display_canvas_focus__proxy: 'sync',
godot_js_display_canvas_focus__sig: 'v',
godot_js_display_canvas_focus: function () {
GodotConfig.canvas.focus();
},
+ godot_js_display_canvas_is_focused__proxy: 'sync',
godot_js_display_canvas_is_focused__sig: 'i',
godot_js_display_canvas_is_focused: function () {
return document.activeElement === GodotConfig.canvas;
@@ -520,6 +540,7 @@ const GodotDisplay = {
/*
* Touchscreen
*/
+ godot_js_display_touchscreen_is_available__proxy: 'sync',
godot_js_display_touchscreen_is_available__sig: 'i',
godot_js_display_touchscreen_is_available: function () {
return 'ontouchstart' in window;
@@ -528,6 +549,7 @@ const GodotDisplay = {
/*
* Clipboard
*/
+ godot_js_display_clipboard_set__proxy: 'sync',
godot_js_display_clipboard_set__sig: 'ii',
godot_js_display_clipboard_set: function (p_text) {
const text = GodotRuntime.parseString(p_text);
@@ -541,6 +563,7 @@ const GodotDisplay = {
return 0;
},
+ godot_js_display_clipboard_get__proxy: 'sync',
godot_js_display_clipboard_get__sig: 'ii',
godot_js_display_clipboard_get: function (callback) {
const func = GodotRuntime.get_func(callback);
@@ -560,11 +583,13 @@ const GodotDisplay = {
/*
* Window
*/
+ godot_js_display_window_title_set__proxy: 'sync',
godot_js_display_window_title_set__sig: 'vi',
godot_js_display_window_title_set: function (p_data) {
document.title = GodotRuntime.parseString(p_data);
},
+ godot_js_display_window_icon_set__proxy: 'sync',
godot_js_display_window_icon_set__sig: 'vii',
godot_js_display_window_icon_set: function (p_ptr, p_len) {
let link = document.getElementById('-gd-engine-icon');
@@ -593,6 +618,7 @@ const GodotDisplay = {
/*
* Cursor
*/
+ godot_js_display_cursor_set_visible__proxy: 'sync',
godot_js_display_cursor_set_visible__sig: 'vi',
godot_js_display_cursor_set_visible: function (p_visible) {
const visible = p_visible !== 0;
@@ -607,16 +633,19 @@ const GodotDisplay = {
}
},
+ godot_js_display_cursor_is_hidden__proxy: 'sync',
godot_js_display_cursor_is_hidden__sig: 'i',
godot_js_display_cursor_is_hidden: function () {
return !GodotDisplayCursor.visible;
},
+ godot_js_display_cursor_set_shape__proxy: 'sync',
godot_js_display_cursor_set_shape__sig: 'vi',
godot_js_display_cursor_set_shape: function (p_string) {
GodotDisplayCursor.set_shape(GodotRuntime.parseString(p_string));
},
+ godot_js_display_cursor_set_custom_shape__proxy: 'sync',
godot_js_display_cursor_set_custom_shape__sig: 'viiiii',
godot_js_display_cursor_set_custom_shape: function (p_shape, p_ptr, p_len, p_hotspot_x, p_hotspot_y) {
const shape = GodotRuntime.parseString(p_shape);
@@ -640,6 +669,7 @@ const GodotDisplay = {
}
},
+ godot_js_display_cursor_lock_set__proxy: 'sync',
godot_js_display_cursor_lock_set__sig: 'vi',
godot_js_display_cursor_lock_set: function (p_lock) {
if (p_lock) {
@@ -649,6 +679,7 @@ const GodotDisplay = {
}
},
+ godot_js_display_cursor_is_locked__proxy: 'sync',
godot_js_display_cursor_is_locked__sig: 'i',
godot_js_display_cursor_is_locked: function () {
return GodotDisplayCursor.isPointerLocked() ? 1 : 0;
@@ -657,6 +688,7 @@ const GodotDisplay = {
/*
* Listeners
*/
+ godot_js_display_fullscreen_cb__proxy: 'sync',
godot_js_display_fullscreen_cb__sig: 'vi',
godot_js_display_fullscreen_cb: function (callback) {
const canvas = GodotConfig.canvas;
@@ -671,6 +703,7 @@ const GodotDisplay = {
GodotEventListeners.add(document, 'webkitfullscreenchange', change_cb, false);
},
+ godot_js_display_window_blur_cb__proxy: 'sync',
godot_js_display_window_blur_cb__sig: 'vi',
godot_js_display_window_blur_cb: function (callback) {
const func = GodotRuntime.get_func(callback);
@@ -679,6 +712,7 @@ const GodotDisplay = {
}, false);
},
+ godot_js_display_notification_cb__proxy: 'sync',
godot_js_display_notification_cb__sig: 'viiiii',
godot_js_display_notification_cb: function (callback, p_enter, p_exit, p_in, p_out) {
const canvas = GodotConfig.canvas;
@@ -691,6 +725,7 @@ const GodotDisplay = {
});
},
+ godot_js_display_setup_canvas__proxy: 'sync',
godot_js_display_setup_canvas__sig: 'viiii',
godot_js_display_setup_canvas: function (p_width, p_height, p_fullscreen, p_hidpi) {
const canvas = GodotConfig.canvas;
@@ -725,6 +760,7 @@ const GodotDisplay = {
/*
* Virtual Keyboard
*/
+ godot_js_display_vk_show__proxy: 'sync',
godot_js_display_vk_show__sig: 'viiii',
godot_js_display_vk_show: function (p_text, p_type, p_start, p_end) {
const text = GodotRuntime.parseString(p_text);
@@ -733,21 +769,25 @@ const GodotDisplay = {
GodotDisplayVK.show(text, p_type, start, end);
},
+ godot_js_display_vk_hide__proxy: 'sync',
godot_js_display_vk_hide__sig: 'v',
godot_js_display_vk_hide: function () {
GodotDisplayVK.hide();
},
+ godot_js_display_vk_available__proxy: 'sync',
godot_js_display_vk_available__sig: 'i',
godot_js_display_vk_available: function () {
return GodotDisplayVK.available();
},
+ godot_js_display_tts_available__proxy: 'sync',
godot_js_display_tts_available__sig: 'i',
godot_js_display_tts_available: function () {
return 'speechSynthesis' in window;
},
+ godot_js_display_vk_cb__proxy: 'sync',
godot_js_display_vk_cb__sig: 'vi',
godot_js_display_vk_cb: function (p_input_cb) {
const input_cb = GodotRuntime.get_func(p_input_cb);
diff --git a/platform/web/js/libs/library_godot_fetch.js b/platform/web/js/libs/library_godot_fetch.js
index 4ef24903e3..00616bc1a5 100644
--- a/platform/web/js/libs/library_godot_fetch.js
+++ b/platform/web/js/libs/library_godot_fetch.js
@@ -125,6 +125,7 @@ const GodotFetch = {
},
},
+ godot_js_fetch_create__proxy: 'sync',
godot_js_fetch_create__sig: 'iiiiiii',
godot_js_fetch_create: function (p_method, p_url, p_headers, p_headers_size, p_body, p_body_size) {
const method = GodotRuntime.parseString(p_method);
@@ -145,6 +146,7 @@ const GodotFetch = {
}), body);
},
+ godot_js_fetch_state_get__proxy: 'sync',
godot_js_fetch_state_get__sig: 'ii',
godot_js_fetch_state_get: function (p_id) {
const obj = IDHandler.get(p_id);
@@ -166,6 +168,7 @@ const GodotFetch = {
return -1;
},
+ godot_js_fetch_http_status_get__proxy: 'sync',
godot_js_fetch_http_status_get__sig: 'ii',
godot_js_fetch_http_status_get: function (p_id) {
const obj = IDHandler.get(p_id);
@@ -175,6 +178,7 @@ const GodotFetch = {
return obj.status;
},
+ godot_js_fetch_read_headers__proxy: 'sync',
godot_js_fetch_read_headers__sig: 'iiii',
godot_js_fetch_read_headers: function (p_id, p_parse_cb, p_ref) {
const obj = IDHandler.get(p_id);
@@ -192,6 +196,7 @@ const GodotFetch = {
return 0;
},
+ godot_js_fetch_read_chunk__proxy: 'sync',
godot_js_fetch_read_chunk__sig: 'iiii',
godot_js_fetch_read_chunk: function (p_id, p_buf, p_buf_size) {
const obj = IDHandler.get(p_id);
@@ -218,6 +223,7 @@ const GodotFetch = {
return p_buf_size - to_read;
},
+ godot_js_fetch_is_chunked__proxy: 'sync',
godot_js_fetch_is_chunked__sig: 'ii',
godot_js_fetch_is_chunked: function (p_id) {
const obj = IDHandler.get(p_id);
@@ -227,6 +233,7 @@ const GodotFetch = {
return obj.chunked ? 1 : 0;
},
+ godot_js_fetch_free__proxy: 'sync',
godot_js_fetch_free__sig: 'vi',
godot_js_fetch_free: function (id) {
GodotFetch.free(id);
diff --git a/platform/web/js/libs/library_godot_input.js b/platform/web/js/libs/library_godot_input.js
index 1b221e78b3..92113e85c9 100644
--- a/platform/web/js/libs/library_godot_input.js
+++ b/platform/web/js/libs/library_godot_input.js
@@ -356,6 +356,7 @@ const GodotInput = {
/*
* Mouse API
*/
+ godot_js_input_mouse_move_cb__proxy: 'sync',
godot_js_input_mouse_move_cb__sig: 'vi',
godot_js_input_mouse_move_cb: function (callback) {
const func = GodotRuntime.get_func(callback);
@@ -374,6 +375,7 @@ const GodotInput = {
GodotEventListeners.add(window, 'mousemove', move_cb, false);
},
+ godot_js_input_mouse_wheel_cb__proxy: 'sync',
godot_js_input_mouse_wheel_cb__sig: 'vi',
godot_js_input_mouse_wheel_cb: function (callback) {
const func = GodotRuntime.get_func(callback);
@@ -385,6 +387,7 @@ const GodotInput = {
GodotEventListeners.add(GodotConfig.canvas, 'wheel', wheel_cb, false);
},
+ godot_js_input_mouse_button_cb__proxy: 'sync',
godot_js_input_mouse_button_cb__sig: 'vi',
godot_js_input_mouse_button_cb: function (callback) {
const func = GodotRuntime.get_func(callback);
@@ -409,6 +412,7 @@ const GodotInput = {
/*
* Touch API
*/
+ godot_js_input_touch_cb__proxy: 'sync',
godot_js_input_touch_cb__sig: 'viii',
godot_js_input_touch_cb: function (callback, ids, coords) {
const func = GodotRuntime.get_func(callback);
@@ -442,6 +446,7 @@ const GodotInput = {
/*
* Key API
*/
+ godot_js_input_key_cb__proxy: 'sync',
godot_js_input_key_cb__sig: 'viii',
godot_js_input_key_cb: function (callback, code, key) {
const func = GodotRuntime.get_func(callback);
@@ -459,23 +464,27 @@ const GodotInput = {
/*
* Gamepad API
*/
+ godot_js_input_gamepad_cb__proxy: 'sync',
godot_js_input_gamepad_cb__sig: 'vi',
godot_js_input_gamepad_cb: function (change_cb) {
const onchange = GodotRuntime.get_func(change_cb);
GodotInputGamepads.init(onchange);
},
+ godot_js_input_gamepad_sample_count__proxy: 'sync',
godot_js_input_gamepad_sample_count__sig: 'i',
godot_js_input_gamepad_sample_count: function () {
return GodotInputGamepads.get_samples().length;
},
+ godot_js_input_gamepad_sample__proxy: 'sync',
godot_js_input_gamepad_sample__sig: 'i',
godot_js_input_gamepad_sample: function () {
GodotInputGamepads.sample();
return 0;
},
+ godot_js_input_gamepad_sample_get__proxy: 'sync',
godot_js_input_gamepad_sample_get__sig: 'iiiiiii',
godot_js_input_gamepad_sample_get: function (p_index, r_btns, r_btns_num, r_axes, r_axes_num, r_standard) {
const sample = GodotInputGamepads.get_sample(p_index);
@@ -502,6 +511,7 @@ const GodotInput = {
/*
* Drag/Drop API
*/
+ godot_js_input_drop_files_cb__proxy: 'sync',
godot_js_input_drop_files_cb__sig: 'vi',
godot_js_input_drop_files_cb: function (callback) {
const func = GodotRuntime.get_func(callback);
@@ -524,6 +534,7 @@ const GodotInput = {
},
/* Paste API */
+ godot_js_input_paste_cb__proxy: 'sync',
godot_js_input_paste_cb__sig: 'vi',
godot_js_input_paste_cb: function (callback) {
const func = GodotRuntime.get_func(callback);
@@ -535,6 +546,7 @@ const GodotInput = {
}, false);
},
+ godot_js_input_vibrate_handheld__proxy: 'sync',
godot_js_input_vibrate_handheld__sig: 'vi',
godot_js_input_vibrate_handheld: function (p_duration_ms) {
if (typeof navigator.vibrate !== 'function') {
diff --git a/platform/web/js/libs/library_godot_javascript_singleton.js b/platform/web/js/libs/library_godot_javascript_singleton.js
index 1764c9a026..b17fde1544 100644
--- a/platform/web/js/libs/library_godot_javascript_singleton.js
+++ b/platform/web/js/libs/library_godot_javascript_singleton.js
@@ -121,6 +121,7 @@ const GodotJSWrapper = {
},
},
+ godot_js_wrapper_interface_get__proxy: 'sync',
godot_js_wrapper_interface_get__sig: 'ii',
godot_js_wrapper_interface_get: function (p_name) {
const name = GodotRuntime.parseString(p_name);
@@ -130,6 +131,7 @@ const GodotJSWrapper = {
return 0;
},
+ godot_js_wrapper_object_get__proxy: 'sync',
godot_js_wrapper_object_get__sig: 'iiii',
godot_js_wrapper_object_get: function (p_id, p_exchange, p_prop) {
const obj = GodotJSWrapper.get_proxied_value(p_id);
@@ -148,6 +150,7 @@ const GodotJSWrapper = {
return GodotJSWrapper.js2variant(obj, p_exchange);
},
+ godot_js_wrapper_object_set__proxy: 'sync',
godot_js_wrapper_object_set__sig: 'viiii',
godot_js_wrapper_object_set: function (p_id, p_name, p_type, p_exchange) {
const obj = GodotJSWrapper.get_proxied_value(p_id);
@@ -162,6 +165,7 @@ const GodotJSWrapper = {
}
},
+ godot_js_wrapper_object_call__proxy: 'sync',
godot_js_wrapper_object_call__sig: 'iiiiiiiii',
godot_js_wrapper_object_call: function (p_id, p_method, p_args, p_argc, p_convert_callback, p_exchange, p_lock, p_free_lock_callback) {
const obj = GodotJSWrapper.get_proxied_value(p_id);
@@ -189,6 +193,7 @@ const GodotJSWrapper = {
}
},
+ godot_js_wrapper_object_unref__proxy: 'sync',
godot_js_wrapper_object_unref__sig: 'vi',
godot_js_wrapper_object_unref: function (p_id) {
const proxy = IDHandler.get(p_id);
@@ -197,6 +202,7 @@ const GodotJSWrapper = {
}
},
+ godot_js_wrapper_create_cb__proxy: 'sync',
godot_js_wrapper_create_cb__sig: 'iii',
godot_js_wrapper_create_cb: function (p_ref, p_func) {
const func = GodotRuntime.get_func(p_func);
@@ -221,11 +227,13 @@ const GodotJSWrapper = {
return id;
},
+ godot_js_wrapper_object_set_cb_ret__proxy: 'sync',
godot_js_wrapper_object_set_cb_ret__sig: 'vii',
godot_js_wrapper_object_set_cb_ret: function (p_val_type, p_val_ex) {
GodotJSWrapper.cb_ret = GodotJSWrapper.variant2js(p_val_type, p_val_ex);
},
+ godot_js_wrapper_object_getvar__proxy: 'sync',
godot_js_wrapper_object_getvar__sig: 'iiii',
godot_js_wrapper_object_getvar: function (p_id, p_type, p_exchange) {
const obj = GodotJSWrapper.get_proxied_value(p_id);
@@ -244,6 +252,7 @@ const GodotJSWrapper = {
}
},
+ godot_js_wrapper_object_setvar__proxy: 'sync',
godot_js_wrapper_object_setvar__sig: 'iiiiii',
godot_js_wrapper_object_setvar: function (p_id, p_key_type, p_key_ex, p_val_type, p_val_ex) {
const obj = GodotJSWrapper.get_proxied_value(p_id);
@@ -260,6 +269,7 @@ const GodotJSWrapper = {
}
},
+ godot_js_wrapper_create_object__proxy: 'sync',
godot_js_wrapper_create_object__sig: 'iiiiiiii',
godot_js_wrapper_create_object: function (p_object, p_args, p_argc, p_convert_callback, p_exchange, p_lock, p_free_lock_callback) {
const name = GodotRuntime.parseString(p_object);
diff --git a/platform/web/js/libs/library_godot_os.js b/platform/web/js/libs/library_godot_os.js
index 00ae399583..92635cb6ae 100644
--- a/platform/web/js/libs/library_godot_os.js
+++ b/platform/web/js/libs/library_godot_os.js
@@ -91,11 +91,13 @@ const GodotConfig = {
},
},
+ godot_js_config_canvas_id_get__proxy: 'sync',
godot_js_config_canvas_id_get__sig: 'vii',
godot_js_config_canvas_id_get: function (p_ptr, p_ptr_max) {
GodotRuntime.stringToHeap(`#${GodotConfig.canvas.id}`, p_ptr, p_ptr_max);
},
+ godot_js_config_locale_get__proxy: 'sync',
godot_js_config_locale_get__sig: 'vii',
godot_js_config_locale_get: function (p_ptr, p_ptr_max) {
GodotRuntime.stringToHeap(GodotConfig.locale, p_ptr, p_ptr_max);
@@ -266,22 +268,26 @@ const GodotOS = {
},
},
+ godot_js_os_finish_async__proxy: 'sync',
godot_js_os_finish_async__sig: 'vi',
godot_js_os_finish_async: function (p_callback) {
const func = GodotRuntime.get_func(p_callback);
GodotOS.finish_async(func);
},
+ godot_js_os_request_quit_cb__proxy: 'sync',
godot_js_os_request_quit_cb__sig: 'vi',
godot_js_os_request_quit_cb: function (p_callback) {
GodotOS.request_quit = GodotRuntime.get_func(p_callback);
},
+ godot_js_os_fs_is_persistent__proxy: 'sync',
godot_js_os_fs_is_persistent__sig: 'i',
godot_js_os_fs_is_persistent: function () {
return GodotFS.is_persistent();
},
+ godot_js_os_fs_sync__proxy: 'sync',
godot_js_os_fs_sync__sig: 'vi',
godot_js_os_fs_sync: function (callback) {
const func = GodotRuntime.get_func(callback);
@@ -291,6 +297,7 @@ const GodotOS = {
});
},
+ godot_js_os_has_feature__proxy: 'sync',
godot_js_os_has_feature__sig: 'ii',
godot_js_os_has_feature: function (p_ftr) {
const ftr = GodotRuntime.parseString(p_ftr);
@@ -313,6 +320,7 @@ const GodotOS = {
return 0;
},
+ godot_js_os_execute__proxy: 'sync',
godot_js_os_execute__sig: 'ii',
godot_js_os_execute: function (p_json) {
const json_args = GodotRuntime.parseString(p_json);
@@ -324,11 +332,13 @@ const GodotOS = {
return 1;
},
+ godot_js_os_shell_open__proxy: 'sync',
godot_js_os_shell_open__sig: 'vi',
godot_js_os_shell_open: function (p_uri) {
window.open(GodotRuntime.parseString(p_uri), '_blank');
},
+ godot_js_os_hw_concurrency_get__proxy: 'sync',
godot_js_os_hw_concurrency_get__sig: 'i',
godot_js_os_hw_concurrency_get: function () {
// TODO Godot core needs fixing to avoid spawning too many threads (> 24).
@@ -336,6 +346,7 @@ const GodotOS = {
return concurrency < 2 ? concurrency : 2;
},
+ godot_js_os_download_buffer__proxy: 'sync',
godot_js_os_download_buffer__sig: 'viiii',
godot_js_os_download_buffer: function (p_ptr, p_size, p_name, p_mime) {
const buf = GodotRuntime.heapSlice(HEAP8, p_ptr, p_size);
@@ -426,6 +437,7 @@ const GodotPWA = {
},
},
+ godot_js_pwa_cb__proxy: 'sync',
godot_js_pwa_cb__sig: 'vi',
godot_js_pwa_cb: function (p_update_cb) {
if ('serviceWorker' in navigator) {
@@ -434,6 +446,7 @@ const GodotPWA = {
}
},
+ godot_js_pwa_update__proxy: 'sync',
godot_js_pwa_update__sig: 'i',
godot_js_pwa_update: function () {
if ('serviceWorker' in navigator && GodotPWA.hasUpdate) {
diff --git a/platform/web/js/libs/library_godot_webgl2.js b/platform/web/js/libs/library_godot_webgl2.js
index 3c6de4a071..dbaec9f01b 100644
--- a/platform/web/js/libs/library_godot_webgl2.js
+++ b/platform/web/js/libs/library_godot_webgl2.js
@@ -32,6 +32,19 @@ const GodotWebGL2 = {
$GodotWebGL2__deps: ['$GL', '$GodotRuntime'],
$GodotWebGL2: {},
+ // This is implemented as "glGetBufferSubData" in new emscripten versions.
+ // Since we have to support older (pre 2.0.17) emscripten versions, we add this wrapper function instead.
+ godot_webgl2_glGetBufferSubData__proxy: 'sync',
+ godot_webgl2_glGetBufferSubData__sig: 'vippp',
+ godot_webgl2_glGetBufferSubData__deps: ['$GL', 'emscripten_webgl_get_current_context'],
+ godot_webgl2_glGetBufferSubData: function (target, offset, size, data) {
+ const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef
+ const gl = GL.getContext(gl_context_handle);
+ if (gl) {
+ gl.GLctx['getBufferSubData'](target, offset, HEAPU8, data, size);
+ }
+ },
+
godot_webgl2_glFramebufferTextureMultiviewOVR__deps: ['emscripten_webgl_get_current_context'],
godot_webgl2_glFramebufferTextureMultiviewOVR__proxy: 'sync',
godot_webgl2_glFramebufferTextureMultiviewOVR__sig: 'viiiiii',
diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp
index 186e4abf92..cbdcbf565d 100644
--- a/platform/web/os_web.cpp
+++ b/platform/web/os_web.cpp
@@ -160,6 +160,12 @@ String OS_Web::get_name() const {
return "Web";
}
+void OS_Web::add_frame_delay(bool p_can_draw) {
+#ifndef PROXY_TO_PTHREAD_ENABLED
+ OS::add_frame_delay(p_can_draw);
+#endif
+}
+
void OS_Web::vibrate_handheld(int p_duration_ms) {
godot_js_input_vibrate_handheld(p_duration_ms);
}
diff --git a/platform/web/os_web.h b/platform/web/os_web.h
index f262337f00..5a48997c17 100644
--- a/platform/web/os_web.h
+++ b/platform/web/os_web.h
@@ -91,9 +91,10 @@ public:
String get_executable_path() const override;
Error shell_open(String p_uri) override;
String get_name() const override;
+
// Override default OS implementation which would block the main thread with delay_usec.
// Implemented in web_main.cpp loop callback instead.
- void add_frame_delay(bool p_can_draw) override {}
+ void add_frame_delay(bool p_can_draw) override;
void vibrate_handheld(int p_duration_ms) override;
diff --git a/platform/web/web_main.cpp b/platform/web/web_main.cpp
index f199f8ffd8..ad2a801881 100644
--- a/platform/web/web_main.cpp
+++ b/platform/web/web_main.cpp
@@ -40,7 +40,10 @@
#include <stdlib.h>
static OS_Web *os = nullptr;
+#ifndef PROXY_TO_PTHREAD_ENABLED
static uint64_t target_ticks = 0;
+#endif
+
static bool main_started = false;
static bool shutdown_complete = false;
@@ -63,15 +66,20 @@ void cleanup_after_sync() {
}
void main_loop_callback() {
+#ifndef PROXY_TO_PTHREAD_ENABLED
uint64_t current_ticks = os->get_ticks_usec();
+#endif
bool force_draw = DisplayServerWeb::get_singleton()->check_size_force_redraw();
if (force_draw) {
Main::force_redraw();
+#ifndef PROXY_TO_PTHREAD_ENABLED
} else if (current_ticks < target_ticks) {
return; // Skip frame.
+#endif
}
+#ifndef PROXY_TO_PTHREAD_ENABLED
int max_fps = Engine::get_singleton()->get_max_fps();
if (max_fps > 0) {
if (current_ticks - target_ticks > 1000000) {
@@ -81,6 +89,8 @@ void main_loop_callback() {
}
target_ticks += (uint64_t)(1000000 / max_fps);
}
+#endif
+
if (os->main_loop_iterate()) {
emscripten_cancel_main_loop(); // Cancel current loop and set the cleanup one.
emscripten_set_main_loop(exit_callback, -1, false);
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 7caa0153d7..bdacdbb9ba 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -419,6 +419,7 @@ def configure_msvc(env, vcvars_msvc_config):
"dwmapi",
"dwrite",
"wbemuuid",
+ "ntdll",
]
if env.debug_features:
@@ -610,6 +611,7 @@ def configure_mingw(env):
"dwmapi",
"dwrite",
"wbemuuid",
+ "ntdll",
]
)
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index cc5ae9ad45..bb0b64ba10 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -346,11 +346,31 @@ Error DisplayServerWindows::file_dialog_show(const String &p_title, const String
}
}
if (!p_callback.is_null()) {
- p_callback.call(true, file_names, index);
+ Variant v_result = true;
+ Variant v_files = file_names;
+ Variant v_index = index;
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[3] = { &v_result, &v_files, &v_index };
+
+ p_callback.callp(args, 3, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat(RTR("Failed to execute file dialogs callback: %s."), Variant::get_callable_error_text(p_callback, args, 3, ce)));
+ }
}
} else {
if (!p_callback.is_null()) {
- p_callback.call(false, Vector<String>(), index);
+ Variant v_result = false;
+ Variant v_files = Vector<String>();
+ Variant v_index = index;
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[3] = { &v_result, &v_files, &v_index };
+
+ p_callback.callp(args, 3, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat(RTR("Failed to execute file dialogs callback: %s."), Variant::get_callable_error_text(p_callback, args, 3, ce)));
+ }
}
}
pfd->Release();
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index 0ac6c2c8b0..60edb00dd2 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -109,6 +109,7 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) {
static GUID IID_XOneSWirelessGamepad = { MAKELONG(0x045E, 0x02EA), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
static GUID IID_XOneSBluetoothGamepad = { MAKELONG(0x045E, 0x02E0), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
static GUID IID_XOneEliteWirelessGamepad = { MAKELONG(0x045E, 0x02E3), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
+ static GUID IID_XOneElite2WirelessGamepad = { MAKELONG(0x045E, 0x0B22), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
if (memcmp(p_guid, &IID_ValveStreamingGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_X360WiredGamepad, sizeof(*p_guid)) == 0 ||
@@ -120,7 +121,8 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) {
memcmp(p_guid, &IID_XOneNewWirelessGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_XOneSWirelessGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_XOneSBluetoothGamepad, sizeof(*p_guid)) == 0 ||
- memcmp(p_guid, &IID_XOneEliteWirelessGamepad, sizeof(*p_guid)) == 0)
+ memcmp(p_guid, &IID_XOneEliteWirelessGamepad, sizeof(*p_guid)) == 0 ||
+ memcmp(p_guid, &IID_XOneElite2WirelessGamepad, sizeof(*p_guid)) == 0)
return true;
PRAWINPUTDEVICELIST dev_list = nullptr;