summaryrefslogtreecommitdiffstats
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/display_server_android.cpp14
-rw-r--r--platform/android/display_server_android.h3
-rw-r--r--platform/android/export/export_plugin.cpp4
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.kt33
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt15
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java9
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java2
-rw-r--r--platform/android/java_class_wrapper.cpp8
-rw-r--r--platform/android/java_godot_wrapper.cpp35
-rw-r--r--platform/android/java_godot_wrapper.h8
-rw-r--r--platform/android/os_android.cpp11
-rw-r--r--platform/ios/detect.py10
-rw-r--r--platform/ios/display_server_ios.h3
-rw-r--r--platform/ios/display_server_ios.mm36
-rw-r--r--platform/ios/godot_view.mm6
-rw-r--r--platform/ios/view_controller.mm4
-rw-r--r--platform/linuxbsd/detect.py4
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.cpp106
-rw-r--r--platform/linuxbsd/freedesktop_portal_desktop.h8
-rw-r--r--platform/linuxbsd/platform_gl.h4
-rw-r--r--platform/linuxbsd/x11/SCsub4
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp84
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h2
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11.cpp6
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11_egl.cpp63
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11_egl.h61
-rw-r--r--platform/macos/detect.py13
-rw-r--r--platform/macos/display_server_macos.h19
-rw-r--r--platform/macos/display_server_macos.mm618
-rw-r--r--platform/macos/doc_classes/EditorExportPlatformMacOS.xml12
-rw-r--r--platform/macos/export/export_plugin.cpp6
-rw-r--r--platform/macos/godot_menu_delegate.mm28
-rw-r--r--platform/macos/godot_menu_item.h1
-rw-r--r--platform/macos/godot_window_delegate.mm2
-rw-r--r--platform/web/api/api.cpp2
-rw-r--r--platform/web/audio_driver_web.h5
-rw-r--r--platform/web/detect.py4
-rw-r--r--platform/web/display_server_web.h32
-rw-r--r--platform/web/godot_js.h2
-rw-r--r--platform/web/javascript_bridge_singleton.cpp12
-rw-r--r--platform/web/js/libs/library_godot_javascript_singleton.js2
-rw-r--r--platform/web/os_web.cpp4
-rw-r--r--platform/web/os_web.h11
-rw-r--r--platform/windows/crash_handler_windows.h2
-rw-r--r--platform/windows/detect.py1
-rw-r--r--platform/windows/display_server_windows.cpp119
-rw-r--r--platform/windows/display_server_windows.h3
-rw-r--r--platform/windows/gl_manager_windows_native.cpp18
-rw-r--r--platform/windows/platform_windows_builders.py16
-rw-r--r--platform/windows/wgl_detect_version.cpp24
-rw-r--r--platform/windows/wgl_detect_version.h4
52 files changed, 1177 insertions, 330 deletions
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 3904d3afc4..11c0945ce0 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -111,6 +111,20 @@ void DisplayServerAndroid::tts_stop() {
TTS_Android::stop();
}
+bool DisplayServerAndroid::is_dark_mode_supported() const {
+ GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
+ ERR_FAIL_NULL_V(godot_java, false);
+
+ return godot_java->is_dark_mode_supported();
+}
+
+bool DisplayServerAndroid::is_dark_mode() const {
+ GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
+ ERR_FAIL_NULL_V(godot_java, false);
+
+ return godot_java->is_dark_mode();
+}
+
void DisplayServerAndroid::clipboard_set(const String &p_text) {
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
ERR_FAIL_NULL(godot_java);
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
index e0ad2cb916..54912212dc 100644
--- a/platform/android/display_server_android.h
+++ b/platform/android/display_server_android.h
@@ -103,6 +103,9 @@ public:
virtual void tts_resume() override;
virtual void tts_stop() override;
+ virtual bool is_dark_mode_supported() const override;
+ virtual bool is_dark_mode() const override;
+
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
virtual bool clipboard_has() const override;
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 661f7cbc80..aeaa7b9ce7 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -2635,6 +2635,8 @@ void EditorExportPlatformAndroid::_clear_assets_directory() {
if (da_res->dir_exists(APK_ASSETS_DIRECTORY)) {
print_verbose("Clearing APK assets directory...");
Ref<DirAccess> da_assets = DirAccess::open(APK_ASSETS_DIRECTORY);
+ ERR_FAIL_COND(da_assets.is_null());
+
da_assets->erase_contents_recursive();
da_res->remove(APK_ASSETS_DIRECTORY);
}
@@ -2643,6 +2645,8 @@ void EditorExportPlatformAndroid::_clear_assets_directory() {
if (da_res->dir_exists(AAB_ASSETS_DIRECTORY)) {
print_verbose("Clearing AAB assets directory...");
Ref<DirAccess> da_assets = DirAccess::open(AAB_ASSETS_DIRECTORY);
+ ERR_FAIL_COND(da_assets.is_null());
+
da_assets->erase_contents_recursive();
da_res->remove(AAB_ASSETS_DIRECTORY);
}
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
index 7cedfa6888..02709d4dc5 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
@@ -91,10 +91,6 @@ open class GodotEditor : GodotActivity() {
private val commandLineParams = ArrayList<String>()
override fun onCreate(savedInstanceState: Bundle?) {
- // We exclude certain permissions from the set we request at startup, as they'll be
- // requested on demand based on use-cases.
- PermissionsUtil.requestManifestPermissions(this, setOf(Manifest.permission.RECORD_AUDIO))
-
val params = intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS)
Log.d(TAG, "Received parameters ${params.contentToString()}")
updateCommandLineParams(params)
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 e115494cfd..f819063a22 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
@@ -35,6 +35,7 @@ import android.app.Activity
import android.app.AlertDialog
import android.content.*
import android.content.pm.PackageManager
+import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.Rect
import android.hardware.Sensor
@@ -694,6 +695,25 @@ class Godot(private val context: Context) : SensorEventListener {
}
}
+ /**
+ * Returns true if dark mode is supported, false otherwise.
+ */
+ @Keep
+ private fun isDarkModeSupported(): Boolean {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
+ }
+
+ /**
+ * Returns true if dark mode is supported and enabled, false otherwise.
+ */
+ @Keep
+ private fun isDarkMode(): Boolean {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ return context.resources?.configuration?.uiMode?.and(Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
+ }
+ return false
+ }
+
fun hasClipboard(): Boolean {
return mClipboard.hasPrimaryClip()
}
@@ -908,6 +928,19 @@ class Godot(private val context: Context) : SensorEventListener {
}
/**
+ * Return true if the given feature is supported.
+ */
+ @Keep
+ private fun hasFeature(feature: String): Boolean {
+ for (plugin in pluginRegistry.allPlugins) {
+ if (plugin.supportsFeature(feature)) {
+ return true
+ }
+ }
+ return false
+ }
+
+ /**
* Get the list of gdextension modules to register.
*/
@Keep
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 4636f753af..417be7cef4 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt
@@ -30,12 +30,15 @@
package org.godotengine.godot
+import android.Manifest
import android.app.Activity
import android.content.Intent
+import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import androidx.annotation.CallSuper
import androidx.fragment.app.FragmentActivity
+import org.godotengine.godot.utils.PermissionsUtil
import org.godotengine.godot.utils.ProcessPhoenix
/**
@@ -62,6 +65,10 @@ abstract class GodotActivity : FragmentActivity(), GodotHost {
private set
override fun onCreate(savedInstanceState: Bundle?) {
+ // We exclude certain permissions from the set we request at startup, as they'll be
+ // requested on demand based on use-cases.
+ PermissionsUtil.requestManifestPermissions(this, setOf(Manifest.permission.RECORD_AUDIO))
+
super.onCreate(savedInstanceState)
setContentView(R.layout.godot_app_layout)
@@ -148,6 +155,14 @@ abstract class GodotActivity : FragmentActivity(), GodotHost {
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
godotFragment?.onRequestPermissionsResult(requestCode, permissions, grantResults)
+
+ if (requestCode == PermissionsUtil.REQUEST_ALL_PERMISSION_REQ_CODE) {
+ Log.d(TAG, "Received permissions request result..")
+ for (i in permissions.indices) {
+ val permissionGranted = grantResults[i] == PackageManager.PERMISSION_GRANTED
+ Log.d(TAG, "Permission ${permissions[i]} ${if (permissionGranted) { "granted"} else { "denied" }}")
+ }
+ }
}
override fun onBackPressed() {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
index 7f3a3ac7a3..c0912ca4dc 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
@@ -316,6 +316,15 @@ public abstract class GodotPlugin {
}
/**
+ * Returns whether the plugin supports the given feature tag.
+ *
+ * @see <a href="https://docs.godotengine.org/en/stable/tutorials/export/feature_tags.html">Feature tags</a>
+ */
+ public boolean supportsFeature(String featureTag) {
+ return false;
+ }
+
+ /**
* Runs the specified action on the UI thread. If the current thread is the UI
* thread, then the action is executed immediately. If the current thread is
* not the UI thread, the action is posted to the event queue of the UI thread.
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
index 8353fc8dc6..9a82204467 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
@@ -160,6 +160,7 @@ public final class PermissionsUtil {
try {
if (manifestPermission.equals(Manifest.permission.MANAGE_EXTERNAL_STORAGE)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
+ Log.d(TAG, "Requesting permission " + manifestPermission);
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.setData(Uri.parse(String.format("package:%s", activity.getPackageName())));
@@ -173,6 +174,7 @@ public final class PermissionsUtil {
PermissionInfo permissionInfo = getPermissionInfo(activity, manifestPermission);
int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel;
if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, manifestPermission) != PackageManager.PERMISSION_GRANTED) {
+ Log.d(TAG, "Requesting permission " + manifestPermission);
requestedPermissions.add(manifestPermission);
}
}
diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp
index edc934e927..d6455cbf1c 100644
--- a/platform/android/java_class_wrapper.cpp
+++ b/platform/android/java_class_wrapper.cpp
@@ -50,14 +50,14 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
}
int pc = E.param_types.size();
- if (pc > p_argcount) {
+ if (p_argcount < pc) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = pc;
+ r_error.expected = pc;
continue;
}
- if (pc < p_argcount) {
+ if (p_argcount > pc) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = pc;
+ r_error.expected = pc;
continue;
}
uint32_t *ptypes = E.param_types.ptrw();
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index a01a74f1fd..cb6ebf14a8 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -62,6 +62,8 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
_finish = p_env->GetMethodID(godot_class, "forceQuit", "(I)Z");
_set_keep_screen_on = p_env->GetMethodID(godot_class, "setKeepScreenOn", "(Z)V");
_alert = p_env->GetMethodID(godot_class, "alert", "(Ljava/lang/String;Ljava/lang/String;)V");
+ _is_dark_mode_supported = p_env->GetMethodID(godot_class, "isDarkModeSupported", "()Z");
+ _is_dark_mode = p_env->GetMethodID(godot_class, "isDarkMode", "()Z");
_get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;");
_set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V");
_has_clipboard = p_env->GetMethodID(godot_class, "hasClipboard", "()Z");
@@ -80,6 +82,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
_end_benchmark_measure = p_env->GetMethodID(godot_class, "nativeEndBenchmarkMeasure", "(Ljava/lang/String;)V");
_dump_benchmark = p_env->GetMethodID(godot_class, "nativeDumpBenchmark", "(Ljava/lang/String;)V");
_get_gdextension_list_config_file = p_env->GetMethodID(godot_class, "getGDExtensionConfigFiles", "()[Ljava/lang/String;");
+ _has_feature = p_env->GetMethodID(godot_class, "hasFeature", "(Ljava/lang/String;)Z");
}
GodotJavaWrapper::~GodotJavaWrapper() {
@@ -172,6 +175,26 @@ void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {
}
}
+bool GodotJavaWrapper::is_dark_mode_supported() {
+ if (_is_dark_mode_supported) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL_V(env, false);
+ return env->CallBooleanMethod(godot_instance, _is_dark_mode_supported);
+ } else {
+ return false;
+ }
+}
+
+bool GodotJavaWrapper::is_dark_mode() {
+ if (_is_dark_mode) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL_V(env, false);
+ return env->CallBooleanMethod(godot_instance, _is_dark_mode);
+ } else {
+ return false;
+ }
+}
+
bool GodotJavaWrapper::has_get_clipboard() {
return _get_clipboard != nullptr;
}
@@ -351,3 +374,15 @@ void GodotJavaWrapper::dump_benchmark(const String &benchmark_file) {
env->CallVoidMethod(godot_instance, _dump_benchmark, j_benchmark_file);
}
}
+
+bool GodotJavaWrapper::has_feature(const String &p_feature) const {
+ if (_has_feature) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL_V(env, false);
+
+ jstring j_feature = env->NewStringUTF(p_feature.utf8().get_data());
+ return env->CallBooleanMethod(godot_instance, _has_feature, j_feature);
+ } else {
+ return false;
+ }
+}
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index 2ce756807f..7c6327c9e1 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -53,6 +53,8 @@ private:
jmethodID _finish = nullptr;
jmethodID _set_keep_screen_on = nullptr;
jmethodID _alert = nullptr;
+ jmethodID _is_dark_mode_supported = nullptr;
+ jmethodID _is_dark_mode = nullptr;
jmethodID _get_clipboard = nullptr;
jmethodID _set_clipboard = nullptr;
jmethodID _has_clipboard = nullptr;
@@ -71,6 +73,7 @@ private:
jmethodID _begin_benchmark_measure = nullptr;
jmethodID _end_benchmark_measure = nullptr;
jmethodID _dump_benchmark = nullptr;
+ jmethodID _has_feature = nullptr;
public:
GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_godot_instance);
@@ -86,6 +89,8 @@ public:
bool force_quit(JNIEnv *p_env = nullptr, int p_instance_id = 0);
void set_keep_screen_on(bool p_enabled);
void alert(const String &p_message, const String &p_title);
+ bool is_dark_mode_supported();
+ bool is_dark_mode();
bool has_get_clipboard();
String get_clipboard();
bool has_set_clipboard();
@@ -106,6 +111,9 @@ public:
// Return the list of gdextensions config file.
Vector<String> get_gdextension_list_config_file() const;
+
+ // Return true if the given feature is supported.
+ bool has_feature(const String &p_feature) const;
};
#endif // JAVA_GODOT_WRAPPER_H
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 8f80516a9f..92dc5f909f 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -60,10 +60,10 @@ String _remove_symlink(const String &dir) {
// Change directory to the external data directory.
chdir(dir.utf8().get_data());
// Get the actual directory without the potential symlink.
- char dir_name_wihout_symlink[2048];
- getcwd(dir_name_wihout_symlink, 2048);
+ char dir_name_without_symlink[2048];
+ getcwd(dir_name_without_symlink, 2048);
// Convert back to a String.
- String dir_without_symlink(dir_name_wihout_symlink);
+ String dir_without_symlink(dir_name_without_symlink);
// Restore original current directory.
chdir(current_dir_name);
return dir_without_symlink;
@@ -749,6 +749,11 @@ bool OS_Android::_check_internal_feature_support(const String &p_feature) {
return true;
}
#endif
+
+ if (godot_java->has_feature(p_feature)) {
+ return true;
+ }
+
return false;
}
diff --git a/platform/ios/detect.py b/platform/ios/detect.py
index e11c0b7d91..40eb61abc8 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -103,13 +103,13 @@ def configure(env: "Environment"):
if env["ios_simulator"]:
detect_darwin_sdk_path("iossimulator", env)
- env.Append(ASFLAGS=["-mios-simulator-version-min=11.0"])
- env.Append(CCFLAGS=["-mios-simulator-version-min=11.0"])
+ env.Append(ASFLAGS=["-mios-simulator-version-min=12.0"])
+ env.Append(CCFLAGS=["-mios-simulator-version-min=12.0"])
env.extra_suffix = ".simulator" + env.extra_suffix
else:
detect_darwin_sdk_path("ios", env)
- env.Append(ASFLAGS=["-miphoneos-version-min=11.0"])
- env.Append(CCFLAGS=["-miphoneos-version-min=11.0"])
+ env.Append(ASFLAGS=["-miphoneos-version-min=12.0"])
+ env.Append(CCFLAGS=["-miphoneos-version-min=12.0"])
if env["arch"] == "x86_64":
if not env["ios_simulator"]:
@@ -154,7 +154,7 @@ def configure(env: "Environment"):
env.Append(CPPDEFINES=["VULKAN_ENABLED"])
if env["opengl3"]:
- env.Append(CPPDEFINES=["GLES3_ENABLED"])
+ env.Append(CPPDEFINES=["GLES3_ENABLED", "GLES_SILENCE_DEPRECATION"])
env.Prepend(
CPPPATH=[
"$IOS_SDK_PATH/System/Library/Frameworks/OpenGLES.framework/Headers",
diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h
index da16449c61..be4ea1e6ab 100644
--- a/platform/ios/display_server_ios.h
+++ b/platform/ios/display_server_ios.h
@@ -141,6 +141,9 @@ public:
virtual void tts_resume() override;
virtual void tts_stop() override;
+ virtual bool is_dark_mode_supported() const override;
+ virtual bool is_dark_mode() const override;
+
virtual Rect2i get_display_safe_area() const override;
virtual int get_screen_count() const override;
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm
index 33eb16279f..489894a135 100644
--- a/platform/ios/display_server_ios.mm
+++ b/platform/ios/display_server_ios.mm
@@ -354,20 +354,32 @@ void DisplayServerIOS::tts_stop() {
[tts stopSpeaking];
}
-Rect2i DisplayServerIOS::get_display_safe_area() const {
- if (@available(iOS 11, *)) {
- UIEdgeInsets insets = UIEdgeInsetsZero;
- UIView *view = AppDelegate.viewController.godotView;
- if ([view respondsToSelector:@selector(safeAreaInsets)]) {
- insets = [view safeAreaInsets];
- }
- float scale = screen_get_scale();
- Size2i insets_position = Size2i(insets.left, insets.top) * scale;
- Size2i insets_size = Size2i(insets.left + insets.right, insets.top + insets.bottom) * scale;
- return Rect2i(screen_get_position() + insets_position, screen_get_size() - insets_size);
+bool DisplayServerIOS::is_dark_mode_supported() const {
+ if (@available(iOS 13.0, *)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool DisplayServerIOS::is_dark_mode() const {
+ if (@available(iOS 13.0, *)) {
+ return [UITraitCollection currentTraitCollection].userInterfaceStyle == UIUserInterfaceStyleDark;
} else {
- return Rect2i(screen_get_position(), screen_get_size());
+ return false;
+ }
+}
+
+Rect2i DisplayServerIOS::get_display_safe_area() const {
+ UIEdgeInsets insets = UIEdgeInsetsZero;
+ UIView *view = AppDelegate.viewController.godotView;
+ if ([view respondsToSelector:@selector(safeAreaInsets)]) {
+ insets = [view safeAreaInsets];
}
+ float scale = screen_get_scale();
+ Size2i insets_position = Size2i(insets.left, insets.top) * scale;
+ Size2i insets_size = Size2i(insets.left + insets.right, insets.top + insets.bottom) * scale;
+ return Rect2i(screen_get_position() + insets_position, screen_get_size() - insets_size);
}
int DisplayServerIOS::get_screen_count() const {
diff --git a/platform/ios/godot_view.mm b/platform/ios/godot_view.mm
index 4c9a75fdc0..ff8a4f8921 100644
--- a/platform/ios/godot_view.mm
+++ b/platform/ios/godot_view.mm
@@ -82,10 +82,10 @@ static const float earth_gravity = 9.80665;
layer = [GodotMetalLayer layer];
#endif
} else if ([driverName isEqualToString:@"opengl3"]) {
- if (@available(iOS 13, *)) {
- NSLog(@"OpenGL ES is deprecated on iOS 13");
- }
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations" // OpenGL is deprecated in iOS 12.0
layer = [GodotOpenGLLayer layer];
+#pragma clang diagnostic pop
} else {
return nil;
}
diff --git a/platform/ios/view_controller.mm b/platform/ios/view_controller.mm
index 0ef61da646..1f55670b68 100644
--- a/platform/ios/view_controller.mm
+++ b/platform/ios/view_controller.mm
@@ -161,9 +161,7 @@
[self observeKeyboard];
[self displayLoadingOverlay];
- if (@available(iOS 11.0, *)) {
- [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
- }
+ [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
}
- (void)observeKeyboard {
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index a723bb5d58..72bffceb1f 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -106,7 +106,7 @@ def configure(env: "Environment"):
print("Using linker program: " + env["linker"])
if env["linker"] == "mold" and using_gcc(env): # GCC < 12.1 doesn't support -fuse-ld=mold.
cc_version = get_compiler_version(env)
- cc_semver = (int(cc_version["major"]), int(cc_version["minor"]))
+ cc_semver = (cc_version["major"], cc_version["minor"])
if cc_semver < (12, 1):
found_wrapper = False
for path in ["/usr/libexec", "/usr/local/libexec", "/usr/lib", "/usr/local/lib"]:
@@ -455,7 +455,7 @@ def configure(env: "Environment"):
linker_version_str = subprocess.check_output(
[env.subst(env["LINK"]), "-Wl,--version"] + env.subst(env["LINKFLAGS"])
).decode("utf-8")
- gnu_ld_version = re.search("^GNU ld [^$]*(\d+\.\d+)$", linker_version_str, re.MULTILINE)
+ gnu_ld_version = re.search(r"^GNU ld [^$]*(\d+\.\d+)$", linker_version_str, re.MULTILINE)
if not gnu_ld_version:
print(
"Warning: Creating export template binaries enabled for PCK embedding is currently only supported with GNU ld, not gold, LLD or mold."
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp
index 91c14e0e91..cf4354139a 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.cpp
+++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp
@@ -142,36 +142,40 @@ void FreeDesktopPortalDesktop::append_dbus_string(DBusMessageIter *p_iter, const
}
}
-void FreeDesktopPortalDesktop::append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters) {
+void FreeDesktopPortalDesktop::append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filter_names, const Vector<String> &p_filter_exts) {
DBusMessageIter dict_iter;
DBusMessageIter var_iter;
DBusMessageIter arr_iter;
const char *filters_key = "filters";
+ ERR_FAIL_COND(p_filter_names.size() != p_filter_exts.size());
+
dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &filters_key);
dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "a(sa(us))", &var_iter);
dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "(sa(us))", &arr_iter);
- for (int i = 0; i < p_filters.size(); i++) {
- Vector<String> tokens = p_filters[i].split(";");
- if (tokens.size() == 2) {
- DBusMessageIter struct_iter;
- DBusMessageIter array_iter;
- DBusMessageIter array_struct_iter;
- dbus_message_iter_open_container(&arr_iter, DBUS_TYPE_STRUCT, nullptr, &struct_iter);
- append_dbus_string(&struct_iter, tokens[0]);
-
- dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(us)", &array_iter);
+ for (int i = 0; i < p_filter_names.size(); i++) {
+ DBusMessageIter struct_iter;
+ DBusMessageIter array_iter;
+ DBusMessageIter array_struct_iter;
+ dbus_message_iter_open_container(&arr_iter, DBUS_TYPE_STRUCT, nullptr, &struct_iter);
+ append_dbus_string(&struct_iter, p_filter_names[i]);
+
+ dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(us)", &array_iter);
+ String flt = p_filter_exts[i];
+ int filter_slice_count = flt.get_slice_count(",");
+ for (int j = 0; j < filter_slice_count; j++) {
dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, nullptr, &array_struct_iter);
+ String str = (flt.get_slice(",", j).strip_edges());
{
const unsigned nil = 0;
dbus_message_iter_append_basic(&array_struct_iter, DBUS_TYPE_UINT32, &nil);
}
- append_dbus_string(&array_struct_iter, tokens[1]);
+ append_dbus_string(&array_struct_iter, str);
dbus_message_iter_close_container(&array_iter, &array_struct_iter);
- dbus_message_iter_close_container(&struct_iter, &array_iter);
- dbus_message_iter_close_container(&arr_iter, &struct_iter);
}
+ dbus_message_iter_close_container(&struct_iter, &array_iter);
+ dbus_message_iter_close_container(&arr_iter, &struct_iter);
}
dbus_message_iter_close_container(&var_iter, &arr_iter);
dbus_message_iter_close_container(&dict_iter, &var_iter);
@@ -219,7 +223,7 @@ void FreeDesktopPortalDesktop::append_dbus_dict_bool(DBusMessageIter *p_iter, co
dbus_message_iter_close_container(p_iter, &dict_iter);
}
-bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Vector<String> &r_urls) {
+bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_iter, const Vector<String> &p_names, bool &r_cancel, Vector<String> &r_urls, int &r_index) {
ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_UINT32, false);
dbus_uint32_t resp_code;
@@ -243,7 +247,22 @@ bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_it
DBusMessageIter var_iter;
dbus_message_iter_recurse(&iter, &var_iter);
- if (strcmp(key, "uris") == 0) {
+ if (strcmp(key, "current_filter") == 0) { // (sa(us))
+ if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_STRUCT) {
+ DBusMessageIter struct_iter;
+ dbus_message_iter_recurse(&var_iter, &struct_iter);
+ while (dbus_message_iter_get_arg_type(&struct_iter) == DBUS_TYPE_STRING) {
+ const char *value;
+ dbus_message_iter_get_basic(&struct_iter, &value);
+ String name = String::utf8(value);
+
+ r_index = p_names.find(name);
+ if (!dbus_message_iter_next(&struct_iter)) {
+ break;
+ }
+ }
+ }
+ } else if (strcmp(key, "uris") == 0) { // as
if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_ARRAY) {
DBusMessageIter uri_iter;
dbus_message_iter_recurse(&var_iter, &uri_iter);
@@ -266,17 +285,43 @@ bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_it
return true;
}
-Error FreeDesktopPortalDesktop::file_dialog_show(const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
+Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_window_id, const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
if (unsupported) {
return FAILED;
}
+ ERR_FAIL_INDEX_V(int(p_mode), DisplayServer::FILE_DIALOG_MODE_SAVE_MAX, FAILED);
+
+ Vector<String> filter_names;
+ Vector<String> filter_exts;
+ for (int i = 0; i < p_filters.size(); i++) {
+ Vector<String> tokens = p_filters[i].split(";");
+ if (tokens.size() >= 1) {
+ String flt = tokens[0].strip_edges();
+ if (!flt.is_empty()) {
+ if (tokens.size() == 2) {
+ filter_exts.push_back(flt);
+ filter_names.push_back(tokens[1]);
+ } else {
+ filter_exts.push_back(flt);
+ filter_names.push_back(flt);
+ }
+ }
+ }
+ }
+ if (filter_names.is_empty()) {
+ filter_exts.push_back("*.*");
+ filter_names.push_back(RTR("All Files"));
+ }
+
DBusError err;
dbus_error_init(&err);
// Open connection and add signal handler.
FileDialogData fd;
fd.callback = p_callback;
+ fd.prev_focus = p_window_id;
+ fd.filter_names = filter_names;
CryptoCore::RandomGenerator rng;
ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator.");
@@ -307,16 +352,10 @@ Error FreeDesktopPortalDesktop::file_dialog_show(const String &p_xid, const Stri
// Generate FileChooser message.
const char *method = nullptr;
- switch (p_mode) {
- case DisplayServer::FILE_DIALOG_MODE_SAVE_FILE: {
- method = "SaveFile";
- } break;
- case DisplayServer::FILE_DIALOG_MODE_OPEN_ANY:
- case DisplayServer::FILE_DIALOG_MODE_OPEN_FILE:
- case DisplayServer::FILE_DIALOG_MODE_OPEN_DIR:
- case DisplayServer::FILE_DIALOG_MODE_OPEN_FILES: {
- method = "OpenFile";
- } break;
+ if (p_mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) {
+ method = "SaveFile";
+ } else {
+ method = "OpenFile";
}
DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_FILE_CHOOSER, method);
@@ -333,7 +372,7 @@ Error FreeDesktopPortalDesktop::file_dialog_show(const String &p_xid, const Stri
append_dbus_dict_string(&arr_iter, "handle_token", token);
append_dbus_dict_bool(&arr_iter, "multiple", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES);
append_dbus_dict_bool(&arr_iter, "directory", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR);
- append_dbus_dict_filters(&arr_iter, p_filters);
+ append_dbus_dict_filters(&arr_iter, filter_names, filter_exts);
append_dbus_dict_string(&arr_iter, "current_folder", p_current_directory, true);
if (p_mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) {
append_dbus_dict_string(&arr_iter, "current_name", p_filename);
@@ -408,13 +447,18 @@ void FreeDesktopPortalDesktop::_thread_file_dialog_monitor(void *p_ud) {
if (dbus_message_iter_init(msg, &iter)) {
bool cancel = false;
Vector<String> uris;
- file_chooser_parse_response(&iter, cancel, uris);
+ int index = 0;
+ 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_args[2] = { &v_status, &v_files };
- fd.callback.call_deferredp((const Variant **)&v_args, 2);
+ Variant v_index = index;
+ Variant *v_args[3] = { &v_status, &v_files, &v_index };
+ fd.callback.call_deferredp((const Variant **)&v_args, 3);
+ }
+ if (fd.prev_focus != DisplayServer::INVALID_WINDOW_ID) {
+ callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(fd.prev_focus);
}
}
dbus_message_unref(msg);
diff --git a/platform/linuxbsd/freedesktop_portal_desktop.h b/platform/linuxbsd/freedesktop_portal_desktop.h
index a9b83b3844..503c382207 100644
--- a/platform/linuxbsd/freedesktop_portal_desktop.h
+++ b/platform/linuxbsd/freedesktop_portal_desktop.h
@@ -49,13 +49,15 @@ private:
bool read_setting(const char *p_namespace, const char *p_key, int p_type, void *r_value);
static void append_dbus_string(DBusMessageIter *p_iter, const String &p_string);
- static void append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters);
+ static void append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filter_names, const Vector<String> &p_filter_exts);
static void append_dbus_dict_string(DBusMessageIter *p_iter, const String &p_key, const String &p_value, bool p_as_byte_array = false);
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, bool &r_cancel, Vector<String> &r_urls);
+ static bool file_chooser_parse_response(DBusMessageIter *p_iter, const Vector<String> &p_names, bool &r_cancel, Vector<String> &r_urls, int &r_index);
struct FileDialogData {
+ Vector<String> filter_names;
DBusConnection *connection = nullptr;
+ DisplayServer::WindowID prev_focus = DisplayServer::INVALID_WINDOW_ID;
Callable callback;
String path;
};
@@ -73,7 +75,7 @@ public:
bool is_supported() { return !unsupported; }
- Error file_dialog_show(const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback);
+ Error file_dialog_show(DisplayServer::WindowID p_window_id, const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback);
// Retrieve the system's preferred color scheme.
// 0: No preference or unknown.
diff --git a/platform/linuxbsd/platform_gl.h b/platform/linuxbsd/platform_gl.h
index 1c19c4518a..5d4c2d8978 100644
--- a/platform/linuxbsd/platform_gl.h
+++ b/platform/linuxbsd/platform_gl.h
@@ -35,6 +35,10 @@
#define GL_API_ENABLED // Allow using desktop GL.
#endif
+#ifndef GLES_API_ENABLED
+#define GLES_API_ENABLED // Allow using GLES.
+#endif
+
#include "thirdparty/glad/glad/egl.h"
#include "thirdparty/glad/glad/gl.h"
diff --git a/platform/linuxbsd/x11/SCsub b/platform/linuxbsd/x11/SCsub
index a4890391ce..bbfaaf10d1 100644
--- a/platform/linuxbsd/x11/SCsub
+++ b/platform/linuxbsd/x11/SCsub
@@ -25,7 +25,9 @@ if env["vulkan"]:
if env["opengl3"]:
env.Append(CPPDEFINES=["GLAD_GLX_NO_X11"])
- source_files.append(["gl_manager_x11.cpp", "detect_prime_x11.cpp", "#thirdparty/glad/glx.c"])
+ source_files.append(
+ ["gl_manager_x11_egl.cpp", "gl_manager_x11.cpp", "detect_prime_x11.cpp", "#thirdparty/glad/glx.c"]
+ )
objects = []
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 897a6438d2..31846c80a2 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -364,14 +364,14 @@ bool DisplayServerX11::is_dark_mode() const {
}
Error DisplayServerX11::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
- WindowID window_id = _get_focused_window_or_popup();
+ WindowID window_id = last_focused_window;
if (!windows.has(window_id)) {
window_id = MAIN_WINDOW_ID;
}
String xid = vformat("x11:%x", (uint64_t)windows[window_id].x11_window);
- return portal_desktop->file_dialog_show(xid, p_title, p_current_directory, p_filename, p_mode, p_filters, p_callback);
+ return portal_desktop->file_dialog_show(last_focused_window, xid, p_title, p_current_directory, p_filename, p_mode, p_filters, p_callback);
}
#endif
@@ -1495,6 +1495,9 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
if (gl_manager) {
gl_manager->window_destroy(p_id);
}
+ if (gl_manager_egl) {
+ gl_manager_egl->window_destroy(p_id);
+ }
#endif
if (wd.xic) {
@@ -1534,6 +1537,9 @@ int64_t DisplayServerX11::window_get_native_handle(HandleType p_handle_type, Win
if (gl_manager) {
return (int64_t)gl_manager->get_glx_context(p_window);
}
+ if (gl_manager_egl) {
+ return (int64_t)gl_manager_egl->get_context(p_window);
+ }
return 0;
}
#endif
@@ -1711,6 +1717,9 @@ void DisplayServerX11::gl_window_make_current(DisplayServer::WindowID p_window_i
if (gl_manager) {
gl_manager->window_make_current(p_window_id);
}
+ if (gl_manager_egl) {
+ gl_manager_egl->window_make_current(p_window_id);
+ }
#endif
}
@@ -2012,6 +2021,9 @@ void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) {
if (gl_manager) {
gl_manager->window_resize(p_window, xwa.width, xwa.height);
}
+ if (gl_manager_egl) {
+ gl_manager_egl->window_resize(p_window, xwa.width, xwa.height);
+ }
#endif
}
@@ -3721,6 +3733,9 @@ void DisplayServerX11::_window_changed(XEvent *event) {
if (gl_manager) {
gl_manager->window_resize(window_id, wd.size.width, wd.size.height);
}
+ if (gl_manager_egl) {
+ gl_manager_egl->window_resize(window_id, wd.size.width, wd.size.height);
+ }
#endif
if (!wd.rect_changed_callback.is_null()) {
@@ -4855,6 +4870,9 @@ void DisplayServerX11::release_rendering_thread() {
if (gl_manager) {
gl_manager->release_current();
}
+ if (gl_manager_egl) {
+ gl_manager_egl->release_current();
+ }
#endif
}
@@ -4863,6 +4881,9 @@ void DisplayServerX11::make_rendering_thread() {
if (gl_manager) {
gl_manager->make_current();
}
+ if (gl_manager_egl) {
+ gl_manager_egl->make_current();
+ }
#endif
}
@@ -4871,6 +4892,9 @@ void DisplayServerX11::swap_buffers() {
if (gl_manager) {
gl_manager->swap_buffers();
}
+ if (gl_manager_egl) {
+ gl_manager_egl->swap_buffers();
+ }
#endif
}
@@ -5024,6 +5048,9 @@ void DisplayServerX11::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mo
if (gl_manager) {
gl_manager->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED);
}
+ if (gl_manager_egl) {
+ gl_manager_egl->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED);
+ }
#endif
}
@@ -5038,6 +5065,9 @@ DisplayServer::VSyncMode DisplayServerX11::window_get_vsync_mode(WindowID p_wind
if (gl_manager) {
return gl_manager->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;
}
+ if (gl_manager_egl) {
+ return gl_manager_egl->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;
+ }
#endif
return DisplayServer::VSYNC_ENABLED;
}
@@ -5050,6 +5080,7 @@ Vector<String> DisplayServerX11::get_rendering_drivers_func() {
#endif
#ifdef GLES3_ENABLED
drivers.push_back("opengl3");
+ drivers.push_back("opengl3_es");
#endif
return drivers;
@@ -5092,6 +5123,21 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't acquire visual info from display.");
vi_selected = true;
}
+ if (gl_manager_egl) {
+ XVisualInfo visual_info_template;
+ int visual_id = gl_manager_egl->display_get_native_visual_id(x11_display);
+ ERR_FAIL_COND_V_MSG(visual_id < 0, INVALID_WINDOW_ID, "Unable to get a visual id.");
+
+ visual_info_template.visualid = (VisualID)visual_id;
+
+ int number_of_visuals = 0;
+ XVisualInfo *vi_list = XGetVisualInfo(x11_display, VisualIDMask, &visual_info_template, &number_of_visuals);
+ ERR_FAIL_COND_V(number_of_visuals <= 0, INVALID_WINDOW_ID);
+
+ visualInfo = vi_list[0];
+
+ XFree(vi_list);
+ }
#endif
if (!vi_selected) {
@@ -5364,8 +5410,12 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
if (gl_manager) {
Error err = gl_manager->window_create(id, wd.x11_window, x11_display, win_rect.size.width, win_rect.size.height);
ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create an OpenGL window");
- window_set_vsync_mode(p_vsync_mode, id);
}
+ if (gl_manager_egl) {
+ Error err = gl_manager_egl->window_create(id, x11_display, &wd.x11_window, win_rect.size.width, win_rect.size.height);
+ ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Failed to create an OpenGLES window.");
+ }
+ window_set_vsync_mode(p_vsync_mode, id);
#endif
//set_class_hint(x11_display, wd.x11_window);
@@ -5766,7 +5816,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
#endif
// Initialize context and rendering device.
#if defined(GLES3_ENABLED)
- if (rendering_driver == "opengl3") {
+ if (rendering_driver == "opengl3" || rendering_driver == "opengl3_es") {
if (getenv("DRI_PRIME") == nullptr) {
int use_prime = -1;
@@ -5807,7 +5857,8 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
setenv("DRI_PRIME", "1", 1);
}
}
-
+ }
+ if (rendering_driver == "opengl3") {
GLManager_X11::ContextType opengl_api_type = GLManager_X11::GLES_3_0_COMPATIBLE;
gl_manager = memnew(GLManager_X11(p_resolution, opengl_api_type));
@@ -5820,14 +5871,20 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
driver_found = true;
- if (true) {
- RasterizerGLES3::make_current(true);
- } else {
- memdelete(gl_manager);
- gl_manager = nullptr;
+ RasterizerGLES3::make_current(true);
+ }
+ if (rendering_driver == "opengl3_es") {
+ gl_manager_egl = memnew(GLManagerEGL_X11);
+
+ if (gl_manager_egl->initialize() != OK) {
+ memdelete(gl_manager_egl);
+ gl_manager_egl = nullptr;
r_error = ERR_UNAVAILABLE;
return;
}
+ driver_found = true;
+
+ RasterizerGLES3::make_current(false);
}
#endif
if (!driver_found) {
@@ -6044,6 +6101,9 @@ DisplayServerX11::~DisplayServerX11() {
if (gl_manager) {
gl_manager->window_destroy(E.key);
}
+ if (gl_manager_egl) {
+ gl_manager_egl->window_destroy(E.key);
+ }
#endif
WindowData &wd = E.value;
@@ -6094,6 +6154,10 @@ DisplayServerX11::~DisplayServerX11() {
memdelete(gl_manager);
gl_manager = nullptr;
}
+ if (gl_manager_egl) {
+ memdelete(gl_manager_egl);
+ gl_manager_egl = nullptr;
+ }
#endif
if (xrandr_handle) {
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 71beddce76..9706a4aa11 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -54,6 +54,7 @@
#if defined(GLES3_ENABLED)
#include "x11/gl_manager_x11.h"
+#include "x11/gl_manager_x11_egl.h"
#endif
#if defined(VULKAN_ENABLED)
@@ -138,6 +139,7 @@ class DisplayServerX11 : public DisplayServer {
#if defined(GLES3_ENABLED)
GLManager_X11 *gl_manager = nullptr;
+ GLManagerEGL_X11 *gl_manager_egl = nullptr;
#endif
#if defined(VULKAN_ENABLED)
VulkanContextX11 *context_vulkan = nullptr;
diff --git a/platform/linuxbsd/x11/gl_manager_x11.cpp b/platform/linuxbsd/x11/gl_manager_x11.cpp
index 25c4272607..95947301cf 100644
--- a/platform/linuxbsd/x11/gl_manager_x11.cpp
+++ b/platform/linuxbsd/x11/gl_manager_x11.cpp
@@ -347,12 +347,6 @@ Error GLManager_X11::initialize(Display *p_display) {
}
void GLManager_X11::set_use_vsync(bool p_use) {
- // force vsync in the editor for now, as a safety measure
- bool is_editor = Engine::get_singleton()->is_editor_hint();
- if (is_editor) {
- p_use = true;
- }
-
// we need an active window to get a display to set the vsync
if (!_current_window) {
return;
diff --git a/platform/linuxbsd/x11/gl_manager_x11_egl.cpp b/platform/linuxbsd/x11/gl_manager_x11_egl.cpp
new file mode 100644
index 0000000000..544684ebf1
--- /dev/null
+++ b/platform/linuxbsd/x11/gl_manager_x11_egl.cpp
@@ -0,0 +1,63 @@
+/**************************************************************************/
+/* gl_manager_x11_egl.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "gl_manager_x11_egl.h"
+
+#if defined(X11_ENABLED) && defined(GLES3_ENABLED)
+
+#include <stdio.h>
+#include <stdlib.h>
+
+const char *GLManagerEGL_X11::_get_platform_extension_name() const {
+ return "EGL_KHR_platform_x11";
+}
+
+EGLenum GLManagerEGL_X11::_get_platform_extension_enum() const {
+ return EGL_PLATFORM_X11_KHR;
+}
+
+Vector<EGLAttrib> GLManagerEGL_X11::_get_platform_display_attributes() const {
+ return Vector<EGLAttrib>();
+}
+
+EGLenum GLManagerEGL_X11::_get_platform_api_enum() const {
+ return EGL_OPENGL_ES_API;
+}
+
+Vector<EGLint> GLManagerEGL_X11::_get_platform_context_attribs() const {
+ Vector<EGLint> ret;
+ ret.push_back(EGL_CONTEXT_CLIENT_VERSION);
+ ret.push_back(3);
+ ret.push_back(EGL_NONE);
+
+ return ret;
+}
+
+#endif // WINDOWS_ENABLED && GLES3_ENABLED
diff --git a/platform/linuxbsd/x11/gl_manager_x11_egl.h b/platform/linuxbsd/x11/gl_manager_x11_egl.h
new file mode 100644
index 0000000000..b9c96619c4
--- /dev/null
+++ b/platform/linuxbsd/x11/gl_manager_x11_egl.h
@@ -0,0 +1,61 @@
+/**************************************************************************/
+/* gl_manager_x11_egl.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef GL_MANAGER_X11_EGL_H
+#define GL_MANAGER_X11_EGL_H
+
+#if defined(X11_ENABLED) && defined(GLES3_ENABLED)
+
+#include "core/error/error_list.h"
+#include "core/os/os.h"
+#include "core/templates/local_vector.h"
+#include "drivers/egl/egl_manager.h"
+#include "servers/display_server.h"
+
+#include <X11/Xlib.h>
+
+class GLManagerEGL_X11 : public EGLManager {
+private:
+ virtual const char *_get_platform_extension_name() const override;
+ virtual EGLenum _get_platform_extension_enum() const override;
+ virtual EGLenum _get_platform_api_enum() const override;
+ virtual Vector<EGLAttrib> _get_platform_display_attributes() const override;
+ virtual Vector<EGLint> _get_platform_context_attribs() const override;
+
+public:
+ void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {}
+
+ GLManagerEGL_X11(){};
+ ~GLManagerEGL_X11(){};
+};
+
+#endif // X11_ENABLED && GLES3_ENABLED
+
+#endif // GL_MANAGER_X11_EGL_H
diff --git a/platform/macos/detect.py b/platform/macos/detect.py
index c97cd19211..b41d2141fb 100644
--- a/platform/macos/detect.py
+++ b/platform/macos/detect.py
@@ -120,16 +120,9 @@ def configure(env: "Environment"):
env.Append(CCFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.13"])
env.Append(LINKFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.13"])
- cc_version = get_compiler_version(env) or {
- "major": None,
- "minor": None,
- "patch": None,
- "metadata1": None,
- "metadata2": None,
- "date": None,
- }
- cc_version_major = int(cc_version["major"] or -1)
- cc_version_minor = int(cc_version["minor"] or -1)
+ cc_version = get_compiler_version(env)
+ cc_version_major = cc_version["major"]
+ cc_version_minor = cc_version["minor"]
vanilla = is_vanilla_clang(env)
# Workaround for Xcode 15 linker bug.
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index c03b4765f8..2ca9e493b7 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -139,7 +139,14 @@ private:
NSMenu *apple_menu = nullptr;
NSMenu *dock_menu = nullptr;
- HashMap<String, NSMenu *> submenu;
+ struct MenuData {
+ Callable open;
+ Callable close;
+ NSMenu *menu = nullptr;
+ bool is_open = false;
+ };
+ HashMap<String, MenuData> submenu;
+ HashMap<NSMenu *, String> submenu_inv;
struct WarpEvent {
NSTimeInterval timestamp;
@@ -197,6 +204,7 @@ private:
const NSMenu *_get_menu_root(const String &p_menu_root) const;
NSMenu *_get_menu_root(const String &p_menu_root);
+ bool _is_menu_opened(NSMenu *p_menu) const;
WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect);
void _update_window_style(WindowData p_wd);
@@ -223,6 +231,8 @@ private:
public:
NSMenu *get_dock_menu() const;
void menu_callback(id p_sender);
+ void menu_open(NSMenu *p_menu);
+ void menu_close(NSMenu *p_menu);
bool has_window(WindowID p_window) const;
WindowData &get_window(WindowID p_window);
@@ -245,6 +255,7 @@ public:
WindowID _get_focused_window_or_popup() const;
void mouse_enter_window(WindowID p_window);
void mouse_exit_window(WindowID p_window);
+ void update_presentation_mode();
void window_destroy(WindowID p_window);
void window_resize(WindowID p_window, int p_width, int p_height);
@@ -253,6 +264,8 @@ public:
virtual bool has_feature(Feature p_feature) const override;
virtual String get_name() const override;
+ virtual void global_menu_set_popup_callbacks(const String &p_menu_root, const Callable &p_open_callback = Callable(), const Callable &p_close_callback = Callable()) override;
+
virtual int global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index = -1) override;
virtual int global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
virtual int global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
@@ -276,6 +289,7 @@ public:
virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx) const override;
virtual Key global_menu_get_item_accelerator(const String &p_menu_root, int p_idx) const override;
virtual bool global_menu_is_item_disabled(const String &p_menu_root, int p_idx) const override;
+ virtual bool global_menu_is_item_hidden(const String &p_menu_root, int p_idx) const override;
virtual String global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const override;
virtual int global_menu_get_item_state(const String &p_menu_root, int p_idx) const override;
virtual int global_menu_get_item_max_states(const String &p_menu_root, int p_idx) const override;
@@ -287,11 +301,13 @@ public:
virtual void global_menu_set_item_radio_checkable(const String &p_menu_root, int p_idx, bool p_checkable) override;
virtual void global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback) override;
virtual void global_menu_set_item_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_callback) override;
+ virtual void global_menu_set_item_hover_callbacks(const String &p_menu_root, int p_idx, const Callable &p_callback) override;
virtual void global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag) override;
virtual void global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text) override;
virtual void global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu) override;
virtual void global_menu_set_item_accelerator(const String &p_menu_root, int p_idx, Key p_keycode) override;
virtual void global_menu_set_item_disabled(const String &p_menu_root, int p_idx, bool p_disabled) override;
+ virtual void global_menu_set_item_hidden(const String &p_menu_root, int p_idx, bool p_hidden) override;
virtual void global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip) override;
virtual void global_menu_set_item_state(const String &p_menu_root, int p_idx, int p_state) override;
virtual void global_menu_set_item_max_states(const String &p_menu_root, int p_idx, int p_max_states) override;
@@ -368,6 +384,7 @@ public:
virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual Size2i window_get_title_size(const String &p_title, WindowID p_window) const override;
virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID) override;
virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 2989f8d1c0..67d6f4214f 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -75,7 +75,7 @@ const NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) cons
} else {
// Submenu.
if (submenu.has(p_menu_root)) {
- menu = submenu[p_menu_root];
+ menu = submenu[p_menu_root].menu;
}
}
if (menu == apple_menu) {
@@ -99,9 +99,10 @@ NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) {
NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]];
[n_menu setAutoenablesItems:NO];
[n_menu setDelegate:menu_delegate];
- submenu[p_menu_root] = n_menu;
+ submenu[p_menu_root].menu = n_menu;
+ submenu_inv[n_menu] = p_menu_root;
}
- menu = submenu[p_menu_root];
+ menu = submenu[p_menu_root].menu;
}
if (menu == apple_menu) {
// Do not allow to change Apple menu.
@@ -609,6 +610,30 @@ NSMenu *DisplayServerMacOS::get_dock_menu() const {
return dock_menu;
}
+void DisplayServerMacOS::menu_open(NSMenu *p_menu) {
+ if (submenu_inv.has(p_menu)) {
+ MenuData &md = submenu[submenu_inv[p_menu]];
+ md.is_open = true;
+ if (md.open.is_valid()) {
+ Variant ret;
+ Callable::CallError ce;
+ md.open.callp(nullptr, 0, ret, ce);
+ }
+ }
+}
+
+void DisplayServerMacOS::menu_close(NSMenu *p_menu) {
+ if (submenu_inv.has(p_menu)) {
+ MenuData &md = submenu[submenu_inv[p_menu]];
+ md.is_open = false;
+ if (md.close.is_valid()) {
+ Variant ret;
+ Callable::CallError ce;
+ md.close.callp(nullptr, 0, ret, ce);
+ }
+ }
+}
+
void DisplayServerMacOS::menu_callback(id p_sender) {
if (![p_sender representedObject]) {
return;
@@ -749,6 +774,7 @@ void DisplayServerMacOS::window_destroy(WindowID p_window) {
}
#endif
windows.erase(p_window);
+ update_presentation_mode();
}
void DisplayServerMacOS::window_resize(WindowID p_window, int p_width, int p_height) {
@@ -815,6 +841,24 @@ bool DisplayServerMacOS::_has_help_menu() const {
}
}
+bool DisplayServerMacOS::_is_menu_opened(NSMenu *p_menu) const {
+ if (submenu_inv.has(p_menu)) {
+ const MenuData &md = submenu[submenu_inv[p_menu]];
+ if (md.is_open) {
+ return true;
+ }
+ }
+ for (NSInteger i = (p_menu == [NSApp mainMenu]) ? 1 : 0; i < [p_menu numberOfItems]; i++) {
+ const NSMenuItem *menu_item = [p_menu itemAtIndex:i];
+ if ([menu_item submenu]) {
+ if (_is_menu_opened([menu_item submenu])) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
NSMenuItem *DisplayServerMacOS::_menu_add_item(const String &p_menu_root, const String &p_label, Key p_accel, int p_index, int *r_out) {
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
@@ -998,6 +1042,23 @@ int DisplayServerMacOS::global_menu_add_multistate_item(const String &p_menu_roo
return out;
}
+void DisplayServerMacOS::global_menu_set_popup_callbacks(const String &p_menu_root, const Callable &p_open_callback, const Callable &p_close_callback) {
+ _THREAD_SAFE_METHOD_
+
+ if (p_menu_root != "" && p_menu_root.to_lower() != "_main" && p_menu_root.to_lower() != "_dock") {
+ // Submenu.
+ if (!submenu.has(p_menu_root)) {
+ NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]];
+ [n_menu setAutoenablesItems:NO];
+ [n_menu setDelegate:menu_delegate];
+ submenu[p_menu_root].menu = n_menu;
+ submenu_inv[n_menu] = p_menu_root;
+ }
+ submenu[p_menu_root].open = p_open_callback;
+ submenu[p_menu_root].close = p_close_callback;
+ }
+}
+
int DisplayServerMacOS::global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index) {
_THREAD_SAFE_METHOD_
@@ -1251,13 +1312,9 @@ String DisplayServerMacOS::global_menu_get_item_submenu(const String &p_menu_roo
ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], String());
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
- const NSMenu *sub_menu = [menu_item submenu];
- if (sub_menu) {
- for (const KeyValue<String, NSMenu *> &E : submenu) {
- if (E.value == sub_menu) {
- return E.key;
- }
- }
+ NSMenu *sub_menu = [menu_item submenu];
+ if (sub_menu && submenu_inv.has(sub_menu)) {
+ return submenu_inv[sub_menu];
}
}
}
@@ -1318,6 +1375,24 @@ bool DisplayServerMacOS::global_menu_is_item_disabled(const String &p_menu_root,
return false;
}
+bool DisplayServerMacOS::global_menu_is_item_hidden(const String &p_menu_root, int p_idx) const {
+ _THREAD_SAFE_METHOD_
+
+ const NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ ERR_FAIL_COND_V(p_idx < 0, false);
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false);
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ return [menu_item isHidden];
+ }
+ }
+ return false;
+}
+
String DisplayServerMacOS::global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const {
_THREAD_SAFE_METHOD_
@@ -1497,6 +1572,25 @@ void DisplayServerMacOS::global_menu_set_item_callback(const String &p_menu_root
}
}
+void DisplayServerMacOS::global_menu_set_item_hover_callbacks(const String &p_menu_root, int p_idx, const Callable &p_callback) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ ERR_FAIL_COND(p_idx < 0);
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND(p_idx >= [menu numberOfItems]);
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ GodotMenuItem *obj = [menu_item representedObject];
+ ERR_FAIL_COND(!obj);
+ obj->hover_callback = p_callback;
+ }
+ }
+}
+
void DisplayServerMacOS::global_menu_set_item_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_callback) {
_THREAD_SAFE_METHOD_
@@ -1556,6 +1650,23 @@ void DisplayServerMacOS::global_menu_set_item_submenu(const String &p_menu_root,
_THREAD_SAFE_METHOD_
NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu && p_submenu.is_empty()) {
+ ERR_FAIL_COND(p_idx < 0);
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND(p_idx >= [menu numberOfItems]);
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ if ([menu_item submenu] && _is_menu_opened([menu_item submenu])) {
+ ERR_PRINT("Can't remove open menu!");
+ return;
+ }
+ [menu setSubmenu:nil forItem:menu_item];
+ }
+ return;
+ }
+
NSMenu *sub_menu = _get_menu_root(p_submenu);
if (menu && sub_menu) {
if (sub_menu == menu) {
@@ -1590,9 +1701,13 @@ void DisplayServerMacOS::global_menu_set_item_accelerator(const String &p_menu_r
ERR_FAIL_COND(p_idx >= [menu numberOfItems]);
NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
- [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_keycode)];
- String keycode = KeyMappingMacOS::keycode_get_native_string(p_keycode & KeyModifierMask::CODE_MASK);
- [menu_item setKeyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
+ if (p_keycode == Key::NONE) {
+ [menu_item setKeyEquivalent:@""];
+ } else {
+ [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_keycode)];
+ String keycode = KeyMappingMacOS::keycode_get_native_string(p_keycode & KeyModifierMask::CODE_MASK);
+ [menu_item setKeyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
+ }
}
}
}
@@ -1614,6 +1729,23 @@ void DisplayServerMacOS::global_menu_set_item_disabled(const String &p_menu_root
}
}
+void DisplayServerMacOS::global_menu_set_item_hidden(const String &p_menu_root, int p_idx, bool p_hidden) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ ERR_FAIL_COND(p_idx < 0);
+ if (menu == [NSApp mainMenu]) { // Skip Apple menu.
+ p_idx++;
+ }
+ ERR_FAIL_COND(p_idx >= [menu numberOfItems]);
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ [menu_item setHidden:p_hidden];
+ }
+ }
+}
+
void DisplayServerMacOS::global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip) {
_THREAD_SAFE_METHOD_
@@ -1741,6 +1873,11 @@ void DisplayServerMacOS::global_menu_remove_item(const String &p_menu_root, int
p_idx++;
}
ERR_FAIL_COND(p_idx >= [menu numberOfItems]);
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if ([menu_item submenu] && _is_menu_opened([menu_item submenu])) {
+ ERR_PRINT("Can't remove open menu!");
+ return;
+ }
[menu removeItemAtIndex:p_idx];
}
}
@@ -1750,6 +1887,10 @@ void DisplayServerMacOS::global_menu_clear(const String &p_menu_root) {
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
+ if (_is_menu_opened(menu)) {
+ ERR_PRINT("Can't remove open menu!");
+ return;
+ }
[menu removeAllItems];
// Restore Apple menu.
if (menu == [NSApp mainMenu]) {
@@ -1757,43 +1898,44 @@ void DisplayServerMacOS::global_menu_clear(const String &p_menu_root) {
[menu setSubmenu:apple_menu forItem:menu_item];
}
if (submenu.has(p_menu_root)) {
+ submenu_inv.erase(submenu[p_menu_root].menu);
submenu.erase(p_menu_root);
}
}
}
bool DisplayServerMacOS::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 DisplayServerMacOS::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> DisplayServerMacOS::tts_get_voices() const {
- ERR_FAIL_COND_V_MSG(!tts, Array(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
+ ERR_FAIL_NULL_V_MSG(tts, Array(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
return [tts getVoices];
}
void DisplayServerMacOS::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 DisplayServerMacOS::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 DisplayServerMacOS::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 DisplayServerMacOS::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];
}
@@ -1870,171 +2012,275 @@ Error DisplayServerMacOS::dialog_show(String p_title, String p_description, Vect
return OK;
}
-Error DisplayServerMacOS::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
- _THREAD_SAFE_METHOD_
+@interface FileDialogDropdown : NSObject {
+ NSSavePanel *dialog;
+ NSMutableArray *allowed_types;
+ int cur_index;
+}
+
+- (instancetype)initWithDialog:(NSSavePanel *)p_dialog fileTypes:(NSMutableArray *)p_allowed_types;
+- (void)popupAction:(id)sender;
+- (int)getIndex;
+
+@end
+
+@implementation FileDialogDropdown
+
+- (int)getIndex {
+ return cur_index;
+}
+
+- (instancetype)initWithDialog:(NSSavePanel *)p_dialog fileTypes:(NSMutableArray *)p_allowed_types {
+ if ((self = [super init])) {
+ dialog = p_dialog;
+ allowed_types = p_allowed_types;
+ cur_index = 0;
+ }
+ return self;
+}
+
+- (void)popupAction:(id)sender {
+ NSUInteger index = [sender indexOfSelectedItem];
+ if (index < [allowed_types count]) {
+ [dialog setAllowedFileTypes:[allowed_types objectAtIndex:index]];
+ cur_index = index;
+ } else {
+ [dialog setAllowedFileTypes:@[]];
+ cur_index = -1;
+ }
+}
+
+@end
+
+FileDialogDropdown *_make_accessory_view(NSSavePanel *p_panel, const Vector<String> &p_filters) {
+ NSView *group = [[NSView alloc] initWithFrame:NSZeroRect];
+ group.translatesAutoresizingMaskIntoConstraints = NO;
+
+ NSTextField *label = [NSTextField labelWithString:[NSString stringWithUTF8String:RTR("Format").utf8().get_data()]];
+ label.translatesAutoresizingMaskIntoConstraints = NO;
+ if (@available(macOS 10.14, *)) {
+ label.textColor = NSColor.secondaryLabelColor;
+ }
+ if (@available(macOS 11.10, *)) {
+ label.font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
+ }
+ [group addSubview:label];
+
+ NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO];
+ popup.translatesAutoresizingMaskIntoConstraints = NO;
- NSString *url = [NSString stringWithUTF8String:p_current_directory.utf8().get_data()];
NSMutableArray *allowed_types = [[NSMutableArray alloc] init];
bool allow_other = false;
for (int i = 0; i < p_filters.size(); i++) {
Vector<String> tokens = p_filters[i].split(";");
- if (tokens.size() > 0) {
- if (tokens[0].strip_edges() == "*.*") {
- allow_other = true;
- } else {
- [allowed_types addObject:[NSString stringWithUTF8String:tokens[0].replace("*.", "").strip_edges().utf8().get_data()]];
+ if (tokens.size() >= 1) {
+ String flt = tokens[0].strip_edges();
+ int filter_slice_count = flt.get_slice_count(",");
+
+ NSMutableArray *type_filters = [[NSMutableArray alloc] init];
+ for (int j = 0; j < filter_slice_count; j++) {
+ String str = (flt.get_slice(",", j).strip_edges());
+ if (str.strip_edges() == "*.*" || str.strip_edges() == "*") {
+ allow_other = true;
+ } else if (!str.is_empty()) {
+ [type_filters addObject:[NSString stringWithUTF8String:str.replace("*.", "").strip_edges().utf8().get_data()]];
+ }
+ }
+
+ if ([type_filters count] > 0) {
+ NSString *name_str = [NSString stringWithUTF8String:((tokens.size() == 1) ? tokens[0] : vformat("%s (%s)", tokens[1], tokens[0])).strip_edges().utf8().get_data()];
+ [allowed_types addObject:type_filters];
+ [popup addItemWithTitle:name_str];
}
}
}
+ FileDialogDropdown *handler = [[FileDialogDropdown alloc] initWithDialog:p_panel fileTypes:allowed_types];
+ popup.target = handler;
+ popup.action = @selector(popupAction:);
- Callable callback = p_callback; // Make a copy for async completion handler.
- switch (p_mode) {
- case FILE_DIALOG_MODE_SAVE_FILE: {
- NSSavePanel *panel = [NSSavePanel savePanel];
+ [group addSubview:popup];
- [panel setDirectoryURL:[NSURL fileURLWithPath:url]];
- if ([allowed_types count]) {
- [panel setAllowedFileTypes:allowed_types];
- }
- [panel setAllowsOtherFileTypes:allow_other];
- [panel setExtensionHidden:YES];
- [panel setCanSelectHiddenExtension:YES];
- [panel setCanCreateDirectories:YES];
- [panel setShowsHiddenFiles:p_show_hidden];
- if (p_filename != "") {
- NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()];
- [panel setNameFieldStringValue:fileurl];
- }
+ NSView *view = [[NSView alloc] initWithFrame:NSZeroRect];
+ view.translatesAutoresizingMaskIntoConstraints = NO;
+ [view addSubview:group];
+
+ NSMutableArray *constraints = [NSMutableArray array];
+ [constraints addObject:[popup.topAnchor constraintEqualToAnchor:group.topAnchor constant:10]];
+ [constraints addObject:[label.leadingAnchor constraintEqualToAnchor:group.leadingAnchor constant:10]];
+ [constraints addObject:[popup.leadingAnchor constraintEqualToAnchor:label.trailingAnchor constant:10]];
+ [constraints addObject:[popup.firstBaselineAnchor constraintEqualToAnchor:label.firstBaselineAnchor]];
+ [constraints addObject:[group.trailingAnchor constraintEqualToAnchor:popup.trailingAnchor constant:10]];
+ [constraints addObject:[group.bottomAnchor constraintEqualToAnchor:popup.bottomAnchor constant:10]];
+ [constraints addObject:[group.topAnchor constraintEqualToAnchor:view.topAnchor]];
+ [constraints addObject:[group.centerXAnchor constraintEqualToAnchor:view.centerXAnchor]];
+ [constraints addObject:[view.bottomAnchor constraintEqualToAnchor:group.bottomAnchor]];
+ [NSLayoutConstraint activateConstraints:constraints];
+
+ [p_panel setAllowsOtherFileTypes:allow_other];
+ if ([allowed_types count] > 0) {
+ [p_panel setAccessoryView:view];
+ [p_panel setAllowedFileTypes:[allowed_types objectAtIndex:0]];
+ }
+
+ return handler;
+}
+
+Error DisplayServerMacOS::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
+ _THREAD_SAFE_METHOD_
- [panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow]
- completionHandler:^(NSInteger ret) {
- if (ret == NSModalResponseOK) {
- // Save bookmark for folder.
- if (OS::get_singleton()->is_sandboxed()) {
- NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"];
+ ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED);
+
+ NSString *url = [NSString stringWithUTF8String:p_current_directory.utf8().get_data()];
+ FileDialogDropdown *handler = nullptr;
+
+ WindowID prev_focus = last_focused_window;
+
+ Callable callback = p_callback; // Make a copy for async completion handler.
+ if (p_mode == FILE_DIALOG_MODE_SAVE_FILE) {
+ NSSavePanel *panel = [NSSavePanel savePanel];
+
+ [panel setDirectoryURL:[NSURL fileURLWithPath:url]];
+ handler = _make_accessory_view(panel, p_filters);
+ [panel setExtensionHidden:YES];
+ [panel setCanSelectHiddenExtension:YES];
+ [panel setCanCreateDirectories:YES];
+ [panel setShowsHiddenFiles:p_show_hidden];
+ if (p_filename != "") {
+ NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()];
+ [panel setNameFieldStringValue:fileurl];
+ }
+
+ [panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow]
+ completionHandler:^(NSInteger ret) {
+ if (ret == NSModalResponseOK) {
+ // Save bookmark for folder.
+ if (OS::get_singleton()->is_sandboxed()) {
+ NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"];
+ bool skip = false;
+ for (id bookmark in bookmarks) {
+ NSError *error = nil;
+ BOOL isStale = NO;
+ NSURL *exurl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error];
+ if (!error && !isStale && ([[exurl path] compare:[[panel directoryURL] path]] == NSOrderedSame)) {
+ skip = true;
+ break;
+ }
+ }
+ if (!skip) {
+ NSError *error = nil;
+ NSData *bookmark = [[panel directoryURL] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
+ if (!error) {
+ NSArray *new_bookmarks = [bookmarks arrayByAddingObject:bookmark];
+ [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"];
+ }
+ }
+ }
+ // Callback.
+ Vector<String> files;
+ String url;
+ url.parse_utf8([[[panel URL] path] UTF8String]);
+ files.push_back(url);
+ if (!callback.is_null()) {
+ Variant v_status = true;
+ Variant v_files = files;
+ Variant v_index = [handler getIndex];
+ Variant *v_args[3] = { &v_status, &v_files, &v_index };
+ Variant ret;
+ Callable::CallError ce;
+ callback.callp((const Variant **)&v_args, 3, ret, ce);
+ }
+ } else {
+ if (!callback.is_null()) {
+ Variant v_status = false;
+ Variant v_files = Vector<String>();
+ Variant v_index = [handler getIndex];
+ Variant *v_args[3] = { &v_status, &v_files, &v_index };
+ Variant ret;
+ Callable::CallError ce;
+ callback.callp((const Variant **)&v_args, 3, ret, ce);
+ }
+ }
+ if (prev_focus != INVALID_WINDOW_ID) {
+ callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus);
+ }
+ }];
+ } else {
+ NSOpenPanel *panel = [NSOpenPanel openPanel];
+
+ [panel setDirectoryURL:[NSURL fileURLWithPath:url]];
+ handler = _make_accessory_view(panel, p_filters);
+ [panel setExtensionHidden:YES];
+ [panel setCanSelectHiddenExtension:YES];
+ [panel setCanCreateDirectories:YES];
+ [panel setCanChooseFiles:(p_mode != FILE_DIALOG_MODE_OPEN_DIR)];
+ [panel setCanChooseDirectories:(p_mode == FILE_DIALOG_MODE_OPEN_DIR || p_mode == FILE_DIALOG_MODE_OPEN_ANY)];
+ [panel setShowsHiddenFiles:p_show_hidden];
+ if (p_filename != "") {
+ NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()];
+ [panel setNameFieldStringValue:fileurl];
+ }
+ [panel setAllowsMultipleSelection:(p_mode == FILE_DIALOG_MODE_OPEN_FILES)];
+
+ [panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow]
+ completionHandler:^(NSInteger ret) {
+ if (ret == NSModalResponseOK) {
+ // Save bookmark for folder.
+ NSArray *urls = [(NSOpenPanel *)panel URLs];
+ if (OS::get_singleton()->is_sandboxed()) {
+ NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"];
+ NSMutableArray *new_bookmarks = [bookmarks mutableCopy];
+ for (NSUInteger i = 0; i != [urls count]; ++i) {
bool skip = false;
for (id bookmark in bookmarks) {
NSError *error = nil;
BOOL isStale = NO;
NSURL *exurl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error];
- if (!error && !isStale && ([[exurl path] compare:[[panel directoryURL] path]] == NSOrderedSame)) {
+ if (!error && !isStale && ([[exurl path] compare:[[urls objectAtIndex:i] path]] == NSOrderedSame)) {
skip = true;
break;
}
}
if (!skip) {
NSError *error = nil;
- NSData *bookmark = [[panel directoryURL] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
+ NSData *bookmark = [[urls objectAtIndex:i] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
if (!error) {
- NSArray *new_bookmarks = [bookmarks arrayByAddingObject:bookmark];
- [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"];
+ [new_bookmarks addObject:bookmark];
}
}
}
- // Callback.
- Vector<String> files;
+ [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"];
+ }
+ // Callback.
+ Vector<String> files;
+ for (NSUInteger i = 0; i != [urls count]; ++i) {
String url;
- url.parse_utf8([[[panel URL] path] UTF8String]);
+ url.parse_utf8([[[urls objectAtIndex:i] path] UTF8String]);
files.push_back(url);
- if (!callback.is_null()) {
- Variant v_status = true;
- Variant v_files = files;
- Variant *v_args[2] = { &v_status, &v_files };
- Variant ret;
- Callable::CallError ce;
- callback.callp((const Variant **)&v_args, 2, ret, ce);
- }
- } else {
- if (!callback.is_null()) {
- Variant v_status = false;
- Variant v_files = Vector<String>();
- Variant *v_args[2] = { &v_status, &v_files };
- Variant ret;
- Callable::CallError ce;
- callback.callp((const Variant **)&v_args, 2, ret, ce);
- }
}
- }];
- } break;
- case FILE_DIALOG_MODE_OPEN_ANY:
- case FILE_DIALOG_MODE_OPEN_FILE:
- case FILE_DIALOG_MODE_OPEN_FILES:
- case FILE_DIALOG_MODE_OPEN_DIR: {
- NSOpenPanel *panel = [NSOpenPanel openPanel];
-
- [panel setDirectoryURL:[NSURL fileURLWithPath:url]];
- if ([allowed_types count]) {
- [panel setAllowedFileTypes:allowed_types];
- }
- [panel setAllowsOtherFileTypes:allow_other];
- [panel setExtensionHidden:YES];
- [panel setCanSelectHiddenExtension:YES];
- [panel setCanCreateDirectories:YES];
- [panel setCanChooseFiles:(p_mode != FILE_DIALOG_MODE_OPEN_DIR)];
- [panel setCanChooseDirectories:(p_mode == FILE_DIALOG_MODE_OPEN_DIR || p_mode == FILE_DIALOG_MODE_OPEN_ANY)];
- [panel setShowsHiddenFiles:p_show_hidden];
- if (p_filename != "") {
- NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()];
- [panel setNameFieldStringValue:fileurl];
- }
- [panel setAllowsMultipleSelection:(p_mode == FILE_DIALOG_MODE_OPEN_FILES)];
-
- [panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow]
- completionHandler:^(NSInteger ret) {
- if (ret == NSModalResponseOK) {
- // Save bookmark for folder.
- NSArray *urls = [(NSOpenPanel *)panel URLs];
- if (OS::get_singleton()->is_sandboxed()) {
- NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"];
- NSMutableArray *new_bookmarks = [bookmarks mutableCopy];
- for (NSUInteger i = 0; i != [urls count]; ++i) {
- bool skip = false;
- for (id bookmark in bookmarks) {
- NSError *error = nil;
- BOOL isStale = NO;
- NSURL *exurl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error];
- if (!error && !isStale && ([[exurl path] compare:[[urls objectAtIndex:i] path]] == NSOrderedSame)) {
- skip = true;
- break;
- }
- }
- if (!skip) {
- NSError *error = nil;
- NSData *bookmark = [[urls objectAtIndex:i] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
- if (!error) {
- [new_bookmarks addObject:bookmark];
- }
- }
- }
- [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"];
- }
- // Callback.
- Vector<String> files;
- for (NSUInteger i = 0; i != [urls count]; ++i) {
- String url;
- url.parse_utf8([[[urls objectAtIndex:i] path] UTF8String]);
- files.push_back(url);
- }
- if (!callback.is_null()) {
- Variant v_status = true;
- Variant v_files = files;
- Variant *v_args[2] = { &v_status, &v_files };
- Variant ret;
- Callable::CallError ce;
- callback.callp((const Variant **)&v_args, 2, ret, ce);
- }
- } else {
- if (!callback.is_null()) {
- Variant v_status = false;
- Variant v_files = Vector<String>();
- Variant *v_args[2] = { &v_status, &v_files };
- Variant ret;
- Callable::CallError ce;
- callback.callp((const Variant **)&v_args, 2, ret, ce);
- }
+ if (!callback.is_null()) {
+ Variant v_status = true;
+ Variant v_files = files;
+ Variant v_index = [handler getIndex];
+ Variant *v_args[3] = { &v_status, &v_files, &v_index };
+ Variant ret;
+ Callable::CallError ce;
+ callback.callp((const Variant **)&v_args, 3, ret, ce);
}
- }];
- } break;
+ } else {
+ if (!callback.is_null()) {
+ Variant v_status = false;
+ Variant v_files = Vector<String>();
+ Variant v_index = [handler getIndex];
+ Variant *v_args[3] = { &v_status, &v_files, &v_index };
+ Variant ret;
+ Callable::CallError ce;
+ callback.callp((const Variant **)&v_args, 3, ret, ce);
+ }
+ }
+ if (prev_focus != INVALID_WINDOW_ID) {
+ callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus);
+ }
+ }];
}
return OK;
@@ -2626,6 +2872,47 @@ void DisplayServerMacOS::window_set_title(const String &p_title, WindowID p_wind
[wd.window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]];
}
+Size2i DisplayServerMacOS::window_get_title_size(const String &p_title, WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ Size2i size;
+ ERR_FAIL_COND_V(!windows.has(p_window), size);
+
+ const WindowData &wd = windows[p_window];
+ if (wd.fullscreen || wd.borderless) {
+ return size;
+ }
+ if ([wd.window_object respondsToSelector:@selector(isMiniaturized)]) {
+ if ([wd.window_object isMiniaturized]) {
+ return size;
+ }
+ }
+
+ float scale = screen_get_max_scale();
+
+ if (wd.window_button_view) {
+ size.x = ([wd.window_button_view getOffset].x + [wd.window_button_view frame].size.width);
+ size.y = ([wd.window_button_view getOffset].y + [wd.window_button_view frame].size.height);
+ } else {
+ NSButton *cb = [wd.window_object standardWindowButton:NSWindowCloseButton];
+ NSButton *mb = [wd.window_object standardWindowButton:NSWindowMiniaturizeButton];
+ float cb_frame = NSMinX([cb frame]);
+ float mb_frame = NSMinX([mb frame]);
+ bool is_rtl = ([wd.window_object windowTitlebarLayoutDirection] == NSUserInterfaceLayoutDirectionRightToLeft);
+
+ float window_buttons_spacing = (is_rtl) ? (cb_frame - mb_frame) : (mb_frame - cb_frame);
+ size.x = window_buttons_spacing * 4;
+ size.y = [cb frame].origin.y + [cb frame].size.height;
+ }
+
+ NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSFont titleBarFontOfSize:0], NSFontAttributeName, nil];
+ NSSize text_size = [[[NSAttributedString alloc] initWithString:[NSString stringWithUTF8String:p_title.utf8().get_data()] attributes:attributes] size];
+ size.x += text_size.width;
+ size.y = MAX(size.y, text_size.height);
+
+ return size * scale;
+}
+
void DisplayServerMacOS::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {
_THREAD_SAFE_METHOD_
@@ -2868,6 +3155,15 @@ Size2i DisplayServerMacOS::window_get_max_size(WindowID p_window) const {
return wd.max_size;
}
+void DisplayServerMacOS::update_presentation_mode() {
+ for (const KeyValue<WindowID, WindowData> &wd : windows) {
+ if (wd.value.fullscreen && wd.value.exclusive_fullscreen) {
+ return;
+ }
+ }
+ [NSApp setPresentationOptions:NSApplicationPresentationDefault];
+}
+
void DisplayServerMacOS::window_set_min_size(const Size2i p_size, WindowID p_window) {
_THREAD_SAFE_METHOD_
@@ -2978,7 +3274,7 @@ void DisplayServerMacOS::window_set_mode(WindowMode p_mode, WindowID p_window) {
[wd.window_object toggleFullScreen:nil];
if (old_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
- [NSApp setPresentationOptions:NSApplicationPresentationDefault];
+ update_presentation_mode();
}
wd.fullscreen = false;
@@ -3176,7 +3472,9 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win
} break;
case WINDOW_FLAG_BORDERLESS: {
// OrderOut prevents a lose focus bug with the window.
+ bool was_visible = false;
if ([wd.window_object isVisible]) {
+ was_visible = true;
[wd.window_object orderOut:nil];
}
wd.borderless = p_enabled;
@@ -3191,7 +3489,7 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win
[wd.window_object setFrame:frameRect display:NO];
}
_update_window_style(wd);
- if ([wd.window_object isVisible]) {
+ if (was_visible || [wd.window_object isVisible]) {
if ([wd.window_object isMiniaturized]) {
return;
} else if (wd.no_focus) {
@@ -4127,15 +4425,19 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM
nsappname = [[NSProcessInfo processInfo] processName];
}
+ menu_delegate = [[GodotMenuDelegate alloc] init];
+
// Setup Dock menu.
dock_menu = [[NSMenu alloc] initWithTitle:@"_dock"];
[dock_menu setAutoenablesItems:NO];
+ [dock_menu setDelegate:menu_delegate];
// Setup Apple menu.
apple_menu = [[NSMenu alloc] initWithTitle:@""];
title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname];
[apple_menu addItemWithTitle:title action:@selector(showAbout:) keyEquivalent:@""];
[apple_menu setAutoenablesItems:NO];
+ [apple_menu setDelegate:menu_delegate];
[apple_menu addItem:[NSMenuItem separatorItem]];
@@ -4165,8 +4467,6 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM
[main_menu setSubmenu:apple_menu forItem:menu_item];
[main_menu setAutoenablesItems:NO];
- menu_delegate = [[GodotMenuDelegate alloc] init];
-
//!!!!!!!!!!!!!!!!!!!!!!!!!!
//TODO - do Vulkan and OpenGL support checks, driver selection and fallback
rendering_driver = p_rendering_driver;
diff --git a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
index 99187fe60e..85fefe65c0 100644
--- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
+++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
@@ -66,7 +66,7 @@
Array of the additional command line arguments passed to the code signing tool.
</member>
<member name="codesign/entitlements/address_book" type="bool" setter="" getter="">
- Enable to allow access to contacts in the user's address book, if it's enabled you should also provide usage message in the [code]privacy/address_book_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_addressbook]com.apple.security.personal-information.addressbook[/url].
+ Enable to allow access to contacts in the user's address book, if it's enabled you should also provide usage message in the [member privacy/address_book_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_addressbook]com.apple.security.personal-information.addressbook[/url].
</member>
<member name="codesign/entitlements/allow_dyld_environment_variables" type="bool" setter="" getter="">
Allows app to use dynamic linker environment variables to inject code. If you are using add-ons with dynamic or self-modifying native code, enable them according to the add-on documentation. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_allow-dyld-environment-variables]com.apple.security.cs.allow-dyld-environment-variables[/url].
@@ -115,13 +115,13 @@
Enable to allow app to send Apple events to other apps. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_automation_apple-events]com.apple.security.automation.apple-events[/url].
</member>
<member name="codesign/entitlements/audio_input" type="bool" setter="" getter="">
- Enable if you need to use the microphone or other audio input sources, if it's enabled you should also provide usage message in the [code]privacy/microphone_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_audio-input]com.apple.security.device.audio-input[/url].
+ Enable if you need to use the microphone or other audio input sources, if it's enabled you should also provide usage message in the [member privacy/microphone_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_audio-input]com.apple.security.device.audio-input[/url].
</member>
<member name="codesign/entitlements/calendars" type="bool" setter="" getter="">
- Enable to allow access to the user's calendar, if it's enabled you should also provide usage message in the [code]privacy/calendar_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_calendars]com.apple.security.personal-information.calendars[/url].
+ Enable to allow access to the user's calendar, if it's enabled you should also provide usage message in the [member privacy/calendar_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_calendars]com.apple.security.personal-information.calendars[/url].
</member>
<member name="codesign/entitlements/camera" type="bool" setter="" getter="">
- Enable if you need to use the camera, if it's enabled you should also provide usage message in the [code]privacy/camera_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_camera]com.apple.security.device.camera[/url].
+ Enable if you need to use the camera, if it's enabled you should also provide usage message in the [member privacy/camera_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_camera]com.apple.security.device.camera[/url].
</member>
<member name="codesign/entitlements/custom_file" type="String" setter="" getter="">
Custom entitlements [code].plist[/code] file, if specified the rest of entitlements in the export config are ignored.
@@ -133,10 +133,10 @@
Allows app to load arbitrary libraries and frameworks (not signed with the same Team ID as the main executable or by Apple). Enable it if you are using GDExtension add-ons or ad-hoc signing, or want to support user-provided external add-ons. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_disable-library-validation]com.apple.security.cs.disable-library-validation[/url].
</member>
<member name="codesign/entitlements/location" type="bool" setter="" getter="">
- Enable if you need to use location information from Location Services, if it's enabled you should also provide usage message in the [code]privacy/location_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_location]com.apple.security.personal-information.location[/url].
+ Enable if you need to use location information from Location Services, if it's enabled you should also provide usage message in the [member privacy/location_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_location]com.apple.security.personal-information.location[/url].
</member>
<member name="codesign/entitlements/photos_library" type="bool" setter="" getter="">
- Enable to allow access to the user's Photos library, if it's enabled you should also provide usage message in the [code]privacy/photos_library_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_photos-library]com.apple.security.personal-information.photos-library[/url].
+ Enable to allow access to the user's Photos library, if it's enabled you should also provide usage message in the [member privacy/photos_library_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_photos-library]com.apple.security.personal-information.photos-library[/url].
</member>
<member name="codesign/identity" type="String" setter="" getter="">
The "Full Name", "Common Name" or SHA-1 hash of the signing identity used to sign [code].app[/code] bundle.
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index 639a7699ee..804028053d 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -1047,6 +1047,8 @@ Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pre
args.push_back("--p12-password");
args.push_back(certificate_pass);
}
+ args.push_back("--code-signature-flags");
+ args.push_back("runtime");
args.push_back("-v"); /* provide some more feedback */
@@ -1139,7 +1141,6 @@ Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pre
Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPreset> &p_preset, const String &p_path,
const String &p_ent_path, bool p_should_error_on_non_code) {
-#ifdef MACOS_ENABLED
static Vector<String> extensions_to_sign;
if (extensions_to_sign.is_empty()) {
@@ -1186,7 +1187,6 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres
current_file = dir_access->get_next();
}
-#endif
return OK;
}
@@ -1225,7 +1225,7 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access
if (extensions_to_sign.find(p_in_app_path.get_extension()) > -1) {
err = _code_sign(p_preset, p_in_app_path, p_ent_path, false);
}
- if (is_executable(p_in_app_path)) {
+ if (dir_access->file_exists(p_in_app_path) && is_executable(p_in_app_path)) {
// chmod with 0755 if the file is executable.
FileAccess::set_unix_permissions(p_in_app_path, 0755);
}
diff --git a/platform/macos/godot_menu_delegate.mm b/platform/macos/godot_menu_delegate.mm
index ebfe8b1f6d..1bfcfa7d9e 100644
--- a/platform/macos/godot_menu_delegate.mm
+++ b/platform/macos/godot_menu_delegate.mm
@@ -39,6 +39,34 @@
- (void)doNothing:(id)sender {
}
+- (void)menuNeedsUpdate:(NSMenu *)menu {
+ if (DisplayServer::get_singleton()) {
+ DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
+ ds->menu_open(menu);
+ }
+}
+
+- (void)menuDidClose:(NSMenu *)menu {
+ if (DisplayServer::get_singleton()) {
+ DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
+ ds->menu_close(menu);
+ }
+}
+
+- (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item {
+ if (item) {
+ GodotMenuItem *value = [item representedObject];
+ if (value && value->hover_callback != Callable()) {
+ // If custom callback is set, use it.
+ Variant tag = value->meta;
+ Variant *tagp = &tag;
+ Variant ret;
+ Callable::CallError ce;
+ value->hover_callback.callp((const Variant **)&tagp, 1, ret, ce);
+ }
+ }
+}
+
- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action {
NSString *ev_key = [[event charactersIgnoringModifiers] lowercaseString];
NSUInteger ev_modifiers = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
diff --git a/platform/macos/godot_menu_item.h b/platform/macos/godot_menu_item.h
index 8876ceae6a..b816ea1b3a 100644
--- a/platform/macos/godot_menu_item.h
+++ b/platform/macos/godot_menu_item.h
@@ -46,6 +46,7 @@ enum GlobalMenuCheckType {
@public
Callable callback;
Callable key_callback;
+ Callable hover_callback;
Variant meta;
GlobalMenuCheckType checkable_type;
int max_states;
diff --git a/platform/macos/godot_window_delegate.mm b/platform/macos/godot_window_delegate.mm
index 46355b4ae8..002ab0155f 100644
--- a/platform/macos/godot_window_delegate.mm
+++ b/platform/macos/godot_window_delegate.mm
@@ -157,7 +157,7 @@
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
if (wd.exclusive_fullscreen) {
- [NSApp setPresentationOptions:NSApplicationPresentationDefault];
+ ds->update_presentation_mode();
}
wd.fullscreen = false;
diff --git a/platform/web/api/api.cpp b/platform/web/api/api.cpp
index ab7154b0fb..a695091a04 100644
--- a/platform/web/api/api.cpp
+++ b/platform/web/api/api.cpp
@@ -96,7 +96,7 @@ Ref<JavaScriptObject> JavaScriptBridge::create_callback(const Callable &p_callab
Variant JavaScriptBridge::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 1) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 0;
+ r_error.expected = 1;
return Ref<JavaScriptObject>();
}
if (p_args[0]->get_type() != Variant::STRING) {
diff --git a/platform/web/audio_driver_web.h b/platform/web/audio_driver_web.h
index 7bfed834e1..12a61746c3 100644
--- a/platform/web/audio_driver_web.h
+++ b/platform/web/audio_driver_web.h
@@ -32,6 +32,7 @@
#define AUDIO_DRIVER_WEB_H
#include "godot_audio.h"
+#include "godot_js.h"
#include "core/os/mutex.h"
#include "core/os/thread.h"
@@ -55,8 +56,8 @@ private:
int mix_rate = 0;
int channel_count = 0;
- static void _state_change_callback(int p_state);
- static void _latency_update_callback(float p_latency);
+ WASM_EXPORT static void _state_change_callback(int p_state);
+ WASM_EXPORT static void _latency_update_callback(float p_latency);
static AudioDriverWeb *singleton;
diff --git a/platform/web/detect.py b/platform/web/detect.py
index 3e2bb5cd30..043ddca65d 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -60,6 +60,8 @@ def get_flags():
("target", "template_debug"),
("builtin_pcre2_with_jit", False),
("vulkan", False),
+ # Embree is heavy and requires too much memory (GH-70621).
+ ("module_raycast_enabled", False),
# Use -Os to prioritize optimizing for reduced file size. This is
# particularly valuable for the web platform because it directly
# decreases download time.
@@ -201,7 +203,7 @@ def configure(env: "Environment"):
# Get version info for checks below.
cc_version = get_compiler_version(env)
- cc_semver = (int(cc_version["major"]), int(cc_version["minor"]), int(cc_version["patch"]))
+ cc_semver = (cc_version["major"], cc_version["minor"], cc_version["patch"])
if env["lto"] != "none":
# Workaround https://github.com/emscripten-core/emscripten/issues/19781.
diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h
index a4fd75f33b..1653deff80 100644
--- a/platform/web/display_server_web.h
+++ b/platform/web/display_server_web.h
@@ -33,6 +33,8 @@
#include "servers/display_server.h"
+#include "godot_js.h"
+
#include <emscripten.h>
#include <emscripten/html5.h>
@@ -88,28 +90,28 @@ private:
static const char *godot2dom_cursor(DisplayServer::CursorShape p_shape);
// events
- static void fullscreen_change_callback(int p_fullscreen);
- static int mouse_button_callback(int p_pressed, int p_button, double p_x, double p_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);
- static int mouse_wheel_callback(double p_delta_x, double p_delta_y);
- static void touch_callback(int p_type, int p_count);
- static void key_callback(int p_pressed, int p_repeat, int p_modifiers);
- static void vk_input_text_callback(const char *p_text, int p_cursor);
- static void gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid);
+ WASM_EXPORT 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);
+ WASM_EXPORT 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);
+ WASM_EXPORT 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);
+ WASM_EXPORT static void vk_input_text_callback(const char *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);
void process_joypads();
- static void _js_utterance_callback(int p_event, int p_id, int p_pos);
+ 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);
- static void request_quit_callback();
- static void window_blur_callback();
- static void update_voices_callback(int p_size, const char **p_voice);
- static void update_clipboard_callback(const char *p_text);
- static void send_window_event_callback(int p_notification);
- static void drop_files_js_callback(char **p_filev, int p_filec);
+ 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 3341cf8a67..f172148bf9 100644
--- a/platform/web/godot_js.h
+++ b/platform/web/godot_js.h
@@ -31,6 +31,8 @@
#ifndef GODOT_JS_H
#define GODOT_JS_H
+#define WASM_EXPORT __attribute__((visibility("default")))
+
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/platform/web/javascript_bridge_singleton.cpp b/platform/web/javascript_bridge_singleton.cpp
index 45bce1b480..41206f14a5 100644
--- a/platform/web/javascript_bridge_singleton.cpp
+++ b/platform/web/javascript_bridge_singleton.cpp
@@ -68,11 +68,11 @@ private:
int _js_id = 0;
Callable _callable;
- static int _variant2js(const void **p_args, int p_pos, godot_js_wrapper_ex *r_val, void **p_lock);
- static void _free_lock(void **p_lock, int p_type);
- static Variant _js2variant(int p_type, godot_js_wrapper_ex *p_val);
- static void *_alloc_variants(int p_size);
- static void _callback(void *p_ref, int p_arg_id, int p_argc);
+ WASM_EXPORT static int _variant2js(const void **p_args, int p_pos, godot_js_wrapper_ex *r_val, void **p_lock);
+ 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);
protected:
bool _set(const StringName &p_name, const Variant &p_value) override;
@@ -289,7 +289,7 @@ Ref<JavaScriptObject> JavaScriptBridge::get_interface(const String &p_interface)
Variant JavaScriptBridge::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 1) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 0;
+ r_error.expected = 1;
return Ref<JavaScriptObject>();
}
if (p_args[0]->get_type() != Variant::STRING) {
diff --git a/platform/web/js/libs/library_godot_javascript_singleton.js b/platform/web/js/libs/library_godot_javascript_singleton.js
index cbe59230ee..1764c9a026 100644
--- a/platform/web/js/libs/library_godot_javascript_singleton.js
+++ b/platform/web/js/libs/library_godot_javascript_singleton.js
@@ -210,7 +210,7 @@ const GodotJSWrapper = {
// This is safe! JavaScript is single threaded (and using it in threads is not a good idea anyway).
GodotJSWrapper.cb_ret = null;
const args = Array.from(arguments);
- const argsProxy = GodotJSWrapper.MyProxy(args);
+ const argsProxy = new GodotJSWrapper.MyProxy(args);
func(p_ref, argsProxy.get_id(), args.length);
argsProxy.unref();
const ret = GodotJSWrapper.cb_ret;
diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp
index f038f0248a..186e4abf92 100644
--- a/platform/web/os_web.cpp
+++ b/platform/web/os_web.cpp
@@ -132,6 +132,10 @@ int OS_Web::get_processor_count() const {
return godot_js_os_hw_concurrency_get();
}
+String OS_Web::get_unique_id() const {
+ ERR_FAIL_V_MSG("", "OS::get_unique_id() is not available on the Web platform.");
+}
+
bool OS_Web::_check_internal_feature_support(const String &p_feature) {
if (p_feature == "web") {
return true;
diff --git a/platform/web/os_web.h b/platform/web/os_web.h
index b9570f9ca1..f262337f00 100644
--- a/platform/web/os_web.h
+++ b/platform/web/os_web.h
@@ -33,6 +33,8 @@
#include "audio_driver_web.h"
+#include "godot_js.h"
+
#include "core/input/input.h"
#include "drivers/unix/os_unix.h"
#include "servers/audio_server.h"
@@ -48,11 +50,11 @@ class OS_Web : public OS_Unix {
bool idb_needs_sync = false;
bool pwa_is_waiting = false;
- static void main_loop_callback();
+ WASM_EXPORT static void main_loop_callback();
- static void file_access_close_callback(const String &p_file, int p_flags);
- static void fs_sync_callback();
- static void update_pwa_state_callback();
+ WASM_EXPORT static void file_access_close_callback(const String &p_file, int p_flags);
+ WASM_EXPORT static void fs_sync_callback();
+ WASM_EXPORT static void update_pwa_state_callback();
protected:
void initialize() override;
@@ -83,6 +85,7 @@ public:
int get_process_id() const override;
bool is_process_running(const ProcessID &p_pid) const override;
int get_processor_count() const override;
+ String get_unique_id() const override;
int get_default_thread_pool_size() const override { return 1; }
String get_executable_path() const override;
diff --git a/platform/windows/crash_handler_windows.h b/platform/windows/crash_handler_windows.h
index ef5831f10c..3871210977 100644
--- a/platform/windows/crash_handler_windows.h
+++ b/platform/windows/crash_handler_windows.h
@@ -35,7 +35,7 @@
#include <windows.h>
// Crash handler exception only enabled with MSVC
-#if defined(DEBUG_ENABLED) && defined(MSVC)
+#if defined(DEBUG_ENABLED) && defined(_MSC_VER)
#define CRASH_HANDLER_EXCEPTION 1
extern DWORD CrashHandlerException(EXCEPTION_POINTERS *ep);
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 340b95e4ab..7caa0153d7 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -385,7 +385,6 @@ def configure_msvc(env, vcvars_msvc_config):
"WINMIDI_ENABLED",
"TYPED_METHOD_BIND",
"WIN32",
- "MSVC",
"WINVER=%s" % env["target_win_version"],
"_WIN32_WINNT=%s" % env["target_win_version"],
]
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 113f777964..ded80ba5f1 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -222,24 +222,45 @@ void DisplayServerWindows::tts_stop() {
Error DisplayServerWindows::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
_THREAD_SAFE_METHOD_
+ ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED);
+
Vector<Char16String> filter_names;
Vector<Char16String> filter_exts;
for (const String &E : p_filters) {
Vector<String> tokens = E.split(";");
- if (tokens.size() == 2) {
- filter_exts.push_back(tokens[0].strip_edges().utf16());
- filter_names.push_back(tokens[1].strip_edges().utf16());
- } else if (tokens.size() == 1) {
- filter_exts.push_back(tokens[0].strip_edges().utf16());
- filter_names.push_back(tokens[0].strip_edges().utf16());
+ if (tokens.size() >= 1) {
+ String flt = tokens[0].strip_edges();
+ int filter_slice_count = flt.get_slice_count(",");
+ Vector<String> exts;
+ for (int j = 0; j < filter_slice_count; j++) {
+ String str = (flt.get_slice(",", j).strip_edges());
+ if (!str.is_empty()) {
+ exts.push_back(str);
+ }
+ }
+ if (!exts.is_empty()) {
+ String str = String(";").join(exts);
+ filter_exts.push_back(str.utf16());
+ if (tokens.size() == 2) {
+ filter_names.push_back(tokens[1].strip_edges().utf16());
+ } else {
+ filter_names.push_back(str.utf16());
+ }
+ }
}
}
+ if (filter_names.is_empty()) {
+ filter_exts.push_back(String("*.*").utf16());
+ filter_names.push_back(RTR("All Files").utf16());
+ }
Vector<COMDLG_FILTERSPEC> filters;
for (int i = 0; i < filter_names.size(); i++) {
filters.push_back({ (LPCWSTR)filter_names[i].ptr(), (LPCWSTR)filter_exts[i].ptr() });
}
+ WindowID prev_focus = last_focused_window;
+
HRESULT hr = S_OK;
IFileDialog *pfd = nullptr;
if (p_mode == FILE_DIALOG_MODE_SAVE_FILE) {
@@ -285,6 +306,9 @@ Error DisplayServerWindows::file_dialog_show(const String &p_title, const String
}
hr = pfd->Show(windows[window_id].hWnd);
+ UINT index = 0;
+ pfd->GetFileTypeIndex(&index);
+
if (SUCCEEDED(hr)) {
Vector<String> file_names;
@@ -324,22 +348,27 @@ Error DisplayServerWindows::file_dialog_show(const String &p_title, const String
if (!p_callback.is_null()) {
Variant v_status = true;
Variant v_files = file_names;
- Variant *v_args[2] = { &v_status, &v_files };
+ Variant v_index = index;
+ Variant *v_args[3] = { &v_status, &v_files, &v_index };
Variant ret;
Callable::CallError ce;
- p_callback.callp((const Variant **)&v_args, 2, ret, ce);
+ p_callback.callp((const Variant **)&v_args, 3, ret, ce);
}
} else {
if (!p_callback.is_null()) {
Variant v_status = false;
Variant v_files = Vector<String>();
- Variant *v_args[2] = { &v_status, &v_files };
+ Variant v_index = index;
+ Variant *v_args[3] = { &v_status, &v_files, &v_index };
Variant ret;
Callable::CallError ce;
- p_callback.callp((const Variant **)&v_args, 2, ret, ce);
+ p_callback.callp((const Variant **)&v_args, 3, ret, ce);
}
}
pfd->Release();
+ if (prev_focus != INVALID_WINDOW_ID) {
+ callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus);
+ }
return OK;
} else {
@@ -1191,6 +1220,51 @@ void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_wi
SetWindowTextW(windows[p_window].hWnd, (LPCWSTR)(p_title.utf16().get_data()));
}
+Size2i DisplayServerWindows::window_get_title_size(const String &p_title, WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ Size2i size;
+ ERR_FAIL_COND_V(!windows.has(p_window), size);
+
+ const WindowData &wd = windows[p_window];
+ if (wd.fullscreen || wd.minimized || wd.borderless) {
+ return size;
+ }
+
+ HDC hdc = GetDCEx(wd.hWnd, NULL, DCX_WINDOW);
+ if (hdc) {
+ Char16String s = p_title.utf16();
+ SIZE text_size;
+ if (GetTextExtentPoint32W(hdc, (LPCWSTR)(s.get_data()), s.length(), &text_size)) {
+ size.x = text_size.cx;
+ size.y = text_size.cy;
+ }
+
+ ReleaseDC(wd.hWnd, hdc);
+ }
+ RECT rect;
+ if (DwmGetWindowAttribute(wd.hWnd, DWMWA_CAPTION_BUTTON_BOUNDS, &rect, sizeof(RECT)) == S_OK) {
+ if (rect.right - rect.left > 0) {
+ ClientToScreen(wd.hWnd, (POINT *)&rect.left);
+ ClientToScreen(wd.hWnd, (POINT *)&rect.right);
+
+ if (win81p_PhysicalToLogicalPointForPerMonitorDPI) {
+ win81p_PhysicalToLogicalPointForPerMonitorDPI(0, (POINT *)&rect.left);
+ win81p_PhysicalToLogicalPointForPerMonitorDPI(0, (POINT *)&rect.right);
+ }
+
+ size.x += (rect.right - rect.left);
+ size.y = MAX(size.y, rect.bottom - rect.top);
+ }
+ }
+ if (icon.is_valid()) {
+ size.x += 32;
+ } else {
+ size.x += 16;
+ }
+ return size;
+}
+
void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {
_THREAD_SAFE_METHOD_
@@ -2407,7 +2481,7 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) {
f->seek(pos);
f->get_buffer((uint8_t *)&data_big.write[0], bytecount_big);
HICON icon_big = CreateIconFromResource((PBYTE)&data_big.write[0], bytecount_big, TRUE, 0x00030000);
- ERR_FAIL_COND_MSG(!icon_big, "Could not create " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
+ ERR_FAIL_NULL_MSG(icon_big, "Could not create " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
// Read the small icon.
DWORD bytecount_small = icon_dir->idEntries[small_icon_index].dwBytesInRes;
@@ -2417,7 +2491,7 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) {
f->seek(pos);
f->get_buffer((uint8_t *)&data_small.write[0], bytecount_small);
HICON icon_small = CreateIconFromResource((PBYTE)&data_small.write[0], bytecount_small, TRUE, 0x00030000);
- ERR_FAIL_COND_MSG(!icon_small, "Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
+ ERR_FAIL_NULL_MSG(icon_small, "Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
// Online tradition says to be sure last error is cleared and set the small icon first.
int err = 0;
@@ -4385,6 +4459,7 @@ bool DisplayServerWindows::winink_available = false;
GetPointerTypePtr DisplayServerWindows::win8p_GetPointerType = nullptr;
GetPointerPenInfoPtr DisplayServerWindows::win8p_GetPointerPenInfo = nullptr;
LogicalToPhysicalPointForPerMonitorDPIPtr DisplayServerWindows::win81p_LogicalToPhysicalPointForPerMonitorDPI = nullptr;
+PhysicalToLogicalPointForPerMonitorDPIPtr DisplayServerWindows::win81p_PhysicalToLogicalPointForPerMonitorDPI = nullptr;
typedef enum _SHC_PROCESS_DPI_AWARENESS {
SHC_PROCESS_DPI_UNAWARE = 0,
@@ -4522,6 +4597,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType");
win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo");
win81p_LogicalToPhysicalPointForPerMonitorDPI = (LogicalToPhysicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "LogicalToPhysicalPointForPerMonitorDPI");
+ win81p_PhysicalToLogicalPointForPerMonitorDPI = (PhysicalToLogicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "PhysicalToLogicalPointForPerMonitorDPI");
winink_available = win8p_GetPointerType && win8p_GetPointerPenInfo;
}
@@ -4579,9 +4655,22 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
// Init context and rendering device
#if defined(GLES3_ENABLED)
- if (rendering_driver == "opengl3") {
- int gl_version = detect_wgl_version();
- if (gl_version < 30003) {
+ bool fallback = GLOBAL_GET("rendering/gl_compatibility/fallback_to_angle");
+ if (fallback && (rendering_driver == "opengl3")) {
+ Dictionary gl_info = detect_wgl();
+
+ bool force_angle = false;
+
+ Array device_list = GLOBAL_GET("rendering/gl_compatibility/force_angle_on_devices");
+ for (int i = 0; i < device_list.size(); i++) {
+ const Dictionary &device = device_list[i];
+ if (device.has("vendor") && device.has("name") && device["vendor"].operator String().to_upper() == gl_info["vendor"].operator String().to_upper() && (device["name"] == "*" || device["name"].operator String().to_upper() == gl_info["name"].operator String().to_upper())) {
+ force_angle = true;
+ break;
+ }
+ }
+
+ if (force_angle || (gl_info["version"].operator int() < 30003)) {
WARN_PRINT("Your video card drivers seem not to support the required OpenGL 3.3 version, switching to ANGLE.");
rendering_driver = "opengl3_angle";
}
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 28f2f7e6ff..48c8c20280 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -262,6 +262,7 @@ typedef struct tagPOINTER_PEN_INFO {
typedef BOOL(WINAPI *GetPointerTypePtr)(uint32_t p_id, POINTER_INPUT_TYPE *p_type);
typedef BOOL(WINAPI *GetPointerPenInfoPtr)(uint32_t p_id, POINTER_PEN_INFO *p_pen_info);
typedef BOOL(WINAPI *LogicalToPhysicalPointForPerMonitorDPIPtr)(HWND hwnd, LPPOINT lpPoint);
+typedef BOOL(WINAPI *PhysicalToLogicalPointForPerMonitorDPIPtr)(HWND hwnd, LPPOINT lpPoint);
typedef struct {
BYTE bWidth; // Width, in pixels, of the image
@@ -309,6 +310,7 @@ class DisplayServerWindows : public DisplayServer {
// DPI conversion API
static LogicalToPhysicalPointForPerMonitorDPIPtr win81p_LogicalToPhysicalPointForPerMonitorDPI;
+ static PhysicalToLogicalPointForPerMonitorDPIPtr win81p_PhysicalToLogicalPointForPerMonitorDPI;
void _update_tablet_ctx(const String &p_old_driver, const String &p_new_driver);
String tablet_driver;
@@ -569,6 +571,7 @@ public:
virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual Size2i window_get_title_size(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID) override;
virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override;
diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp
index 2e41946eb2..b350786d11 100644
--- a/platform/windows/gl_manager_windows_native.cpp
+++ b/platform/windows/gl_manager_windows_native.cpp
@@ -88,6 +88,7 @@ typedef int(__cdecl *NvAPI_DRS_CreateApplication_t)(NvDRSSessionHandle, NvDRSPro
typedef int(__cdecl *NvAPI_DRS_SaveSettings_t)(NvDRSSessionHandle);
typedef int(__cdecl *NvAPI_DRS_SetSetting_t)(NvDRSSessionHandle, NvDRSProfileHandle, NVDRS_SETTING *);
typedef int(__cdecl *NvAPI_DRS_FindProfileByName_t)(NvDRSSessionHandle, NvAPI_UnicodeString, NvDRSProfileHandle *);
+typedef int(__cdecl *NvAPI_DRS_FindApplicationByName_t)(NvDRSSessionHandle, NvAPI_UnicodeString, NvDRSProfileHandle *, NVDRS_APPLICATION *);
NvAPI_GetErrorMessage_t NvAPI_GetErrorMessage__;
static bool nvapi_err_check(const char *msg, int status) {
@@ -138,6 +139,7 @@ void GLManagerNative_Windows::_nvapi_disable_threaded_optimization() {
NvAPI_DRS_SaveSettings_t NvAPI_DRS_SaveSettings = (NvAPI_DRS_SaveSettings_t)NvAPI_QueryInterface(0xFCBC7E14);
NvAPI_DRS_SetSetting_t NvAPI_DRS_SetSetting = (NvAPI_DRS_SetSetting_t)NvAPI_QueryInterface(0x577DD202);
NvAPI_DRS_FindProfileByName_t NvAPI_DRS_FindProfileByName = (NvAPI_DRS_FindProfileByName_t)NvAPI_QueryInterface(0x7E4A9A0B);
+ NvAPI_DRS_FindApplicationByName_t NvAPI_DRS_FindApplicationByName = (NvAPI_DRS_FindApplicationByName_t)NvAPI_QueryInterface(0xEEE566B2);
if (!nvapi_err_check("NVAPI: Init failed", NvAPI_Initialize())) {
return;
@@ -172,9 +174,9 @@ void GLManagerNative_Windows::_nvapi_disable_threaded_optimization() {
NvDRSProfileHandle profile_handle = 0;
- int status = NvAPI_DRS_FindProfileByName(session_handle, (NvU16 *)(app_profile_name_u16.ptrw()), &profile_handle);
+ int profile_status = NvAPI_DRS_FindProfileByName(session_handle, (NvU16 *)(app_profile_name_u16.ptrw()), &profile_handle);
- if (status != 0) {
+ if (profile_status != 0) {
print_verbose("NVAPI: Profile not found, creating....");
NVDRS_PROFILE profile_info;
@@ -187,9 +189,17 @@ void GLManagerNative_Windows::_nvapi_disable_threaded_optimization() {
NvAPI_Unload();
return;
}
+ }
+
+ NvDRSProfileHandle app_profile_handle = 0;
+ NVDRS_APPLICATION_V4 app;
+ app.version = NVDRS_APPLICATION_VER_V4;
+
+ int app_status = NvAPI_DRS_FindApplicationByName(session_handle, (NvU16 *)(app_executable_name_u16.ptrw()), &app_profile_handle, &app);
+
+ if (app_status != 0) {
+ print_verbose("NVAPI: Application not found, adding to profile...");
- NVDRS_APPLICATION_V4 app;
- app.version = NVDRS_APPLICATION_VER_V4;
app.isPredefined = 0;
app.isMetro = 1;
app.isCommandLine = 1;
diff --git a/platform/windows/platform_windows_builders.py b/platform/windows/platform_windows_builders.py
index b522a75a9c..51652fa814 100644
--- a/platform/windows/platform_windows_builders.py
+++ b/platform/windows/platform_windows_builders.py
@@ -5,14 +5,24 @@ All such functions are invoked in a subprocess on Windows to prevent build flaki
"""
import os
from detect import get_mingw_bin_prefix
+from detect import try_cmd
from platform_methods import subprocess_main
def make_debug_mingw(target, source, env):
mingw_bin_prefix = get_mingw_bin_prefix(env["mingw_prefix"], env["arch"])
- os.system(mingw_bin_prefix + "objcopy --only-keep-debug {0} {0}.debugsymbols".format(target[0]))
- os.system(mingw_bin_prefix + "strip --strip-debug --strip-unneeded {0}".format(target[0]))
- os.system(mingw_bin_prefix + "objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(target[0]))
+ if try_cmd("objcopy --version", env["mingw_prefix"], env["arch"]):
+ os.system(mingw_bin_prefix + "objcopy --only-keep-debug {0} {0}.debugsymbols".format(target[0]))
+ else:
+ os.system("objcopy --only-keep-debug {0} {0}.debugsymbols".format(target[0]))
+ if try_cmd("strip --version", env["mingw_prefix"], env["arch"]):
+ os.system(mingw_bin_prefix + "strip --strip-debug --strip-unneeded {0}".format(target[0]))
+ else:
+ os.system("strip --strip-debug --strip-unneeded {0}".format(target[0]))
+ if try_cmd("objcopy --version", env["mingw_prefix"], env["arch"]):
+ os.system(mingw_bin_prefix + "objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(target[0]))
+ else:
+ os.system("objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(target[0]))
if __name__ == "__main__":
diff --git a/platform/windows/wgl_detect_version.cpp b/platform/windows/wgl_detect_version.cpp
index 264cd525c5..49da4b58c7 100644
--- a/platform/windows/wgl_detect_version.cpp
+++ b/platform/windows/wgl_detect_version.cpp
@@ -35,6 +35,7 @@
#include "core/string/print_string.h"
#include "core/string/ustring.h"
+#include "core/variant/dictionary.h"
#include <windows.h>
@@ -64,9 +65,11 @@ typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int
typedef void *(APIENTRY *PFNWGLGETPROCADDRESS)(LPCSTR);
typedef const char *(APIENTRY *PFNWGLGETSTRINGPROC)(unsigned int);
-int detect_wgl_version() {
- int major = 0;
- int minor = 0;
+Dictionary detect_wgl() {
+ Dictionary gl_info;
+ gl_info["version"] = 0;
+ gl_info["vendor"] = String();
+ gl_info["name"] = String();
PFNWGLCREATECONTEXT gd_wglCreateContext;
PFNWGLMAKECURRENT gd_wglMakeCurrent;
@@ -75,14 +78,14 @@ int detect_wgl_version() {
HMODULE module = LoadLibraryW(L"opengl32.dll");
if (!module) {
- return 0;
+ return gl_info;
}
gd_wglCreateContext = (PFNWGLCREATECONTEXT)GetProcAddress(module, "wglCreateContext");
gd_wglMakeCurrent = (PFNWGLMAKECURRENT)GetProcAddress(module, "wglMakeCurrent");
gd_wglDeleteContext = (PFNWGLDELETECONTEXT)GetProcAddress(module, "wglDeleteContext");
gd_wglGetProcAddress = (PFNWGLGETPROCADDRESS)GetProcAddress(module, "wglGetProcAddress");
if (!gd_wglCreateContext || !gd_wglMakeCurrent || !gd_wglDeleteContext || !gd_wglGetProcAddress) {
- return 0;
+ return gl_info;
}
LPCWSTR class_name = L"EngineWGLDetect";
@@ -151,8 +154,8 @@ int detect_wgl_version() {
};
const char *version = (const char *)gd_wglGetString(WGL_VERSION);
if (version) {
- const String device_vendor = String::utf8((const char *)gd_wglGetString(WGL_VENDOR)).strip_edges();
- const String device_name = String::utf8((const char *)gd_wglGetString(WGL_RENDERER)).strip_edges();
+ const String device_vendor = String::utf8((const char *)gd_wglGetString(WGL_VENDOR)).strip_edges().trim_suffix(" Corporation");
+ const String device_name = String::utf8((const char *)gd_wglGetString(WGL_RENDERER)).strip_edges().trim_suffix("/PCIe/SSE2");
for (int i = 0; prefixes[i]; i++) {
size_t length = strlen(prefixes[i]);
if (strncmp(version, prefixes[i], length) == 0) {
@@ -160,12 +163,17 @@ int detect_wgl_version() {
break;
}
}
+ int major = 0;
+ int minor = 0;
#ifdef _MSC_VER
sscanf_s(version, "%d.%d", &major, &minor);
#else
sscanf(version, "%d.%d", &major, &minor);
#endif
print_verbose(vformat("Native OpenGL API detected: %d.%d: %s - %s", major, minor, device_vendor, device_name));
+ gl_info["vendor"] = device_vendor;
+ gl_info["name"] = device_name;
+ gl_info["version"] = major * 10000 + minor;
}
}
}
@@ -183,7 +191,7 @@ int detect_wgl_version() {
}
UnregisterClassW(class_name, hInstance);
- return major * 10000 + minor;
+ return gl_info;
}
#endif // WINDOWS_ENABLED && GLES3_ENABLED
diff --git a/platform/windows/wgl_detect_version.h b/platform/windows/wgl_detect_version.h
index 0be2923ba3..c110b1219e 100644
--- a/platform/windows/wgl_detect_version.h
+++ b/platform/windows/wgl_detect_version.h
@@ -33,7 +33,9 @@
#if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED)
-int detect_wgl_version();
+class Dictionary;
+
+Dictionary detect_wgl();
#endif // WINDOWS_ENABLED && GLES3_ENABLED