diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml index 556a87c3d143..813392930030 100644 --- a/.github/workflows/windows_builds.yml +++ b/.github/workflows/windows_builds.yml @@ -8,7 +8,6 @@ env: dev_mode=yes module_text_server_fb_enabled=yes debug_symbols=no - d3d12=yes "angle_libs=${{ github.workspace }}/" "accesskit_sdk_path=${{ github.workspace }}/accesskit-c-0.17.0/" SCONS_CACHE_MSVC_CONFIG: true @@ -73,7 +72,16 @@ jobs: uses: ./.github/actions/godot-deps - name: Download Direct3D 12 SDK components - run: python ./misc/scripts/install_d3d12_sdk_windows.py + shell: sh + id: d3d12-sdk + run: | + if python ./misc/scripts/install_d3d12_sdk_windows.py; then + echo "D3D12_ENABLED=yes" >> "$GITHUB_OUTPUT" + else + echo "::warning::Windows: Direct3D 12 SDK installation failed, building without Direct3D 12 support." + echo "D3D12_ENABLED=no" >> "$GITHUB_OUTPUT" + fi + continue-on-error: true - name: Download pre-built ANGLE static libraries uses: dsaltares/fetch-gh-release-asset@1.1.2 @@ -100,7 +108,7 @@ jobs: - name: Compilation uses: ./.github/actions/godot-build with: - scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} + scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} d3d12=${{ steps.d3d12-sdk.outputs.D3D12_ENABLED }} platform: windows target: ${{ matrix.target }} diff --git a/SConstruct b/SConstruct index a450b04a8e90..bf93e64fd0e8 100644 --- a/SConstruct +++ b/SConstruct @@ -679,17 +679,11 @@ elif methods.using_clang(env): # Apple LLVM versions differ from upstream LLVM version \o/, compare # in https://en.wikipedia.org/wiki/Xcode#Toolchain_versions if methods.is_apple_clang(env): - if cc_version_major < 10: + if cc_version_major < 16: print_error( - "Detected Apple Clang version older than 10, which does not fully " - "support C++17. Supported versions are Apple Clang 10 and later." + "Detected Apple Clang version older than 16, supported versions are Apple Clang 16 (Xcode 16) and later." ) Exit(255) - elif env["debug_paths_relative"] and cc_version_major < 12: - print_warning( - "Apple Clang < 12 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option." - ) - env["debug_paths_relative"] = False else: if cc_version_major < 6: print_error( diff --git a/core/extension/gdextension_library_loader.cpp b/core/extension/gdextension_library_loader.cpp index 0841443eba64..8db6ef390ef4 100644 --- a/core/extension/gdextension_library_loader.cpp +++ b/core/extension/gdextension_library_loader.cpp @@ -195,9 +195,13 @@ Error GDExtensionLibraryLoader::open_library(const String &p_path) { &abs_dependencies_paths, // library_dependencies }; - err = OS::get_singleton()->open_dynamic_library(is_static_library ? String() : abs_path, library, &data); + // Apple has a complex lookup system which goes beyond looking up the filename, so we try that first. + err = OS::get_singleton()->open_dynamic_library(abs_path, library, &data); if (err != OK) { - return err; + err = OS::get_singleton()->open_dynamic_library(String(), library, &data); + if (err != OK) { + return err; + } } return OK; @@ -361,8 +365,6 @@ Error GDExtensionLibraryLoader::parse_gdextension_file(const String &p_path) { return ERR_FILE_NOT_FOUND; } - is_static_library = library_path.ends_with(".a") || library_path.ends_with(".xcframework"); - if (!library_path.is_resource_file() && !library_path.is_absolute_path()) { library_path = p_path.get_base_dir().path_join(library_path); } diff --git a/core/extension/gdextension_library_loader.h b/core/extension/gdextension_library_loader.h index 75ad0ebedf7d..d416c83da796 100644 --- a/core/extension/gdextension_library_loader.h +++ b/core/extension/gdextension_library_loader.h @@ -49,8 +49,6 @@ class GDExtensionLibraryLoader : public GDExtensionLoader { String library_path; String entry_symbol; - bool is_static_library = false; - #ifdef TOOLS_ENABLED bool is_reloadable = false; #endif diff --git a/drivers/apple_embedded/display_server_apple_embedded.h b/drivers/apple_embedded/display_server_apple_embedded.h index df7bcd9b345f..6ef197d2474d 100644 --- a/drivers/apple_embedded/display_server_apple_embedded.h +++ b/drivers/apple_embedded/display_server_apple_embedded.h @@ -230,4 +230,7 @@ class DisplayServerAppleEmbedded : public DisplayServer { void resize_window(CGSize size); virtual void swap_buffers() override {} + + virtual void set_native_icon(const String &p_filename) override; + virtual void set_icon(const Ref &p_icon) override; }; diff --git a/drivers/apple_embedded/display_server_apple_embedded.mm b/drivers/apple_embedded/display_server_apple_embedded.mm index 886a17dcef04..5b5556934b83 100644 --- a/drivers/apple_embedded/display_server_apple_embedded.mm +++ b/drivers/apple_embedded/display_server_apple_embedded.mm @@ -818,3 +818,11 @@ _FORCE_INLINE_ int _convert_utf32_offset_to_utf16(const String &p_existing_text, #endif return DisplayServer::VSYNC_ENABLED; } + +void DisplayServerAppleEmbedded::set_native_icon(const String &p_filename) { + // Not supported on Apple embedded platforms. +} + +void DisplayServerAppleEmbedded::set_icon(const Ref &p_icon) { + // Not supported on Apple embedded platforms. +} diff --git a/drivers/apple_embedded/os_apple_embedded.mm b/drivers/apple_embedded/os_apple_embedded.mm index 07d1f8a270c7..5acfce83ebbe 100644 --- a/drivers/apple_embedded/os_apple_embedded.mm +++ b/drivers/apple_embedded/os_apple_embedded.mm @@ -274,6 +274,11 @@ Rect2 fit_keep_aspect_covered(const Vector2 &p_container, const Vector2 &p_rect) path = get_framework_executable(get_executable_path().get_base_dir().path_join(p_path.get_file().get_basename() + ".framework")); } + if (!FileAccess::exists(path)) { + // Load .dylib from within the executable path. + path = get_framework_executable(get_executable_path().get_base_dir().path_join(p_path.get_file().get_basename() + ".dylib")); + } + if (!FileAccess::exists(path)) { // Load .dylib or framework from a standard iOS location. path = get_framework_executable(get_executable_path().get_base_dir().path_join("Frameworks").path_join(p_path.get_file())); @@ -284,8 +289,16 @@ Rect2 fit_keep_aspect_covered(const Vector2 &p_container, const Vector2 &p_rect) path = get_framework_executable(get_executable_path().get_base_dir().path_join("Frameworks").path_join(p_path.get_file().get_basename() + ".framework")); } - ERR_FAIL_COND_V(!FileAccess::exists(path), ERR_FILE_NOT_FOUND); + if (!FileAccess::exists(path)) { + // Load .dylib from a standard iOS location. + path = get_framework_executable(get_executable_path().get_base_dir().path_join("Frameworks").path_join(p_path.get_file().get_basename() + ".dylib")); + } + if (!FileAccess::exists(path) && (p_path.ends_with(".a") || p_path.ends_with(".xcframework"))) { + path = String(); // Try loading static library. + } else { + ERR_FAIL_COND_V(!FileAccess::exists(path), ERR_FILE_NOT_FOUND); + } p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror())); diff --git a/drivers/coreaudio/audio_driver_coreaudio.mm b/drivers/coreaudio/audio_driver_coreaudio.mm index 6f9f690d5280..fe81283040b7 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.mm +++ b/drivers/coreaudio/audio_driver_coreaudio.mm @@ -394,6 +394,11 @@ UInt32 flag = 1; result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)); ERR_FAIL_COND_V(result != noErr, FAILED); +#ifdef MACOS_ENABLED + flag = 0; + result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)); + ERR_FAIL_COND_V(result != noErr, FAILED); +#endif UInt32 size; #ifdef MACOS_ENABLED diff --git a/drivers/unix/file_access_unix_pipe.cpp b/drivers/unix/file_access_unix_pipe.cpp index b4f3cbd56d6d..e808c5ded239 100644 --- a/drivers/unix/file_access_unix_pipe.cpp +++ b/drivers/unix/file_access_unix_pipe.cpp @@ -41,6 +41,11 @@ #include #include #include +#include + +#ifndef sighandler_t +typedef typeof(void(int)) *sighandler_t; +#endif Error FileAccessUnixPipe::open_existing(int p_rfd, int p_wfd, bool p_blocking) { // Open pipe using handles created by pipe(fd) call in the OS.execute_with_pipe. @@ -165,7 +170,11 @@ bool FileAccessUnixPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) { ERR_FAIL_COND_V_MSG(fd[1] < 0, false, "Pipe must be opened before use."); ERR_FAIL_COND_V(!p_src && p_length > 0, false); - if (::write(fd[1], p_src, p_length) != (ssize_t)p_length) { + sighandler_t sig_pipe = signal(SIGPIPE, SIG_IGN); + ssize_t ret = ::write(fd[1], p_src, p_length); + signal(SIGPIPE, sig_pipe); + + if (ret != (ssize_t)p_length) { last_error = ERR_FILE_CANT_WRITE; return false; } else { diff --git a/editor/export/editor_export_platform_apple_embedded.cpp b/editor/export/editor_export_platform_apple_embedded.cpp index 29de9267c973..66b8f78f3b7f 100644 --- a/editor/export/editor_export_platform_apple_embedded.cpp +++ b/editor/export/editor_export_platform_apple_embedded.cpp @@ -235,6 +235,10 @@ bool EditorExportPlatformAppleEmbedded::get_export_option_visibility(const Edito p_option == "application/signature") { return advanced_options_enabled; } + if (p_option == "capabilities/performance_a12") { + String rendering_method = get_project_setting(Ref(p_preset), "rendering/renderer/rendering_method.mobile"); + return !(rendering_method == "forward_plus" || rendering_method == "mobile"); + } return true; } @@ -501,6 +505,7 @@ void EditorExportPlatformAppleEmbedded::_fix_config_file(const Ref capabilities_list = p_config.capabilities; + String rendering_method = get_project_setting(p_preset, "rendering/renderer/rendering_method.mobile"); if ((bool)p_preset->get("capabilities/access_wifi") && !capabilities_list.has("wifi")) { capabilities_list.push_back("wifi"); @@ -508,7 +513,7 @@ void EditorExportPlatformAppleEmbedded::_fix_config_file(const Refget("capabilities/performance_gaming_tier") && !capabilities_list.has("iphone-performance-gaming-tier")) { capabilities_list.push_back("iphone-performance-gaming-tier"); } - if ((bool)p_preset->get("capabilities/performance_a12") && !capabilities_list.has("iphone-ipad-minimum-performance-a12")) { + if (((bool)p_preset->get("capabilities/performance_a12") || rendering_method == "forward_plus" || rendering_method == "mobile") && !capabilities_list.has("iphone-ipad-minimum-performance-a12")) { capabilities_list.push_back("iphone-ipad-minimum-performance-a12"); } for (int idx = 0; idx < capabilities_list.size(); idx++) { diff --git a/editor/export/gdextension_export_plugin.h b/editor/export/gdextension_export_plugin.h index 9087725c1cd3..07e2692a96e3 100644 --- a/editor/export/gdextension_export_plugin.h +++ b/editor/export/gdextension_export_plugin.h @@ -149,7 +149,8 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p } } - Vector dependencies_shared_objects = GDExtensionLibraryLoader::find_extension_dependencies(p_path, config, [p_features](String p_feature) { return p_features.has(p_feature); }); + Vector dependencies_shared_objects = GDExtensionLibraryLoader::find_extension_dependencies( + p_path, config, [features_wo_arch, arch_tag](String p_feature) { return features_wo_arch.has(p_feature) || (p_feature == arch_tag); }); for (const SharedObject &shared_object : dependencies_shared_objects) { _add_shared_object(shared_object); } diff --git a/editor/export/shader_baker_export_plugin.cpp b/editor/export/shader_baker_export_plugin.cpp index b1f86544e71d..1257715aebec 100644 --- a/editor/export/shader_baker_export_plugin.cpp +++ b/editor/export/shader_baker_export_plugin.cpp @@ -54,20 +54,13 @@ bool ShaderBakerExportPlugin::_is_active(const Vector &p_features) const return RendererSceneRenderRD::get_singleton() != nullptr && RendererRD::MaterialStorage::get_singleton() != nullptr && p_features.has("shader_baker"); } -bool ShaderBakerExportPlugin::_initialize_container_format(const Ref &p_platform, const Vector &p_features, const Ref &p_preset) { - Variant driver_variant = GLOBAL_GET("rendering/rendering_device/driver." + p_platform->get_os_name().to_lower()); - if (!driver_variant.is_string()) { - driver_variant = GLOBAL_GET("rendering/rendering_device/driver"); - if (!driver_variant.is_string()) { - return false; - } - } - - shader_container_driver = driver_variant; +bool ShaderBakerExportPlugin::_initialize_container_format(const Ref &p_platform, const Ref &p_preset) { + shader_container_driver = p_preset->get_project_setting("rendering/rendering_device/driver"); + ERR_FAIL_COND_V_MSG(shader_container_driver.is_empty(), false, "Invalid `rendering/rendering_device/driver` setting, disabling shader baking."); for (Ref platform : platforms) { if (platform->matches_driver(shader_container_driver)) { - shader_container_format = platform->create_shader_container_format(p_platform, get_export_preset()); + shader_container_format = platform->create_shader_container_format(p_platform, p_preset); ERR_FAIL_NULL_V_MSG(shader_container_format, false, "Unable to create shader container format for the export platform."); return true; } @@ -99,7 +92,7 @@ bool ShaderBakerExportPlugin::_begin_customize_resources(const Ref &p_features) const; - virtual bool _initialize_container_format(const Ref &p_platform, const Vector &p_features, const Ref &p_preset); + virtual bool _initialize_container_format(const Ref &p_platform, const Ref &p_preset); virtual void _cleanup_container_format(); virtual bool _initialize_cache_directory(); virtual bool _begin_customize_resources(const Ref &p_platform, const Vector &p_features) override; diff --git a/methods.py b/methods.py index 25fc5813a176..659ca00de7be 100644 --- a/methods.py +++ b/methods.py @@ -724,11 +724,14 @@ def get_compiler_version(env): version = subprocess.check_output(args, encoding="utf-8").strip() for line in version.splitlines(): split = line.split(":", 1) - if split[0] == "catalog_productDisplayVersion": - sem_ver = split[1].split(".") - ret["major"] = int(sem_ver[0]) - ret["minor"] = int(sem_ver[1]) - ret["patch"] = int(sem_ver[2].split()[0]) + if split[0] == "catalog_productSemanticVersion": + match = re.match(r" ([0-9]*).([0-9]*).([0-9]*)-?([a-z0-9.+]*)", split[1]) + if match is not None: + ret["major"] = int(match.group(1)) + ret["minor"] = int(match.group(2)) + ret["patch"] = int(match.group(3)) + # Semantic suffix (i.e. insiders+11116.177) + ret["metadata2"] = match.group(4) # Could potentially add section for determining preview version, but # that can wait until metadata is actually used for something. if split[0] == "catalog_buildVersion": diff --git a/misc/dist/linux/org.godotengine.Godot.appdata.xml b/misc/dist/linux/org.godotengine.Godot.appdata.xml index 9c0cbfcc1233..95ec4fee3672 100644 --- a/misc/dist/linux/org.godotengine.Godot.appdata.xml +++ b/misc/dist/linux/org.godotengine.Godot.appdata.xml @@ -20,9 +20,9 @@

- + 3D project loaded in the Godot Engine editor - https://download.tuxfamily.org/godotengine/media/screenshots/editor_3d_fracteed-720p.jpg + https://docs.godotengine.org/en/stable/_images/introduction_editor.webp https://godotengine.org diff --git a/modules/basis_universal/SCsub b/modules/basis_universal/SCsub index 6eac52984ffd..da24460d3175 100644 --- a/modules/basis_universal/SCsub +++ b/modules/basis_universal/SCsub @@ -60,6 +60,10 @@ if env["builtin_zstd"]: env_thirdparty = env_basisu.Clone() env_thirdparty.disable_warnings() +if not env.msvc: + # Required by upstream for GCC. Enabling also for LLVM-based compilers to be consistent. + env_thirdparty.Append(CCFLAGS=["-fno-strict-aliasing"]) + # Disable unneeded features to reduce binary size. # env_thirdparty.Append( diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index c303edf1e61e..4cd2513935fe 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -711,6 +711,14 @@ void DisplayServerAndroid::notify_surface_changed(int p_width, int p_height) { } } +void DisplayServerAndroid::notify_application_paused() { +#if defined(RD_ENABLED) + if (rendering_device) { + rendering_device->update_pipeline_cache(); + } +#endif // defined(RD_ENABLED) +} + DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) { rendering_driver = p_rendering_driver; diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index 5b09128ee3c4..91c478cbba3d 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -245,6 +245,7 @@ class DisplayServerAndroid : public DisplayServer { void reset_window(); void notify_surface_changed(int p_width, int p_height); + void notify_application_paused(); virtual Point2i mouse_get_position() const override; virtual BitField mouse_get_button_state() const override; diff --git a/platform/android/editor/game_menu_utils_jni.cpp b/platform/android/editor/game_menu_utils_jni.cpp index 0049ee94e1a6..5e6955e3f26e 100644 --- a/platform/android/editor/game_menu_utils_jni.cpp +++ b/platform/android/editor/game_menu_utils_jni.cpp @@ -35,7 +35,7 @@ #include "editor/editor_node.h" #include "editor/run/game_view_plugin.h" -static GameViewPlugin *_get_game_view_plugin() { +_FORCE_INLINE_ static GameViewPlugin *_get_game_view_plugin() { ERR_FAIL_NULL_V(EditorNode::get_singleton(), nullptr); ERR_FAIL_NULL_V(EditorNode::get_singleton()->get_editor_main_screen(), nullptr); return Object::cast_to(EditorNode::get_singleton()->get_editor_main_screen()->get_plugin_by_name("Game")); diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index b0155879e2df..97eee2926be4 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -97,7 +97,7 @@ android { defaultConfig { // The default ignore pattern for the 'assets' directory includes hidden files and directories which are used by Godot projects. aaptOptions { - ignoreAssetsPattern "!.svn:!.git:!.gitignore:!.ds_store:!*.scc:_*:!CVS:!thumbs.db:!picasa.ini:!*~" + ignoreAssetsPattern "!.svn:!.git:!.gitignore:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~" } ndk { diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index 30e4251de886..623b99acccff 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -7,7 +7,7 @@ ext.versions = [ minSdk : 24, // Also update 'platform/android/export/export_plugin.cpp#DEFAULT_TARGET_SDK_VERSION' targetSdk : 35, - buildTools : '35.0.0', + buildTools : '35.0.1', kotlinVersion : '2.1.20', fragmentVersion : '1.8.6', nexusPublishVersion: '1.3.0', 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 529c9eae69d8..85087400cffc 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -107,6 +107,8 @@ class Godot private constructor(val context: Context) { } } + private const val EXIT_RENDERER_TIMEOUT_IN_MS = 1500L + // Supported build flavors private const val EDITOR_FLAVOR = "editor" private const val TEMPLATE_FLAVOR = "template" @@ -390,7 +392,7 @@ class Godot private constructor(val context: Context) { } } else { if (rootView.rootWindowInsets != null) { - if (!useImmersive.get() || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)) { + if (!useImmersive.get()) { val windowInsets = WindowInsetsCompat.toWindowInsetsCompat(rootView.rootWindowInsets) val insets = windowInsets.getInsets(getInsetType()) rootView.setPadding(insets.left, insets.top, insets.right, insets.bottom) @@ -399,8 +401,7 @@ class Godot private constructor(val context: Context) { ViewCompat.setOnApplyWindowInsetsListener(rootView) { v: View, insets: WindowInsetsCompat -> v.post { - if (useImmersive.get() && Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { - // Fixes issue where padding remained visible in immersive mode on some devices. + if (useImmersive.get()) { v.setPadding(0, 0, 0, 0) } else { val windowInsets = insets.getInsets(getInsetType()) @@ -736,7 +737,12 @@ class Godot private constructor(val context: Context) { plugin.onMainDestroy() } - renderView?.onActivityDestroyed() + if (renderView?.blockingExitRenderer(EXIT_RENDERER_TIMEOUT_IN_MS) != true) { + Log.w(TAG, "Unable to exit the renderer within $EXIT_RENDERER_TIMEOUT_IN_MS ms... Force quitting the process.") + onGodotTerminating() + forceQuit(0) + } + this.primaryHost = null } @@ -749,7 +755,9 @@ class Godot private constructor(val context: Context) { val newDarkMode = newConfig.uiMode.and(Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES if (darkMode != newDarkMode) { darkMode = newDarkMode - GodotLib.onNightModeChanged() + runOnRenderThread { + GodotLib.onNightModeChanged() + } } } @@ -761,7 +769,9 @@ class Godot private constructor(val context: Context) { plugin.onMainActivityResult(requestCode, resultCode, data) } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - FilePicker.handleActivityResult(context, requestCode, resultCode, data) + runOnRenderThread { + FilePicker.handleActivityResult(context, requestCode, resultCode, data) + } } } @@ -776,11 +786,13 @@ class Godot private constructor(val context: Context) { for (plugin in pluginRegistry.allPlugins) { plugin.onMainRequestPermissionsResult(requestCode, permissions, grantResults) } - for (i in permissions.indices) { - GodotLib.requestPermissionResult( - permissions[i], - grantResults[i] == PackageManager.PERMISSION_GRANTED - ) + runOnRenderThread { + for (i in permissions.indices) { + GodotLib.requestPermissionResult( + permissions[i], + grantResults[i] == PackageManager.PERMISSION_GRANTED + ) + } } } @@ -1089,7 +1101,7 @@ class Godot private constructor(val context: Context) { for (plugin in pluginRegistry.allPlugins) { plugin.onMainBackPressed() } - renderView?.queueOnRenderThread { GodotLib.back() } + runOnRenderThread { GodotLib.back() } } /** diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java index fdd0d544485f..eb50140057db 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java @@ -130,8 +130,8 @@ public void onActivityStarted() { } @Override - public void onActivityDestroyed() { - requestRenderThreadExitAndWait(); + public boolean blockingExitRenderer(long blockingTimeInMs) { + return requestRenderThreadExitAndWait(blockingTimeInMs); } @Override diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java index 9fe4fb20aa7a..7b7beadb0563 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java @@ -56,7 +56,7 @@ public interface GodotRenderView { void onActivityStarted(); - void onActivityDestroyed(); + boolean blockingExitRenderer(long blockingTimeInMs); GodotInputHandler getInputHandler(); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java index 6287065f114b..e30c14d12550 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java @@ -115,8 +115,8 @@ public void onActivityResumed() { } @Override - public void onActivityDestroyed() { - requestRenderThreadExitAndWait(); + public boolean blockingExitRenderer(long blockingTimeInMs) { + return requestRenderThreadExitAndWait(blockingTimeInMs); } @Override diff --git a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java index 6a4e9da699e3..8b25fdfeceb6 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java @@ -604,6 +604,18 @@ protected final void requestRenderThreadExitAndWait() { mGLThread.requestExitAndWait(); } } + + /** + * Requests the render thread to exit and block up to the given timeInMs until it's done. + * + * @return true if the thread exited, false otherwise. + */ + protected final boolean requestRenderThreadExitAndWait(long timeInMs) { + if (mGLThread != null) { + return mGLThread.requestExitAndWait(timeInMs); + } + return false; + } // -- GODOT end -- /** @@ -1796,6 +1808,23 @@ public void requestExitAndWait() { } } + public boolean requestExitAndWait(long timeInMs) { + // Don't call this from GLThread thread or it is a guaranteed deadlock! + synchronized(sGLThreadManager) { + mShouldExit = true; + sGLThreadManager.notifyAll(); + if (!mExited) { + try { + sGLThreadManager.wait(timeInMs); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + + return mExited; + } + } + public void requestReleaseEglContextLocked() { mShouldReleaseEglContext = true; sGLThreadManager.notifyAll(); diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java index a0ec7312911d..770364e5645a 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java @@ -221,7 +221,15 @@ public void setView(final GodotRenderView view) { public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) { /* Let SurfaceView get focus if back key is input. */ if (keyCode == KeyEvent.KEYCODE_BACK) { + // Clear focus from EditText immediately + clearFocus(); + + // Transfer focus to render view mRenderView.getView().requestFocus(); + + // Forward this back key event to the render view's input handler + // since we're no longer the focused view + return mRenderView.getInputHandler().onKeyDown(keyCode, keyEvent); } // When a hardware keyboard is connected, all key events come through so we can route them @@ -248,6 +256,11 @@ public boolean onKeyUp(int keyCode, KeyEvent keyEvent) { return mRenderView.getInputHandler().onKeyUp(keyCode, keyEvent); } + // If this is a BACK key and we don't have focus anymore, forward to render view + if (keyCode == KeyEvent.KEYCODE_BACK && !hasFocus()) { + return mRenderView.getInputHandler().onKeyUp(keyCode, keyEvent); + } + if (needHandlingInGodot(keyCode, keyEvent) && mRenderView.getInputHandler().onKeyUp(keyCode, keyEvent)) { return true; } else { diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/StorageScope.kt b/platform/android/java/lib/src/org/godotengine/godot/io/StorageScope.kt index 574ecd58eb7d..0e8ed71e6954 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/io/StorageScope.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/io/StorageScope.kt @@ -69,6 +69,7 @@ internal enum class StorageScope { private val internalAppDir: String? = context.filesDir.canonicalPath private val internalCacheDir: String? = context.cacheDir.canonicalPath private val externalAppDir: String? = context.getExternalFilesDir(null)?.canonicalPath + private val obbDir: String? = context.obbDir.canonicalPath private val sharedDir : String? = Environment.getExternalStorageDirectory().canonicalPath private val downloadsSharedDir: String? = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).canonicalPath private val documentsSharedDir: String? = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).canonicalPath @@ -127,6 +128,10 @@ internal enum class StorageScope { return APP } + if (obbDir != null && canonicalPathFile.startsWith(obbDir)) { + return APP + } + if (sharedDir != null && canonicalPathFile.startsWith(sharedDir)) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { // Before R, apps had access to shared storage so long as they have the right diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt index 9e30de6a153f..c711fc519f5b 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt @@ -119,6 +119,15 @@ open internal class VkSurfaceView(context: Context) : SurfaceView(context), Surf vkThread.requestExitAndWait() } + /** + * Requests the render thread to exit and block up to the given [timeInMs] until it's done. + * + * @return true if the thread exited, false otherwise. + */ + fun requestRenderThreadExitAndWait(timeInMs: Long): Boolean { + return vkThread.requestExitAndWait(timeInMs) + } + override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { vkThread.onSurfaceChanged(width, height) } diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt index c7cb97d911ed..e5e3b89b5957 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt @@ -32,6 +32,7 @@ package org.godotengine.godot.vulkan import android.util.Log +import java.util.concurrent.TimeUnit import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock @@ -104,13 +105,37 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk try { Log.i(TAG, "Waiting on exit for $name") lockCondition.await() - } catch (ex: InterruptedException) { + } catch (_: InterruptedException) { currentThread().interrupt() } } } } + /** + * Request the thread to exit and block up to the given [timeInMs] until it's done. + * + * @return true if the thread exited, false otherwise. + */ + fun requestExitAndWait(timeInMs: Long): Boolean { + lock.withLock { + shouldExit = true + lockCondition.signalAll() + + var remainingTimeInNanos = TimeUnit.MILLISECONDS.toNanos(timeInMs) + while (!exited && remainingTimeInNanos > 0) { + try { + Log.i(TAG, "Waiting on exit for $name for $remainingTimeInNanos") + remainingTimeInNanos = lockCondition.awaitNanos(remainingTimeInNanos) + } catch (_: InterruptedException) { + currentThread().interrupt() + } + } + + return exited + } + } + /** * Invoked when the app resumes. */ diff --git a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt index 1584c6fd5ce0..0c3956841d6d 100644 --- a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt +++ b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt @@ -21,4 +21,12 @@ target_include_directories(${PROJECT_NAME} ${ANDROID_ROOT_DIR} ${OPENXR_INCLUDE_DIR}) -add_definitions(-DUNIX_ENABLED -DVULKAN_ENABLED -DANDROID_ENABLED -DGLES3_ENABLED -DTOOLS_ENABLED -DDEBUG_ENABLED) +add_definitions( + -DUNIX_ENABLED + -DVULKAN_ENABLED + -DANDROID_ENABLED + -DGLES3_ENABLED + -DTOOLS_ENABLED + -DDEBUG_ENABLED + -DRD_ENABLED +) diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index c7a549f1a851..31920fe53d60 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -605,6 +605,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIE if (os_android->get_main_loop()) { os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_PAUSED); } + + if (DisplayServerAndroid *dsa = Object::cast_to(DisplayServer::get_singleton())) { + dsa->notify_application_paused(); + } } JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_shouldDispatchInputToRenderThread(JNIEnv *env, jclass clazz) { diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 2f495b1539c0..cebf2e49c377 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -73,6 +73,14 @@ String _remove_symlink(const String &dir) { return dir_without_symlink; } +#ifdef TOOLS_ENABLED +_FORCE_INLINE_ static GameViewPlugin *_get_game_view_plugin() { + ERR_FAIL_NULL_V(EditorNode::get_singleton(), nullptr); + ERR_FAIL_NULL_V(EditorNode::get_singleton()->get_editor_main_screen(), nullptr); + return Object::cast_to(EditorNode::get_singleton()->get_editor_main_screen()->get_plugin_by_name("Game")); +} +#endif + class AndroidLogger : public Logger { public: virtual void logv(const char *p_format, va_list p_list, bool p_err) { @@ -346,7 +354,7 @@ void OS_Android::main_loop_begin() { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { - GameViewPlugin *game_view_plugin = Object::cast_to(EditorNode::get_singleton()->get_editor_main_screen()->get_plugin_by_name("Game")); + GameViewPlugin *game_view_plugin = _get_game_view_plugin(); if (game_view_plugin != nullptr) { game_view_plugin->connect("main_screen_changed", callable_mp_static(&OS_Android::_on_main_screen_changed)); } @@ -376,7 +384,7 @@ bool OS_Android::main_loop_iterate(bool *r_should_swap_buffers) { void OS_Android::main_loop_end() { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { - GameViewPlugin *game_view_plugin = Object::cast_to(EditorNode::get_singleton()->get_editor_main_screen()->get_plugin_by_name("Game")); + GameViewPlugin *game_view_plugin = _get_game_view_plugin(); if (game_view_plugin != nullptr) { game_view_plugin->disconnect("main_screen_changed", callable_mp_static(&OS_Android::_on_main_screen_changed)); } diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index 65c85ff82283..8cd81a222d9e 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -301,9 +301,7 @@ def configure(env: "SConsEnvironment"): env.ParseConfig("pkg-config libpcre2-32 --cflags --libs") if not env["builtin_recastnavigation"]: - # No pkgconfig file so far, hardcode default paths. - env.Prepend(CPPPATH=["/usr/include/recastnavigation"]) - env.Append(LIBS=["Recast"]) + env.ParseConfig("pkg-config recastnavigation --cflags --libs") if not env["builtin_embree"] and env["arch"] in ["x86_64", "arm64"]: # No pkgconfig file so far, hardcode expected lib name. diff --git a/platform/linuxbsd/godot_linuxbsd.cpp b/platform/linuxbsd/godot_linuxbsd.cpp index 9a4372e61dab..8bda6d932c13 100644 --- a/platform/linuxbsd/godot_linuxbsd.cpp +++ b/platform/linuxbsd/godot_linuxbsd.cpp @@ -41,6 +41,18 @@ #include #endif +#if defined(__x86_64) || defined(__x86_64__) +void __cpuid(int *r_cpuinfo, int p_info) { + // Note: Some compilers have a buggy `__cpuid` intrinsic, using inline assembly (based on LLVM-20 implementation) instead. + __asm__ __volatile__( + "xchgq %%rbx, %q1;" + "cpuid;" + "xchgq %%rbx, %q1;" + : "=a"(r_cpuinfo[0]), "=r"(r_cpuinfo[1]), "=c"(r_cpuinfo[2]), "=d"(r_cpuinfo[3]) + : "0"(p_info)); +} +#endif + // For export templates, add a section; the exporter will patch it to enclose // the data appended to the executable (bundled PCK). #if !defined(TOOLS_ENABLED) && defined(__GNUC__) @@ -54,6 +66,27 @@ extern "C" const char *pck_section_dummy_call() { #endif int main(int argc, char *argv[]) { +#if defined(__x86_64) || defined(__x86_64__) + int cpuinfo[4]; + __cpuid(cpuinfo, 0x01); + + if (!(cpuinfo[2] & (1 << 20))) { + printf("A CPU with SSE4.2 instruction set support is required.\n"); + + int ret = system("zenity --warning --title \"Godot Engine\" --text \"A CPU with SSE4.2 instruction set support is required.\" 2> /dev/null"); + if (ret != 0) { + ret = system("kdialog --title \"Godot Engine\" --sorry \"A CPU with SSE4.2 instruction set support is required.\" 2> /dev/null"); + } + if (ret != 0) { + ret = system("Xdialog --title \"Godot Engine\" --msgbox \"A CPU with SSE4.2 instruction set support is required.\" 0 0 2> /dev/null"); + } + if (ret != 0) { + ret = system("xmessage -center -title \"Godot Engine\" \"A CPU with SSE4.2 instruction set support is required.\" 2> /dev/null"); + } + abort(); + } +#endif + #if defined(SANITIZERS_ENABLED) // Note: Set stack size to be at least 30 MB (vs 8 MB default) to avoid overflow, address sanitizer can increase stack usage up to 3 times. struct rlimit stack_lim = { 0x1E00000, 0x1E00000 }; diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp index bd1db41b0541..cce731b5b628 100644 --- a/platform/linuxbsd/wayland/wayland_thread.cpp +++ b/platform/linuxbsd/wayland/wayland_thread.cpp @@ -742,7 +742,7 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry if (name == registry->wp_viewporter_name) { for (KeyValue &pair : registry->wayland_thread->windows) { - WindowState ws = pair.value; + WindowState &ws = pair.value; if (registry->wp_viewporter) { wp_viewporter_destroy(registry->wp_viewporter); registry->wp_viewporter = nullptr; @@ -780,7 +780,7 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry if (name == registry->wp_fractional_scale_manager_name) { for (KeyValue &pair : registry->wayland_thread->windows) { - WindowState ws = pair.value; + WindowState &ws = pair.value; if (registry->wp_fractional_scale_manager) { wp_fractional_scale_manager_v1_destroy(registry->wp_fractional_scale_manager); @@ -1550,6 +1550,16 @@ void WaylandThread::_wl_seat_on_capabilities(void *data, struct wl_seat *wl_seat ss->xkb_context = nullptr; } + if (ss->xkb_keymap) { + xkb_keymap_unref(ss->xkb_keymap); + ss->xkb_keymap = nullptr; + } + + if (ss->xkb_state) { + xkb_state_unref(ss->xkb_state); + ss->xkb_state = nullptr; + } + if (ss->wl_keyboard) { wl_keyboard_destroy(ss->wl_keyboard); ss->wl_keyboard = nullptr; @@ -2164,7 +2174,7 @@ void WaylandThread::_wl_keyboard_on_repeat_info(void *data, struct wl_keyboard * SeatState *ss = (SeatState *)data; ERR_FAIL_NULL(ss); - ss->repeat_key_delay_msec = 1000 / rate; + ss->repeat_key_delay_msec = rate ? 1000 / rate : 0; ss->repeat_start_delay_msec = delay; } diff --git a/platform/linuxbsd/wayland/wayland_thread.h b/platform/linuxbsd/wayland/wayland_thread.h index 64e1babcf672..f7d29a0947ce 100644 --- a/platform/linuxbsd/wayland/wayland_thread.h +++ b/platform/linuxbsd/wayland/wayland_thread.h @@ -466,8 +466,11 @@ class WaylandThread { xkb_layout_index_t current_layout_index = 0; - int32_t repeat_key_delay_msec = 0; - int32_t repeat_start_delay_msec = 0; + // Clients with `wl_seat`s older than version 4 do not support + // `wl_keyboard::repeat_info`, so we'll provide a reasonable default of 25 + // keys per second, with a start delay of 600 milliseconds. + int32_t repeat_key_delay_msec = 1000 / 25; + int32_t repeat_start_delay_msec = 600; xkb_keycode_t repeating_keycode = XKB_KEYCODE_INVALID; uint64_t last_repeat_start_msec = 0; diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 0be999cc867b..d08704eea459 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -1798,59 +1798,57 @@ Ref DisplayServerX11::screen_get_image(int p_screen) const { float DisplayServerX11::screen_get_refresh_rate(int p_screen) const { _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V_MSG(!xrandr_ext_ok || !xrr_get_monitors, SCREEN_REFRESH_RATE_FALLBACK, "XRandR extension is not available."); + p_screen = _get_screen_index(p_screen); int screen_count = get_screen_count(); ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK); - //Use xrandr to get screen refresh rate. - if (xrandr_ext_ok) { - XRRScreenResources *screen_info = XRRGetScreenResourcesCurrent(x11_display, windows[MAIN_WINDOW_ID].x11_window); - if (screen_info) { - RRMode current_mode = 0; - xrr_monitor_info *monitors = nullptr; - - if (xrr_get_monitors) { - int count = 0; - monitors = xrr_get_monitors(x11_display, windows[MAIN_WINDOW_ID].x11_window, true, &count); - ERR_FAIL_INDEX_V(p_screen, count, SCREEN_REFRESH_RATE_FALLBACK); - } else { - ERR_PRINT("An error occurred while trying to get the screen refresh rate."); - return SCREEN_REFRESH_RATE_FALLBACK; - } - - bool found_active_mode = false; - for (int crtc = 0; crtc < screen_info->ncrtc; crtc++) { // Loop through outputs to find which one is currently outputting. - XRRCrtcInfo *monitor_info = XRRGetCrtcInfo(x11_display, screen_info, screen_info->crtcs[crtc]); - if (monitor_info->x != monitors[p_screen].x || monitor_info->y != monitors[p_screen].y) { // If X and Y aren't the same as the monitor we're looking for, this isn't the right monitor. Continue. - continue; - } + int target_x; + int target_y; + { + int count = 0; + xrr_monitor_info *monitors = xrr_get_monitors(x11_display, windows[MAIN_WINDOW_ID].x11_window, true, &count); + ERR_FAIL_NULL_V(monitors, SCREEN_REFRESH_RATE_FALLBACK); + if (count <= p_screen) { + xrr_free_monitors(monitors); + ERR_FAIL_V_MSG(SCREEN_REFRESH_RATE_FALLBACK, vformat("Invalid screen index: %d (count: %d).", p_screen, count)); + } + target_x = monitors[p_screen].x; + target_y = monitors[p_screen].y; + xrr_free_monitors(monitors); + } - if (monitor_info->mode != None) { - current_mode = monitor_info->mode; - found_active_mode = true; - break; - } - } + XRRScreenResources *screen_res = XRRGetScreenResourcesCurrent(x11_display, windows[MAIN_WINDOW_ID].x11_window); + ERR_FAIL_NULL_V(screen_res, SCREEN_REFRESH_RATE_FALLBACK); - if (found_active_mode) { - for (int mode = 0; mode < screen_info->nmode; mode++) { - XRRModeInfo m_info = screen_info->modes[mode]; - if (m_info.id == current_mode) { - // Snap to nearest 0.01 to stay consistent with other platforms. - return Math::snapped((float)m_info.dotClock / ((float)m_info.hTotal * (float)m_info.vTotal), 0.01); - } - } + XRRModeInfo *mode_info = nullptr; + for (int crtc = 0; crtc < screen_res->ncrtc; crtc++) { // Loop through outputs to find which one is currently outputting. + XRRCrtcInfo *monitor_info = XRRGetCrtcInfo(x11_display, screen_res, screen_res->crtcs[crtc]); + if (monitor_info->x != target_x || monitor_info->y != target_y || monitor_info->mode == None) { + XRRFreeCrtcInfo(monitor_info); + continue; + } + for (int mode = 0; mode < screen_res->nmode; mode++) { + if (screen_res->modes[mode].id == monitor_info->mode) { + mode_info = &screen_res->modes[mode]; } - - ERR_PRINT("An error occurred while trying to get the screen refresh rate."); // We should have returned the refresh rate by now. An error must have occurred. - return SCREEN_REFRESH_RATE_FALLBACK; - } else { - ERR_PRINT("An error occurred while trying to get the screen refresh rate."); - return SCREEN_REFRESH_RATE_FALLBACK; } + XRRFreeCrtcInfo(monitor_info); + break; + } + + float result; + if (mode_info) { + // Snap to nearest 0.01 to stay consistent with other platforms. + result = Math::snapped((float)mode_info->dotClock / ((float)mode_info->hTotal * (float)mode_info->vTotal), 0.01); + } else { + ERR_PRINT("An error occurred while trying to get the screen refresh rate."); + result = SCREEN_REFRESH_RATE_FALLBACK; } - ERR_PRINT("An error occurred while trying to get the screen refresh rate."); - return SCREEN_REFRESH_RATE_FALLBACK; + + XRRFreeScreenResources(screen_res); + return result; } #ifdef DBUS_ENABLED @@ -3696,8 +3694,11 @@ Key DisplayServerX11::keyboard_get_label_from_physical(Key p_keycode) const { Key key = KeyMappingX11::get_keycode(xkeysym); #ifdef XKB_ENABLED if (xkb_loaded_v08p) { - String keysym = String::chr(xkb_keysym_to_utf32(xkb_keysym_to_upper(xkeysym))); - key = fix_key_label(keysym[0], KeyMappingX11::get_keycode(xkeysym)); + char32_t chr = xkb_keysym_to_utf32(xkb_keysym_to_upper(xkeysym)); + if (chr != 0) { + String keysym = String::chr(chr); + key = fix_key_label(keysym[0], KeyMappingX11::get_keycode(xkeysym)); + } } #endif @@ -4535,7 +4536,7 @@ Bool DisplayServerX11::_predicate_all_events(Display *display, XEvent *event, XP return True; } -bool DisplayServerX11::_wait_for_events() const { +bool DisplayServerX11::_wait_for_events(int timeout_seconds, int timeout_microseconds) const { int x11_fd = ConnectionNumber(x11_display); fd_set in_fds; @@ -4545,8 +4546,8 @@ bool DisplayServerX11::_wait_for_events() const { FD_SET(x11_fd, &in_fds); struct timeval tv; - tv.tv_usec = 0; - tv.tv_sec = 1; + tv.tv_sec = timeout_seconds; + tv.tv_usec = timeout_microseconds; // Wait for next event or timeout. int num_ready_fds = select(x11_fd + 1, &in_fds, nullptr, nullptr, &tv); @@ -4565,7 +4566,8 @@ bool DisplayServerX11::_wait_for_events() const { void DisplayServerX11::_poll_events() { while (!events_thread_done.is_set()) { - _wait_for_events(); + // Wait with a shorter timeout from the events thread to avoid delayed inputs. + _wait_for_events(0, 1000); // Process events from the queue. { diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index e155248a3da7..2b9f33737ae3 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -377,7 +377,7 @@ class DisplayServerX11 : public DisplayServer { SafeFlag events_thread_done; LocalVector polled_events; static void _poll_events_thread(void *ud); - bool _wait_for_events() const; + bool _wait_for_events(int timeout_seconds = 1, int timeout_microseconds = 0) const; void _poll_events(); void _check_pending_events(LocalVector &r_events); diff --git a/platform/macos/SCsub b/platform/macos/SCsub index 19b0acb74f3e..180fd3b2527b 100644 --- a/platform/macos/SCsub +++ b/platform/macos/SCsub @@ -11,10 +11,7 @@ files = [ "godot_application_delegate.mm", "crash_handler_macos.mm", "display_server_macos_base.mm", - "display_server_embedded.mm", "display_server_macos.mm", - "embedded_debugger.mm", - "embedded_gl_manager.mm", "godot_button_view.mm", "godot_content_view.mm", "godot_status_item.mm", @@ -35,6 +32,9 @@ files = [ if env.editor_build: files += [ + "display_server_embedded.mm", + "embedded_debugger.mm", + "embedded_gl_manager.mm", "editor/embedded_game_view_plugin.mm", "editor/embedded_process_macos.mm", ] diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index a6f4f36c89e1..75a30dd89cd8 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -64,14 +64,18 @@ @class GodotContentView; @class GodotWindowDelegate; @class GodotButtonView; +#ifdef TOOLS_ENABLED @class GodotEmbeddedView; @class CALayerHost; +#endif #undef BitMap #undef CursorShape #undef FontVariation +#ifdef TOOLS_ENABLED class EmbeddedProcessMacOS; +#endif class DisplayServerMacOS : public DisplayServerMacOSBase { GDSOFTCLASS(DisplayServerMacOS, DisplayServerMacOSBase); @@ -239,12 +243,14 @@ class DisplayServerMacOS : public DisplayServerMacOSBase { Error _file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector &p_filters, const TypedArray &p_options, const Callable &p_callback, bool p_options_in_cb, WindowID p_window_id); +#ifdef TOOLS_ENABLED struct EmbeddedProcessData { - EmbeddedProcessMacOS *process; + EmbeddedProcessMacOS *process = nullptr; WindowData *wd = nullptr; CALayer *layer_host = nil; }; HashMap embedded_processes; +#endif void _window_update_display_id(WindowData *p_wd); public: @@ -440,9 +446,9 @@ class DisplayServerMacOS : public DisplayServerMacOSBase { virtual void enable_for_stealing_focus(OS::ProcessID pid) override; #ifdef TOOLS_ENABLED Error embed_process_update(WindowID p_window, EmbeddedProcessMacOS *p_process); -#endif virtual Error request_close_embedded_process(OS::ProcessID p_pid) override; virtual Error remove_embedded_process(OS::ProcessID p_pid) override; +#endif void _process_events(bool p_pump); virtual void process_events() override; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index deeb43557d70..138365c791e9 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -41,9 +41,12 @@ #import "godot_window.h" #import "godot_window_delegate.h" #import "key_mapping_macos.h" -#import "macos_quartz_core_spi.h" #import "os_macos.h" +#ifdef TOOLS_ENABLED +#import "macos_quartz_core_spi.h" +#endif + #include "core/config/project_settings.h" #include "core/io/marshalls.h" #include "core/math/geometry_2d.h" @@ -2645,9 +2648,11 @@ [wd.window_object setBackgroundColor:[NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:0.004f]]; } // Force update of the window styles. - NSRect frameRect = [wd.window_object frame]; - [wd.window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO]; - [wd.window_object setFrame:frameRect display:NO]; + if ([wd.window_object isVisible]) { + NSRect frameRect = [wd.window_object frame]; + [wd.window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO]; + [wd.window_object setFrame:frameRect display:NO]; + } } _update_window_style(wd, p_window); if (was_visible || [wd.window_object isVisible]) { @@ -2681,9 +2686,11 @@ wd.layered_window = p_enabled; set_window_per_pixel_transparency_enabled(p_enabled, p_window); // Force update of the window styles. - NSRect frameRect = [wd.window_object frame]; - [wd.window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO]; - [wd.window_object setFrame:frameRect display:NO]; + if ([wd.window_object isVisible]) { + NSRect frameRect = [wd.window_object frame]; + [wd.window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO]; + [wd.window_object setFrame:frameRect display:NO]; + } } break; case WINDOW_FLAG_NO_FOCUS: { wd.no_focus = p_enabled; @@ -3219,8 +3226,6 @@ return OK; } -#endif - Error DisplayServerMacOS::request_close_embedded_process(OS::ProcessID p_pid) { return OK; } @@ -3236,6 +3241,8 @@ return OK; } +#endif + void DisplayServerMacOS::process_events() { _process_events(true); } diff --git a/platform/macos/editor/embedded_process_macos.mm b/platform/macos/editor/embedded_process_macos.mm index d9bd1ce018f3..627d90b9bb01 100644 --- a/platform/macos/editor/embedded_process_macos.mm +++ b/platform/macos/editor/embedded_process_macos.mm @@ -104,6 +104,13 @@ if (current_process_id != 0 && is_embedding_completed()) { ds->remove_embedded_process(current_process_id); } + DisplayServer *ds = DisplayServer::get_singleton(); + for (int i = 0; i < DisplayServer::CURSOR_MAX; i++) { + ds->cursor_set_custom_image(Ref(), (DisplayServer::CursorShape)i, Vector2()); + } + if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_VISIBLE) { + ds->mouse_set_mode(DisplayServer::MOUSE_MODE_VISIBLE); + } current_process_id = 0; embedding_state = EmbeddingState::IDLE; context_id = 0; @@ -191,6 +198,7 @@ EmbeddedProcessBase() { layer_host = memnew(LayerHost(this)); add_child(layer_host); + set_focus_mode(FOCUS_NONE); layer_host->set_focus_mode(FOCUS_ALL); layer_host->set_anchors_and_offsets_preset(PRESET_FULL_RECT); layer_host->set_custom_minimum_size(Size2(100, 100)); @@ -218,18 +226,23 @@ if (script_debugger) { script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_MOUSE_ENTER }); } + if (get_window()->has_focus()) { + grab_focus(); + } } break; case NOTIFICATION_FOCUS_ENTER: { // Restore mouse capture, if necessary. - DisplayServer *ds = DisplayServer::get_singleton(); - if (process->get_mouse_mode() != ds->mouse_get_mode()) { - // Restore embedded process mouse mode. - ds->mouse_set_mode(process->get_mouse_mode()); - } if (!window_focused && script_debugger) { + DisplayServer *ds = DisplayServer::get_singleton(); + if (process->get_mouse_mode() != ds->mouse_get_mode()) { + // Restore embedded process mouse mode. + ds->mouse_set_mode(process->get_mouse_mode()); + } + script_debugger->send_message("embed:notification", { NOTIFICATION_APPLICATION_FOCUS_IN }); script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_FOCUS_IN }); window_focused = true; } + process->queue_redraw(); } break; case NOTIFICATION_MOUSE_EXIT: { DisplayServer *ds = DisplayServer::get_singleton(); @@ -242,14 +255,16 @@ } break; case NOTIFICATION_FOCUS_EXIT: { // Temporarily set mouse state back to visible, so the user can interact with the editor. - DisplayServer *ds = DisplayServer::get_singleton(); - if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_VISIBLE) { - ds->mouse_set_mode(DisplayServer::MOUSE_MODE_VISIBLE); - } if (window_focused && script_debugger) { + DisplayServer *ds = DisplayServer::get_singleton(); + if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_VISIBLE) { + ds->mouse_set_mode(DisplayServer::MOUSE_MODE_VISIBLE); + } script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_FOCUS_OUT }); + script_debugger->send_message("embed:notification", { NOTIFICATION_APPLICATION_FOCUS_OUT }); window_focused = false; } + process->queue_redraw(); } break; case MainLoop::NOTIFICATION_OS_IME_UPDATE: { if (script_debugger && has_focus()) { @@ -265,28 +280,37 @@ for (int i = 0; i < DisplayServer::CURSOR_MAX; i++) { ds->cursor_set_custom_image(Ref(), (DisplayServer::CursorShape)i, Vector2()); } + if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_VISIBLE) { + ds->mouse_set_mode(DisplayServer::MOUSE_MODE_VISIBLE); + } } } break; + case NOTIFICATION_APPLICATION_FOCUS_IN: case NOTIFICATION_WM_WINDOW_FOCUS_IN: { - if (!window_focused && script_debugger) { + if (has_focus() && !window_focused && script_debugger) { + DisplayServer *ds = DisplayServer::get_singleton(); + if (process->get_mouse_mode() != ds->mouse_get_mode()) { + // Restore embedded process mouse mode. + ds->mouse_set_mode(process->get_mouse_mode()); + if (process->get_mouse_mode() != DisplayServer::MOUSE_MODE_VISIBLE) { + get_window()->grab_focus(); + } + } + script_debugger->send_message("embed:notification", { NOTIFICATION_APPLICATION_FOCUS_IN }); script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_FOCUS_IN }); window_focused = true; } } break; + case NOTIFICATION_APPLICATION_FOCUS_OUT: case NOTIFICATION_WM_WINDOW_FOCUS_OUT: { - if (window_focused && script_debugger) { + if (has_focus() && window_focused && script_debugger) { + DisplayServer *ds = DisplayServer::get_singleton(); + if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_VISIBLE) { + ds->mouse_set_mode(DisplayServer::MOUSE_MODE_VISIBLE); + } script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_FOCUS_OUT }); - window_focused = false; - } - } break; - case NOTIFICATION_APPLICATION_FOCUS_IN: { - if (script_debugger) { - script_debugger->send_message("embed:notification", { NOTIFICATION_APPLICATION_FOCUS_IN }); - } - } break; - case NOTIFICATION_APPLICATION_FOCUS_OUT: { - if (script_debugger) { script_debugger->send_message("embed:notification", { NOTIFICATION_APPLICATION_FOCUS_OUT }); + window_focused = false; } } break; } diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm index 165289c5905b..97263c58a151 100644 --- a/platform/macos/godot_content_view.mm +++ b/platform/macos/godot_content_view.mm @@ -543,7 +543,7 @@ - (void)mouseMoved:(NSEvent *)event { ds->get_key_modifier_state([event modifierFlags], mm); const NSRect contentRect = [wd.window_view frame]; - if (NSPointInRect([event locationInWindow], contentRect)) { + if (NSPointInRect([event locationInWindow], contentRect) && [NSWindow windowNumberAtPoint:[NSEvent mouseLocation] belowWindowWithWindowNumber:0 /*topmost*/] == [wd.window_object windowNumber]) { ds->mouse_enter_window(window_id); } diff --git a/platform/macos/godot_main_macos.mm b/platform/macos/godot_main_macos.mm index 3807464fcf2e..d29596487b8a 100644 --- a/platform/macos/godot_main_macos.mm +++ b/platform/macos/godot_main_macos.mm @@ -110,7 +110,7 @@ int main(int argc, char **argv) { OS_MacOS *os = nullptr; if (is_embedded) { -#ifdef DEBUG_ENABLED +#ifdef TOOLS_ENABLED os = memnew(OS_MacOS_Embedded(args[0], remaining_args, remaining_args > 0 ? &args[1] : nullptr)); #else WARN_PRINT("Embedded mode is not supported in release builds."); diff --git a/platform/macos/os_macos.h b/platform/macos/os_macos.h index 8cf47902b1d9..a15dcc6cf3b8 100644 --- a/platform/macos/os_macos.h +++ b/platform/macos/os_macos.h @@ -178,7 +178,7 @@ class OS_MacOS_Headless : public OS_MacOS { OS_MacOS_Headless(const char *p_execpath, int p_argc, char **p_argv); }; -#ifdef DEBUG_ENABLED +#ifdef TOOLS_ENABLED class OS_MacOS_Embedded : public OS_MacOS { public: diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm index a15b253b4009..67df52ac3550 100644 --- a/platform/macos/os_macos.mm +++ b/platform/macos/os_macos.mm @@ -31,7 +31,7 @@ #import "os_macos.h" #import "dir_access_macos.h" -#ifdef DEBUG_ENABLED +#ifdef TOOLS_ENABLED #import "display_server_embedded.h" #endif #import "display_server_macos.h" @@ -1228,7 +1228,7 @@ static void handle_interrupt(int sig) { // MARK: - OS_MacOS_Embedded -#ifdef DEBUG_ENABLED +#ifdef TOOLS_ENABLED void OS_MacOS_Embedded::run() { CFRunLoopGetCurrent(); diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index 6e9acae91136..28a0238b45e7 100644 --- a/platform/web/display_server_web.cpp +++ b/platform/web/display_server_web.cpp @@ -1003,7 +1003,7 @@ Vector DisplayServerWeb::get_rendering_drivers_func() { // Clipboard void DisplayServerWeb::update_clipboard_callback(const char *p_text) { - String text = p_text; + String text = String::utf8(p_text); #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 4c5215fd462a..111a08ee935e 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -407,6 +407,7 @@ def spawn_capture(sh, escape, cmd, args, env): "dwrite", "wbemuuid", "ntdll", + "hid", ] if env.debug_features: @@ -786,6 +787,7 @@ def configure_mingw(env: "SConsEnvironment"): "dwrite", "wbemuuid", "ntdll", + "hid", ] ) diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 9233ce58ee97..7cee08223bfd 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -4763,9 +4763,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA // if the same nIDEvent is passed, the timer is replaced and the same timer_id is returned. // The problem with the timer is that the window cannot be resized or the buttons cannot be used correctly // if the window is not activated first. This happens because the code in the activation process runs - // after the mouse click is handled. To address this, the timer is now used only when the window is created. + // after the mouse click is handled. To address this, the timer is now used only during the window creation, + // and only as part of the activation process. We don't want 'Input::release_pressed_events()' + // to be called immediately in '_process_activate_event' when the window is not yet activated, + // as it would reset the currently pressed keys when hiding a window, which is incorrect behavior. windows[window_id].activate_state = GET_WM_ACTIVATE_STATE(wParam, lParam); - if (windows[window_id].first_activation_done) { + if (windows[window_id].first_activation_done && (windows[window_id].activate_state == WA_ACTIVE || windows[window_id].activate_state == WA_CLICKACTIVE)) { _process_activate_event(window_id); } else { windows[window_id].activate_timer_id = SetTimer(windows[window_id].hWnd, DisplayServerWindows::TIMER_ID_WINDOW_ACTIVATION, USER_TIMER_MINIMUM, (TIMERPROC) nullptr); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 6c76e538ded9..66f1518eb106 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,7 @@ #include #include #include +#include #if defined(RD_ENABLED) #include "servers/rendering/rendering_device.h" @@ -2735,9 +2737,108 @@ bool OS_Windows::_test_create_rendering_device_and_gl(const String &p_display_dr } #endif +using GetProcAddressType = FARPROC(__stdcall *)(HMODULE, LPCSTR); +GetProcAddressType Original_GetProcAddress = nullptr; + +using HidD_GetProductStringType = BOOLEAN(__stdcall *)(HANDLE, void *, ULONG); +HidD_GetProductStringType Original_HidD_GetProductString = nullptr; + +#ifndef HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER +#define HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER 0x08 +#endif + +bool _hid_is_controller(HANDLE p_hid_handle) { + PHIDP_PREPARSED_DATA hid_preparsed = nullptr; + BOOLEAN preparsed_res = HidD_GetPreparsedData(p_hid_handle, &hid_preparsed); + if (!preparsed_res) { + return false; + } + + HIDP_CAPS hid_caps = {}; + NTSTATUS caps_res = HidP_GetCaps(hid_preparsed, &hid_caps); + HidD_FreePreparsedData(hid_preparsed); + if (caps_res != HIDP_STATUS_SUCCESS) { + return false; + } + + if (hid_caps.UsagePage != HID_USAGE_PAGE_GENERIC) { + return false; + } + + if (hid_caps.Usage == HID_USAGE_GENERIC_JOYSTICK || hid_caps.Usage == HID_USAGE_GENERIC_GAMEPAD || hid_caps.Usage == HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER) { + return true; + } + + return false; +} + +BOOLEAN __stdcall Hook_HidD_GetProductString(HANDLE p_object, void *p_buffer, ULONG p_buffer_length) { + constexpr const wchar_t unknown_product_string[] = L"Unknown HID Device"; + constexpr size_t unknown_product_length = sizeof(unknown_product_string); + + if (_hid_is_controller(p_object)) { + return HidD_GetProductString(p_object, p_buffer, p_buffer_length); + } + + // The HID is (probably) not a controller, so we don't care about returning its actual product string. + // This avoids stalls on `EnumDevices` because DirectInput attempts to enumerate all HIDs, including some DACs + // and other devices which take too long to respond to those requests, added to the lack of a shorter timeout. + if (p_buffer_length >= unknown_product_length) { + memcpy(p_buffer, unknown_product_string, unknown_product_length); + return TRUE; + } + return FALSE; +} + +FARPROC __stdcall Hook_GetProcAddress(HMODULE p_module, LPCSTR p_name) { + if (String(p_name) == "HidD_GetProductString") { + return (FARPROC)(LPVOID)Hook_HidD_GetProductString; + } + if (Original_GetProcAddress) { + return Original_GetProcAddress(p_module, p_name); + } + return nullptr; +} + +LPVOID install_iat_hook(const String &p_target, const String &p_module, const String &p_symbol, LPVOID p_hook_func) { + LPVOID image_base = LoadLibraryA(p_target.ascii().get_data()); + if (image_base) { + PIMAGE_NT_HEADERS nt_headers = (PIMAGE_NT_HEADERS)((DWORD_PTR)image_base + ((PIMAGE_DOS_HEADER)image_base)->e_lfanew); + PIMAGE_IMPORT_DESCRIPTOR import_descriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)image_base + nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + while (import_descriptor->Name != 0) { + LPCSTR library_name = (LPCSTR)((DWORD_PTR)image_base + import_descriptor->Name); + if (String(library_name).to_lower() == p_module) { + PIMAGE_THUNK_DATA original_first_thunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)image_base + import_descriptor->OriginalFirstThunk); + PIMAGE_THUNK_DATA first_thunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)image_base + import_descriptor->FirstThunk); + + while ((LPVOID)original_first_thunk->u1.AddressOfData != nullptr) { + PIMAGE_IMPORT_BY_NAME function_import = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)image_base + original_first_thunk->u1.AddressOfData); + if (String(function_import->Name).to_lower() == p_symbol.to_lower()) { + DWORD old_protect = 0; + VirtualProtect((LPVOID)(&first_thunk->u1.Function), 8, PAGE_READWRITE, &old_protect); + + LPVOID old_func = (LPVOID)first_thunk->u1.Function; + first_thunk->u1.Function = (DWORD_PTR)p_hook_func; + + VirtualProtect((LPVOID)(&first_thunk->u1.Function), 8, old_protect, nullptr); + return old_func; + } + original_first_thunk++; + first_thunk++; + } + } + import_descriptor++; + } + } + return nullptr; +} + OS_Windows::OS_Windows(HINSTANCE _hInstance) { hInstance = _hInstance; + Original_GetProcAddress = (GetProcAddressType)install_iat_hook("dinput8.dll", "kernel32.dll", "GetProcAddress", (LPVOID)Hook_GetProcAddress); + Original_HidD_GetProductString = (HidD_GetProductStringType)install_iat_hook("dinput8.dll", "hid.dll", "HidD_GetProductString", (LPVOID)Hook_HidD_GetProductString); + _init_encodings(); // Reset CWD to ensure long path is used. diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index f513b907a8e4..dce530a3ef40 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -133,8 +133,8 @@ RID PopupMenu::bind_global_menu() { nmenu->set_item_tooltip(global_menu, index, item.tooltip); if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { Array events = item.shortcut->get_events(); - for (int j = 0; j < events.size(); j++) { - Ref ie = events[j]; + for (const Variant &v : events) { + Ref ie = v; if (ie.is_valid() && _set_item_accelerator(index, ie)) { break; } @@ -2853,7 +2853,26 @@ void PopupMenu::_unref_shortcut(Ref p_sc) { void PopupMenu::_shortcut_changed() { for (int i = 0; i < items.size(); i++) { - items.write[i].dirty = true; + Item &item = items.write[i]; + item.dirty = true; + if (global_menu.is_valid() && !item.separator) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + nmenu->set_item_accelerator(global_menu, i, Key::NONE); + if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { + Array events = item.shortcut->get_events(); + for (const Variant &v : events) { + Ref ie = v; + if (ie.is_valid() && _set_item_accelerator(i, ie)) { + break; + } + } + if (item.shortcut_is_global) { + nmenu->set_item_key_callback(global_menu, i, callable_mp(this, &PopupMenu::activate_item)); + } else { + nmenu->set_item_key_callback(global_menu, i, Callable()); + } + } + } } control->queue_redraw(); } diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 2419a0195b25..924f4de946c6 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -4187,7 +4187,7 @@ RID RenderingDevice::render_pipeline_create(RID p_shader, FramebufferFormatID p_ ERR_FAIL_COND_V(!pipeline.driver_id, RID()); if (pipeline_cache_enabled) { - _update_pipeline_cache(); + update_pipeline_cache(); } pipeline.shader = p_shader; @@ -4275,7 +4275,7 @@ RID RenderingDevice::compute_pipeline_create(RID p_shader, const Vector RenderingDevice::_load_pipeline_cache() { } } -void RenderingDevice::_update_pipeline_cache(bool p_closing) { +void RenderingDevice::update_pipeline_cache(bool p_closing) { _THREAD_SAFE_METHOD_ { @@ -7331,7 +7331,7 @@ void RenderingDevice::finalize() { } if (pipeline_cache_enabled) { - _update_pipeline_cache(true); + update_pipeline_cache(true); driver->pipeline_cache_free(); } diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 0bee8d7de825..0d705ed81f76 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -1167,7 +1167,6 @@ class RenderingDevice : public RenderingDeviceCommons { WorkerThreadPool::TaskID pipeline_cache_save_task = WorkerThreadPool::INVALID_TASK_ID; Vector _load_pipeline_cache(); - void _update_pipeline_cache(bool p_closing = false); static void _save_pipeline_cache(void *p_data); struct ComputePipeline { @@ -1189,6 +1188,8 @@ class RenderingDevice : public RenderingDeviceCommons { RID compute_pipeline_create(RID p_shader, const Vector &p_specialization_constants = Vector()); bool compute_pipeline_is_valid(RID p_pipeline); + void update_pipeline_cache(bool p_closing = false); + private: /****************/ /**** SCREEN ****/