summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COPYRIGHT.txt5
-rw-r--r--SConstruct96
-rw-r--r--core/SCsub4
-rw-r--r--core/core_bind.cpp2
-rw-r--r--core/extension/gdextension.cpp16
-rw-r--r--core/extension/gdextension.h1
-rw-r--r--core/extension/gdextension_interface.h3
-rw-r--r--core/io/dir_access.cpp4
-rw-r--r--core/object/class_db.cpp5
-rw-r--r--core/object/object.cpp11
-rw-r--r--core/object/object.h3
-rw-r--r--core/object/undo_redo.cpp4
-rw-r--r--core/templates/ring_buffer.h4
-rw-r--r--core/templates/safe_refcount.h2
-rw-r--r--doc/classes/DirAccess.xml26
-rw-r--r--doc/classes/DisplayServer.xml4
-rw-r--r--drivers/SCsub2
-rw-r--r--drivers/backtrace/SCsub44
-rw-r--r--drivers/gles3/storage/utilities.cpp4
-rw-r--r--drivers/windows/dir_access_windows.cpp60
-rw-r--r--drivers/windows/dir_access_windows.h6
-rw-r--r--drivers/windows/file_access_windows.cpp65
-rw-r--r--editor/animation_track_editor.cpp1
-rw-r--r--editor/debugger/debug_adapter/debug_adapter_server.h2
-rw-r--r--editor/editor_about.cpp1
-rw-r--r--editor/editor_audio_buses.h2
-rw-r--r--editor/editor_builders.py12
-rw-r--r--editor/editor_data.cpp2
-rw-r--r--editor/editor_help.h2
-rw-r--r--editor/editor_help_search.h2
-rw-r--r--editor/editor_inspector.cpp7
-rw-r--r--editor/editor_node.cpp4
-rw-r--r--editor/editor_node.h2
-rw-r--r--editor/editor_resource_picker.cpp52
-rw-r--r--editor/editor_resource_picker.h4
-rw-r--r--editor/editor_resource_preview.cpp12
-rw-r--r--editor/editor_resource_preview.h5
-rw-r--r--editor/gui/editor_validation_panel.cpp2
-rw-r--r--editor/import/audio_stream_import_settings.h2
-rw-r--r--editor/import/resource_importer_wav.cpp4
-rw-r--r--editor/import_defaults_editor.cpp2
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.h2
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.cpp6
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.h2
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.cpp6
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.h2
-rw-r--r--editor/plugins/animation_library_editor.h2
-rw-r--r--editor/plugins/animation_player_editor_plugin.h2
-rw-r--r--editor/plugins/animation_tree_editor_plugin.h2
-rw-r--r--editor/plugins/asset_library_editor_plugin.h4
-rw-r--r--editor/plugins/audio_stream_editor_plugin.h2
-rw-r--r--editor/plugins/audio_stream_randomizer_editor_plugin.h2
-rw-r--r--editor/plugins/bit_map_editor_plugin.h2
-rw-r--r--editor/plugins/bone_map_editor_plugin.cpp75
-rw-r--r--editor/plugins/bone_map_editor_plugin.h2
-rw-r--r--editor/plugins/camera_3d_editor_plugin.h2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h2
-rw-r--r--editor/plugins/cast_2d_editor_plugin.h2
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.h2
-rw-r--r--editor/plugins/control_editor_plugin.cpp8
-rw-r--r--editor/plugins/control_editor_plugin.h5
-rw-r--r--editor/plugins/cpu_particles_2d_editor_plugin.h2
-rw-r--r--editor/plugins/curve_editor_plugin.h2
-rw-r--r--editor/plugins/debugger_editor_plugin.h2
-rw-r--r--editor/plugins/editor_plugin.compat.inc (renamed from editor/editor_plugin.compat.inc)0
-rw-r--r--editor/plugins/editor_plugin.cpp (renamed from editor/editor_plugin.cpp)0
-rw-r--r--editor/plugins/editor_plugin.h (renamed from editor/editor_plugin.h)0
-rw-r--r--editor/plugins/editor_plugin_settings.cpp (renamed from editor/editor_plugin_settings.cpp)0
-rw-r--r--editor/plugins/editor_plugin_settings.h (renamed from editor/editor_plugin_settings.h)2
-rw-r--r--editor/plugins/font_config_plugin.h2
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.h2
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.h2
-rw-r--r--editor/plugins/gpu_particles_collision_sdf_editor_plugin.h2
-rw-r--r--editor/plugins/gradient_editor_plugin.h2
-rw-r--r--editor/plugins/gradient_texture_2d_editor_plugin.h2
-rw-r--r--editor/plugins/input_event_editor_plugin.h2
-rw-r--r--editor/plugins/lightmap_gi_editor_plugin.h2
-rw-r--r--editor/plugins/material_editor_plugin.h2
-rw-r--r--editor/plugins/mesh_editor_plugin.h2
-rw-r--r--editor/plugins/mesh_instance_3d_editor_plugin.h2
-rw-r--r--editor/plugins/mesh_library_editor_plugin.h2
-rw-r--r--editor/plugins/multimesh_editor_plugin.h2
-rw-r--r--editor/plugins/navigation_link_2d_editor_plugin.h2
-rw-r--r--editor/plugins/navigation_obstacle_3d_editor_plugin.h2
-rw-r--r--editor/plugins/navigation_polygon_editor_plugin.h2
-rw-r--r--editor/plugins/node_3d_editor_plugin.h2
-rw-r--r--editor/plugins/occluder_instance_3d_editor_plugin.h2
-rw-r--r--editor/plugins/packed_scene_editor_plugin.h2
-rw-r--r--editor/plugins/parallax_background_editor_plugin.h2
-rw-r--r--editor/plugins/particle_process_material_editor_plugin.h2
-rw-r--r--editor/plugins/path_2d_editor_plugin.h2
-rw-r--r--editor/plugins/path_3d_editor_plugin.h2
-rw-r--r--editor/plugins/physical_bone_3d_editor_plugin.h2
-rw-r--r--editor/plugins/plugin_config_dialog.cpp (renamed from editor/plugin_config_dialog.cpp)83
-rw-r--r--editor/plugins/plugin_config_dialog.h (renamed from editor/plugin_config_dialog.h)2
-rw-r--r--editor/plugins/polygon_3d_editor_plugin.h2
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.h2
-rw-r--r--editor/plugins/script_editor_plugin.h2
-rw-r--r--editor/plugins/shader_editor_plugin.h2
-rw-r--r--editor/plugins/shader_file_editor_plugin.h2
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.h2
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.h2
-rw-r--r--editor/plugins/skeleton_ik_3d_editor_plugin.h2
-rw-r--r--editor/plugins/sprite_2d_editor_plugin.h2
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.h2
-rw-r--r--editor/plugins/style_box_editor_plugin.h2
-rw-r--r--editor/plugins/sub_viewport_preview_editor_plugin.h2
-rw-r--r--editor/plugins/text_shader_editor.cpp3
-rw-r--r--editor/plugins/texture_3d_editor_plugin.h2
-rw-r--r--editor/plugins/texture_editor_plugin.h2
-rw-r--r--editor/plugins/texture_layered_editor_plugin.h2
-rw-r--r--editor/plugins/texture_region_editor_plugin.h2
-rw-r--r--editor/plugins/theme_editor_plugin.h2
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.h2
-rw-r--r--editor/plugins/version_control_editor_plugin.h2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h2
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.h2
-rw-r--r--editor/project_settings_editor.h2
-rw-r--r--editor/shader_globals_editor.h2
-rw-r--r--gles3_builders.py6
-rw-r--r--glsl_builders.py7
-rw-r--r--methods.py146
-rw-r--r--misc/dist/ios_xcode/PrivacyInfo.xcprivacy10
-rw-r--r--misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj4
-rw-r--r--modules/csg/editor/csg_gizmos.h2
-rw-r--r--modules/gdscript/gdscript.cpp9
-rw-r--r--modules/gdscript/gdscript.h2
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp42
-rw-r--r--modules/gdscript/gdscript_parser.h10
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.h2
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp29
-rw-r--r--modules/gltf/editor/editor_scene_exporter_gltf_plugin.h2
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.h2
-rw-r--r--modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h2
-rw-r--r--modules/mobile_vr/mobile_vr_interface.cpp2
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/multiplayer/editor/multiplayer_editor_plugin.h2
-rw-r--r--modules/multiplayer/editor/replication_editor.h2
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.h2
-rw-r--r--modules/noise/editor/noise_editor_plugin.h2
-rw-r--r--modules/noise/register_types.cpp2
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.h2
-rw-r--r--modules/openxr/editor/openxr_editor_plugin.h2
-rw-r--r--modules/text_server_adv/gdextension_build/SConstruct27
-rw-r--r--modules/text_server_adv/gdextension_build/methods.py117
-rw-r--r--modules/text_server_fb/gdextension_build/SConstruct27
-rw-r--r--modules/text_server_fb/gdextension_build/methods.py117
-rw-r--r--platform/android/SCsub3
-rw-r--r--platform/android/detect.py21
-rw-r--r--platform/android/display_server_android.cpp2
-rw-r--r--platform/ios/detect.py8
-rw-r--r--platform/ios/display_server_ios.mm2
-rw-r--r--platform/ios/doc_classes/EditorExportPlatformIOS.xml444
-rw-r--r--platform/ios/export/export_plugin.cpp289
-rw-r--r--platform/linuxbsd/detect.py56
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.cpp193
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.h62
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp2
-rw-r--r--platform/macos/detect.py8
-rw-r--r--platform/macos/display_server_macos.mm12
-rw-r--r--platform/macos/export/export_plugin.cpp48
-rw-r--r--platform/macos/export/export_plugin.h8
-rw-r--r--platform/macos/export/macho.cpp20
-rw-r--r--platform/macos/export/macho.h1
-rw-r--r--platform/macos/godot_content_view.mm2
-rw-r--r--platform/macos/godot_window_delegate.mm4
-rw-r--r--platform/web/SCsub4
-rw-r--r--platform/web/api/web_tools_editor_plugin.h2
-rw-r--r--platform/web/detect.py16
-rw-r--r--platform/web/display_server_web.cpp10
-rw-r--r--platform/web/javascript_bridge_singleton.cpp2
-rw-r--r--platform/web/js/engine/features.js3
-rw-r--r--platform/windows/SCsub6
-rw-r--r--platform/windows/console_wrapper_windows.cpp4
-rw-r--r--platform/windows/crash_handler_windows.h5
-rw-r--r--platform/windows/crash_handler_windows_seh.cpp (renamed from platform/windows/crash_handler_windows.cpp)2
-rw-r--r--platform/windows/crash_handler_windows_signal.cpp205
-rw-r--r--platform/windows/detect.py74
-rw-r--r--platform/windows/display_server_windows.cpp10
-rw-r--r--platform/windows/godot_windows.cpp2
-rw-r--r--platform/windows/os_windows.cpp34
-rw-r--r--platform/windows/windows_terminal_logger.cpp22
-rw-r--r--platform_methods.py3
-rw-r--r--scene/3d/label_3d.cpp4
-rw-r--r--scene/animation/animation_player.cpp2
-rw-r--r--scene/gui/rich_text_label.cpp925
-rw-r--r--scene/gui/rich_text_label.h10
-rw-r--r--scene/gui/texture_progress_bar.cpp12
-rw-r--r--scene/resources/shader.cpp39
-rw-r--r--scu_builders.py7
-rw-r--r--servers/physics_2d/godot_area_2d.h4
-rw-r--r--servers/physics_3d/godot_area_3d.h4
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp4
-rw-r--r--servers/rendering/renderer_rd/shaders/particles.glsl6
-rw-r--r--servers/rendering/renderer_rd/storage_rd/utilities.cpp4
-rw-r--r--servers/rendering/rendering_server_default.h4
-rw-r--r--tests/display_server_mock.h2
-rw-r--r--thirdparty/README.md22
-rw-r--r--thirdparty/clipper2/include/clipper2/clipper.core.h4
-rw-r--r--thirdparty/clipper2/patches/gcc14-warning.patch22
-rw-r--r--thirdparty/glad/EGL/eglplatform.h6
-rw-r--r--thirdparty/glad/gl.c147
-rw-r--r--thirdparty/glad/glad/egl.h4
-rw-r--r--thirdparty/glad/glad/gl.h7
-rw-r--r--thirdparty/glad/glad/glx.h4
-rw-r--r--thirdparty/glad/patches/patch_enable_both_gl_and_gles.diff12
-rw-r--r--thirdparty/libbacktrace/LICENSE29
-rw-r--r--thirdparty/libbacktrace/alloc.c167
-rw-r--r--thirdparty/libbacktrace/atomic.c113
-rw-r--r--thirdparty/libbacktrace/backtrace-supported.h66
-rw-r--r--thirdparty/libbacktrace/backtrace.c129
-rw-r--r--thirdparty/libbacktrace/backtrace.h189
-rw-r--r--thirdparty/libbacktrace/config.h170
-rw-r--r--thirdparty/libbacktrace/dwarf.c4402
-rw-r--r--thirdparty/libbacktrace/fileline.c346
-rw-r--r--thirdparty/libbacktrace/filenames.h52
-rw-r--r--thirdparty/libbacktrace/internal.h380
-rw-r--r--thirdparty/libbacktrace/pecoff.c935
-rw-r--r--thirdparty/libbacktrace/posix.c104
-rw-r--r--thirdparty/libbacktrace/print.c92
-rw-r--r--thirdparty/libbacktrace/read.c110
-rw-r--r--thirdparty/libbacktrace/simple.c108
-rw-r--r--thirdparty/libbacktrace/sort.c108
-rw-r--r--thirdparty/libbacktrace/state.c72
-rw-r--r--thirdparty/mbedtls/include/godot_module_mbedtls_config.h4
225 files changed, 10386 insertions, 1428 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index c1711a9e81..2e246e6634 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -293,6 +293,11 @@ Comment: jpeg-compressor
Copyright: 2012, Rich Geldreich
License: public-domain or Apache-2.0
+Files: ./thirdparty/libbacktrace/
+Comment: libbacktrace
+Copyright: 2012-2021, Free Software Foundation, Inc.
+License: BSD-3-clause
+
Files: ./thirdparty/libktx/
Comment: KTX
Copyright: 2013-2020, Mark Callow
diff --git a/SConstruct b/SConstruct
index 18511ff5ee..81ce4bca52 100644
--- a/SConstruct
+++ b/SConstruct
@@ -15,6 +15,17 @@ from collections import OrderedDict
from importlib.util import spec_from_file_location, module_from_spec
from SCons import __version__ as scons_raw_version
+# Enable ANSI escape code support on Windows 10 and later (for colored console output).
+# <https://github.com/python/cpython/issues/73245>
+if sys.platform == "win32":
+ from ctypes import windll, c_int, byref
+
+ stdout_handle = windll.kernel32.GetStdHandle(c_int(-11))
+ mode = c_int(0)
+ windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode))
+ mode = c_int(mode.value | 4)
+ windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode)
+
# Explicitly resolve the helper modules, this is done to avoid clash with
# modules of the same name that might be randomly added (e.g. someone adding
# an `editor.py` file at the root of the module creates a clash with the editor
@@ -57,6 +68,7 @@ import methods
import glsl_builders
import gles3_builders
import scu_builders
+from methods import print_warning, print_error
from platform_methods import architectures, architecture_aliases, generate_export_icons
if ARGUMENTS.get("target", "editor") == "editor":
@@ -311,38 +323,41 @@ if selected_platform == "":
selected_platform = "windows"
if selected_platform != "":
- print("Automatically detected platform: " + selected_platform)
+ print(f"Automatically detected platform: {selected_platform}")
if selected_platform == "osx":
# Deprecated alias kept for compatibility.
- print('Platform "osx" has been renamed to "macos" in Godot 4. Building for platform "macos".')
+ print_warning('Platform "osx" has been renamed to "macos" in Godot 4. Building for platform "macos".')
selected_platform = "macos"
if selected_platform == "iphone":
# Deprecated alias kept for compatibility.
- print('Platform "iphone" has been renamed to "ios" in Godot 4. Building for platform "ios".')
+ print_warning('Platform "iphone" has been renamed to "ios" in Godot 4. Building for platform "ios".')
selected_platform = "ios"
if selected_platform in ["linux", "bsd", "x11"]:
if selected_platform == "x11":
# Deprecated alias kept for compatibility.
- print('Platform "x11" has been renamed to "linuxbsd" in Godot 4. Building for platform "linuxbsd".')
+ print_warning('Platform "x11" has been renamed to "linuxbsd" in Godot 4. Building for platform "linuxbsd".')
# Alias for convenience.
selected_platform = "linuxbsd"
if selected_platform == "javascript":
# Deprecated alias kept for compatibility.
- print('Platform "javascript" has been renamed to "web" in Godot 4. Building for platform "web".')
+ print_warning('Platform "javascript" has been renamed to "web" in Godot 4. Building for platform "web".')
selected_platform = "web"
if selected_platform not in platform_list:
- if selected_platform == "":
- print("Could not detect platform automatically.")
- elif selected_platform != "list":
- print(f'Invalid target platform "{selected_platform}".')
+ text = "The following platforms are available:\n\t{}\n".format("\n\t".join(platform_list))
+ text += "Please run SCons again and select a valid platform: platform=<string>."
+
+ if selected_platform == "list":
+ print(text)
+ elif selected_platform == "":
+ print_error("Could not detect platform automatically.\n" + text)
+ else:
+ print_error(f'Invalid target platform "{selected_platform}".\n' + text)
- print("The following platforms are available:\n\t{}\n".format("\n\t".join(platform_list)))
- print("Please run SCons again and select a valid platform: platform=<string>.")
Exit(0 if selected_platform == "list" else 255)
# Make sure to update this to the found, valid platform as it's used through the buildsystem as the reference.
@@ -368,7 +383,7 @@ if env["custom_modules"]:
try:
module_search_paths.append(methods.convert_custom_modules_path(p))
except ValueError as e:
- print(e)
+ print_error(e)
Exit(255)
for path in module_search_paths:
@@ -507,7 +522,7 @@ env.SetOption("num_jobs", altered_num_jobs)
if env.GetOption("num_jobs") == altered_num_jobs:
cpu_count = os.cpu_count()
if cpu_count is None:
- print("Couldn't auto-detect CPU count to configure build parallelism. Specify it with the -j argument.")
+ print_warning("Couldn't auto-detect CPU count to configure build parallelism. Specify it with the -j argument.")
else:
safer_cpu_count = cpu_count if cpu_count <= 4 else cpu_count - 1
print(
@@ -531,7 +546,7 @@ env.Append(LINKFLAGS=env.get("linkflags", "").split())
# Feature build profile
disabled_classes = []
if env["build_profile"] != "":
- print("Using feature build profile: " + env["build_profile"])
+ print('Using feature build profile: "{}"'.format(env["build_profile"]))
import json
try:
@@ -543,7 +558,7 @@ if env["build_profile"] != "":
for c in dbo:
env[c] = dbo[c]
except:
- print("Error opening feature build profile: " + env["build_profile"])
+ print_error('Failed to open feature build profile: "{}"'.format(env["build_profile"]))
Exit(255)
methods.write_disabled_classes(disabled_classes)
@@ -605,14 +620,14 @@ cc_version_metadata1 = cc_version["metadata1"] or ""
if methods.using_gcc(env):
if cc_version_major == -1:
- print(
+ print_warning(
"Couldn't detect compiler version, skipping version checks. "
"Build may fail if the compiler doesn't support C++17 fully."
)
# GCC 8 before 8.4 has a regression in the support of guaranteed copy elision
# which causes a build failure: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86521
elif cc_version_major == 8 and cc_version_minor < 4:
- print(
+ print_error(
"Detected GCC 8 version < 8.4, which is not supported due to a "
"regression in its C++17 guaranteed copy elision support. Use a "
'newer GCC version, or Clang 6 or later by passing "use_llvm=yes" '
@@ -620,7 +635,7 @@ if methods.using_gcc(env):
)
Exit(255)
elif cc_version_major < 7:
- print(
+ print_error(
"Detected GCC version older than 7, which does not fully support "
"C++17. Supported versions are GCC 7, 9 and later. Use a newer GCC "
'version, or Clang 6 or later by passing "use_llvm=yes" to the '
@@ -628,7 +643,7 @@ if methods.using_gcc(env):
)
Exit(255)
elif cc_version_metadata1 == "win32":
- print(
+ print_error(
"Detected mingw version is not using posix threads. Only posix "
"version of mingw is supported. "
'Use "update-alternatives --config x86_64-w64-mingw32-g++" '
@@ -636,11 +651,11 @@ if methods.using_gcc(env):
)
Exit(255)
if env["debug_paths_relative"] and cc_version_major < 8:
- print("GCC < 8 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
+ print_warning("GCC < 8 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
env["debug_paths_relative"] = False
elif methods.using_clang(env):
if cc_version_major == -1:
- print(
+ print_warning(
"Couldn't detect compiler version, skipping version checks. "
"Build may fail if the compiler doesn't support C++17 fully."
)
@@ -649,28 +664,30 @@ elif methods.using_clang(env):
elif env["platform"] == "macos" or env["platform"] == "ios":
vanilla = methods.is_vanilla_clang(env)
if vanilla and cc_version_major < 6:
- print(
+ print_warning(
"Detected Clang version older than 6, which does not fully support "
"C++17. Supported versions are Clang 6 and later."
)
Exit(255)
elif not vanilla and cc_version_major < 10:
- print(
+ print_error(
"Detected Apple Clang version older than 10, which does not fully "
"support C++17. Supported versions are Apple Clang 10 and later."
)
Exit(255)
if env["debug_paths_relative"] and not vanilla and cc_version_major < 12:
- print("Apple Clang < 12 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
+ print_warning(
+ "Apple Clang < 12 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option."
+ )
env["debug_paths_relative"] = False
elif cc_version_major < 6:
- print(
+ print_error(
"Detected Clang version older than 6, which does not fully support "
"C++17. Supported versions are Clang 6 and later."
)
Exit(255)
if env["debug_paths_relative"] and cc_version_major < 10:
- print("Clang < 10 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
+ print_warning("Clang < 10 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
env["debug_paths_relative"] = False
# Set optimize and debug_symbols flags.
@@ -906,7 +923,7 @@ if env.editor_build:
# And check if they are met.
if not env.module_check_dependencies("editor"):
- print("Not all modules required by editor builds are enabled.")
+ print_error("Not all modules required by editor builds are enabled.")
Exit(255)
methods.generate_version_header(env.module_version_string)
@@ -932,14 +949,14 @@ env["SHOBJPREFIX"] = env["object_prefix"]
if env["disable_3d"]:
if env.editor_build:
- print("Build option 'disable_3d=yes' cannot be used for editor builds, only for export template builds.")
+ print_error("Build option `disable_3d=yes` cannot be used for editor builds, only for export template builds.")
Exit(255)
else:
env.Append(CPPDEFINES=["_3D_DISABLED"])
if env["disable_advanced_gui"]:
if env.editor_build:
- print(
- "Build option 'disable_advanced_gui=yes' cannot be used for editor builds, "
+ print_error(
+ "Build option `disable_advanced_gui=yes` cannot be used for editor builds, "
"only for export template builds."
)
Exit(255)
@@ -951,7 +968,7 @@ if env["brotli"]:
env.Append(CPPDEFINES=["BROTLI_ENABLED"])
if not env["verbose"]:
- methods.no_verbose(sys, env)
+ methods.no_verbose(env)
GLSL_BUILDERS = {
"RD_GLSL": env.Builder(
@@ -983,7 +1000,7 @@ if env["vsproj"]:
if env["compiledb"] and env.scons_version < (4, 0, 0):
# Generating the compilation DB (`compile_commands.json`) requires SCons 4.0.0 or later.
- print("The `compiledb=yes` option requires SCons 4.0 or later, but your version is %s." % scons_raw_version)
+ print_error("The `compiledb=yes` option requires SCons 4.0 or later, but your version is %s." % scons_raw_version)
Exit(255)
if env.scons_version >= (4, 0, 0):
env.Tool("compilation_db")
@@ -991,7 +1008,7 @@ if env.scons_version >= (4, 0, 0):
if env["ninja"]:
if env.scons_version < (4, 2, 0):
- print("The `ninja=yes` option requires SCons 4.2 or later, but your version is %s." % scons_raw_version)
+ print_error("The `ninja=yes` option requires SCons 4.2 or later, but your version is %s." % scons_raw_version)
Exit(255)
SetOption("experimental", "ninja")
@@ -1048,9 +1065,16 @@ methods.dump(env)
def print_elapsed_time():
- elapsed_time_sec = round(time.time() - time_at_start, 3)
- time_ms = round((elapsed_time_sec % 1) * 1000)
- print("[Time elapsed: {}.{:03}]".format(time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)), time_ms))
+ elapsed_time_sec = round(time.time() - time_at_start, 2)
+ time_centiseconds = round((elapsed_time_sec % 1) * 100)
+ print(
+ "{}[Time elapsed: {}.{:02}]{}".format(
+ methods.ANSI.GRAY,
+ time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)),
+ time_centiseconds,
+ methods.ANSI.RESET,
+ )
+ )
atexit.register(print_elapsed_time)
diff --git a/core/SCsub b/core/SCsub
index ec4658e8ca..91620cb075 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -29,8 +29,8 @@ if "SCRIPT_AES256_ENCRYPTION_KEY" in os.environ:
ec_valid = False
txt += txts
if not ec_valid:
- print("Error: Invalid AES256 encryption key, not 64 hexadecimal characters: '" + key + "'.")
- print(
+ methods.print_error(
+ f'Invalid AES256 encryption key, not 64 hexadecimal characters: "{key}".\n'
"Unset 'SCRIPT_AES256_ENCRYPTION_KEY' in your environment "
"or make sure that it contains exactly 64 hexadecimal characters."
)
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 467b696eae..0996db9d89 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -1921,7 +1921,7 @@ void EngineDebugger::send_message(const String &p_msg, const Array &p_data) {
Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) {
Callable &capture = *(Callable *)p_user;
- if (capture.is_null()) {
+ if (!capture.is_valid()) {
return FAILED;
}
Variant cmd = p_cmd, data = p_data;
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 22a5df9935..b48ea97040 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -387,7 +387,7 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library
p_extension_funcs->set_func, // GDExtensionClassSet set_func;
p_extension_funcs->get_func, // GDExtensionClassGet get_func;
p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
- p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
+ nullptr, // GDExtensionClassFreePropertyList2 free_property_list_func;
p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func;
p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func;
nullptr, // GDExtensionClassValidateProperty validate_property_func;
@@ -406,7 +406,8 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library
};
const ClassCreationDeprecatedInfo legacy = {
- p_extension_funcs->notification_func,
+ p_extension_funcs->notification_func, // GDExtensionClassNotification notification_func;
+ p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
};
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3, &legacy);
}
@@ -420,7 +421,7 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar
p_extension_funcs->set_func, // GDExtensionClassSet set_func;
p_extension_funcs->get_func, // GDExtensionClassGet get_func;
p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
- p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
+ nullptr, // GDExtensionClassFreePropertyList2 free_property_list_func;
p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func;
p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func;
p_extension_funcs->validate_property_func, // GDExtensionClassValidateProperty validate_property_func;
@@ -438,7 +439,11 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar
p_extension_funcs->class_userdata, // void *class_userdata;
};
- _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3);
+ const ClassCreationDeprecatedInfo legacy = {
+ nullptr, // GDExtensionClassNotification notification_func;
+ p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
+ };
+ _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3, &legacy);
}
#endif // DISABLE_DEPRECATED
@@ -514,13 +519,14 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
extension->gdextension.set = p_extension_funcs->set_func;
extension->gdextension.get = p_extension_funcs->get_func;
extension->gdextension.get_property_list = p_extension_funcs->get_property_list_func;
- extension->gdextension.free_property_list = p_extension_funcs->free_property_list_func;
+ extension->gdextension.free_property_list2 = p_extension_funcs->free_property_list_func;
extension->gdextension.property_can_revert = p_extension_funcs->property_can_revert_func;
extension->gdextension.property_get_revert = p_extension_funcs->property_get_revert_func;
extension->gdextension.validate_property = p_extension_funcs->validate_property_func;
#ifndef DISABLE_DEPRECATED
if (p_deprecated_funcs) {
extension->gdextension.notification = p_deprecated_funcs->notification_func;
+ extension->gdextension.free_property_list = p_deprecated_funcs->free_property_list_func;
}
#endif // DISABLE_DEPRECATED
extension->gdextension.notification2 = p_extension_funcs->notification_func;
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index 23b1f51208..3b15639890 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -71,6 +71,7 @@ class GDExtension : public Resource {
struct ClassCreationDeprecatedInfo {
#ifndef DISABLE_DEPRECATED
GDExtensionClassNotification notification_func = nullptr;
+ GDExtensionClassFreePropertyList free_property_list_func = nullptr;
#endif // DISABLE_DEPRECATED
};
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index e9c570e994..00a98af4e2 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -256,6 +256,7 @@ typedef struct {
typedef const GDExtensionPropertyInfo *(*GDExtensionClassGetPropertyList)(GDExtensionClassInstancePtr p_instance, uint32_t *r_count);
typedef void (*GDExtensionClassFreePropertyList)(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list);
+typedef void (*GDExtensionClassFreePropertyList2)(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list, uint32_t p_count);
typedef GDExtensionBool (*GDExtensionClassPropertyCanRevert)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name);
typedef GDExtensionBool (*GDExtensionClassPropertyGetRevert)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret);
typedef GDExtensionBool (*GDExtensionClassValidateProperty)(GDExtensionClassInstancePtr p_instance, GDExtensionPropertyInfo *p_property);
@@ -333,7 +334,7 @@ typedef struct {
GDExtensionClassSet set_func;
GDExtensionClassGet get_func;
GDExtensionClassGetPropertyList get_property_list_func;
- GDExtensionClassFreePropertyList free_property_list_func;
+ GDExtensionClassFreePropertyList2 free_property_list_func;
GDExtensionClassPropertyCanRevert property_can_revert_func;
GDExtensionClassPropertyGetRevert property_get_revert_func;
GDExtensionClassValidateProperty validate_property_func;
diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp
index e99885befa..5df67b1103 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -582,6 +582,10 @@ void DirAccess::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove", "path"), &DirAccess::remove);
ClassDB::bind_static_method("DirAccess", D_METHOD("remove_absolute", "path"), &DirAccess::remove_absolute);
+ ClassDB::bind_method(D_METHOD("is_link", "path"), &DirAccess::is_link);
+ ClassDB::bind_method(D_METHOD("read_link", "path"), &DirAccess::read_link);
+ ClassDB::bind_method(D_METHOD("create_link", "source", "target"), &DirAccess::create_link);
+
ClassDB::bind_method(D_METHOD("set_include_navigational", "enable"), &DirAccess::set_include_navigational);
ClassDB::bind_method(D_METHOD("get_include_navigational"), &DirAccess::get_include_navigational);
ClassDB::bind_method(D_METHOD("set_include_hidden", "enable"), &DirAccess::set_include_hidden);
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index 7ef1ce74ed..876635529c 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -138,7 +138,7 @@ public:
return nullptr;
}
- static void placeholder_instance_free_property_list(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list) {
+ static void placeholder_instance_free_property_list(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list, uint32_t p_count) {
}
static GDExtensionBool placeholder_instance_property_can_revert(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name) {
@@ -600,12 +600,13 @@ ObjectGDExtension *ClassDB::get_placeholder_extension(const StringName &p_class)
placeholder_extension->set = &PlaceholderExtensionInstance::placeholder_instance_set;
placeholder_extension->get = &PlaceholderExtensionInstance::placeholder_instance_get;
placeholder_extension->get_property_list = &PlaceholderExtensionInstance::placeholder_instance_get_property_list;
- placeholder_extension->free_property_list = &PlaceholderExtensionInstance::placeholder_instance_free_property_list;
+ placeholder_extension->free_property_list2 = &PlaceholderExtensionInstance::placeholder_instance_free_property_list;
placeholder_extension->property_can_revert = &PlaceholderExtensionInstance::placeholder_instance_property_can_revert;
placeholder_extension->property_get_revert = &PlaceholderExtensionInstance::placeholder_instance_property_get_revert;
placeholder_extension->validate_property = &PlaceholderExtensionInstance::placeholder_instance_validate_property;
#ifndef DISABLE_DEPRECATED
placeholder_extension->notification = nullptr;
+ placeholder_extension->free_property_list = nullptr;
#endif // DISABLE_DEPRECATED
placeholder_extension->notification2 = &PlaceholderExtensionInstance::placeholder_instance_notification;
placeholder_extension->to_string = &PlaceholderExtensionInstance::placeholder_instance_to_string;
diff --git a/core/object/object.cpp b/core/object/object.cpp
index f8d2feb5a8..b6c8a87a22 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -503,9 +503,14 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
for (uint32_t i = 0; i < pcount; i++) {
p_list->push_back(PropertyInfo(pinfo[i]));
}
- if (current_extension->free_property_list) {
+ if (current_extension->free_property_list2) {
+ current_extension->free_property_list2(_extension_instance, pinfo, pcount);
+ }
+#ifndef DISABLE_DEPRECATED
+ else if (current_extension->free_property_list) {
current_extension->free_property_list(_extension_instance, pinfo);
}
+#endif // DISABLE_DEPRECATED
#ifdef TOOLS_ENABLED
}
#endif
@@ -1455,7 +1460,7 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui
}
bool Object::is_connected(const StringName &p_signal, const Callable &p_callable) const {
- ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, "Cannot determine if connected to '" + p_signal + "': the provided callable is null.");
+ ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, "Cannot determine if connected to '" + p_signal + "': the provided callable is null."); // Should use `is_null`, see note in `connect` about the use of `is_valid`.
const SignalData *s = signal_map.getptr(p_signal);
if (!s) {
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal);
@@ -1478,7 +1483,7 @@ void Object::disconnect(const StringName &p_signal, const Callable &p_callable)
}
bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) {
- ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, "Cannot disconnect from '" + p_signal + "': the provided callable is null.");
+ ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, "Cannot disconnect from '" + p_signal + "': the provided callable is null."); // Should use `is_null`, see note in `connect` about the use of `is_valid`.
SignalData *s = signal_map.getptr(p_signal);
if (!s) {
diff --git a/core/object/object.h b/core/object/object.h
index 915c3a8c25..adb50268d2 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -324,12 +324,13 @@ struct ObjectGDExtension {
GDExtensionClassSet set;
GDExtensionClassGet get;
GDExtensionClassGetPropertyList get_property_list;
- GDExtensionClassFreePropertyList free_property_list;
+ GDExtensionClassFreePropertyList2 free_property_list2;
GDExtensionClassPropertyCanRevert property_can_revert;
GDExtensionClassPropertyGetRevert property_get_revert;
GDExtensionClassValidateProperty validate_property;
#ifndef DISABLE_DEPRECATED
GDExtensionClassNotification notification;
+ GDExtensionClassFreePropertyList free_property_list;
#endif // DISABLE_DEPRECATED
GDExtensionClassNotification2 notification2;
GDExtensionClassToString to_string;
diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp
index 6a1385e268..4d67cd930e 100644
--- a/core/object/undo_redo.cpp
+++ b/core/object/undo_redo.cpp
@@ -144,7 +144,7 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode, bool p_back
}
void UndoRedo::add_do_method(const Callable &p_callable) {
- ERR_FAIL_COND(p_callable.is_null());
+ ERR_FAIL_COND(!p_callable.is_valid());
ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size());
@@ -169,7 +169,7 @@ void UndoRedo::add_do_method(const Callable &p_callable) {
}
void UndoRedo::add_undo_method(const Callable &p_callable) {
- ERR_FAIL_COND(p_callable.is_null());
+ ERR_FAIL_COND(!p_callable.is_valid());
ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size());
diff --git a/core/templates/ring_buffer.h b/core/templates/ring_buffer.h
index 54148a59bf..f5161cefa4 100644
--- a/core/templates/ring_buffer.h
+++ b/core/templates/ring_buffer.h
@@ -211,10 +211,10 @@ public:
size_mask = mask;
}
- RingBuffer<T>(int p_power = 0) {
+ RingBuffer(int p_power = 0) {
resize(p_power);
}
- ~RingBuffer<T>() {}
+ ~RingBuffer() {}
};
#endif // RING_BUFFER_H
diff --git a/core/templates/safe_refcount.h b/core/templates/safe_refcount.h
index 637b068da9..16b605eaff 100644
--- a/core/templates/safe_refcount.h
+++ b/core/templates/safe_refcount.h
@@ -146,7 +146,7 @@ public:
}
}
- _ALWAYS_INLINE_ explicit SafeNumeric<T>(T p_value = static_cast<T>(0)) {
+ _ALWAYS_INLINE_ explicit SafeNumeric(T p_value = static_cast<T>(0)) {
set(p_value);
}
};
diff --git a/doc/classes/DirAccess.xml b/doc/classes/DirAccess.xml
index 03d7f68f43..9c71addf0c 100644
--- a/doc/classes/DirAccess.xml
+++ b/doc/classes/DirAccess.xml
@@ -94,6 +94,16 @@
Static version of [method copy]. Supports only absolute paths.
</description>
</method>
+ <method name="create_link">
+ <return type="int" enum="Error" />
+ <param index="0" name="source" type="String" />
+ <param index="1" name="target" type="String" />
+ <description>
+ Creates symbolic link between files or folders.
+ [b]Note:[/b] On Windows, this method works only if the application is running with elevated privileges or Developer Mode is enabled.
+ [b]Note:[/b] This method is implemented on macOS, Linux, and Windows.
+ </description>
+ </method>
<method name="current_is_dir" qualifiers="const">
<return type="bool" />
<description>
@@ -212,6 +222,14 @@
[b]Note:[/b] This method is implemented on macOS, Linux (for EXT4 and F2FS filesystems only) and Windows. On other platforms, it always returns [code]true[/code].
</description>
</method>
+ <method name="is_link">
+ <return type="bool" />
+ <param index="0" name="path" type="String" />
+ <description>
+ Returns [code]true[/code] if the file or directory is a symbolic link, directory junction, or other reparse point.
+ [b]Note:[/b] This method is implemented on macOS, Linux, and Windows.
+ </description>
+ </method>
<method name="list_dir_begin">
<return type="int" enum="Error" />
<description>
@@ -264,6 +282,14 @@
Returns [code]null[/code] if opening the directory failed. You can use [method get_open_error] to check the error that occurred.
</description>
</method>
+ <method name="read_link">
+ <return type="String" />
+ <param index="0" name="path" type="String" />
+ <description>
+ Returns target of the symbolic link.
+ [b]Note:[/b] This method is implemented on macOS, Linux, and Windows.
+ </description>
+ </method>
<method name="remove">
<return type="int" enum="Error" />
<param index="0" name="path" type="String" />
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 42ca336a25..e614a897d5 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -1602,7 +1602,7 @@
<param index="0" name="max_size" type="Vector2i" />
<param index="1" name="window_id" type="int" default="0" />
<description>
- Sets the maximum size of the window specified by [param window_id] in pixels. Normally, the user will not be able to drag the window to make it smaller than the specified size. See also [method window_get_max_size].
+ Sets the maximum size of the window specified by [param window_id] in pixels. Normally, the user will not be able to drag the window to make it larger than the specified size. See also [method window_get_max_size].
[b]Note:[/b] It's recommended to change this value using [member Window.max_size] instead.
[b]Note:[/b] Using third-party tools, it is possible for users to disable window geometry restrictions and therefore bypass this limit.
</description>
@@ -1612,7 +1612,7 @@
<param index="0" name="min_size" type="Vector2i" />
<param index="1" name="window_id" type="int" default="0" />
<description>
- Sets the minimum size for the given window to [param min_size] (in pixels). Normally, the user will not be able to drag the window to make it larger than the specified size. See also [method window_get_min_size].
+ Sets the minimum size for the given window to [param min_size] in pixels. Normally, the user will not be able to drag the window to make it smaller than the specified size. See also [method window_get_min_size].
[b]Note:[/b] It's recommended to change this value using [member Window.min_size] instead.
[b]Note:[/b] By default, the main window has a minimum size of [code]Vector2i(64, 64)[/code]. This prevents issues that can arise when the window is resized to a near-zero size.
[b]Note:[/b] Using third-party tools, it is possible for users to disable window geometry restrictions and therefore bypass this limit.
diff --git a/drivers/SCsub b/drivers/SCsub
index 9c8b16d3e5..e77b96cc87 100644
--- a/drivers/SCsub
+++ b/drivers/SCsub
@@ -14,6 +14,8 @@ SConscript("coreaudio/SCsub")
SConscript("pulseaudio/SCsub")
if env["platform"] == "windows":
SConscript("wasapi/SCsub")
+ if not env.msvc:
+ SConscript("backtrace/SCsub")
if env["xaudio2"]:
SConscript("xaudio2/SCsub")
diff --git a/drivers/backtrace/SCsub b/drivers/backtrace/SCsub
new file mode 100644
index 0000000000..f61fb21581
--- /dev/null
+++ b/drivers/backtrace/SCsub
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+Import("env")
+
+env_backtrace = env.Clone()
+
+# Thirdparty source files
+
+thirdparty_obj = []
+
+thirdparty_dir = "#thirdparty/libbacktrace/"
+thirdparty_sources = [
+ "atomic.c",
+ "dwarf.c",
+ "fileline.c",
+ "posix.c",
+ "print.c",
+ "sort.c",
+ "state.c",
+ "backtrace.c",
+ "simple.c",
+ "pecoff.c",
+ "read.c",
+ "alloc.c",
+]
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+env_backtrace.Prepend(CPPPATH=[thirdparty_dir])
+
+env_thirdparty = env_backtrace.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+
+env.drivers_sources += thirdparty_obj
+
+# Godot source files
+
+driver_obj = []
+
+env_backtrace.add_source_files(driver_obj, "*.cpp")
+env.drivers_sources += driver_obj
+
+# Needed to force rebuilding the driver files when the thirdparty library is updated.
+env.Depends(driver_obj, thirdparty_obj)
diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp
index 7e2e3dfa2b..356dc06733 100644
--- a/drivers/gles3/storage/utilities.cpp
+++ b/drivers/gles3/storage/utilities.cpp
@@ -299,7 +299,7 @@ void Utilities::visibility_notifier_call(RID p_notifier, bool p_enter, bool p_de
ERR_FAIL_NULL(vn);
if (p_enter) {
- if (!vn->enter_callback.is_null()) {
+ if (vn->enter_callback.is_valid()) {
if (p_deferred) {
vn->enter_callback.call_deferred();
} else {
@@ -307,7 +307,7 @@ void Utilities::visibility_notifier_call(RID p_notifier, bool p_enter, bool p_de
}
}
} else {
- if (!vn->exit_callback.is_null()) {
+ if (vn->exit_callback.is_valid()) {
if (p_deferred) {
vn->exit_callback.call_deferred();
} else {
diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp
index 43dd62cdf6..63ba6a6c96 100644
--- a/drivers/windows/dir_access_windows.cpp
+++ b/drivers/windows/dir_access_windows.cpp
@@ -396,6 +396,66 @@ bool DirAccessWindows::is_case_sensitive(const String &p_path) const {
}
}
+bool DirAccessWindows::is_link(String p_file) {
+ String f = p_file;
+
+ if (!f.is_absolute_path()) {
+ f = get_current_dir().path_join(f);
+ }
+ f = fix_path(f);
+
+ DWORD attr = GetFileAttributesW((LPCWSTR)(f.utf16().get_data()));
+ if (attr == INVALID_FILE_ATTRIBUTES) {
+ return false;
+ }
+
+ return (attr & FILE_ATTRIBUTE_REPARSE_POINT);
+}
+
+String DirAccessWindows::read_link(String p_file) {
+ String f = p_file;
+
+ if (!f.is_absolute_path()) {
+ f = get_current_dir().path_join(f);
+ }
+ f = fix_path(f);
+
+ HANDLE hfile = CreateFileW((LPCWSTR)(f.utf16().get_data()), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+ if (hfile == INVALID_HANDLE_VALUE) {
+ return f;
+ }
+
+ DWORD ret = GetFinalPathNameByHandleW(hfile, nullptr, 0, VOLUME_NAME_DOS | FILE_NAME_NORMALIZED);
+ if (ret == 0) {
+ return f;
+ }
+ Char16String cs;
+ cs.resize(ret + 1);
+ GetFinalPathNameByHandleW(hfile, (LPWSTR)cs.ptrw(), ret, VOLUME_NAME_DOS | FILE_NAME_NORMALIZED);
+ CloseHandle(hfile);
+
+ return String::utf16((const char16_t *)cs.ptr(), ret).trim_prefix(R"(\\?\)");
+}
+
+Error DirAccessWindows::create_link(String p_source, String p_target) {
+ if (p_target.is_relative_path()) {
+ p_target = get_current_dir().path_join(p_target);
+ }
+
+ p_source = fix_path(p_source);
+ p_target = fix_path(p_target);
+
+ DWORD file_attr = GetFileAttributesW((LPCWSTR)(p_source.utf16().get_data()));
+ bool is_dir = (file_attr & FILE_ATTRIBUTE_DIRECTORY);
+
+ DWORD flags = ((is_dir) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
+ if (CreateSymbolicLinkW((LPCWSTR)p_target.utf16().get_data(), (LPCWSTR)p_source.utf16().get_data(), flags) != 0) {
+ return OK;
+ } else {
+ return FAILED;
+ }
+}
+
DirAccessWindows::DirAccessWindows() {
p = memnew(DirAccessWindowsPrivate);
p->h = INVALID_HANDLE_VALUE;
diff --git a/drivers/windows/dir_access_windows.h b/drivers/windows/dir_access_windows.h
index 576ba18d9a..46755cbf33 100644
--- a/drivers/windows/dir_access_windows.h
+++ b/drivers/windows/dir_access_windows.h
@@ -77,9 +77,9 @@ public:
virtual Error rename(String p_path, String p_new_path) override;
virtual Error remove(String p_path) override;
- virtual bool is_link(String p_file) override { return false; };
- virtual String read_link(String p_file) override { return p_file; };
- virtual Error create_link(String p_source, String p_target) override { return FAILED; };
+ virtual bool is_link(String p_file) override;
+ virtual String read_link(String p_file) override;
+ virtual Error create_link(String p_source, String p_target) override;
uint64_t get_space_left() override;
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index 726e0fdc5a..9885d9d7ee 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -32,6 +32,7 @@
#include "file_access_windows.h"
+#include "core/config/project_settings.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
@@ -121,19 +122,63 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) {
// Windows is case insensitive, but all other platforms are sensitive to it
// To ease cross-platform development, we issue a warning if users try to access
// a file using the wrong case (which *works* on Windows, but won't on other
- // platforms).
- if (p_mode_flags == READ) {
- WIN32_FIND_DATAW d;
- HANDLE fnd = FindFirstFileW((LPCWSTR)(path.utf16().get_data()), &d);
- if (fnd != INVALID_HANDLE_VALUE) {
- String fname = String::utf16((const char16_t *)(d.cFileName));
- if (!fname.is_empty()) {
- String base_file = path.get_file();
- if (base_file != fname && base_file.findn(fname) == 0) {
- WARN_PRINT("Case mismatch opening requested file '" + base_file + "', stored as '" + fname + "' in the filesystem. This file will not open when exported to other case-sensitive platforms.");
+ // platforms), we only check for relative paths, or paths in res:// or user://,
+ // other paths aren't likely to be portable anyway.
+ if (p_mode_flags == READ && (p_path.is_relative_path() || get_access_type() != ACCESS_FILESYSTEM)) {
+ String base_path = path;
+ String working_path;
+ String proper_path;
+
+ if (get_access_type() == ACCESS_RESOURCES) {
+ if (ProjectSettings::get_singleton()) {
+ working_path = ProjectSettings::get_singleton()->get_resource_path();
+ if (!working_path.is_empty()) {
+ base_path = working_path.path_to_file(base_path);
}
}
+ proper_path = "res://";
+ } else if (get_access_type() == ACCESS_USERDATA) {
+ working_path = OS::get_singleton()->get_user_data_dir();
+ if (!working_path.is_empty()) {
+ base_path = working_path.path_to_file(base_path);
+ }
+ proper_path = "user://";
+ }
+
+ WIN32_FIND_DATAW d;
+ Vector<String> parts = base_path.split("/");
+
+ bool mismatch = false;
+
+ for (const String &part : parts) {
+ working_path = working_path.path_join(part);
+
+ // Skip if relative.
+ if (part == "." || part == "..") {
+ proper_path = proper_path.path_join(part);
+ continue;
+ }
+
+ HANDLE fnd = FindFirstFileW((LPCWSTR)(working_path.utf16().get_data()), &d);
+
+ if (fnd == INVALID_HANDLE_VALUE) {
+ mismatch = false;
+ break;
+ }
+
+ const String fname = String::utf16((const char16_t *)(d.cFileName));
+
FindClose(fnd);
+
+ if (!mismatch) {
+ mismatch = (part != fname && part.findn(fname) == 0);
+ }
+
+ proper_path = proper_path.path_join(fname);
+ }
+
+ if (mismatch) {
+ WARN_PRINT("Case mismatch opening requested file '" + p_path + "', stored as '" + proper_path + "' in the filesystem. This file will not open when exported to other case-sensitive platforms.");
}
}
#endif
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index bec95d40c6..86fe9dd1fb 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -4485,6 +4485,7 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
if (p_id.type == Animation::TYPE_VALUE) {
undo_redo->add_do_method(reset_anim, "value_track_set_update_mode", p_next_tracks.reset, update_mode);
}
+ undo_redo->add_do_method(reset_anim, "track_set_interpolation_type", p_next_tracks.reset, interp_type);
undo_redo->add_do_method(reset_anim, "track_insert_key", p_next_tracks.reset, 0.0f, value);
undo_redo->add_undo_method(reset_anim, "remove_track", reset_anim->get_track_count());
p_next_tracks.reset++;
diff --git a/editor/debugger/debug_adapter/debug_adapter_server.h b/editor/debugger/debug_adapter/debug_adapter_server.h
index c834ab2182..310bac1b6c 100644
--- a/editor/debugger/debug_adapter/debug_adapter_server.h
+++ b/editor/debugger/debug_adapter/debug_adapter_server.h
@@ -32,7 +32,7 @@
#define DEBUG_ADAPTER_SERVER_H
#include "debug_adapter_protocol.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class DebugAdapterServer : public EditorPlugin {
GDCLASS(DebugAdapterServer, EditorPlugin);
diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp
index 61c4eed669..af0419a81a 100644
--- a/editor/editor_about.cpp
+++ b/editor/editor_about.cpp
@@ -63,7 +63,6 @@ void EditorAbout::_notification(int p_what) {
_logo->set_texture(get_editor_theme_icon(SNAME("Logo")));
- Ref<StyleBoxEmpty> empty_stylebox = memnew(StyleBoxEmpty);
for (ItemList *il : name_lists) {
for (int i = 0; i < il->get_item_count(); i++) {
if (il->get_item_metadata(i)) {
diff --git a/editor/editor_audio_buses.h b/editor/editor_audio_buses.h
index 99e3214781..b1f811fbf6 100644
--- a/editor/editor_audio_buses.h
+++ b/editor/editor_audio_buses.h
@@ -31,7 +31,7 @@
#ifndef EDITOR_AUDIO_BUSES_H
#define EDITOR_AUDIO_BUSES_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/control.h"
diff --git a/editor/editor_builders.py b/editor/editor_builders.py
index a25f4949c4..cfe6e56b49 100644
--- a/editor/editor_builders.py
+++ b/editor/editor_builders.py
@@ -7,6 +7,7 @@ import subprocess
import tempfile
import uuid
import zlib
+from methods import print_warning
def make_doc_header(target, source, env):
@@ -57,7 +58,7 @@ def make_translations_header(target, source, env, category):
msgfmt_available = shutil.which("msgfmt") is not None
if not msgfmt_available:
- print("WARNING: msgfmt is not found, using .po files instead of .mo")
+ print_warning("msgfmt is not found, using .po files instead of .mo")
xl_names = []
for i in range(len(sorted_paths)):
@@ -71,8 +72,8 @@ def make_translations_header(target, source, env, category):
with open(mo_path, "rb") as f:
buf = f.read()
except OSError as e:
- print(
- "WARNING: msgfmt execution failed, using .po file instead of .mo: path=%r; [%s] %s"
+ print_warning(
+ "msgfmt execution failed, using .po file instead of .mo: path=%r; [%s] %s"
% (sorted_paths[i], e.__class__.__name__, e)
)
with open(sorted_paths[i], "rb") as f:
@@ -82,9 +83,8 @@ def make_translations_header(target, source, env, category):
os.remove(mo_path)
except OSError as e:
# Do not fail the entire build if it cannot delete a temporary file.
- print(
- "WARNING: Could not delete temporary .mo file: path=%r; [%s] %s"
- % (mo_path, e.__class__.__name__, e)
+ print_warning(
+ "Could not delete temporary .mo file: path=%r; [%s] %s" % (mo_path, e.__class__.__name__, e)
)
else:
with open(sorted_paths[i], "rb") as f:
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index bdc6504417..bd1eef8e53 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -36,9 +36,9 @@
#include "core/io/image_loader.h"
#include "core/io/resource_loader.h"
#include "editor/editor_node.h"
-#include "editor/editor_plugin.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/multi_node_edit.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/themes/editor_scale.h"
#include "scene/resources/packed_scene.h"
diff --git a/editor/editor_help.h b/editor/editor_help.h
index 078b42b439..ca3a05275f 100644
--- a/editor/editor_help.h
+++ b/editor/editor_help.h
@@ -34,7 +34,7 @@
#include "core/os/thread.h"
#include "editor/code_editor.h"
#include "editor/doc_tools.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/popup.h"
diff --git a/editor/editor_help_search.h b/editor/editor_help_search.h
index 39ffc2f71b..58061dae4c 100644
--- a/editor/editor_help_search.h
+++ b/editor/editor_help_search.h
@@ -34,7 +34,7 @@
#include "core/templates/rb_map.h"
#include "editor/code_editor.h"
#include "editor/editor_help.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/option_button.h"
#include "scene/gui/tree.h"
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 50cc89c618..41396a2ed1 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -3379,7 +3379,12 @@ void EditorInspector::update_tree() {
if (use_doc_hints) {
// `|` separators used in `EditorHelpBit`.
if (theme_item_name.is_empty()) {
- if (p.usage & PROPERTY_USAGE_INTERNAL) {
+ if (p.name.contains("shader_parameter/")) {
+ ShaderMaterial *shader_material = Object::cast_to<ShaderMaterial>(object);
+ if (shader_material) {
+ ep->set_tooltip_text("property|" + shader_material->get_shader()->get_path() + "|" + property_prefix + p.name);
+ }
+ } else if (p.usage & PROPERTY_USAGE_INTERNAL) {
ep->set_tooltip_text("internal_property|" + classname + "|" + property_prefix + p.name);
} else {
ep->set_tooltip_text("property|" + classname + "|" + property_prefix + p.name);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 134733fa11..b9ac7b6d42 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -90,7 +90,6 @@
#include "editor/editor_log.h"
#include "editor/editor_native_shader_source_visualizer.h"
#include "editor/editor_paths.h"
-#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
#include "editor/editor_property_name_processor.h"
#include "editor/editor_quick_open.h"
@@ -134,12 +133,12 @@
#include "editor/inspector_dock.h"
#include "editor/multi_node_edit.h"
#include "editor/node_dock.h"
-#include "editor/plugin_config_dialog.h"
#include "editor/plugins/animation_player_editor_plugin.h"
#include "editor/plugins/asset_library_editor_plugin.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/plugins/debugger_editor_plugin.h"
#include "editor/plugins/dedicated_server_export_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/editor_preview_plugins.h"
#include "editor/plugins/editor_resource_conversion_plugin.h"
#include "editor/plugins/gdextension_export_plugin.h"
@@ -148,6 +147,7 @@
#include "editor/plugins/node_3d_editor_plugin.h"
#include "editor/plugins/packed_scene_translation_parser_plugin.h"
#include "editor/plugins/particle_process_material_editor_plugin.h"
+#include "editor/plugins/plugin_config_dialog.h"
#include "editor/plugins/root_motion_editor_plugin.h"
#include "editor/plugins/script_text_editor.h"
#include "editor/plugins/text_editor.h"
diff --git a/editor/editor_node.h b/editor/editor_node.h
index ad0a7ec5e0..54c84bc6a6 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -35,7 +35,7 @@
#include "core/templates/safe_refcount.h"
#include "editor/editor_data.h"
#include "editor/editor_folding.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
typedef void (*EditorNodeInitCallback)();
typedef void (*EditorPluginInitializeCallback)();
diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp
index 9e4bf2b7d9..e082366c44 100644
--- a/editor/editor_resource_picker.cpp
+++ b/editor/editor_resource_picker.cpp
@@ -488,8 +488,8 @@ void EditorResourcePicker::set_create_options(Object *p_menu_node) {
if (!base_type.is_empty()) {
int idx = 0;
- HashSet<StringName> allowed_types;
- _get_allowed_types(false, &allowed_types);
+ _ensure_allowed_types();
+ HashSet<StringName> allowed_types = allowed_types_without_convert;
for (const StringName &E : allowed_types) {
const String &t = E;
@@ -593,23 +593,29 @@ static void _add_allowed_type(const StringName &p_type, HashSet<StringName> *p_v
}
}
-void EditorResourcePicker::_get_allowed_types(bool p_with_convert, HashSet<StringName> *p_vector) const {
+void EditorResourcePicker::_ensure_allowed_types() const {
+ if (!allowed_types_without_convert.is_empty()) {
+ return;
+ }
+
Vector<String> allowed_types = base_type.split(",");
int size = allowed_types.size();
for (int i = 0; i < size; i++) {
- String base = allowed_types[i].strip_edges();
+ const String base = allowed_types[i].strip_edges();
+ _add_allowed_type(base, &allowed_types_without_convert);
+ }
- _add_allowed_type(base, p_vector);
+ allowed_types_with_convert = HashSet<StringName>(allowed_types_without_convert);
- if (p_with_convert) {
- if (base == "BaseMaterial3D") {
- p_vector->insert("Texture2D");
- } else if (base == "ShaderMaterial") {
- p_vector->insert("Shader");
- } else if (base == "Texture2D") {
- p_vector->insert("Image");
- }
+ for (int i = 0; i < size; i++) {
+ const String base = allowed_types[i].strip_edges();
+ if (base == "BaseMaterial3D") {
+ allowed_types_with_convert.insert("Texture2D");
+ } else if (base == "ShaderMaterial") {
+ allowed_types_with_convert.insert("Shader");
+ } else if (base == "Texture2D") {
+ allowed_types_with_convert.insert("Image");
}
}
}
@@ -645,8 +651,8 @@ bool EditorResourcePicker::_is_drop_valid(const Dictionary &p_drag_data) const {
}
}
- HashSet<StringName> allowed_types;
- _get_allowed_types(true, &allowed_types);
+ _ensure_allowed_types();
+ HashSet<StringName> allowed_types = allowed_types_with_convert;
if (res.is_valid()) {
String res_type = _get_resource_type(res);
@@ -713,8 +719,8 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
}
if (dropped_resource.is_valid()) {
- HashSet<StringName> allowed_types;
- _get_allowed_types(false, &allowed_types);
+ _ensure_allowed_types();
+ HashSet<StringName> allowed_types = allowed_types_without_convert;
String res_type = _get_resource_type(dropped_resource);
@@ -835,8 +841,8 @@ void EditorResourcePicker::set_base_type(const String &p_base_type) {
// There is a possibility that the new base type is conflicting with the existing value.
// Keep the value, but warn the user that there is a potential mistake.
if (!base_type.is_empty() && edited_resource.is_valid()) {
- HashSet<StringName> allowed_types;
- _get_allowed_types(true, &allowed_types);
+ _ensure_allowed_types();
+ HashSet<StringName> allowed_types = allowed_types_with_convert;
StringName custom_class;
bool is_custom = false;
@@ -857,8 +863,8 @@ String EditorResourcePicker::get_base_type() const {
}
Vector<String> EditorResourcePicker::get_allowed_types() const {
- HashSet<StringName> allowed_types;
- _get_allowed_types(false, &allowed_types);
+ _ensure_allowed_types();
+ HashSet<StringName> allowed_types = allowed_types_without_convert;
Vector<String> types;
types.resize(allowed_types.size());
@@ -881,8 +887,8 @@ void EditorResourcePicker::set_edited_resource(Ref<Resource> p_resource) {
}
if (!base_type.is_empty()) {
- HashSet<StringName> allowed_types;
- _get_allowed_types(true, &allowed_types);
+ _ensure_allowed_types();
+ HashSet<StringName> allowed_types = allowed_types_with_convert;
StringName custom_class;
bool is_custom = false;
diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h
index 333d9c9915..28229e6b37 100644
--- a/editor/editor_resource_picker.h
+++ b/editor/editor_resource_picker.h
@@ -52,6 +52,8 @@ class EditorResourcePicker : public HBoxContainer {
bool dropping = false;
Vector<String> inheritors_array;
+ mutable HashSet<StringName> allowed_types_without_convert;
+ mutable HashSet<StringName> allowed_types_with_convert;
Button *assign_button = nullptr;
TextureRect *preview_rect = nullptr;
@@ -97,7 +99,7 @@ class EditorResourcePicker : public HBoxContainer {
void _button_input(const Ref<InputEvent> &p_event);
String _get_resource_type(const Ref<Resource> &p_resource) const;
- void _get_allowed_types(bool p_with_convert, HashSet<StringName> *p_vector) const;
+ void _ensure_allowed_types() const;
bool _is_drop_valid(const Dictionary &p_drag_data) const;
bool _is_type_valid(const String &p_type_name, const HashSet<StringName> &p_allowed_types) const;
diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp
index ddf230dfdb..dd698d74b6 100644
--- a/editor/editor_resource_preview.cpp
+++ b/editor/editor_resource_preview.cpp
@@ -118,8 +118,6 @@ Variant EditorResourcePreviewGenerator::DrawRequester::_post_semaphore() const {
return Variant(); // Needed because of how the callback is used.
}
-EditorResourcePreview *EditorResourcePreview::singleton = nullptr;
-
bool EditorResourcePreview::is_threaded() const {
return RSG::texture_storage->can_create_resources_async();
}
@@ -291,13 +289,17 @@ void EditorResourcePreview::_iterate() {
bool has_small_texture;
uint64_t last_modtime;
String hash;
- _read_preview_cache(f, &tsize, &has_small_texture, &last_modtime, &hash, &preview_metadata);
+ bool outdated;
+ _read_preview_cache(f, &tsize, &has_small_texture, &last_modtime, &hash, &preview_metadata, &outdated);
bool cache_valid = true;
if (tsize != thumbnail_size) {
cache_valid = false;
f.unref();
+ } else if (outdated) {
+ cache_valid = false;
+ f.unref();
} else if (last_modtime != modtime) {
String last_md5 = f->get_line();
String md5 = FileAccess::get_md5(item.path);
@@ -357,14 +359,16 @@ void EditorResourcePreview::_write_preview_cache(Ref<FileAccess> p_file, int p_t
p_file->store_line(itos(p_modified_time));
p_file->store_line(p_hash);
p_file->store_line(VariantUtilityFunctions::var_to_str(p_metadata).replace("\n", " "));
+ p_file->store_line(itos(CURRENT_METADATA_VERSION));
}
-void EditorResourcePreview::_read_preview_cache(Ref<FileAccess> p_file, int *r_thumbnail_size, bool *r_has_small_texture, uint64_t *r_modified_time, String *r_hash, Dictionary *r_metadata) {
+void EditorResourcePreview::_read_preview_cache(Ref<FileAccess> p_file, int *r_thumbnail_size, bool *r_has_small_texture, uint64_t *r_modified_time, String *r_hash, Dictionary *r_metadata, bool *r_outdated) {
*r_thumbnail_size = p_file->get_line().to_int();
*r_has_small_texture = p_file->get_line().to_int();
*r_modified_time = p_file->get_line().to_int();
*r_hash = p_file->get_line();
*r_metadata = VariantUtilityFunctions::str_to_var(p_file->get_line());
+ *r_outdated = p_file->get_line().to_int() < CURRENT_METADATA_VERSION;
}
void EditorResourcePreview::_thread() {
diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h
index 8461651732..6b67acceaa 100644
--- a/editor/editor_resource_preview.h
+++ b/editor/editor_resource_preview.h
@@ -76,7 +76,8 @@ public:
class EditorResourcePreview : public Node {
GDCLASS(EditorResourcePreview, Node);
- static EditorResourcePreview *singleton;
+ inline static constexpr int CURRENT_METADATA_VERSION = 1; // Increment this number to invalidate all previews.
+ inline static EditorResourcePreview *singleton = nullptr;
struct QueueItem {
Ref<Resource> resource;
@@ -118,7 +119,7 @@ class EditorResourcePreview : public Node {
void _iterate();
void _write_preview_cache(Ref<FileAccess> p_file, int p_thumbnail_size, bool p_has_small_texture, uint64_t p_modified_time, const String &p_hash, const Dictionary &p_metadata);
- void _read_preview_cache(Ref<FileAccess> p_file, int *r_thumbnail_size, bool *r_has_small_texture, uint64_t *r_modified_time, String *r_hash, Dictionary *r_metadata);
+ void _read_preview_cache(Ref<FileAccess> p_file, int *r_thumbnail_size, bool *r_has_small_texture, uint64_t *r_modified_time, String *r_hash, Dictionary *r_metadata, bool *r_outdated);
Vector<Ref<EditorResourcePreviewGenerator>> preview_generators;
diff --git a/editor/gui/editor_validation_panel.cpp b/editor/gui/editor_validation_panel.cpp
index c08af1915f..80bb08517c 100644
--- a/editor/gui/editor_validation_panel.cpp
+++ b/editor/gui/editor_validation_panel.cpp
@@ -81,7 +81,7 @@ void EditorValidationPanel::set_update_callback(const Callable &p_callback) {
}
void EditorValidationPanel::update() {
- ERR_FAIL_COND(update_callback.is_null());
+ ERR_FAIL_COND(!update_callback.is_valid());
if (pending_update) {
return;
diff --git a/editor/import/audio_stream_import_settings.h b/editor/import/audio_stream_import_settings.h
index da6344adb9..931faf45af 100644
--- a/editor/import/audio_stream_import_settings.h
+++ b/editor/import/audio_stream_import_settings.h
@@ -31,7 +31,7 @@
#ifndef AUDIO_STREAM_IMPORT_SETTINGS_H
#define AUDIO_STREAM_IMPORT_SETTINGS_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/audio/audio_stream_player.h"
#include "scene/gui/color_rect.h"
#include "scene/gui/dialogs.h"
diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp
index ab14a5f01d..c8add6ab20 100644
--- a/editor/import/resource_importer_wav.cpp
+++ b/editor/import/resource_importer_wav.cpp
@@ -330,14 +330,12 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
for (int i = 0; i < new_data_frames; i++) {
// Cubic interpolation should be enough.
- float mu = frac;
-
float y0 = data[MAX(0, ipos - 1) * format_channels + c];
float y1 = data[ipos * format_channels + c];
float y2 = data[MIN(frames - 1, ipos + 1) * format_channels + c];
float y3 = data[MIN(frames - 1, ipos + 2) * format_channels + c];
- new_data.write[i * format_channels + c] = Math::cubic_interpolate(y1, y2, y0, y3, mu);
+ new_data.write[i * format_channels + c] = Math::cubic_interpolate(y1, y2, y0, y3, frac);
// update position and always keep fractional part within ]0...1]
// in order to avoid 32bit floating point precision errors
diff --git a/editor/import_defaults_editor.cpp b/editor/import_defaults_editor.cpp
index 569fdc091e..968f2a8915 100644
--- a/editor/import_defaults_editor.cpp
+++ b/editor/import_defaults_editor.cpp
@@ -34,9 +34,9 @@
#include "core/io/resource_importer.h"
#include "editor/action_map_editor.h"
#include "editor/editor_autoload_settings.h"
-#include "editor/editor_plugin_settings.h"
#include "editor/editor_sectioned_inspector.h"
#include "editor/localization_editor.h"
+#include "editor/plugins/editor_plugin_settings.h"
#include "editor/shader_globals_editor.h"
#include "scene/gui/center_container.h"
diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h
index 70cccebf40..42170d9ffd 100644
--- a/editor/plugins/abstract_polygon_2d_editor.h
+++ b/editor/plugins/abstract_polygon_2d_editor.h
@@ -31,7 +31,7 @@
#ifndef ABSTRACT_POLYGON_2D_EDITOR_H
#define ABSTRACT_POLYGON_2D_EDITOR_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/polygon_2d.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp
index 8c2b738549..1baeddbe86 100644
--- a/editor/plugins/animation_blend_space_1d_editor.cpp
+++ b/editor/plugins/animation_blend_space_1d_editor.cpp
@@ -583,9 +583,9 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) {
snap->set_icon(get_editor_theme_icon(SNAME("SnapGrid")));
open_editor->set_icon(get_editor_theme_icon(SNAME("Edit")));
interpolation->clear();
- interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackContinuous")), "", 0);
- interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackDiscrete")), "", 1);
- interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackCapture")), "", 2);
+ interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackContinuous")), TTR("Continuous"), 0);
+ interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackDiscrete")), TTR("Discrete"), 1);
+ interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackCapture")), TTR("Capture"), 2);
} break;
case NOTIFICATION_PROCESS: {
diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h
index a0ed813100..00bd3c767d 100644
--- a/editor/plugins/animation_blend_space_1d_editor.h
+++ b/editor/plugins/animation_blend_space_1d_editor.h
@@ -31,8 +31,8 @@
#ifndef ANIMATION_BLEND_SPACE_1D_EDITOR_H
#define ANIMATION_BLEND_SPACE_1D_EDITOR_H
-#include "editor/editor_plugin.h"
#include "editor/plugins/animation_tree_editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/animation/animation_blend_space_1d.h"
#include "scene/gui/graph_edit.h"
#include "scene/gui/popup.h"
diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp
index ec67fb7254..06853f0862 100644
--- a/editor/plugins/animation_blend_space_2d_editor.cpp
+++ b/editor/plugins/animation_blend_space_2d_editor.cpp
@@ -807,9 +807,9 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) {
open_editor->set_icon(get_editor_theme_icon(SNAME("Edit")));
auto_triangles->set_icon(get_editor_theme_icon(SNAME("AutoTriangle")));
interpolation->clear();
- interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackContinuous")), "", 0);
- interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackDiscrete")), "", 1);
- interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackCapture")), "", 2);
+ interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackContinuous")), TTR("Continuous"), 0);
+ interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackDiscrete")), TTR("Discrete"), 1);
+ interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackCapture")), TTR("Capture"), 2);
} break;
case NOTIFICATION_PROCESS: {
diff --git a/editor/plugins/animation_blend_space_2d_editor.h b/editor/plugins/animation_blend_space_2d_editor.h
index a89a7b4511..15d81c0707 100644
--- a/editor/plugins/animation_blend_space_2d_editor.h
+++ b/editor/plugins/animation_blend_space_2d_editor.h
@@ -31,8 +31,8 @@
#ifndef ANIMATION_BLEND_SPACE_2D_EDITOR_H
#define ANIMATION_BLEND_SPACE_2D_EDITOR_H
-#include "editor/editor_plugin.h"
#include "editor/plugins/animation_tree_editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/animation/animation_blend_space_2d.h"
#include "scene/gui/graph_edit.h"
#include "scene/gui/popup.h"
diff --git a/editor/plugins/animation_library_editor.h b/editor/plugins/animation_library_editor.h
index a268e68932..c8d9274f4f 100644
--- a/editor/plugins/animation_library_editor.h
+++ b/editor/plugins/animation_library_editor.h
@@ -32,7 +32,7 @@
#define ANIMATION_LIBRARY_EDITOR_H
#include "editor/animation_track_editor.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/animation/animation_mixer.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/tree.h"
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index a1175e2a0f..70b31759fc 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -32,8 +32,8 @@
#define ANIMATION_PLAYER_EDITOR_PLUGIN_H
#include "editor/animation_track_editor.h"
-#include "editor/editor_plugin.h"
#include "editor/plugins/animation_library_editor.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/animation/animation_player.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/slider.h"
diff --git a/editor/plugins/animation_tree_editor_plugin.h b/editor/plugins/animation_tree_editor_plugin.h
index da979a5315..8dc820695a 100644
--- a/editor/plugins/animation_tree_editor_plugin.h
+++ b/editor/plugins/animation_tree_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef ANIMATION_TREE_EDITOR_PLUGIN_H
#define ANIMATION_TREE_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/animation/animation_tree.h"
#include "scene/gui/graph_edit.h"
diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h
index cb38933bf4..a80e7c8f96 100644
--- a/editor/plugins/asset_library_editor_plugin.h
+++ b/editor/plugins/asset_library_editor_plugin.h
@@ -32,8 +32,8 @@
#define ASSET_LIBRARY_EDITOR_PLUGIN_H
#include "editor/editor_asset_installer.h"
-#include "editor/editor_plugin.h"
-#include "editor/editor_plugin_settings.h"
+#include "editor/plugins/editor_plugin.h"
+#include "editor/plugins/editor_plugin_settings.h"
#include "scene/gui/box_container.h"
#include "scene/gui/check_box.h"
#include "scene/gui/grid_container.h"
diff --git a/editor/plugins/audio_stream_editor_plugin.h b/editor/plugins/audio_stream_editor_plugin.h
index 52aa5f6150..0501409c17 100644
--- a/editor/plugins/audio_stream_editor_plugin.h
+++ b/editor/plugins/audio_stream_editor_plugin.h
@@ -32,7 +32,7 @@
#define AUDIO_STREAM_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/audio/audio_stream_player.h"
#include "scene/gui/button.h"
#include "scene/gui/color_rect.h"
diff --git a/editor/plugins/audio_stream_randomizer_editor_plugin.h b/editor/plugins/audio_stream_randomizer_editor_plugin.h
index 535ab4114b..9d2fc76e9e 100644
--- a/editor/plugins/audio_stream_randomizer_editor_plugin.h
+++ b/editor/plugins/audio_stream_randomizer_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef AUDIO_STREAM_RANDOMIZER_EDITOR_PLUGIN_H
#define AUDIO_STREAM_RANDOMIZER_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "servers/audio/audio_stream.h"
class AudioStreamRandomizerEditorPlugin : public EditorPlugin {
diff --git a/editor/plugins/bit_map_editor_plugin.h b/editor/plugins/bit_map_editor_plugin.h
index afab1da2f7..030536ab6b 100644
--- a/editor/plugins/bit_map_editor_plugin.h
+++ b/editor/plugins/bit_map_editor_plugin.h
@@ -32,7 +32,7 @@
#define BIT_MAP_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/resources/bit_map.h"
class TextureRect;
diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp
index e67f3bd596..2bf9ad9e09 100644
--- a/editor/plugins/bone_map_editor_plugin.cpp
+++ b/editor/plugins/bone_map_editor_plugin.cpp
@@ -861,6 +861,8 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
picklist.clear();
// 4-1. Guess Finger
+ int tips_index = -1;
+ bool thumb_tips_size = 0;
bool named_finger_is_found = false;
LocalVector<String> fingers;
fingers.push_back("thumb|pollex");
@@ -894,6 +896,33 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
search_path.push_back(finger);
finger = skeleton->get_bone_parent(finger);
}
+ // Tips detection by name matching with "distal" from root.
+ for (int j = search_path.size() - 1; j >= 0; j--) {
+ if (RegEx("distal").search(skeleton->get_bone_name(search_path[j]).to_lower()).is_valid()) {
+ tips_index = j - 1;
+ break;
+ }
+ }
+ // Tips detection by name matching with "tip|leaf" from end.
+ if (tips_index < 0) {
+ for (int j = 0; j < search_path.size(); j++) {
+ if (RegEx("tip|leaf").search(skeleton->get_bone_name(search_path[j]).to_lower()).is_valid()) {
+ tips_index = j;
+ break;
+ }
+ }
+ }
+ // Tips detection by thumb children size.
+ if (tips_index < 0) {
+ if (i == 0) {
+ thumb_tips_size = MAX(0, search_path.size() - 3);
+ }
+ tips_index = thumb_tips_size - 1;
+ }
+ // Remove tips.
+ for (int j = 0; j <= tips_index; j++) {
+ search_path.remove_at(0);
+ }
search_path.reverse();
if (search_path.size() == 1) {
p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
@@ -941,6 +970,14 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
}
}
search_path.push_back(finger_root);
+ // Tips detection by thumb children size.
+ if (i == 0) {
+ thumb_tips_size = MAX(0, search_path.size() - 3);
+ }
+ tips_index = thumb_tips_size - 1;
+ for (int j = 0; j <= tips_index; j++) {
+ search_path.remove_at(0);
+ }
search_path.reverse();
if (search_path.size() == 1) {
p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
@@ -958,6 +995,9 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
picklist.clear();
}
}
+
+ tips_index = -1;
+ thumb_tips_size = 0;
named_finger_is_found = false;
if (right_hand_or_palm != -1) {
LocalVector<LocalVector<String>> right_fingers_map;
@@ -985,6 +1025,33 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
search_path.push_back(finger);
finger = skeleton->get_bone_parent(finger);
}
+ // Tips detection by name matching with "distal" from root.
+ for (int j = search_path.size() - 1; j >= 0; j--) {
+ if (RegEx("distal").search(skeleton->get_bone_name(search_path[j]).to_lower()).is_valid()) {
+ tips_index = j - 1;
+ break;
+ }
+ }
+ // Tips detection by name matching with "tip|leaf" from end.
+ if (tips_index < 0) {
+ for (int j = 0; j < search_path.size(); j++) {
+ if (RegEx("tip|leaf").search(skeleton->get_bone_name(search_path[j]).to_lower()).is_valid()) {
+ tips_index = j;
+ break;
+ }
+ }
+ }
+ // Tips detection by thumb children size.
+ if (tips_index < 0) {
+ if (i == 0) {
+ thumb_tips_size = MAX(0, search_path.size() - 3);
+ }
+ tips_index = thumb_tips_size - 1;
+ }
+ // Remove tips.
+ for (int j = 0; j <= tips_index; j++) {
+ search_path.remove_at(0);
+ }
search_path.reverse();
if (search_path.size() == 1) {
p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
@@ -1032,6 +1099,14 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
}
}
search_path.push_back(finger_root);
+ // Tips detection by thumb children size.
+ if (i == 0) {
+ thumb_tips_size = MAX(0, search_path.size() - 3);
+ }
+ tips_index = thumb_tips_size - 1;
+ for (int j = 0; j <= tips_index; j++) {
+ search_path.remove_at(0);
+ }
search_path.reverse();
if (search_path.size() == 1) {
p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
diff --git a/editor/plugins/bone_map_editor_plugin.h b/editor/plugins/bone_map_editor_plugin.h
index 2e7d1ff124..f3aa2fc84d 100644
--- a/editor/plugins/bone_map_editor_plugin.h
+++ b/editor/plugins/bone_map_editor_plugin.h
@@ -32,8 +32,8 @@
#define BONE_MAP_EDITOR_PLUGIN_H
#include "editor/editor_node.h"
-#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
+#include "editor/plugins/editor_plugin.h"
#include "modules/modules_enabled.gen.h" // For regex.
#ifdef MODULE_REGEX_ENABLED
diff --git a/editor/plugins/camera_3d_editor_plugin.h b/editor/plugins/camera_3d_editor_plugin.h
index 7d5fae6f2b..2e4d8a1ee3 100644
--- a/editor/plugins/camera_3d_editor_plugin.h
+++ b/editor/plugins/camera_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef CAMERA_3D_EDITOR_PLUGIN_H
#define CAMERA_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/camera_3d.h"
class Camera3DEditor : public Control {
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index f52ad3dc4f..6d951a77ec 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef CANVAS_ITEM_EDITOR_PLUGIN_H
#define CANVAS_ITEM_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/base_button.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/cast_2d_editor_plugin.h b/editor/plugins/cast_2d_editor_plugin.h
index 36b302f311..cbe7e03008 100644
--- a/editor/plugins/cast_2d_editor_plugin.h
+++ b/editor/plugins/cast_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef CAST_2D_EDITOR_PLUGIN_H
#define CAST_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/node_2d.h"
class CanvasItemEditor;
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.h b/editor/plugins/collision_shape_2d_editor_plugin.h
index 19b2de3821..672e1d9ce0 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.h
+++ b/editor/plugins/collision_shape_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef COLLISION_SHAPE_2D_EDITOR_PLUGIN_H
#define COLLISION_SHAPE_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/physics/collision_shape_2d.h"
class CanvasItemEditor;
diff --git a/editor/plugins/control_editor_plugin.cpp b/editor/plugins/control_editor_plugin.cpp
index 5b0831eeb8..939527b56f 100644
--- a/editor/plugins/control_editor_plugin.cpp
+++ b/editor/plugins/control_editor_plugin.cpp
@@ -413,7 +413,15 @@ bool EditorInspectorPluginControl::can_handle(Object *p_object) {
return Object::cast_to<Control>(p_object) != nullptr;
}
+void EditorInspectorPluginControl::parse_category(Object *p_object, const String &p_category) {
+ inside_control_category = p_category == "Control";
+}
+
void EditorInspectorPluginControl::parse_group(Object *p_object, const String &p_group) {
+ if (!inside_control_category) {
+ return;
+ }
+
Control *control = Object::cast_to<Control>(p_object);
if (!control || p_group != "Layout") {
return;
diff --git a/editor/plugins/control_editor_plugin.h b/editor/plugins/control_editor_plugin.h
index 4a411c0241..be52187cd3 100644
--- a/editor/plugins/control_editor_plugin.h
+++ b/editor/plugins/control_editor_plugin.h
@@ -32,7 +32,7 @@
#define CONTROL_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/check_box.h"
@@ -127,8 +127,11 @@ public:
class EditorInspectorPluginControl : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginControl, EditorInspectorPlugin);
+ bool inside_control_category = false;
+
public:
virtual bool can_handle(Object *p_object) override;
+ virtual void parse_category(Object *p_object, const String &p_category) override;
virtual void parse_group(Object *p_object, const String &p_group) override;
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
};
diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.h b/editor/plugins/cpu_particles_2d_editor_plugin.h
index a408f771eb..4d59c9981e 100644
--- a/editor/plugins/cpu_particles_2d_editor_plugin.h
+++ b/editor/plugins/cpu_particles_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef CPU_PARTICLES_2D_EDITOR_PLUGIN_H
#define CPU_PARTICLES_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/cpu_particles_2d.h"
#include "scene/2d/physics/collision_polygon_2d.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h
index b6a74d9b93..c844f42029 100644
--- a/editor/plugins/curve_editor_plugin.h
+++ b/editor/plugins/curve_editor_plugin.h
@@ -32,8 +32,8 @@
#define CURVE_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
#include "editor/editor_resource_preview.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/resources/curve.h"
class EditorSpinSlider;
diff --git a/editor/plugins/debugger_editor_plugin.h b/editor/plugins/debugger_editor_plugin.h
index b7453e3e81..a6df83496e 100644
--- a/editor/plugins/debugger_editor_plugin.h
+++ b/editor/plugins/debugger_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef DEBUGGER_EDITOR_PLUGIN_H
#define DEBUGGER_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class EditorFileServer;
class MenuButton;
diff --git a/editor/editor_plugin.compat.inc b/editor/plugins/editor_plugin.compat.inc
index 7edf938604..7edf938604 100644
--- a/editor/editor_plugin.compat.inc
+++ b/editor/plugins/editor_plugin.compat.inc
diff --git a/editor/editor_plugin.cpp b/editor/plugins/editor_plugin.cpp
index f42a1555a2..f42a1555a2 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/plugins/editor_plugin.cpp
diff --git a/editor/editor_plugin.h b/editor/plugins/editor_plugin.h
index f45a512b89..f45a512b89 100644
--- a/editor/editor_plugin.h
+++ b/editor/plugins/editor_plugin.h
diff --git a/editor/editor_plugin_settings.cpp b/editor/plugins/editor_plugin_settings.cpp
index 2920cf19c0..2920cf19c0 100644
--- a/editor/editor_plugin_settings.cpp
+++ b/editor/plugins/editor_plugin_settings.cpp
diff --git a/editor/editor_plugin_settings.h b/editor/plugins/editor_plugin_settings.h
index 8db8ffd78c..5b470b3e58 100644
--- a/editor/editor_plugin_settings.h
+++ b/editor/plugins/editor_plugin_settings.h
@@ -32,7 +32,7 @@
#define EDITOR_PLUGIN_SETTINGS_H
#include "editor/editor_data.h"
-#include "editor/plugin_config_dialog.h"
+#include "editor/plugins/plugin_config_dialog.h"
class Tree;
diff --git a/editor/plugins/font_config_plugin.h b/editor/plugins/font_config_plugin.h
index 1adb578950..7b2d26da4a 100644
--- a/editor/plugins/font_config_plugin.h
+++ b/editor/plugins/font_config_plugin.h
@@ -32,9 +32,9 @@
#define FONT_CONFIG_PLUGIN_H
#include "core/io/marshalls.h"
-#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
#include "editor/editor_properties_array_dict.h"
+#include "editor/plugins/editor_plugin.h"
/*************************************************************************/
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.h b/editor/plugins/gpu_particles_2d_editor_plugin.h
index aad623ee60..bb0ca5de3a 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.h
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef GPU_PARTICLES_2D_EDITOR_PLUGIN_H
#define GPU_PARTICLES_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/gpu_particles_2d.h"
#include "scene/2d/physics/collision_polygon_2d.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.h b/editor/plugins/gpu_particles_3d_editor_plugin.h
index 176a45df56..3b2ab2f8ca 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.h
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef GPU_PARTICLES_3D_EDITOR_PLUGIN_H
#define GPU_PARTICLES_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/gpu_particles_3d.h"
#include "scene/gui/spin_box.h"
diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
index 9f59f6e2cd..bba8bd2584 100644
--- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
+++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef GPU_PARTICLES_COLLISION_SDF_EDITOR_PLUGIN_H
#define GPU_PARTICLES_COLLISION_SDF_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/gpu_particles_collision_3d.h"
#include "scene/resources/material.h"
diff --git a/editor/plugins/gradient_editor_plugin.h b/editor/plugins/gradient_editor_plugin.h
index 06d79d55ab..f211e4ef30 100644
--- a/editor/plugins/gradient_editor_plugin.h
+++ b/editor/plugins/gradient_editor_plugin.h
@@ -32,7 +32,7 @@
#define GRADIENT_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class EditorSpinSlider;
class ColorPicker;
diff --git a/editor/plugins/gradient_texture_2d_editor_plugin.h b/editor/plugins/gradient_texture_2d_editor_plugin.h
index 33570593cc..a9f247c3cc 100644
--- a/editor/plugins/gradient_texture_2d_editor_plugin.h
+++ b/editor/plugins/gradient_texture_2d_editor_plugin.h
@@ -32,7 +32,7 @@
#define GRADIENT_TEXTURE_2D_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class Button;
class EditorSpinSlider;
diff --git a/editor/plugins/input_event_editor_plugin.h b/editor/plugins/input_event_editor_plugin.h
index 779e59edd4..48c16a4807 100644
--- a/editor/plugins/input_event_editor_plugin.h
+++ b/editor/plugins/input_event_editor_plugin.h
@@ -33,7 +33,7 @@
#include "editor/action_map_editor.h"
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class InputEventConfigContainer : public VBoxContainer {
GDCLASS(InputEventConfigContainer, VBoxContainer);
diff --git a/editor/plugins/lightmap_gi_editor_plugin.h b/editor/plugins/lightmap_gi_editor_plugin.h
index 1234438c8d..3e739adf9e 100644
--- a/editor/plugins/lightmap_gi_editor_plugin.h
+++ b/editor/plugins/lightmap_gi_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef LIGHTMAP_GI_EDITOR_PLUGIN_H
#define LIGHTMAP_GI_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/lightmap_gi.h"
#include "scene/resources/material.h"
diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h
index c60de1ade9..fb6bafc0ef 100644
--- a/editor/plugins/material_editor_plugin.h
+++ b/editor/plugins/material_editor_plugin.h
@@ -32,7 +32,7 @@
#define MATERIAL_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/editor_resource_conversion_plugin.h"
#include "scene/resources/3d/primitive_meshes.h"
#include "scene/resources/material.h"
diff --git a/editor/plugins/mesh_editor_plugin.h b/editor/plugins/mesh_editor_plugin.h
index a8ef476f84..85d92e7800 100644
--- a/editor/plugins/mesh_editor_plugin.h
+++ b/editor/plugins/mesh_editor_plugin.h
@@ -32,7 +32,7 @@
#define MESH_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.h b/editor/plugins/mesh_instance_3d_editor_plugin.h
index ce7d23239c..20c151fb92 100644
--- a/editor/plugins/mesh_instance_3d_editor_plugin.h
+++ b/editor/plugins/mesh_instance_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef MESH_INSTANCE_3D_EDITOR_PLUGIN_H
#define MESH_INSTANCE_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/gui/option_button.h"
diff --git a/editor/plugins/mesh_library_editor_plugin.h b/editor/plugins/mesh_library_editor_plugin.h
index 3895d10c37..94f46beea1 100644
--- a/editor/plugins/mesh_library_editor_plugin.h
+++ b/editor/plugins/mesh_library_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef MESH_LIBRARY_EDITOR_PLUGIN_H
#define MESH_LIBRARY_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/resources/3d/mesh_library.h"
class EditorFileDialog;
diff --git a/editor/plugins/multimesh_editor_plugin.h b/editor/plugins/multimesh_editor_plugin.h
index b21a932809..5051926c64 100644
--- a/editor/plugins/multimesh_editor_plugin.h
+++ b/editor/plugins/multimesh_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef MULTIMESH_EDITOR_PLUGIN_H
#define MULTIMESH_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/multimesh_instance_3d.h"
#include "scene/gui/slider.h"
#include "scene/gui/spin_box.h"
diff --git a/editor/plugins/navigation_link_2d_editor_plugin.h b/editor/plugins/navigation_link_2d_editor_plugin.h
index ea731ca2cd..7a4be18c31 100644
--- a/editor/plugins/navigation_link_2d_editor_plugin.h
+++ b/editor/plugins/navigation_link_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef NAVIGATION_LINK_2D_EDITOR_PLUGIN_H
#define NAVIGATION_LINK_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/navigation_link_2d.h"
class CanvasItemEditor;
diff --git a/editor/plugins/navigation_obstacle_3d_editor_plugin.h b/editor/plugins/navigation_obstacle_3d_editor_plugin.h
index 74094dc86f..c62a5a281b 100644
--- a/editor/plugins/navigation_obstacle_3d_editor_plugin.h
+++ b/editor/plugins/navigation_obstacle_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef NAVIGATION_OBSTACLE_3D_EDITOR_PLUGIN_H
#define NAVIGATION_OBSTACLE_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/physics/collision_polygon_3d.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/navigation_polygon_editor_plugin.h b/editor/plugins/navigation_polygon_editor_plugin.h
index bf2474bc55..4d6d245cc5 100644
--- a/editor/plugins/navigation_polygon_editor_plugin.h
+++ b/editor/plugins/navigation_polygon_editor_plugin.h
@@ -33,7 +33,7 @@
#include "editor/plugins/abstract_polygon_2d_editor.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class AcceptDialog;
class HBoxContainer;
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 66fa932f7c..96210de403 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef NODE_3D_EDITOR_PLUGIN_H
#define NODE_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/node_3d_editor_gizmos.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/occluder_instance_3d_editor_plugin.h b/editor/plugins/occluder_instance_3d_editor_plugin.h
index 54ac95c9e1..7920ff59c9 100644
--- a/editor/plugins/occluder_instance_3d_editor_plugin.h
+++ b/editor/plugins/occluder_instance_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef OCCLUDER_INSTANCE_3D_EDITOR_PLUGIN_H
#define OCCLUDER_INSTANCE_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/occluder_instance_3d.h"
#include "scene/resources/material.h"
diff --git a/editor/plugins/packed_scene_editor_plugin.h b/editor/plugins/packed_scene_editor_plugin.h
index 308bcf1c05..0e18d98978 100644
--- a/editor/plugins/packed_scene_editor_plugin.h
+++ b/editor/plugins/packed_scene_editor_plugin.h
@@ -32,7 +32,7 @@
#define PACKED_SCENE_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
class PackedSceneEditor : public VBoxContainer {
diff --git a/editor/plugins/parallax_background_editor_plugin.h b/editor/plugins/parallax_background_editor_plugin.h
index ba394d04dc..07a562edac 100644
--- a/editor/plugins/parallax_background_editor_plugin.h
+++ b/editor/plugins/parallax_background_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef PARALLAX_BACKGROUND_EDITOR_PLUGIN_H
#define PARALLAX_BACKGROUND_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class HBoxContainer;
class MenuButton;
diff --git a/editor/plugins/particle_process_material_editor_plugin.h b/editor/plugins/particle_process_material_editor_plugin.h
index 0d9725397e..623d3d4ba3 100644
--- a/editor/plugins/particle_process_material_editor_plugin.h
+++ b/editor/plugins/particle_process_material_editor_plugin.h
@@ -31,8 +31,8 @@
#ifndef PARTICLE_PROCESS_MATERIAL_EDITOR_PLUGIN_H
#define PARTICLE_PROCESS_MATERIAL_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
+#include "editor/plugins/editor_plugin.h"
class Button;
class EditorSpinSlider;
diff --git a/editor/plugins/path_2d_editor_plugin.h b/editor/plugins/path_2d_editor_plugin.h
index 8efd651494..7d1a64160b 100644
--- a/editor/plugins/path_2d_editor_plugin.h
+++ b/editor/plugins/path_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef PATH_2D_EDITOR_PLUGIN_H
#define PATH_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/path_2d.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/path_3d_editor_plugin.h b/editor/plugins/path_3d_editor_plugin.h
index 6a933a419f..ee73df1617 100644
--- a/editor/plugins/path_3d_editor_plugin.h
+++ b/editor/plugins/path_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef PATH_3D_EDITOR_PLUGIN_H
#define PATH_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/node_3d_editor_gizmos.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/path_3d.h"
diff --git a/editor/plugins/physical_bone_3d_editor_plugin.h b/editor/plugins/physical_bone_3d_editor_plugin.h
index 5c49e641a5..fb6f30cc57 100644
--- a/editor/plugins/physical_bone_3d_editor_plugin.h
+++ b/editor/plugins/physical_bone_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef PHYSICAL_BONE_3D_EDITOR_PLUGIN_H
#define PHYSICAL_BONE_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
diff --git a/editor/plugin_config_dialog.cpp b/editor/plugins/plugin_config_dialog.cpp
index 184a9233a8..a535b18b9d 100644
--- a/editor/plugin_config_dialog.cpp
+++ b/editor/plugins/plugin_config_dialog.cpp
@@ -33,9 +33,10 @@
#include "core/io/config_file.h"
#include "core/io/dir_access.h"
#include "core/object/script_language.h"
+#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
-#include "editor/editor_plugin.h"
#include "editor/gui/editor_validation_panel.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/project_settings_editor.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/grid_container.h"
@@ -58,30 +59,35 @@ void PluginConfigDialog::_on_confirmed() {
return;
}
}
-
- int lang_idx = script_option_edit->get_selected();
- ScriptLanguage *language = ScriptServer::get_language(lang_idx);
- if (language == nullptr) {
- return;
- }
- String ext = language->get_extension();
- String script_name = script_edit->get_text().is_empty() ? _get_subfolder() : script_edit->get_text();
- if (script_name.get_extension() != ext) {
- script_name += "." + ext;
- }
- String script_path = path.path_join(script_name);
-
+ // Create the plugin.cfg file.
Ref<ConfigFile> cf = memnew(ConfigFile);
cf->load(path.path_join("plugin.cfg"));
cf->set_value("plugin", "name", name_edit->get_text());
cf->set_value("plugin", "description", desc_edit->get_text());
cf->set_value("plugin", "author", author_edit->get_text());
cf->set_value("plugin", "version", version_edit->get_text());
- cf->set_value("plugin", "script", script_name);
-
+ // Language-specific settings.
+ int lang_index = script_option_edit->get_selected();
+ _create_script_for_plugin(path, cf, lang_index);
+ // Save and inform the editor.
cf->save(path.path_join("plugin.cfg"));
+ EditorNode::get_singleton()->get_project_settings()->update_plugins();
+ EditorFileSystem::get_singleton()->scan();
+ _clear_fields();
+}
- if (!_edit_mode) {
+void PluginConfigDialog::_create_script_for_plugin(const String &p_plugin_path, Ref<ConfigFile> p_config_file, int p_script_lang_index) {
+ ScriptLanguage *language = ScriptServer::get_language(p_script_lang_index);
+ ERR_FAIL_COND(language == nullptr);
+ String ext = language->get_extension();
+ String script_name = script_edit->get_text().is_empty() ? _get_subfolder() : script_edit->get_text();
+ if (script_name.get_extension() != ext) {
+ script_name += "." + ext;
+ }
+ String script_path = p_plugin_path.path_join(script_name);
+ p_config_file->set_value("plugin", "script", script_name);
+ // If the requested script does not exist, create it.
+ if (!_edit_mode && !FileAccess::exists(script_path)) {
String class_name = script_name.get_basename();
String template_content = "";
Vector<ScriptLanguage::ScriptTemplate> templates = language->get_built_in_templates("EditorPlugin");
@@ -91,12 +97,9 @@ void PluginConfigDialog::_on_confirmed() {
Ref<Script> scr = language->make_template(template_content, class_name, "EditorPlugin");
scr->set_path(script_path, true);
ResourceSaver::save(scr);
-
+ p_config_file->save(p_plugin_path.path_join("plugin.cfg"));
emit_signal(SNAME("plugin_ready"), scr.ptr(), active_edit->is_pressed() ? _to_absolute_plugin_path(_get_subfolder()) : "");
- } else {
- EditorNode::get_singleton()->get_project_settings()->update_plugins();
}
- _clear_fields();
}
void PluginConfigDialog::_on_canceled() {
@@ -104,19 +107,9 @@ void PluginConfigDialog::_on_canceled() {
}
void PluginConfigDialog::_on_required_text_changed() {
- int lang_idx = script_option_edit->get_selected();
- ScriptLanguage *language = ScriptServer::get_language(lang_idx);
- if (language == nullptr) {
- return;
- }
- String ext = language->get_extension();
-
if (name_edit->get_text().is_empty()) {
validation_panel->set_message(MSG_ID_PLUGIN, TTR("Plugin name cannot be blank."), EditorValidationPanel::MSG_ERROR);
}
- if ((!script_edit->get_text().get_extension().is_empty() && script_edit->get_text().get_extension() != ext) || script_edit->get_text().ends_with(".")) {
- validation_panel->set_message(MSG_ID_SCRIPT, vformat(TTR("Script extension must match chosen language extension (.%s)."), ext), EditorValidationPanel::MSG_ERROR);
- }
if (subfolder_edit->is_visible()) {
if (!subfolder_edit->get_text().is_empty() && !subfolder_edit->get_text().is_valid_filename()) {
validation_panel->set_message(MSG_ID_SUBFOLDER, TTR("Subfolder name is not a valid folder name."), EditorValidationPanel::MSG_ERROR);
@@ -129,6 +122,16 @@ void PluginConfigDialog::_on_required_text_changed() {
} else {
validation_panel->set_message(MSG_ID_SUBFOLDER, "", EditorValidationPanel::MSG_OK);
}
+ // Language and script validation.
+ int lang_idx = script_option_edit->get_selected();
+ ScriptLanguage *language = ScriptServer::get_language(lang_idx);
+ if (language == nullptr) {
+ return;
+ }
+ String ext = language->get_extension();
+ if ((!script_edit->get_text().get_extension().is_empty() && script_edit->get_text().get_extension() != ext) || script_edit->get_text().ends_with(".")) {
+ validation_panel->set_message(MSG_ID_SCRIPT, vformat(TTR("Script extension must match chosen language extension (.%s)."), ext), EditorValidationPanel::MSG_ERROR);
+ }
if (active_edit->is_visible()) {
if (language->get_name() == "C#") {
active_edit->set_pressed(false);
@@ -296,10 +299,10 @@ PluginConfigDialog::PluginConfigDialog() {
grid->add_child(script_option_edit);
// Plugin Script Name
- Label *script_lb = memnew(Label);
- script_lb->set_text(TTR("Script Name:"));
- script_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
- grid->add_child(script_lb);
+ Label *script_name_label = memnew(Label);
+ script_name_label->set_text(TTR("Script Name:"));
+ script_name_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
+ grid->add_child(script_name_label);
script_edit = memnew(LineEdit);
script_edit->set_tooltip_text(TTR("Optional. The path to the script (relative to the add-on folder). If left empty, will default to \"plugin.gd\"."));
@@ -308,11 +311,11 @@ PluginConfigDialog::PluginConfigDialog() {
grid->add_child(script_edit);
// Activate now checkbox
- Label *active_lb = memnew(Label);
- active_lb->set_text(TTR("Activate now?"));
- active_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
- grid->add_child(active_lb);
- plugin_edit_hidden_controls.push_back(active_lb);
+ Label *active_label = memnew(Label);
+ active_label->set_text(TTR("Activate now?"));
+ active_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
+ grid->add_child(active_label);
+ plugin_edit_hidden_controls.push_back(active_label);
active_edit = memnew(CheckBox);
active_edit->set_pressed(true);
diff --git a/editor/plugin_config_dialog.h b/editor/plugins/plugin_config_dialog.h
index 2fdf6368a0..7d6eab5e18 100644
--- a/editor/plugin_config_dialog.h
+++ b/editor/plugins/plugin_config_dialog.h
@@ -39,6 +39,7 @@
#include "scene/gui/text_edit.h"
#include "scene/gui/texture_rect.h"
+class ConfigFile;
class EditorValidationPanel;
class PluginConfigDialog : public ConfirmationDialog {
@@ -70,6 +71,7 @@ class PluginConfigDialog : public ConfirmationDialog {
void _on_confirmed();
void _on_canceled();
void _on_required_text_changed();
+ void _create_script_for_plugin(const String &p_plugin_path, Ref<ConfigFile> p_config_file, int p_script_lang_index);
String _get_subfolder();
static String _to_absolute_plugin_path(const String &p_plugin_name);
diff --git a/editor/plugins/polygon_3d_editor_plugin.h b/editor/plugins/polygon_3d_editor_plugin.h
index 85cfd807e4..f496638515 100644
--- a/editor/plugins/polygon_3d_editor_plugin.h
+++ b/editor/plugins/polygon_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef POLYGON_3D_EDITOR_PLUGIN_H
#define POLYGON_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/physics/collision_polygon_3d.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/resource_preloader_editor_plugin.h b/editor/plugins/resource_preloader_editor_plugin.h
index 7a4cabbb69..76ef2fe9a4 100644
--- a/editor/plugins/resource_preloader_editor_plugin.h
+++ b/editor/plugins/resource_preloader_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef RESOURCE_PRELOADER_EDITOR_PLUGIN_H
#define RESOURCE_PRELOADER_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/tree.h"
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index de3ab3fd5a..e6bb8f14a9 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -32,7 +32,7 @@
#define SCRIPT_EDITOR_PLUGIN_H
#include "core/object/script_language.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/panel_container.h"
#include "scene/resources/syntax_highlighter.h"
diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h
index 2558184982..386261d844 100644
--- a/editor/plugins/shader_editor_plugin.h
+++ b/editor/plugins/shader_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef SHADER_EDITOR_PLUGIN_H
#define SHADER_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class HSplitContainer;
class ItemList;
diff --git a/editor/plugins/shader_file_editor_plugin.h b/editor/plugins/shader_file_editor_plugin.h
index 989668e37d..9a915513ef 100644
--- a/editor/plugins/shader_file_editor_plugin.h
+++ b/editor/plugins/shader_file_editor_plugin.h
@@ -32,7 +32,7 @@
#define SHADER_FILE_EDITOR_PLUGIN_H
#include "editor/code_editor.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/rich_text_label.h"
diff --git a/editor/plugins/skeleton_2d_editor_plugin.h b/editor/plugins/skeleton_2d_editor_plugin.h
index 9f3f1c3b34..74fd59f1c4 100644
--- a/editor/plugins/skeleton_2d_editor_plugin.h
+++ b/editor/plugins/skeleton_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef SKELETON_2D_EDITOR_PLUGIN_H
#define SKELETON_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/skeleton_2d.h"
class AcceptDialog;
diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h
index f62d017c40..79dc16ae2f 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.h
+++ b/editor/plugins/skeleton_3d_editor_plugin.h
@@ -31,9 +31,9 @@
#ifndef SKELETON_3D_EDITOR_PLUGIN_H
#define SKELETON_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
#include "editor/gui/editor_file_dialog.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/node_3d_editor_plugin.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/mesh_instance_3d.h"
diff --git a/editor/plugins/skeleton_ik_3d_editor_plugin.h b/editor/plugins/skeleton_ik_3d_editor_plugin.h
index 3d311e581e..2ef5467263 100644
--- a/editor/plugins/skeleton_ik_3d_editor_plugin.h
+++ b/editor/plugins/skeleton_ik_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef SKELETON_IK_3D_EDITOR_PLUGIN_H
#define SKELETON_IK_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class Button;
class SkeletonIK3D;
diff --git a/editor/plugins/sprite_2d_editor_plugin.h b/editor/plugins/sprite_2d_editor_plugin.h
index 1121481341..7cbde77081 100644
--- a/editor/plugins/sprite_2d_editor_plugin.h
+++ b/editor/plugins/sprite_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef SPRITE_2D_EDITOR_PLUGIN_H
#define SPRITE_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/sprite_2d.h"
#include "scene/gui/spin_box.h"
diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h
index e9fbaf7dde..0e26a793a7 100644
--- a/editor/plugins/sprite_frames_editor_plugin.h
+++ b/editor/plugins/sprite_frames_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef SPRITE_FRAMES_EDITOR_PLUGIN_H
#define SPRITE_FRAMES_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/animated_sprite_2d.h"
#include "scene/3d/sprite_3d.h"
#include "scene/gui/button.h"
diff --git a/editor/plugins/style_box_editor_plugin.h b/editor/plugins/style_box_editor_plugin.h
index 824f8db8e4..e793c2c2f3 100644
--- a/editor/plugins/style_box_editor_plugin.h
+++ b/editor/plugins/style_box_editor_plugin.h
@@ -32,7 +32,7 @@
#define STYLE_BOX_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/texture_rect.h"
class Button;
diff --git a/editor/plugins/sub_viewport_preview_editor_plugin.h b/editor/plugins/sub_viewport_preview_editor_plugin.h
index 999d7d8b43..d05e90b61e 100644
--- a/editor/plugins/sub_viewport_preview_editor_plugin.h
+++ b/editor/plugins/sub_viewport_preview_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef SUB_VIEWPORT_PREVIEW_EDITOR_PLUGIN_H
#define SUB_VIEWPORT_PREVIEW_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/texture_editor_plugin.h"
#include "scene/main/viewport.h"
diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp
index c640994811..83a1700306 100644
--- a/editor/plugins/text_shader_editor.cpp
+++ b/editor/plugins/text_shader_editor.cpp
@@ -311,6 +311,9 @@ void ShaderTextEditor::_load_theme_settings() {
syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
syntax_highlighter->add_color_region("//", "", comment_color, true);
+ const Color doc_comment_color = EDITOR_GET("text_editor/theme/highlighting/doc_comment_color");
+ syntax_highlighter->add_color_region("/**", "*/", doc_comment_color, false);
+
// Disabled preprocessor branches use translucent text color to be easier to distinguish from comments.
syntax_highlighter->set_disabled_branch_color(Color(EDITOR_GET("text_editor/theme/highlighting/text_color")) * Color(1, 1, 1, 0.5));
diff --git a/editor/plugins/texture_3d_editor_plugin.h b/editor/plugins/texture_3d_editor_plugin.h
index 2509cf86ba..7a33a97a8f 100644
--- a/editor/plugins/texture_3d_editor_plugin.h
+++ b/editor/plugins/texture_3d_editor_plugin.h
@@ -32,7 +32,7 @@
#define TEXTURE_3D_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/spin_box.h"
#include "scene/resources/shader.h"
#include "scene/resources/texture.h"
diff --git a/editor/plugins/texture_editor_plugin.h b/editor/plugins/texture_editor_plugin.h
index f045e7b1b0..ea31429238 100644
--- a/editor/plugins/texture_editor_plugin.h
+++ b/editor/plugins/texture_editor_plugin.h
@@ -32,7 +32,7 @@
#define TEXTURE_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/margin_container.h"
#include "scene/resources/texture.h"
diff --git a/editor/plugins/texture_layered_editor_plugin.h b/editor/plugins/texture_layered_editor_plugin.h
index ea807c0080..83729f922e 100644
--- a/editor/plugins/texture_layered_editor_plugin.h
+++ b/editor/plugins/texture_layered_editor_plugin.h
@@ -32,7 +32,7 @@
#define TEXTURE_LAYERED_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/spin_box.h"
#include "scene/resources/shader.h"
#include "scene/resources/texture.h"
diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h
index 59a1b56c19..0e71ec16e0 100644
--- a/editor/plugins/texture_region_editor_plugin.h
+++ b/editor/plugins/texture_region_editor_plugin.h
@@ -32,7 +32,7 @@
#define TEXTURE_REGION_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/dialogs.h"
class AtlasTexture;
diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h
index 0bc02789aa..ba8e3a30b7 100644
--- a/editor/plugins/theme_editor_plugin.h
+++ b/editor/plugins/theme_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef THEME_EDITOR_PLUGIN_H
#define THEME_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/theme_editor_preview.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/margin_container.h"
diff --git a/editor/plugins/tiles/tiles_editor_plugin.h b/editor/plugins/tiles/tiles_editor_plugin.h
index 23a6a52a5c..f1da9e31fa 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.h
+++ b/editor/plugins/tiles/tiles_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef TILES_EDITOR_PLUGIN_H
#define TILES_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "tile_atlas_view.h"
diff --git a/editor/plugins/version_control_editor_plugin.h b/editor/plugins/version_control_editor_plugin.h
index 8ecb7c5029..4e60cb0a84 100644
--- a/editor/plugins/version_control_editor_plugin.h
+++ b/editor/plugins/version_control_editor_plugin.h
@@ -31,8 +31,8 @@
#ifndef VERSION_CONTROL_EDITOR_PLUGIN_H
#define VERSION_CONTROL_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
#include "editor/editor_vcs_interface.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/check_button.h"
#include "scene/gui/container.h"
#include "scene/gui/file_dialog.h"
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index e499bbde1e..246e44a40d 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -31,8 +31,8 @@
#ifndef VISUAL_SHADER_EDITOR_PLUGIN_H
#define VISUAL_SHADER_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/editor_resource_conversion_plugin.h"
#include "scene/gui/graph_edit.h"
#include "scene/resources/syntax_highlighter.h"
diff --git a/editor/plugins/voxel_gi_editor_plugin.h b/editor/plugins/voxel_gi_editor_plugin.h
index ad68ff5d91..58ef22ddc6 100644
--- a/editor/plugins/voxel_gi_editor_plugin.h
+++ b/editor/plugins/voxel_gi_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef VOXEL_GI_EDITOR_PLUGIN_H
#define VOXEL_GI_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/voxel_gi.h"
#include "scene/resources/material.h"
diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h
index 55f4677fb7..fb294c6bbd 100644
--- a/editor/project_settings_editor.h
+++ b/editor/project_settings_editor.h
@@ -35,11 +35,11 @@
#include "editor/action_map_editor.h"
#include "editor/editor_autoload_settings.h"
#include "editor/editor_data.h"
-#include "editor/editor_plugin_settings.h"
#include "editor/editor_sectioned_inspector.h"
#include "editor/group_settings_editor.h"
#include "editor/import_defaults_editor.h"
#include "editor/localization_editor.h"
+#include "editor/plugins/editor_plugin_settings.h"
#include "editor/shader_globals_editor.h"
#include "scene/gui/tab_container.h"
diff --git a/editor/shader_globals_editor.h b/editor/shader_globals_editor.h
index de871f76bf..fd94e483ff 100644
--- a/editor/shader_globals_editor.h
+++ b/editor/shader_globals_editor.h
@@ -33,8 +33,8 @@
#include "editor/editor_autoload_settings.h"
#include "editor/editor_data.h"
-#include "editor/editor_plugin_settings.h"
#include "editor/editor_sectioned_inspector.h"
+#include "editor/plugins/editor_plugin_settings.h"
#include "scene/gui/tab_container.h"
class ShaderGlobalsEditorInterface;
diff --git a/gles3_builders.py b/gles3_builders.py
index cf7c74f32d..78882f97fd 100644
--- a/gles3_builders.py
+++ b/gles3_builders.py
@@ -1,7 +1,7 @@
"""Functions used to generate source files during build time"""
import os.path
-
+from methods import print_error
from typing import Optional
@@ -94,11 +94,11 @@ def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct,
if not included_file in header_data.vertex_included_files and header_data.reading == "vertex":
header_data.vertex_included_files += [included_file]
if include_file_in_gles3_header(included_file, header_data, depth + 1) is None:
- print("Error in file '" + filename + "': #include " + includeline + "could not be found!")
+ print_error(f'In file "{filename}": #include "{includeline}" could not be found!"')
elif not included_file in header_data.fragment_included_files and header_data.reading == "fragment":
header_data.fragment_included_files += [included_file]
if include_file_in_gles3_header(included_file, header_data, depth + 1) is None:
- print("Error in file '" + filename + "': #include " + includeline + "could not be found!")
+ print_error(f'In file "{filename}": #include "{includeline}" could not be found!"')
line = fs.readline()
diff --git a/glsl_builders.py b/glsl_builders.py
index 5a17e3ca7f..22f4de74b1 100644
--- a/glsl_builders.py
+++ b/glsl_builders.py
@@ -1,6 +1,7 @@
"""Functions used to generate source files during build time"""
import os.path
+from methods import print_error
from typing import Optional, Iterable
@@ -79,15 +80,15 @@ def include_file_in_rd_header(filename: str, header_data: RDHeaderStruct, depth:
if not included_file in header_data.vertex_included_files and header_data.reading == "vertex":
header_data.vertex_included_files += [included_file]
if include_file_in_rd_header(included_file, header_data, depth + 1) is None:
- print("Error in file '" + filename + "': #include " + includeline + "could not be found!")
+ print_error(f'In file "{filename}": #include "{includeline}" could not be found!"')
elif not included_file in header_data.fragment_included_files and header_data.reading == "fragment":
header_data.fragment_included_files += [included_file]
if include_file_in_rd_header(included_file, header_data, depth + 1) is None:
- print("Error in file '" + filename + "': #include " + includeline + "could not be found!")
+ print_error(f'In file "{filename}": #include "{includeline}" could not be found!"')
elif not included_file in header_data.compute_included_files and header_data.reading == "compute":
header_data.compute_included_files += [included_file]
if include_file_in_rd_header(included_file, header_data, depth + 1) is None:
- print("Error in file '" + filename + "': #include " + includeline + "could not be found!")
+ print_error(f'In file "{filename}": #include "{includeline}" could not be found!"')
line = fs.readline()
diff --git a/methods.py b/methods.py
index 01b127ea30..30c7cb0331 100644
--- a/methods.py
+++ b/methods.py
@@ -5,6 +5,7 @@ import glob
import subprocess
from collections import OrderedDict
from collections.abc import Mapping
+from enum import Enum
from typing import Iterator
from pathlib import Path
from os.path import normpath, basename
@@ -15,6 +16,10 @@ base_folder_only = os.path.basename(os.path.normpath(base_folder_path))
# Listing all the folders we have converted
# for SCU in scu_builders.py
_scu_folders = set()
+# Colors are disabled in non-TTY environments such as pipes. This means
+# that if output is redirected to a file, it won't contain color codes.
+# Colors are always enabled on continuous integration.
+_colorize = bool(sys.stdout.isatty() or os.environ.get("CI"))
def set_scu_folders(scu_folders):
@@ -22,13 +27,55 @@ def set_scu_folders(scu_folders):
_scu_folders = scu_folders
+class ANSI(Enum):
+ """
+ Enum class for adding ansi colorcodes directly into strings.
+ Automatically converts values to strings representing their
+ internal value, or an empty string in a non-colorized scope.
+ """
+
+ GRAY = "\x1b[0;30m"
+ RED = "\x1b[0;31m"
+ GREEN = "\x1b[0;32m"
+ YELLOW = "\x1b[0;33m"
+ BLUE = "\x1b[0;34m"
+ PURPLE = "\x1b[0;35m"
+ CYAN = "\x1b[0;36m"
+ WHITE = "\x1b[0;37m"
+
+ BOLD_GRAY = "\x1b[1;90m"
+ BOLD_RED = "\x1b[1;91m"
+ BOLD_GREEN = "\x1b[1;92m"
+ BOLD_YELLOW = "\x1b[1;93m"
+ BOLD_BLUE = "\x1b[1;94m"
+ BOLD_PURPLE = "\x1b[1;95m"
+ BOLD_CYAN = "\x1b[1;96m"
+ BOLD_WHITE = "\x1b[1;97m"
+
+ RESET = "\x1b[0m"
+
+ def __str__(self):
+ global _colorize
+ return self.value if _colorize else ""
+
+
+def print_warning(*values: object) -> None:
+ """Prints a warning message with formatting."""
+ print(f"{ANSI.BOLD_YELLOW}WARNING:{ANSI.YELLOW}", *values, ANSI.RESET, file=sys.stderr)
+
+
+def print_error(*values: object) -> None:
+ """Prints an error message with formatting."""
+ print(f"{ANSI.BOLD_RED}ERROR:{ANSI.RED}", *values, ANSI.RESET, file=sys.stderr)
+
+
def add_source_files_orig(self, sources, files, allow_gen=False):
# Convert string to list of absolute paths (including expanding wildcard)
if isinstance(files, (str, bytes)):
# Keep SCons project-absolute path as they are (no wildcard support)
if files.startswith("#"):
if "*" in files:
- print("ERROR: Wildcards can't be expanded in SCons project-absolute path: '{}'".format(files))
+ print_error("Wildcards can't be expanded in SCons project-absolute path: '{}'".format(files))
return
files = [files]
else:
@@ -44,7 +91,7 @@ def add_source_files_orig(self, sources, files, allow_gen=False):
for path in files:
obj = self.Object(path)
if obj in sources:
- print('WARNING: Object "{}" already included in environment sources.'.format(obj))
+ print_warning('Object "{}" already included in environment sources.'.format(obj))
continue
sources.append(obj)
@@ -517,7 +564,7 @@ def module_check_dependencies(self, module):
missing_deps.append(dep)
if missing_deps != []:
- print(
+ print_warning(
"Disabling '{}' module as the following dependencies are not satisfied: {}".format(
module, ", ".join(missing_deps)
)
@@ -576,9 +623,7 @@ def use_windows_spawn_fix(self, platform=None):
_, err = proc.communicate()
rv = proc.wait()
if rv:
- print("=====")
- print(err)
- print("=====")
+ print_error(err)
return rv
def mySpawn(sh, escape, cmd, args, env):
@@ -601,65 +646,34 @@ def use_windows_spawn_fix(self, platform=None):
self["SPAWN"] = mySpawn
-def no_verbose(sys, env):
- colors = {}
-
- # Colors are disabled in non-TTY environments such as pipes. This means
- # that if output is redirected to a file, it will not contain color codes
- if sys.stdout.isatty():
- colors["blue"] = "\033[0;94m"
- colors["bold_blue"] = "\033[1;94m"
- colors["reset"] = "\033[0m"
- else:
- colors["blue"] = ""
- colors["bold_blue"] = ""
- colors["reset"] = ""
+def no_verbose(env):
+ colors = [ANSI.BLUE, ANSI.BOLD_BLUE, ANSI.RESET]
# There is a space before "..." to ensure that source file names can be
# Ctrl + clicked in the VS Code terminal.
- compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compile_shared_source_message = "{}Compiling shared {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_program_message = "{}Linking Program {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_library_message = "{}Linking Static Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- ranlib_library_message = "{}Ranlib Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- generated_file_message = "{}Generating {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
-
- env.Append(CXXCOMSTR=[compile_source_message])
- env.Append(CCCOMSTR=[compile_source_message])
- env.Append(SHCCCOMSTR=[compile_shared_source_message])
- env.Append(SHCXXCOMSTR=[compile_shared_source_message])
- env.Append(ARCOMSTR=[link_library_message])
- env.Append(RANLIBCOMSTR=[ranlib_library_message])
- env.Append(SHLINKCOMSTR=[link_shared_library_message])
- env.Append(LINKCOMSTR=[link_program_message])
- env.Append(JARCOMSTR=[java_library_message])
- env.Append(JAVACCOMSTR=[java_compile_source_message])
- env.Append(RCCOMSTR=[compiled_resource_message])
- env.Append(GENCOMSTR=[generated_file_message])
+ compile_source_message = "{0}Compiling {1}$SOURCE{0} ...{2}".format(*colors)
+ java_compile_source_message = "{0}Compiling {1}$SOURCE{0} ...{2}".format(*colors)
+ compile_shared_source_message = "{0}Compiling shared {1}$SOURCE{0} ...{2}".format(*colors)
+ link_program_message = "{0}Linking Program {1}$TARGET{0} ...{2}".format(*colors)
+ link_library_message = "{0}Linking Static Library {1}$TARGET{0} ...{2}".format(*colors)
+ ranlib_library_message = "{0}Ranlib Library {1}$TARGET{0} ...{2}".format(*colors)
+ link_shared_library_message = "{0}Linking Shared Library {1}$TARGET{0} ...{2}".format(*colors)
+ java_library_message = "{0}Creating Java Archive {1}$TARGET{0} ...{2}".format(*colors)
+ compiled_resource_message = "{0}Creating Compiled Resource {1}$TARGET{0} ...{2}".format(*colors)
+ generated_file_message = "{0}Generating {1}$TARGET{0} ...{2}".format(*colors)
+
+ env.Append(CXXCOMSTR=compile_source_message)
+ env.Append(CCCOMSTR=compile_source_message)
+ env.Append(SHCCCOMSTR=compile_shared_source_message)
+ env.Append(SHCXXCOMSTR=compile_shared_source_message)
+ env.Append(ARCOMSTR=link_library_message)
+ env.Append(RANLIBCOMSTR=ranlib_library_message)
+ env.Append(SHLINKCOMSTR=link_shared_library_message)
+ env.Append(LINKCOMSTR=link_program_message)
+ env.Append(JARCOMSTR=java_library_message)
+ env.Append(JAVACCOMSTR=java_compile_source_message)
+ env.Append(RCCOMSTR=compiled_resource_message)
+ env.Append(GENCOMSTR=generated_file_message)
def detect_visual_c_compiler_version(tools_env):
@@ -790,7 +804,7 @@ def generate_cpp_hint_file(filename):
with open(filename, "w", encoding="utf-8", newline="\n") as fd:
fd.write("#define GDCLASS(m_class, m_inherits)\n")
except OSError:
- print("Could not write cpp.hint file.")
+ print_warning("Could not write cpp.hint file.")
def glob_recursive(pattern, node="."):
@@ -881,7 +895,7 @@ def detect_darwin_sdk_path(platform, env):
if sdk_path:
env[var_name] = sdk_path
except (subprocess.CalledProcessError, OSError):
- print("Failed to find SDK path while running xcrun --sdk {} --show-sdk-path.".format(sdk_name))
+ print_error("Failed to find SDK path while running xcrun --sdk {} --show-sdk-path.".format(sdk_name))
raise
@@ -891,7 +905,7 @@ def is_vanilla_clang(env):
try:
version = subprocess.check_output([env.subst(env["CXX"]), "--version"]).strip().decode("utf-8")
except (subprocess.CalledProcessError, OSError):
- print("Couldn't parse CXX environment variable to infer compiler version.")
+ print_warning("Couldn't parse CXX environment variable to infer compiler version.")
return False
return not version.startswith("Apple")
@@ -928,7 +942,7 @@ def get_compiler_version(env):
.decode("utf-8")
)
except (subprocess.CalledProcessError, OSError):
- print("Couldn't parse CXX environment variable to infer compiler version.")
+ print_warning("Couldn't parse CXX environment variable to infer compiler version.")
return ret
else:
# TODO: Implement for MSVC
diff --git a/misc/dist/ios_xcode/PrivacyInfo.xcprivacy b/misc/dist/ios_xcode/PrivacyInfo.xcprivacy
new file mode 100644
index 0000000000..bc4a893d58
--- /dev/null
+++ b/misc/dist/ios_xcode/PrivacyInfo.xcprivacy
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>NSPrivacyAccessedAPITypes</key>
+ $priv_api_types
+ $priv_tracking
+ $priv_collection
+</dict>
+</plist>
diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
index e9efea8809..a5de7e8872 100644
--- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
+++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
@@ -16,6 +16,7 @@
9039D3BE24C093AC0020482C /* MoltenVK.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9039D3BD24C093AC0020482C /* MoltenVK.xcframework */; };
D0BCFE4618AEBDA2004A7AAE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D0BCFE4418AEBDA2004A7AAE /* InfoPlist.strings */; };
D0BCFE7818AEBFEB004A7AAE /* $binary.pck in Resources */ = {isa = PBXBuildFile; fileRef = D0BCFE7718AEBFEB004A7AAE /* $binary.pck */; };
+ F965960D2BC2C3A800579C7E /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = F965960C2BC2C3A800579C7E /* PrivacyInfo.xcprivacy */; };
$pbx_launch_screen_build_reference
/* End PBXBuildFile section */
@@ -47,6 +48,7 @@
D0BCFE4518AEBDA2004A7AAE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
$pbx_locale_file_reference
D0BCFE7718AEBFEB004A7AAE /* $binary.pck */ = {isa = PBXFileReference; lastKnownFileType = file; path = "$binary.pck"; sourceTree = "<group>"; };
+ F965960C2BC2C3A800579C7E /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
$pbx_launch_screen_file_reference
/* End PBXFileReference section */
@@ -75,6 +77,7 @@
D0BCFE4118AEBDA2004A7AAE /* $binary */,
D0BCFE3618AEBDA2004A7AAE /* Frameworks */,
D0BCFE3518AEBDA2004A7AAE /* Products */,
+ F965960C2BC2C3A800579C7E /* PrivacyInfo.xcprivacy */,
$additional_pbx_resources_refs
);
sourceTree = "<group>";
@@ -186,6 +189,7 @@
D0BCFE7818AEBFEB004A7AAE /* $binary.pck in Resources */,
$pbx_launch_screen_build_phase
D0BCFE4618AEBDA2004A7AAE /* InfoPlist.strings in Resources */,
+ F965960D2BC2C3A800579C7E /* PrivacyInfo.xcprivacy in Resources */,
$additional_pbx_resources_build
);
runOnlyForDeploymentPostprocessing = 0;
diff --git a/modules/csg/editor/csg_gizmos.h b/modules/csg/editor/csg_gizmos.h
index 6281db0a21..de19b33e7d 100644
--- a/modules/csg/editor/csg_gizmos.h
+++ b/modules/csg/editor/csg_gizmos.h
@@ -35,7 +35,7 @@
#include "../csg_shape.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/node_3d_editor_gizmos.h"
class Gizmo3DHelper;
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 921ed0b416..f238958f25 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -2892,7 +2892,7 @@ String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) con
return "";
}
-void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
+void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *r_dependencies, bool p_add_types) {
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_MSG(file.is_null(), "Cannot open file '" + p_path + "'.");
@@ -2906,8 +2906,13 @@ void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<S
return;
}
+ GDScriptAnalyzer analyzer(&parser);
+ if (OK != analyzer.analyze()) {
+ return;
+ }
+
for (const String &E : parser.get_dependencies()) {
- p_dependencies->push_back(E);
+ r_dependencies->push_back(E);
}
}
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 781e284bfc..7bd68ac0b1 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -633,7 +633,7 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual bool handles_type(const String &p_type) const override;
virtual String get_resource_type(const String &p_path) const override;
- virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override;
+ virtual void get_dependencies(const String &p_path, List<String> *r_dependencies, bool p_add_types = false) override;
};
class ResourceFormatSaverGDScript : public ResourceFormatSaver {
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 0636ac5083..28a44357eb 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -562,6 +562,11 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
class_type.native_type = result.native_type;
p_class->set_datatype(class_type);
+ // Add base class to the list of dependencies.
+ if (result.kind == GDScriptParser::DataType::CLASS) {
+ parser->add_dependency(result.script_path);
+ }
+
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : p_class->annotations) {
resolve_annotation(E);
@@ -722,13 +727,32 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
} else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) {
const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(first);
- Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(autoload.path);
+ String script_path;
+ if (ResourceLoader::get_resource_type(autoload.path) == "PackedScene") {
+ // Try to get script from scene if possible.
+ if (GDScriptLanguage::get_singleton()->has_any_global_constant(autoload.name)) {
+ Variant constant = GDScriptLanguage::get_singleton()->get_any_global_constant(autoload.name);
+ Node *node = Object::cast_to<Node>(constant);
+ if (node != nullptr) {
+ Ref<GDScript> scr = node->get_script();
+ if (scr.is_valid()) {
+ script_path = scr->get_script_path();
+ }
+ }
+ }
+ } else if (ResourceLoader::get_resource_type(autoload.path) == "GDScript") {
+ script_path = autoload.path;
+ }
+ if (script_path.is_empty()) {
+ return bad_type;
+ }
+ Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(script_path);
if (ref.is_null()) {
- push_error(vformat(R"(The referenced autoload "%s" (from "%s") could not be loaded.)", first, autoload.path), p_type);
+ push_error(vformat(R"(The referenced autoload "%s" (from "%s") could not be loaded.)", first, script_path), p_type);
return bad_type;
}
if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
- push_error(vformat(R"(Could not parse singleton "%s" from "%s".)", first, autoload.path), p_type);
+ push_error(vformat(R"(Could not parse singleton "%s" from "%s".)", first, script_path), p_type);
return bad_type;
}
result = ref->get_parser()->head->get_datatype();
@@ -849,6 +873,11 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
p_type->set_datatype(result);
+
+ if (result.kind == GDScriptParser::DataType::CLASS || result.kind == GDScriptParser::DataType::SCRIPT) {
+ parser->add_dependency(result.script_path);
+ }
+
return result;
}
@@ -3769,7 +3798,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
} break;
case GDScriptParser::ClassNode::Member::FUNCTION: {
- if (is_base && (!base.is_meta_type || member.function->is_static)) {
+ if (is_base && (!base.is_meta_type || member.function->is_static || is_constructor)) {
p_identifier->set_datatype(make_callable_type(member.function->info));
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_FUNCTION;
return;
@@ -4063,6 +4092,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
if (ScriptServer::is_global_class(name)) {
p_identifier->set_datatype(make_global_class_meta_type(name, p_identifier));
+ parser->add_dependency(p_identifier->get_datatype().script_path);
return;
}
@@ -4105,6 +4135,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
result.is_constant = true;
p_identifier->set_datatype(result);
+ parser->add_dependency(autoload.path);
return;
}
}
@@ -4224,7 +4255,6 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
push_error("Preloaded path must be a constant string.", p_preload->path);
} else {
p_preload->resolved_path = p_preload->path->reduced_value;
- // TODO: Save this as script dependency.
if (p_preload->resolved_path.is_relative_path()) {
p_preload->resolved_path = parser->script_path.get_base_dir().path_join(p_preload->resolved_path);
}
@@ -4255,6 +4285,8 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
push_error(vformat(R"(Could not preload resource file "%s".)", p_preload->resolved_path), p_preload->path);
}
}
+
+ parser->add_dependency(p_preload->resolved_path);
}
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 77cb8136f8..7fb9ffe9a5 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -1429,6 +1429,8 @@ private:
void reset_extents(Node *p_node, GDScriptTokenizer::Token p_token);
void reset_extents(Node *p_node, Node *p_from);
+ HashSet<String> dependencies;
+
template <typename T>
T *alloc_node() {
T *node = memnew(T);
@@ -1572,9 +1574,11 @@ public:
bool annotation_exists(const String &p_annotation_name) const;
const List<ParserError> &get_errors() const { return errors; }
- const List<String> get_dependencies() const {
- // TODO: Keep track of deps.
- return List<String>();
+ const HashSet<String> &get_dependencies() const {
+ return dependencies;
+ }
+ void add_dependency(const String &p_dependency) {
+ dependencies.insert(p_dependency);
}
#ifdef DEBUG_ENABLED
const List<GDScriptWarning> &get_warnings() const { return warnings; }
diff --git a/modules/gdscript/language_server/gdscript_language_server.h b/modules/gdscript/language_server/gdscript_language_server.h
index 2ace5ca446..4ae5ab6cbf 100644
--- a/modules/gdscript/language_server/gdscript_language_server.h
+++ b/modules/gdscript/language_server/gdscript_language_server.h
@@ -34,7 +34,7 @@
#include "../gdscript_parser.h"
#include "gdscript_language_protocol.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class GDScriptLanguageServer : public EditorPlugin {
GDCLASS(GDScriptLanguageServer, EditorPlugin);
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 853a8e0f19..a63d32ef30 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -233,18 +233,25 @@ void GDScriptWorkspace::reload_all_workspace_scripts() {
void GDScriptWorkspace::list_script_files(const String &p_root_dir, List<String> &r_files) {
Error err;
Ref<DirAccess> dir = DirAccess::open(p_root_dir, &err);
- if (OK == err) {
- dir->list_dir_begin();
- String file_name = dir->get_next();
- while (file_name.length()) {
- if (dir->current_is_dir() && file_name != "." && file_name != ".." && file_name != "./") {
- list_script_files(p_root_dir.path_join(file_name), r_files);
- } else if (file_name.ends_with(".gd")) {
- String script_file = p_root_dir.path_join(file_name);
- r_files.push_back(script_file);
- }
- file_name = dir->get_next();
+ if (OK != err) {
+ return;
+ }
+
+ // Ignore scripts in directories with a .gdignore file.
+ if (dir->file_exists(".gdignore")) {
+ return;
+ }
+
+ dir->list_dir_begin();
+ String file_name = dir->get_next();
+ while (file_name.length()) {
+ if (dir->current_is_dir() && file_name != "." && file_name != ".." && file_name != "./") {
+ list_script_files(p_root_dir.path_join(file_name), r_files);
+ } else if (file_name.ends_with(".gd")) {
+ String script_file = p_root_dir.path_join(file_name);
+ r_files.push_back(script_file);
}
+ file_name = dir->get_next();
}
}
diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
index 683ff6d4f6..6bc6160571 100644
--- a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
@@ -36,7 +36,7 @@
#include "../gltf_document.h"
#include "editor_scene_exporter_gltf_settings.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class EditorFileDialog;
class EditorInspector;
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.h b/modules/gridmap/editor/grid_map_editor_plugin.h
index 924e21aef5..cfa0f0c35c 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.h
+++ b/modules/gridmap/editor/grid_map_editor_plugin.h
@@ -35,7 +35,7 @@
#include "../grid_map.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/item_list.h"
#include "scene/gui/slider.h"
diff --git a/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h b/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h
index 730d1ca83b..3c50b0d5cc 100644
--- a/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h
+++ b/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h
@@ -32,7 +32,7 @@
#define AUDIO_STREAM_INTERACTIVE_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/dialogs.h"
class CheckBox;
diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp
index 94a3f0777e..bba56f6468 100644
--- a/modules/mobile_vr/mobile_vr_interface.cpp
+++ b/modules/mobile_vr/mobile_vr_interface.cpp
@@ -126,7 +126,7 @@ void MobileVRInterface::set_position_from_sensors() {
// 9dof is a misleading marketing term coming from 3 accelerometer axis + 3 gyro axis + 3 magnetometer axis = 9 axis
// but in reality this only offers 3 dof (yaw, pitch, roll) orientation
- Basis orientation;
+ Basis orientation = head_transform.basis;
uint64_t ticks = OS::get_singleton()->get_ticks_usec();
uint64_t ticks_elapsed = ticks - last_ticks;
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index e3f39c50f4..17df3988ee 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -41,7 +41,7 @@
#include "core/templates/self_list.h"
#ifdef TOOLS_ENABLED
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#endif
class CSharpScript;
diff --git a/modules/multiplayer/editor/multiplayer_editor_plugin.h b/modules/multiplayer/editor/multiplayer_editor_plugin.h
index a22144cdcf..e8ade539a7 100644
--- a/modules/multiplayer/editor/multiplayer_editor_plugin.h
+++ b/modules/multiplayer/editor/multiplayer_editor_plugin.h
@@ -31,8 +31,8 @@
#ifndef MULTIPLAYER_EDITOR_PLUGIN_H
#define MULTIPLAYER_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
#include "editor/plugins/editor_debugger_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class EditorNetworkProfiler;
class MultiplayerEditorDebugger : public EditorDebuggerPlugin {
diff --git a/modules/multiplayer/editor/replication_editor.h b/modules/multiplayer/editor/replication_editor.h
index 80c1892ec3..8f11774292 100644
--- a/modules/multiplayer/editor/replication_editor.h
+++ b/modules/multiplayer/editor/replication_editor.h
@@ -33,7 +33,7 @@
#include "../scene_replication_config.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
class ConfirmationDialog;
diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.h b/modules/navigation/editor/navigation_mesh_editor_plugin.h
index b73d8d2e69..6114c62ebf 100644
--- a/modules/navigation/editor/navigation_mesh_editor_plugin.h
+++ b/modules/navigation/editor/navigation_mesh_editor_plugin.h
@@ -33,7 +33,7 @@
#ifdef TOOLS_ENABLED
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class AcceptDialog;
class Button;
diff --git a/modules/noise/editor/noise_editor_plugin.h b/modules/noise/editor/noise_editor_plugin.h
index 948ccba29b..aa94cf4d23 100644
--- a/modules/noise/editor/noise_editor_plugin.h
+++ b/modules/noise/editor/noise_editor_plugin.h
@@ -33,7 +33,7 @@
#ifdef TOOLS_ENABLED
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class NoiseEditorPlugin : public EditorPlugin {
GDCLASS(NoiseEditorPlugin, EditorPlugin)
diff --git a/modules/noise/register_types.cpp b/modules/noise/register_types.cpp
index 29eb42522f..363b7bdc31 100644
--- a/modules/noise/register_types.cpp
+++ b/modules/noise/register_types.cpp
@@ -40,7 +40,7 @@
#endif
#ifdef TOOLS_ENABLED
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#endif
void initialize_noise_module(ModuleInitializationLevel p_level) {
diff --git a/modules/openxr/editor/openxr_action_map_editor.h b/modules/openxr/editor/openxr_action_map_editor.h
index 22e8853c8c..cfe5fed095 100644
--- a/modules/openxr/editor/openxr_action_map_editor.h
+++ b/modules/openxr/editor/openxr_action_map_editor.h
@@ -36,8 +36,8 @@
#include "openxr_interaction_profile_editor.h"
#include "openxr_select_interaction_profile_dialog.h"
-#include "editor/editor_plugin.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
diff --git a/modules/openxr/editor/openxr_editor_plugin.h b/modules/openxr/editor/openxr_editor_plugin.h
index b80f20d049..672df0de28 100644
--- a/modules/openxr/editor/openxr_editor_plugin.h
+++ b/modules/openxr/editor/openxr_editor_plugin.h
@@ -34,7 +34,7 @@
#include "openxr_action_map_editor.h"
#include "openxr_select_runtime.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class OpenXREditorPlugin : public EditorPlugin {
GDCLASS(OpenXREditorPlugin, EditorPlugin);
diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct
index 1d9d36fbbf..fcf3f64315 100644
--- a/modules/text_server_adv/gdextension_build/SConstruct
+++ b/modules/text_server_adv/gdextension_build/SConstruct
@@ -1,10 +1,20 @@
#!/usr/bin/env python
import atexit
-import os
import sys
import methods
import time
+# Enable ANSI escape code support on Windows 10 and later (for colored console output).
+# <https://github.com/python/cpython/issues/73245>
+if sys.platform == "win32":
+ from ctypes import windll, c_int, byref
+
+ stdout_handle = windll.kernel32.GetStdHandle(c_int(-11))
+ mode = c_int(0)
+ windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode))
+ mode = c_int(mode.value | 4)
+ windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode)
+
# For the reference:
# - CCFLAGS are compilation flags shared between C and C++
# - CFLAGS are for C-specific compilation flags
@@ -30,7 +40,7 @@ opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", Fa
opts.Update(env)
if not env["verbose"]:
- methods.no_verbose(sys, env)
+ methods.no_verbose(env)
if env["platform"] == "windows" and not env["use_mingw"]:
env.AppendUnique(CCFLAGS=["/utf-8"]) # Force to use Unicode encoding.
@@ -764,9 +774,16 @@ Default(library)
def print_elapsed_time():
- elapsed_time_sec = round(time.time() - time_at_start, 3)
- time_ms = round((elapsed_time_sec % 1) * 1000)
- print("[Time elapsed: {}.{:03}]".format(time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)), time_ms))
+ elapsed_time_sec = round(time.time() - time_at_start, 2)
+ time_centiseconds = round((elapsed_time_sec % 1) * 100)
+ print(
+ "{}[Time elapsed: {}.{:02}]{}".format(
+ methods.ANSI.GRAY,
+ time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)),
+ time_centiseconds,
+ methods.ANSI.RESET,
+ )
+ )
atexit.register(print_elapsed_time)
diff --git a/modules/text_server_adv/gdextension_build/methods.py b/modules/text_server_adv/gdextension_build/methods.py
index 32dbc59fd4..1c43759c55 100644
--- a/modules/text_server_adv/gdextension_build/methods.py
+++ b/modules/text_server_adv/gdextension_build/methods.py
@@ -1,66 +1,73 @@
import os
import sys
+from enum import Enum
+# Colors are disabled in non-TTY environments such as pipes. This means
+# that if output is redirected to a file, it won't contain color codes.
+# Colors are always enabled on continuous integration.
+_colorize = bool(sys.stdout.isatty() or os.environ.get("CI"))
-def no_verbose(sys, env):
- colors = {}
- # Colors are disabled in non-TTY environments such as pipes. This means
- # that if output is redirected to a file, it will not contain color codes
- if sys.stdout.isatty():
- colors["blue"] = "\033[0;94m"
- colors["bold_blue"] = "\033[1;94m"
- colors["reset"] = "\033[0m"
- else:
- colors["blue"] = ""
- colors["bold_blue"] = ""
- colors["reset"] = ""
+class ANSI(Enum):
+ """
+ Enum class for adding ansi colorcodes directly into strings.
+ Automatically converts values to strings representing their
+ internal value, or an empty string in a non-colorized scope.
+ """
+
+ GRAY = "\x1b[0;30m"
+ RED = "\x1b[0;31m"
+ GREEN = "\x1b[0;32m"
+ YELLOW = "\x1b[0;33m"
+ BLUE = "\x1b[0;34m"
+ PURPLE = "\x1b[0;35m"
+ CYAN = "\x1b[0;36m"
+ WHITE = "\x1b[0;37m"
+
+ BOLD_GRAY = "\x1b[1;90m"
+ BOLD_RED = "\x1b[1;91m"
+ BOLD_GREEN = "\x1b[1;92m"
+ BOLD_YELLOW = "\x1b[1;93m"
+ BOLD_BLUE = "\x1b[1;94m"
+ BOLD_PURPLE = "\x1b[1;95m"
+ BOLD_CYAN = "\x1b[1;96m"
+ BOLD_WHITE = "\x1b[1;97m"
+
+ RESET = "\x1b[0m"
+
+ def __str__(self):
+ global _colorize
+ return self.value if _colorize else ""
+
+
+def no_verbose(env):
+ colors = [ANSI.BLUE, ANSI.BOLD_BLUE, ANSI.RESET]
# There is a space before "..." to ensure that source file names can be
# Ctrl + clicked in the VS Code terminal.
- compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compile_shared_source_message = "{}Compiling shared {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_program_message = "{}Linking Program {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_library_message = "{}Linking Static Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- ranlib_library_message = "{}Ranlib Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- generated_file_message = "{}Generating {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
-
- env.Append(CXXCOMSTR=[compile_source_message])
- env.Append(CCCOMSTR=[compile_source_message])
- env.Append(SHCCCOMSTR=[compile_shared_source_message])
- env.Append(SHCXXCOMSTR=[compile_shared_source_message])
- env.Append(ARCOMSTR=[link_library_message])
- env.Append(RANLIBCOMSTR=[ranlib_library_message])
- env.Append(SHLINKCOMSTR=[link_shared_library_message])
- env.Append(LINKCOMSTR=[link_program_message])
- env.Append(JARCOMSTR=[java_library_message])
- env.Append(JAVACCOMSTR=[java_compile_source_message])
- env.Append(RCCOMSTR=[compiled_resource_message])
- env.Append(GENCOMSTR=[generated_file_message])
+ compile_source_message = "{0}Compiling {1}$SOURCE{0} ...{2}".format(*colors)
+ java_compile_source_message = "{0}Compiling {1}$SOURCE{0} ...{2}".format(*colors)
+ compile_shared_source_message = "{0}Compiling shared {1}$SOURCE{0} ...{2}".format(*colors)
+ link_program_message = "{0}Linking Program {1}$TARGET{0} ...{2}".format(*colors)
+ link_library_message = "{0}Linking Static Library {1}$TARGET{0} ...{2}".format(*colors)
+ ranlib_library_message = "{0}Ranlib Library {1}$TARGET{0} ...{2}".format(*colors)
+ link_shared_library_message = "{0}Linking Shared Library {1}$TARGET{0} ...{2}".format(*colors)
+ java_library_message = "{0}Creating Java Archive {1}$TARGET{0} ...{2}".format(*colors)
+ compiled_resource_message = "{0}Creating Compiled Resource {1}$TARGET{0} ...{2}".format(*colors)
+ generated_file_message = "{0}Generating {1}$TARGET{0} ...{2}".format(*colors)
+
+ env.Append(CXXCOMSTR=compile_source_message)
+ env.Append(CCCOMSTR=compile_source_message)
+ env.Append(SHCCCOMSTR=compile_shared_source_message)
+ env.Append(SHCXXCOMSTR=compile_shared_source_message)
+ env.Append(ARCOMSTR=link_library_message)
+ env.Append(RANLIBCOMSTR=ranlib_library_message)
+ env.Append(SHLINKCOMSTR=link_shared_library_message)
+ env.Append(LINKCOMSTR=link_program_message)
+ env.Append(JARCOMSTR=java_library_message)
+ env.Append(JAVACCOMSTR=java_compile_source_message)
+ env.Append(RCCOMSTR=compiled_resource_message)
+ env.Append(GENCOMSTR=generated_file_message)
def disable_warnings(self):
diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct
index 29801ede8e..07940719eb 100644
--- a/modules/text_server_fb/gdextension_build/SConstruct
+++ b/modules/text_server_fb/gdextension_build/SConstruct
@@ -1,10 +1,20 @@
#!/usr/bin/env python
import atexit
-import os
import sys
import methods
import time
+# Enable ANSI escape code support on Windows 10 and later (for colored console output).
+# <https://github.com/python/cpython/issues/73245>
+if sys.platform == "win32":
+ from ctypes import windll, c_int, byref
+
+ stdout_handle = windll.kernel32.GetStdHandle(c_int(-11))
+ mode = c_int(0)
+ windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode))
+ mode = c_int(mode.value | 4)
+ windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode)
+
# For the reference:
# - CCFLAGS are compilation flags shared between C and C++
# - CFLAGS are for C-specific compilation flags
@@ -28,7 +38,7 @@ opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", Fa
opts.Update(env)
if not env["verbose"]:
- methods.no_verbose(sys, env)
+ methods.no_verbose(env)
# ThorVG
if env["thorvg_enabled"] and env["freetype_enabled"]:
@@ -311,9 +321,16 @@ Default(library)
def print_elapsed_time():
- elapsed_time_sec = round(time.time() - time_at_start, 3)
- time_ms = round((elapsed_time_sec % 1) * 1000)
- print("[Time elapsed: {}.{:03}]".format(time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)), time_ms))
+ elapsed_time_sec = round(time.time() - time_at_start, 2)
+ time_centiseconds = round((elapsed_time_sec % 1) * 100)
+ print(
+ "{}[Time elapsed: {}.{:02}]{}".format(
+ methods.ANSI.GRAY,
+ time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)),
+ time_centiseconds,
+ methods.ANSI.RESET,
+ )
+ )
atexit.register(print_elapsed_time)
diff --git a/modules/text_server_fb/gdextension_build/methods.py b/modules/text_server_fb/gdextension_build/methods.py
index 32dbc59fd4..1c43759c55 100644
--- a/modules/text_server_fb/gdextension_build/methods.py
+++ b/modules/text_server_fb/gdextension_build/methods.py
@@ -1,66 +1,73 @@
import os
import sys
+from enum import Enum
+# Colors are disabled in non-TTY environments such as pipes. This means
+# that if output is redirected to a file, it won't contain color codes.
+# Colors are always enabled on continuous integration.
+_colorize = bool(sys.stdout.isatty() or os.environ.get("CI"))
-def no_verbose(sys, env):
- colors = {}
- # Colors are disabled in non-TTY environments such as pipes. This means
- # that if output is redirected to a file, it will not contain color codes
- if sys.stdout.isatty():
- colors["blue"] = "\033[0;94m"
- colors["bold_blue"] = "\033[1;94m"
- colors["reset"] = "\033[0m"
- else:
- colors["blue"] = ""
- colors["bold_blue"] = ""
- colors["reset"] = ""
+class ANSI(Enum):
+ """
+ Enum class for adding ansi colorcodes directly into strings.
+ Automatically converts values to strings representing their
+ internal value, or an empty string in a non-colorized scope.
+ """
+
+ GRAY = "\x1b[0;30m"
+ RED = "\x1b[0;31m"
+ GREEN = "\x1b[0;32m"
+ YELLOW = "\x1b[0;33m"
+ BLUE = "\x1b[0;34m"
+ PURPLE = "\x1b[0;35m"
+ CYAN = "\x1b[0;36m"
+ WHITE = "\x1b[0;37m"
+
+ BOLD_GRAY = "\x1b[1;90m"
+ BOLD_RED = "\x1b[1;91m"
+ BOLD_GREEN = "\x1b[1;92m"
+ BOLD_YELLOW = "\x1b[1;93m"
+ BOLD_BLUE = "\x1b[1;94m"
+ BOLD_PURPLE = "\x1b[1;95m"
+ BOLD_CYAN = "\x1b[1;96m"
+ BOLD_WHITE = "\x1b[1;97m"
+
+ RESET = "\x1b[0m"
+
+ def __str__(self):
+ global _colorize
+ return self.value if _colorize else ""
+
+
+def no_verbose(env):
+ colors = [ANSI.BLUE, ANSI.BOLD_BLUE, ANSI.RESET]
# There is a space before "..." to ensure that source file names can be
# Ctrl + clicked in the VS Code terminal.
- compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compile_shared_source_message = "{}Compiling shared {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_program_message = "{}Linking Program {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_library_message = "{}Linking Static Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- ranlib_library_message = "{}Ranlib Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- generated_file_message = "{}Generating {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
-
- env.Append(CXXCOMSTR=[compile_source_message])
- env.Append(CCCOMSTR=[compile_source_message])
- env.Append(SHCCCOMSTR=[compile_shared_source_message])
- env.Append(SHCXXCOMSTR=[compile_shared_source_message])
- env.Append(ARCOMSTR=[link_library_message])
- env.Append(RANLIBCOMSTR=[ranlib_library_message])
- env.Append(SHLINKCOMSTR=[link_shared_library_message])
- env.Append(LINKCOMSTR=[link_program_message])
- env.Append(JARCOMSTR=[java_library_message])
- env.Append(JAVACCOMSTR=[java_compile_source_message])
- env.Append(RCCOMSTR=[compiled_resource_message])
- env.Append(GENCOMSTR=[generated_file_message])
+ compile_source_message = "{0}Compiling {1}$SOURCE{0} ...{2}".format(*colors)
+ java_compile_source_message = "{0}Compiling {1}$SOURCE{0} ...{2}".format(*colors)
+ compile_shared_source_message = "{0}Compiling shared {1}$SOURCE{0} ...{2}".format(*colors)
+ link_program_message = "{0}Linking Program {1}$TARGET{0} ...{2}".format(*colors)
+ link_library_message = "{0}Linking Static Library {1}$TARGET{0} ...{2}".format(*colors)
+ ranlib_library_message = "{0}Ranlib Library {1}$TARGET{0} ...{2}".format(*colors)
+ link_shared_library_message = "{0}Linking Shared Library {1}$TARGET{0} ...{2}".format(*colors)
+ java_library_message = "{0}Creating Java Archive {1}$TARGET{0} ...{2}".format(*colors)
+ compiled_resource_message = "{0}Creating Compiled Resource {1}$TARGET{0} ...{2}".format(*colors)
+ generated_file_message = "{0}Generating {1}$TARGET{0} ...{2}".format(*colors)
+
+ env.Append(CXXCOMSTR=compile_source_message)
+ env.Append(CCCOMSTR=compile_source_message)
+ env.Append(SHCCCOMSTR=compile_shared_source_message)
+ env.Append(SHCXXCOMSTR=compile_shared_source_message)
+ env.Append(ARCOMSTR=link_library_message)
+ env.Append(RANLIBCOMSTR=ranlib_library_message)
+ env.Append(SHLINKCOMSTR=link_shared_library_message)
+ env.Append(LINKCOMSTR=link_program_message)
+ env.Append(JARCOMSTR=java_library_message)
+ env.Append(JAVACCOMSTR=java_compile_source_message)
+ env.Append(RCCOMSTR=compiled_resource_message)
+ env.Append(GENCOMSTR=generated_file_message)
def disable_warnings(self):
diff --git a/platform/android/SCsub b/platform/android/SCsub
index 31bc7c25b0..7380511d6d 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -1,6 +1,7 @@
#!/usr/bin/env python
import subprocess
+from methods import print_warning
Import("env")
@@ -52,7 +53,7 @@ elif env["arch"] == "x86_32":
elif env["arch"] == "x86_64":
lib_arch_dir = "x86_64"
else:
- print("WARN: Architecture not suitable for embedding into APK; keeping .so at \\bin")
+ print_warning("Architecture not suitable for embedding into APK; keeping .so at \\bin")
if lib_arch_dir != "":
if env.dev_build:
diff --git a/platform/android/detect.py b/platform/android/detect.py
index fea8ec3287..cbd6144182 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -2,7 +2,7 @@ import os
import sys
import platform
import subprocess
-
+from methods import print_warning, print_error
from typing import TYPE_CHECKING
if TYPE_CHECKING:
@@ -76,7 +76,6 @@ def get_flags():
# Check if Android NDK version is installed
# If not, install it.
def install_ndk_if_needed(env: "SConsEnvironment"):
- print("Checking for Android NDK...")
sdk_root = env["ANDROID_HOME"]
if not os.path.exists(get_android_ndk_root(env)):
extension = ".bat" if os.name == "nt" else ""
@@ -87,13 +86,11 @@ def install_ndk_if_needed(env: "SConsEnvironment"):
ndk_download_args = "ndk;" + get_ndk_version()
subprocess.check_call([sdkmanager, ndk_download_args])
else:
- print("Cannot find " + sdkmanager)
- print(
- "Please ensure ANDROID_HOME is correct and cmdline-tools are installed, or install NDK version "
- + get_ndk_version()
- + " manually."
+ print_error(
+ f'Cannot find "{sdkmanager}". Please ensure ANDROID_HOME is correct and cmdline-tools'
+ f'are installed, or install NDK version "{get_ndk_version()}" manually.'
)
- sys.exit()
+ sys.exit(255)
env["ANDROID_NDK_ROOT"] = get_android_ndk_root(env)
@@ -101,15 +98,15 @@ def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
if env["arch"] not in supported_arches:
- print(
+ print_error(
'Unsupported CPU architecture "%s" for Android. Supported architectures are: %s.'
% (env["arch"], ", ".join(supported_arches))
)
- sys.exit()
+ sys.exit(255)
if get_min_sdk_version(env["ndk_platform"]) < get_min_target_api():
- print(
- "WARNING: minimum supported Android target api is %d. Forcing target api %d."
+ print_warning(
+ "Minimum supported Android target api is %d. Forcing target api %d."
% (get_min_target_api(), get_min_target_api())
)
env["ndk_platform"] = "android-" + str(get_min_target_api())
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index c6f2f82117..9869756be1 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -326,7 +326,7 @@ void DisplayServerAndroid::window_set_drop_files_callback(const Callable &p_call
}
void DisplayServerAndroid::_window_callback(const Callable &p_callable, const Variant &p_arg, bool p_deferred) const {
- if (!p_callable.is_null()) {
+ if (p_callable.is_valid()) {
if (p_deferred) {
p_callable.call_deferred(p_arg);
} else {
diff --git a/platform/ios/detect.py b/platform/ios/detect.py
index 0c9b7b3204..e3bac4ec5c 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -1,6 +1,6 @@
import os
import sys
-from methods import detect_darwin_sdk_path
+from methods import print_error, detect_darwin_sdk_path
from typing import TYPE_CHECKING
@@ -60,11 +60,11 @@ def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_64", "arm64"]
if env["arch"] not in supported_arches:
- print(
+ print_error(
'Unsupported CPU architecture "%s" for iOS. Supported architectures are: %s.'
% (env["arch"], ", ".join(supported_arches))
)
- sys.exit()
+ sys.exit(255)
## LTO
@@ -118,7 +118,7 @@ def configure(env: "SConsEnvironment"):
if env["arch"] == "x86_64":
if not env["ios_simulator"]:
- print("ERROR: Building for iOS with 'arch=x86_64' requires 'ios_simulator=yes'.")
+ print_error("Building for iOS with 'arch=x86_64' requires 'ios_simulator=yes'.")
sys.exit(255)
env["ENV"]["MACOSX_DEPLOYMENT_TARGET"] = "10.9"
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm
index cd6f855d77..62bc55dce8 100644
--- a/platform/ios/display_server_ios.mm
+++ b/platform/ios/display_server_ios.mm
@@ -218,7 +218,7 @@ void DisplayServerIOS::send_window_event(DisplayServer::WindowEvent p_event) con
}
void DisplayServerIOS::_window_callback(const Callable &p_callable, const Variant &p_arg) const {
- if (!p_callable.is_null()) {
+ if (p_callable.is_valid()) {
p_callable.call(p_arg);
}
}
diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml
index 0c0ded5fea..20c1647843 100644
--- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml
+++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml
@@ -29,6 +29,9 @@
<member name="application/code_sign_identity_release" type="String" setter="" getter="">
The "Full Name", "Common Name" or SHA-1 hash of the signing identity used for release export.
</member>
+ <member name="application/delete_old_export_files_unconditionally" type="bool" setter="" getter="">
+ If [code]true[/code], existing "project name" and "project name.xcodeproj" in the export destination directory will be unconditionally deleted during export.
+ </member>
<member name="application/export_method_debug" type="int" setter="" getter="">
Application distribution target (debug export).
</member>
@@ -123,12 +126,441 @@
<member name="icons/spotlight_80x80" type="String" setter="" getter="">
Spotlight icon file on iPad and iPhone (2x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url].
</member>
+ <member name="privacy/active_keyboard_access_reasons" type="int" setter="" getter="">
+ The reasons your app use active keyboard API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url].
+ </member>
<member name="privacy/camera_usage_description" type="String" setter="" getter="">
A message displayed when requesting access to the device's camera (in English).
</member>
<member name="privacy/camera_usage_description_localized" type="Dictionary" setter="" getter="">
A message displayed when requesting access to the device's camera (localized).
</member>
+ <member name="privacy/collected_data/advertising_data/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects advertising data.
+ </member>
+ <member name="privacy/collected_data/advertising_data/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects advertising data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/advertising_data/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links advertising data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/advertising_data/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses advertising data for tracking.
+ </member>
+ <member name="privacy/collected_data/audio_data/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects audio data data.
+ </member>
+ <member name="privacy/collected_data/audio_data/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects audio data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/audio_data/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links audio data data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/audio_data/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses audio data data for tracking.
+ </member>
+ <member name="privacy/collected_data/browsing_history/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects browsing history.
+ </member>
+ <member name="privacy/collected_data/browsing_history/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects browsing history. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/browsing_history/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links browsing history to the user's identity.
+ </member>
+ <member name="privacy/collected_data/browsing_history/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses browsing history for tracking.
+ </member>
+ <member name="privacy/collected_data/coarse_location/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects coarse location data.
+ </member>
+ <member name="privacy/collected_data/coarse_location/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects coarse location data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/coarse_location/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links coarse location data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/coarse_location/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses coarse location data for tracking.
+ </member>
+ <member name="privacy/collected_data/contacts/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects contacts.
+ </member>
+ <member name="privacy/collected_data/contacts/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects contacts. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/contacts/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links contacts to the user's identity.
+ </member>
+ <member name="privacy/collected_data/contacts/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses contacts for tracking.
+ </member>
+ <member name="privacy/collected_data/crash_data/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects crash data.
+ </member>
+ <member name="privacy/collected_data/crash_data/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects crash data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/crash_data/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links crash data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/crash_data/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses crash data for tracking.
+ </member>
+ <member name="privacy/collected_data/credit_info/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects credit information.
+ </member>
+ <member name="privacy/collected_data/credit_info/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects credit information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/credit_info/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links credit information to the user's identity.
+ </member>
+ <member name="privacy/collected_data/credit_info/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses credit information for tracking.
+ </member>
+ <member name="privacy/collected_data/customer_support/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects customer support data.
+ </member>
+ <member name="privacy/collected_data/customer_support/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects customer support data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/customer_support/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links customer support data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/customer_support/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses customer support data for tracking.
+ </member>
+ <member name="privacy/collected_data/device_id/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects device IDs.
+ </member>
+ <member name="privacy/collected_data/device_id/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects device IDs. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/device_id/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links device IDs to the user's identity.
+ </member>
+ <member name="privacy/collected_data/device_id/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses device IDs for tracking.
+ </member>
+ <member name="privacy/collected_data/email_address/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects email address.
+ </member>
+ <member name="privacy/collected_data/email_address/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects email address. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/email_address/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links email address to the user's identity.
+ </member>
+ <member name="privacy/collected_data/email_address/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses email address for tracking.
+ </member>
+ <member name="privacy/collected_data/emails_or_text_messages/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects emails or text messages.
+ </member>
+ <member name="privacy/collected_data/emails_or_text_messages/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects emails or text messages. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/emails_or_text_messages/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links emails or text messages to the user's identity.
+ </member>
+ <member name="privacy/collected_data/emails_or_text_messages/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses emails or text messages for tracking.
+ </member>
+ <member name="privacy/collected_data/environment_scanning/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects environment scanning data.
+ </member>
+ <member name="privacy/collected_data/environment_scanning/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects environment scanning data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/environment_scanning/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links environment scanning data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/environment_scanning/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses environment scanning data for tracking.
+ </member>
+ <member name="privacy/collected_data/fitness/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects fitness and exercise data.
+ </member>
+ <member name="privacy/collected_data/fitness/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects fitness and exercise data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/fitness/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links fitness and exercise data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/fitness/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses fitness and exercise data for tracking.
+ </member>
+ <member name="privacy/collected_data/gameplay_content/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects gameplay content.
+ </member>
+ <member name="privacy/collected_data/gameplay_content/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects gameplay content. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/gameplay_content/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links gameplay content to the user's identity.
+ </member>
+ <member name="privacy/collected_data/gameplay_content/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses gameplay content for tracking.
+ </member>
+ <member name="privacy/collected_data/hands/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects user's hand structure and hand movements.
+ </member>
+ <member name="privacy/collected_data/hands/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects user's hand structure and hand movements. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/hands/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links user's hand structure and hand movements to the user's identity.
+ </member>
+ <member name="privacy/collected_data/hands/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses user's hand structure and hand movements for tracking.
+ </member>
+ <member name="privacy/collected_data/head/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects user's head movement.
+ </member>
+ <member name="privacy/collected_data/head/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects user's head movement. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/head/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links user's head movement to the user's identity.
+ </member>
+ <member name="privacy/collected_data/head/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses user's head movement for tracking.
+ </member>
+ <member name="privacy/collected_data/health/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects health and medical data.
+ </member>
+ <member name="privacy/collected_data/health/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects health and medical data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/health/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links health and medical data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/health/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses health and medical data for tracking.
+ </member>
+ <member name="privacy/collected_data/name/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects user's name.
+ </member>
+ <member name="privacy/collected_data/name/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects user's name. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/name/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links user's name to the user's identity.
+ </member>
+ <member name="privacy/collected_data/name/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses user's name for tracking.
+ </member>
+ <member name="privacy/collected_data/other_contact_info/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects any other contact information.
+ </member>
+ <member name="privacy/collected_data/other_contact_info/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects any other contact information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/other_contact_info/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links any other contact information to the user's identity.
+ </member>
+ <member name="privacy/collected_data/other_contact_info/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses any other contact information for tracking.
+ </member>
+ <member name="privacy/collected_data/other_data_types/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects any other data.
+ </member>
+ <member name="privacy/collected_data/other_data_types/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects any other data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/other_data_types/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links any other data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/other_data_types/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses any other data for tracking.
+ </member>
+ <member name="privacy/collected_data/other_diagnostic_data/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects any other diagnostic data.
+ </member>
+ <member name="privacy/collected_data/other_diagnostic_data/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects any other diagnostic data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/other_diagnostic_data/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links any other diagnostic data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/other_diagnostic_data/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses any other diagnostic data for tracking.
+ </member>
+ <member name="privacy/collected_data/other_financial_info/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects any other financial information.
+ </member>
+ <member name="privacy/collected_data/other_financial_info/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects any other financial information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/other_financial_info/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links any other financial information to the user's identity.
+ </member>
+ <member name="privacy/collected_data/other_financial_info/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses any other financial information for tracking.
+ </member>
+ <member name="privacy/collected_data/other_usage_data/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects any other usage data.
+ </member>
+ <member name="privacy/collected_data/other_usage_data/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects any other usage data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/other_usage_data/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links any other usage data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/other_usage_data/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses any other usage data for tracking.
+ </member>
+ <member name="privacy/collected_data/other_user_content/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects any other user generated content.
+ </member>
+ <member name="privacy/collected_data/other_user_content/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects any other user generated content. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/other_user_content/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links any other user generated content to the user's identity.
+ </member>
+ <member name="privacy/collected_data/other_user_content/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses any other user generated content for tracking.
+ </member>
+ <member name="privacy/collected_data/payment_info/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects payment information.
+ </member>
+ <member name="privacy/collected_data/payment_info/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects payment information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/payment_info/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links payment information to the user's identity.
+ </member>
+ <member name="privacy/collected_data/payment_info/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses payment information for tracking.
+ </member>
+ <member name="privacy/collected_data/performance_data/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects performance data.
+ </member>
+ <member name="privacy/collected_data/performance_data/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects performance data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/performance_data/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links performance data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/performance_data/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses performance data for tracking.
+ </member>
+ <member name="privacy/collected_data/phone_number/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects phone number.
+ </member>
+ <member name="privacy/collected_data/phone_number/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects phone number. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/phone_number/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links phone number to the user's identity.
+ </member>
+ <member name="privacy/collected_data/phone_number/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses phone number for tracking.
+ </member>
+ <member name="privacy/collected_data/photos_or_videos/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects photos or videos.
+ </member>
+ <member name="privacy/collected_data/photos_or_videos/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects photos or videos. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/photos_or_videos/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links photos or videos to the user's identity.
+ </member>
+ <member name="privacy/collected_data/photos_or_videos/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses photos or videos for tracking.
+ </member>
+ <member name="privacy/collected_data/physical_address/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects physical address.
+ </member>
+ <member name="privacy/collected_data/physical_address/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects physical address. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/physical_address/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links physical address to the user's identity.
+ </member>
+ <member name="privacy/collected_data/physical_address/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses physical address for tracking.
+ </member>
+ <member name="privacy/collected_data/precise_location/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects precise location data.
+ </member>
+ <member name="privacy/collected_data/precise_location/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects precise location data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/precise_location/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links precise location data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/precise_location/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses precise location data for tracking.
+ </member>
+ <member name="privacy/collected_data/product_interaction/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects product interaction data.
+ </member>
+ <member name="privacy/collected_data/product_interaction/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects product interaction data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/product_interaction/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links product interaction data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/product_interaction/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses product interaction data for tracking.
+ </member>
+ <member name="privacy/collected_data/purchase_history/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects purchase history.
+ </member>
+ <member name="privacy/collected_data/purchase_history/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects purchase history. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/purchase_history/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links purchase history to the user's identity.
+ </member>
+ <member name="privacy/collected_data/purchase_history/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses purchase history for tracking.
+ </member>
+ <member name="privacy/collected_data/search_hhistory/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects search history.
+ </member>
+ <member name="privacy/collected_data/search_hhistory/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects search history. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/search_hhistory/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links search history to the user's identity.
+ </member>
+ <member name="privacy/collected_data/search_hhistory/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses search history for tracking.
+ </member>
+ <member name="privacy/collected_data/sensitive_info/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects sensitive user information.
+ </member>
+ <member name="privacy/collected_data/sensitive_info/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects sensitive user information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/sensitive_info/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links sensitive user information to the user's identity.
+ </member>
+ <member name="privacy/collected_data/sensitive_info/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses sensitive user information for tracking.
+ </member>
+ <member name="privacy/collected_data/user_id/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects user IDs.
+ </member>
+ <member name="privacy/collected_data/user_id/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects user IDs. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/user_id/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links user IDs to the user's identity.
+ </member>
+ <member name="privacy/collected_data/user_id/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses user IDs for tracking.
+ </member>
+ <member name="privacy/disk_space_access_reasons" type="int" setter="" getter="">
+ The reasons your app use free disk space API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url].
+ </member>
+ <member name="privacy/file_timestamp_access_reasons" type="int" setter="" getter="">
+ The reasons your app use file timestamp/metadata API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url].
+ </member>
<member name="privacy/microphone_usage_description" type="String" setter="" getter="">
A message displayed when requesting access to the device's microphone (in English).
</member>
@@ -141,6 +573,18 @@
<member name="privacy/photolibrary_usage_description_localized" type="Dictionary" setter="" getter="">
A message displayed when requesting access to the user's photo library (localized).
</member>
+ <member name="privacy/system_boot_time_access_reasons" type="int" setter="" getter="">
+ The reasons your app use system boot time / absolute time API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url].
+ </member>
+ <member name="privacy/tracking_domains" type="PackedStringArray" setter="" getter="">
+ The list of internet domains your app connects to that engage in tracking. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files]Privacy manifest files[/url].
+ </member>
+ <member name="privacy/tracking_enabled" type="bool" setter="" getter="">
+ Indicates whether your app uses data for tracking. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files]Privacy manifest files[/url].
+ </member>
+ <member name="privacy/user_defaults_access_reasons" type="int" setter="" getter="">
+ The reasons your app use user defaults API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url].
+ </member>
<member name="storyboard/custom_bg_color" type="Color" setter="" getter="">
A custom background color of the storyboard launch screen.
</member>
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 33389129b7..c35c72d093 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -106,6 +106,94 @@ static const IconInfo icon_infos[] = {
{ PNAME("icons/notification_60x60"), "iphone", "Icon-60.png", "60", "3x", "20x20", false }
};
+struct APIAccessInfo {
+ String prop_name;
+ String type_name;
+ Vector<String> prop_flag_value;
+ Vector<String> prop_flag_name;
+ int default_value;
+};
+
+static const APIAccessInfo api_info[] = {
+ { "file_timestamp",
+ "NSPrivacyAccessedAPICategoryFileTimestamp",
+ { "DDA9.1", "C617.1", "3B52.1" },
+ { "Display to user on-device:", "Inside app or group container", "Files provided to app by user" },
+ 3 },
+ { "system_boot_time",
+ "NSPrivacyAccessedAPICategorySystemBootTime",
+ { "35F9.1", "8FFB.1", "3D61.1" },
+ { "Measure time on-device", "Calculate absolute event timestamps", "User-initiated bug report" },
+ 1 },
+ { "disk_space",
+ "NSPrivacyAccessedAPICategoryDiskSpace",
+ { "E174.1", "85F4.1", "7D9E.1", "B728.1" },
+ { "Write or delete file on-device", "Display to user on-device", "User-initiated bug report", "Health research app" },
+ 3 },
+ { "active_keyboard",
+ "NSPrivacyAccessedAPICategoryActiveKeyboards",
+ { "3EC4.1", "54BD.1" },
+ { "Custom keyboard app on-device", "Customize UI on-device:2" },
+ 0 },
+ { "user_defaults",
+ "NSPrivacyAccessedAPICategoryUserDefaults",
+ { "1C8F.1", "AC6B.1", "CA92.1" },
+ { "Access info from same App Group", "Access managed app configuration", "Access info from same app" },
+ 0 }
+};
+
+struct DataCollectionInfo {
+ String prop_name;
+ String type_name;
+};
+
+static const DataCollectionInfo data_collect_type_info[] = {
+ { "name", "NSPrivacyCollectedDataTypeName" },
+ { "email_address", "NSPrivacyCollectedDataTypeEmailAddress" },
+ { "phone_number", "NSPrivacyCollectedDataTypePhoneNumber" },
+ { "physical_address", "NSPrivacyCollectedDataTypePhysicalAddress" },
+ { "other_contact_info", "NSPrivacyCollectedDataTypeOtherUserContactInfo" },
+ { "health", "NSPrivacyCollectedDataTypeHealth" },
+ { "fitness", "NSPrivacyCollectedDataTypeFitness" },
+ { "payment_info", "NSPrivacyCollectedDataTypePaymentInfo" },
+ { "credit_info", "NSPrivacyCollectedDataTypeCreditInfo" },
+ { "other_financial_info", "NSPrivacyCollectedDataTypeOtherFinancialInfo" },
+ { "precise_location", "NSPrivacyCollectedDataTypePreciseLocation" },
+ { "coarse_location", "NSPrivacyCollectedDataTypeCoarseLocation" },
+ { "sensitive_info", "NSPrivacyCollectedDataTypeSensitiveInfo" },
+ { "contacts", "NSPrivacyCollectedDataTypeContacts" },
+ { "emails_or_text_messages", "NSPrivacyCollectedDataTypeEmailsOrTextMessages" },
+ { "photos_or_videos", "NSPrivacyCollectedDataTypePhotosorVideos" },
+ { "audio_data", "NSPrivacyCollectedDataTypeAudioData" },
+ { "gameplay_content", "NSPrivacyCollectedDataTypeGameplayContent" },
+ { "customer_support", "NSPrivacyCollectedDataTypeCustomerSupport" },
+ { "other_user_content", "NSPrivacyCollectedDataTypeOtherUserContent" },
+ { "browsing_history", "NSPrivacyCollectedDataTypeBrowsingHistory" },
+ { "search_hhistory", "NSPrivacyCollectedDataTypeSearchHistory" },
+ { "user_id", "NSPrivacyCollectedDataTypeUserID" },
+ { "device_id", "NSPrivacyCollectedDataTypeDeviceID" },
+ { "purchase_history", "NSPrivacyCollectedDataTypePurchaseHistory" },
+ { "product_interaction", "NSPrivacyCollectedDataTypeProductInteraction" },
+ { "advertising_data", "NSPrivacyCollectedDataTypeAdvertisingData" },
+ { "other_usage_data", "NSPrivacyCollectedDataTypeOtherUsageData" },
+ { "crash_data", "NSPrivacyCollectedDataTypeCrashData" },
+ { "performance_data", "NSPrivacyCollectedDataTypePerformanceData" },
+ { "other_diagnostic_data", "NSPrivacyCollectedDataTypeOtherDiagnosticData" },
+ { "environment_scanning", "NSPrivacyCollectedDataTypeEnvironmentScanning" },
+ { "hands", "NSPrivacyCollectedDataTypeHands" },
+ { "head", "NSPrivacyCollectedDataTypeHead" },
+ { "other_data_types", "NSPrivacyCollectedDataTypeOtherDataTypes" },
+};
+
+static const DataCollectionInfo data_collect_purpose_info[] = {
+ { "Analytics", "NSPrivacyCollectedDataTypePurposeAnalytics" },
+ { "App Functionality", "NSPrivacyCollectedDataTypePurposeAppFunctionality" },
+ { "Developer Advertising", "NSPrivacyCollectedDataTypePurposeDeveloperAdvertising" },
+ { "Third-party Advertising", "NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising" },
+ { "Product Personalization", "NSPrivacyCollectedDataTypePurposeProductPersonalization" },
+ { "Other", "NSPrivacyCollectedDataTypePurposeOther" },
+};
+
String EditorExportPlatformIOS::get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const {
if (p_preset) {
if (p_name == "application/app_store_team_id") {
@@ -119,6 +207,21 @@ String EditorExportPlatformIOS::get_export_option_warning(const EditorExportPres
if (!is_package_name_valid(identifier, &pn_err)) {
return TTR("Invalid Identifier:") + " " + pn_err;
}
+ } else if (p_name == "privacy/file_timestamp_access_reasons") {
+ int access = p_preset->get("privacy/file_timestamp_access_reasons");
+ if (access == 0) {
+ return TTR("At least one file timestamp access reason should be selected.");
+ }
+ } else if (p_name == "privacy/disk_space_access_reasons") {
+ int access = p_preset->get("privacy/disk_space_access_reasons");
+ if (access == 0) {
+ return TTR("At least one disk space access reason should be selected.");
+ }
+ } else if (p_name == "privacy/system_boot_time_access_reasons") {
+ int access = p_preset->get("privacy/system_boot_time_access_reasons");
+ if (access == 0) {
+ return TTR("At least one system boot time access reason should be selected.");
+ }
}
}
return String();
@@ -140,6 +243,15 @@ bool EditorExportPlatformIOS::get_export_option_visibility(const EditorExportPre
return false;
}
+ if (p_preset == nullptr) {
+ return true;
+ }
+
+ bool advanced_options_enabled = p_preset->are_advanced_options_enabled();
+ if (p_option.begins_with("privacy")) {
+ return advanced_options_enabled;
+ }
+
return true;
}
@@ -175,6 +287,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/export_project_only"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/delete_old_export_files_unconditionally"), false));
Vector<PluginConfigIOS> found_plugins = get_plugins();
for (int i = 0; i < found_plugins.size(); i++) {
@@ -220,6 +333,37 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/photolibrary_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()));
+ for (uint64_t i = 0; i < sizeof(api_info) / sizeof(api_info[0]); ++i) {
+ String prop_name = vformat("privacy/%s_access_reasons", api_info[i].prop_name);
+ String hint;
+ for (int j = 0; j < api_info[i].prop_flag_value.size(); j++) {
+ if (j != 0) {
+ hint += ",";
+ }
+ hint += vformat("%s - %s:%d", api_info[i].prop_flag_value[j], api_info[i].prop_flag_name[j], (1 << j));
+ }
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, prop_name, PROPERTY_HINT_FLAGS, hint), api_info[i].default_value));
+ }
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "privacy/tracking_enabled"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "privacy/tracking_domains"), Vector<String>()));
+
+ {
+ String hint;
+ for (uint64_t i = 0; i < sizeof(data_collect_purpose_info) / sizeof(data_collect_purpose_info[0]); ++i) {
+ if (i != 0) {
+ hint += ",";
+ }
+ hint += vformat("%s:%d", data_collect_purpose_info[i].prop_name, (1 << i));
+ }
+ for (uint64_t i = 0; i < sizeof(data_collect_type_info) / sizeof(data_collect_type_info[0]); ++i) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("privacy/collected_data/%s/collected", data_collect_type_info[i].prop_name)), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("privacy/collected_data/%s/linked_to_user", data_collect_type_info[i].prop_name)), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("privacy/collected_data/%s/used_for_tracking", data_collect_type_info[i].prop_name)), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, vformat("privacy/collected_data/%s/collection_purposes", data_collect_type_info[i].prop_name), PROPERTY_HINT_FLAGS, hint), 0));
+ }
+ }
+
HashSet<String> used_names;
for (uint64_t i = 0; i < sizeof(icon_infos) / sizeof(icon_infos[0]); ++i) {
if (!used_names.has(icon_infos[i].preset_key)) {
@@ -522,6 +666,87 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
} else if (lines[i].find("$swift_runtime_build_phase") != -1) {
String value = !p_config.use_swift_runtime ? "" : "90B4C2B62680C7E90039117A /* dummy.swift */,";
strnew += lines[i].replace("$swift_runtime_build_phase", value) + "\n";
+ } else if (lines[i].find("$priv_collection") != -1) {
+ bool section_opened = false;
+ for (uint64_t j = 0; j < sizeof(data_collect_type_info) / sizeof(data_collect_type_info[0]); ++j) {
+ bool data_collected = p_preset->get(vformat("privacy/collected_data/%s/collected", data_collect_type_info[j].prop_name));
+ bool linked = p_preset->get(vformat("privacy/collected_data/%s/linked_to_user", data_collect_type_info[j].prop_name));
+ bool tracking = p_preset->get(vformat("privacy/collected_data/%s/used_for_tracking", data_collect_type_info[j].prop_name));
+ int purposes = p_preset->get(vformat("privacy/collected_data/%s/collection_purposes", data_collect_type_info[j].prop_name));
+ if (data_collected) {
+ if (!section_opened) {
+ section_opened = true;
+ strnew += "\t<key>NSPrivacyCollectedDataTypes</key>\n";
+ strnew += "\t<array>\n";
+ }
+ strnew += "\t\t<dict>\n";
+ strnew += "\t\t\t<key>NSPrivacyCollectedDataType</key>\n";
+ strnew += vformat("\t\t\t<string>%s</string>\n", data_collect_type_info[j].type_name);
+ strnew += "\t\t\t\t<key>NSPrivacyCollectedDataTypeLinked</key>\n";
+ if (linked) {
+ strnew += "\t\t\t\t<true/>\n";
+ } else {
+ strnew += "\t\t\t\t<false/>\n";
+ }
+ strnew += "\t\t\t\t<key>NSPrivacyCollectedDataTypeTracking</key>\n";
+ if (tracking) {
+ strnew += "\t\t\t\t<true/>\n";
+ } else {
+ strnew += "\t\t\t\t<false/>\n";
+ }
+ if (purposes != 0) {
+ strnew += "\t\t\t\t<key>NSPrivacyCollectedDataTypePurposes</key>\n";
+ strnew += "\t\t\t\t<array>\n";
+ for (uint64_t k = 0; k < sizeof(data_collect_purpose_info) / sizeof(data_collect_purpose_info[0]); ++k) {
+ if (purposes & (1 << k)) {
+ strnew += vformat("\t\t\t\t\t<string>%s</string>\n", data_collect_purpose_info[k].type_name);
+ }
+ }
+ strnew += "\t\t\t\t</array>\n";
+ }
+ strnew += "\t\t\t</dict>\n";
+ }
+ }
+ if (section_opened) {
+ strnew += "\t</array>\n";
+ }
+ } else if (lines[i].find("$priv_tracking") != -1) {
+ bool tracking = p_preset->get("privacy/tracking_enabled");
+ strnew += "\t<key>NSPrivacyTracking</key>\n";
+ if (tracking) {
+ strnew += "\t<true/>\n";
+ } else {
+ strnew += "\t<false/>\n";
+ }
+ Vector<String> tracking_domains = p_preset->get("privacy/tracking_domains");
+ if (!tracking_domains.is_empty()) {
+ strnew += "\t<key>NSPrivacyTrackingDomains</key>\n";
+ strnew += "\t<array>\n";
+ for (const String &E : tracking_domains) {
+ strnew += "\t\t<string>" + E + "</string>\n";
+ }
+ strnew += "\t</array>\n";
+ }
+ } else if (lines[i].find("$priv_api_types") != -1) {
+ strnew += "\t<array>\n";
+ for (uint64_t j = 0; j < sizeof(api_info) / sizeof(api_info[0]); ++j) {
+ int api_access = p_preset->get(vformat("privacy/%s_access_reasons", api_info[j].prop_name));
+ if (api_access != 0) {
+ strnew += "\t\t<dict>\n";
+ strnew += "\t\t\t<key>NSPrivacyAccessedAPITypeReasons</key>\n";
+ strnew += "\t\t\t<array>\n";
+ for (int k = 0; k < api_info[j].prop_flag_value.size(); k++) {
+ if (api_access & (1 << k)) {
+ strnew += vformat("\t\t\t\t<string>%s</string>\n", api_info[j].prop_flag_value[k]);
+ }
+ }
+ strnew += "\t\t\t</array>\n";
+ strnew += "\t\t\t<key>NSPrivacyAccessedAPIType</key>\n";
+ strnew += vformat("\t\t\t<string>%s</string>\n", api_info[j].type_name);
+ strnew += "\t\t</dict>\n";
+ }
+ }
+ strnew += "\t</array>\n";
} else {
strnew += lines[i] + "\n";
}
@@ -1632,19 +1857,72 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
}
{
+ bool delete_old = p_preset->get("application/delete_old_export_files_unconditionally");
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
if (da.is_valid()) {
String current_dir = da->get_current_dir();
- // remove leftovers from last export so they don't interfere
- // in case some files are no longer needed
+ // Remove leftovers from last export so they don't interfere in case some files are no longer needed.
if (da->change_dir(binary_dir + ".xcodeproj") == OK) {
- da->erase_contents_recursive();
+ // Check directory content before deleting.
+ int expected_files = 0;
+ int total_files = 0;
+ if (!delete_old) {
+ da->list_dir_begin();
+ for (String n = da->get_next(); !n.is_empty(); n = da->get_next()) {
+ if (!n.begins_with(".")) { // Ignore ".", ".." and hidden files.
+ if (da->current_is_dir()) {
+ if (n == "xcshareddata" || n == "project.xcworkspace") {
+ expected_files++;
+ }
+ } else {
+ if (n == "project.pbxproj") {
+ expected_files++;
+ }
+ }
+ total_files++;
+ }
+ }
+ da->list_dir_end();
+ }
+ if ((total_files == 0) || (expected_files >= Math::floor(total_files * 0.8))) {
+ da->erase_contents_recursive();
+ } else {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Unexpected files found in the export destination directory \"%s.xcodeproj\", delete it manually or select another destination."), binary_dir));
+ return ERR_CANT_CREATE;
+ }
}
+ da->change_dir(current_dir);
+
if (da->change_dir(binary_dir) == OK) {
- da->erase_contents_recursive();
+ // Check directory content before deleting.
+ int expected_files = 0;
+ int total_files = 0;
+ if (!delete_old) {
+ da->list_dir_begin();
+ for (String n = da->get_next(); !n.is_empty(); n = da->get_next()) {
+ if (!n.begins_with(".")) { // Ignore ".", ".." and hidden files.
+ if (da->current_is_dir()) {
+ if (n == "dylibs" || n == "Images.xcassets" || n.ends_with(".lproj") || n == "godot-publish-dotnet" || n.ends_with(".xcframework") || n.ends_with(".framework")) {
+ expected_files++;
+ }
+ } else {
+ if (n == binary_name + "-Info.plist" || n == binary_name + ".entitlements" || n == "Launch Screen.storyboard" || n == "export_options.plist" || n.begins_with("dummy.") || n.ends_with(".gdip")) {
+ expected_files++;
+ }
+ }
+ total_files++;
+ }
+ }
+ da->list_dir_end();
+ }
+ if ((total_files == 0) || (expected_files >= Math::floor(total_files * 0.8))) {
+ da->erase_contents_recursive();
+ } else {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Unexpected files found in the export destination directory \"%s\", delete it manually or select another destination."), binary_dir));
+ return ERR_CANT_CREATE;
+ }
}
-
da->change_dir(current_dir);
if (!da->dir_exists(binary_dir)) {
@@ -1694,6 +1972,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
files_to_parse.insert("godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme");
files_to_parse.insert("godot_ios/godot_ios.entitlements");
files_to_parse.insert("godot_ios/Launch Screen.storyboard");
+ files_to_parse.insert("PrivacyInfo.xcprivacy");
IOSConfigData config_data = {
pkg_name,
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 27dec73b65..afc9d25a80 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -1,7 +1,7 @@
import os
import platform
import sys
-from methods import get_compiler_version, using_gcc
+from methods import print_warning, print_error, get_compiler_version, using_gcc
from platform_methods import detect_arch
from typing import TYPE_CHECKING
@@ -20,7 +20,7 @@ def can_build():
pkgconf_error = os.system("pkg-config --version > /dev/null")
if pkgconf_error:
- print("Error: pkg-config not found. Aborting.")
+ print_error("pkg-config not found. Aborting.")
return False
return True
@@ -75,7 +75,7 @@ def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64", "rv64", "ppc32", "ppc64"]
if env["arch"] not in supported_arches:
- print(
+ print_error(
'Unsupported CPU architecture "%s" for Linux / *BSD. Supported architectures are: %s.'
% (env["arch"], ", ".join(supported_arches))
)
@@ -128,7 +128,9 @@ def configure(env: "SConsEnvironment"):
found_wrapper = True
break
if not found_wrapper:
- print("Couldn't locate mold installation path. Make sure it's installed in /usr or /usr/local.")
+ print_error(
+ "Couldn't locate mold installation path. Make sure it's installed in /usr or /usr/local."
+ )
sys.exit(255)
else:
env.Append(LINKFLAGS=["-fuse-ld=mold"])
@@ -185,7 +187,7 @@ def configure(env: "SConsEnvironment"):
if env["lto"] != "none":
if env["lto"] == "thin":
if not env["use_llvm"]:
- print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
+ print_error("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
sys.exit(255)
env.Append(CCFLAGS=["-flto=thin"])
env.Append(LINKFLAGS=["-flto=thin"])
@@ -209,7 +211,7 @@ def configure(env: "SConsEnvironment"):
if env["wayland"]:
if os.system("wayland-scanner -v 2>/dev/null") != 0:
- print("wayland-scanner not found. Disabling Wayland support.")
+ print_warning("wayland-scanner not found. Disabling Wayland support.")
env["wayland"] = False
if env["touch"]:
@@ -227,7 +229,7 @@ def configure(env: "SConsEnvironment"):
env["builtin_harfbuzz"],
]
if (not all(ft_linked_deps)) and any(ft_linked_deps): # All or nothing.
- print(
+ print_error(
"These libraries should be either all builtin, or all system provided:\n"
"freetype, libpng, zlib, graphite, harfbuzz.\n"
"Please specify `builtin_<name>=no` for all of them, or none."
@@ -318,7 +320,7 @@ def configure(env: "SConsEnvironment"):
env.ParseConfig("pkg-config fontconfig --cflags --libs")
env.Append(CPPDEFINES=["FONTCONFIG_ENABLED"])
else:
- print("Warning: fontconfig development libraries not found. Disabling the system fonts support.")
+ print_warning("fontconfig development libraries not found. Disabling the system fonts support.")
env["fontconfig"] = False
else:
env.Append(CPPDEFINES=["FONTCONFIG_ENABLED"])
@@ -329,7 +331,7 @@ def configure(env: "SConsEnvironment"):
env.ParseConfig("pkg-config alsa --cflags --libs")
env.Append(CPPDEFINES=["ALSA_ENABLED", "ALSAMIDI_ENABLED"])
else:
- print("Warning: ALSA development libraries not found. Disabling the ALSA audio driver.")
+ print_warning("ALSA development libraries not found. Disabling the ALSA audio driver.")
env["alsa"] = False
else:
env.Append(CPPDEFINES=["ALSA_ENABLED", "ALSAMIDI_ENABLED"])
@@ -340,7 +342,7 @@ def configure(env: "SConsEnvironment"):
env.ParseConfig("pkg-config libpulse --cflags --libs")
env.Append(CPPDEFINES=["PULSEAUDIO_ENABLED"])
else:
- print("Warning: PulseAudio development libraries not found. Disabling the PulseAudio audio driver.")
+ print_warning("PulseAudio development libraries not found. Disabling the PulseAudio audio driver.")
env["pulseaudio"] = False
else:
env.Append(CPPDEFINES=["PULSEAUDIO_ENABLED", "_REENTRANT"])
@@ -351,7 +353,7 @@ def configure(env: "SConsEnvironment"):
env.ParseConfig("pkg-config dbus-1 --cflags --libs")
env.Append(CPPDEFINES=["DBUS_ENABLED"])
else:
- print("Warning: D-Bus development libraries not found. Disabling screensaver prevention.")
+ print_warning("D-Bus development libraries not found. Disabling screensaver prevention.")
env["dbus"] = False
else:
env.Append(CPPDEFINES=["DBUS_ENABLED"])
@@ -362,7 +364,7 @@ def configure(env: "SConsEnvironment"):
env.ParseConfig("pkg-config speech-dispatcher --cflags --libs")
env.Append(CPPDEFINES=["SPEECHD_ENABLED"])
else:
- print("Warning: speech-dispatcher development libraries not found. Disabling text to speech support.")
+ print_warning("speech-dispatcher development libraries not found. Disabling text to speech support.")
env["speechd"] = False
else:
env.Append(CPPDEFINES=["SPEECHD_ENABLED"])
@@ -373,11 +375,11 @@ def configure(env: "SConsEnvironment"):
env.Append(CPPDEFINES=["XKB_ENABLED"])
else:
if env["wayland"]:
- print("Error: libxkbcommon development libraries required by Wayland not found. Aborting.")
+ print_error("libxkbcommon development libraries required by Wayland not found. Aborting.")
sys.exit(255)
else:
- print(
- "Warning: libxkbcommon development libraries not found. Disabling dead key composition and key label support."
+ print_warning(
+ "libxkbcommon development libraries not found. Disabling dead key composition and key label support."
)
else:
env.Append(CPPDEFINES=["XKB_ENABLED"])
@@ -390,7 +392,7 @@ def configure(env: "SConsEnvironment"):
env.ParseConfig("pkg-config libudev --cflags --libs")
env.Append(CPPDEFINES=["UDEV_ENABLED"])
else:
- print("Warning: libudev development libraries not found. Disabling controller hotplugging support.")
+ print_warning("libudev development libraries not found. Disabling controller hotplugging support.")
env["udev"] = False
else:
env.Append(CPPDEFINES=["UDEV_ENABLED"])
@@ -416,31 +418,31 @@ def configure(env: "SConsEnvironment"):
if env["x11"]:
if not env["use_sowrap"]:
if os.system("pkg-config --exists x11"):
- print("Error: X11 libraries not found. Aborting.")
+ print_error("X11 libraries not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config x11 --cflags --libs")
if os.system("pkg-config --exists xcursor"):
- print("Error: Xcursor library not found. Aborting.")
+ print_error("Xcursor library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config xcursor --cflags --libs")
if os.system("pkg-config --exists xinerama"):
- print("Error: Xinerama library not found. Aborting.")
+ print_error("Xinerama library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config xinerama --cflags --libs")
if os.system("pkg-config --exists xext"):
- print("Error: Xext library not found. Aborting.")
+ print_error("Xext library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config xext --cflags --libs")
if os.system("pkg-config --exists xrandr"):
- print("Error: XrandR library not found. Aborting.")
+ print_error("XrandR library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config xrandr --cflags --libs")
if os.system("pkg-config --exists xrender"):
- print("Error: XRender library not found. Aborting.")
+ print_error("XRender library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config xrender --cflags --libs")
if os.system("pkg-config --exists xi"):
- print("Error: Xi library not found. Aborting.")
+ print_error("Xi library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config xi --cflags --libs")
env.Append(CPPDEFINES=["X11_ENABLED"])
@@ -448,20 +450,20 @@ def configure(env: "SConsEnvironment"):
if env["wayland"]:
if not env["use_sowrap"]:
if os.system("pkg-config --exists libdecor-0"):
- print("Warning: libdecor development libraries not found. Disabling client-side decorations.")
+ print_warning("libdecor development libraries not found. Disabling client-side decorations.")
env["libdecor"] = False
else:
env.ParseConfig("pkg-config libdecor-0 --cflags --libs")
if os.system("pkg-config --exists wayland-client"):
- print("Error: Wayland client library not found. Aborting.")
+ print_error("Wayland client library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config wayland-client --cflags --libs")
if os.system("pkg-config --exists wayland-cursor"):
- print("Error: Wayland cursor library not found. Aborting.")
+ print_error("Wayland cursor library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config wayland-cursor --cflags --libs")
if os.system("pkg-config --exists wayland-egl"):
- print("Error: Wayland EGL library not found. Aborting.")
+ print_error("Wayland EGL library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config wayland-egl --cflags --libs")
diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp
index 7f9008e952..3a63b87c7a 100644
--- a/platform/linuxbsd/wayland/wayland_thread.cpp
+++ b/platform/linuxbsd/wayland/wayland_thread.cpp
@@ -371,28 +371,22 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
}
if (strcmp(interface, zxdg_exporter_v1_interface.name) == 0) {
- registry->wl_exporter = (struct zxdg_exporter_v1 *)wl_registry_bind(wl_registry, name, &zxdg_exporter_v1_interface, 1);
- registry->wl_exporter_name = name;
+ registry->xdg_exporter = (struct zxdg_exporter_v1 *)wl_registry_bind(wl_registry, name, &zxdg_exporter_v1_interface, 1);
+ registry->xdg_exporter_name = name;
return;
}
if (strcmp(interface, wl_compositor_interface.name) == 0) {
- registry->wl_compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4);
+ registry->wl_compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, CLAMP((int)version, 1, 6));
registry->wl_compositor_name = name;
return;
}
- if (strcmp(interface, wl_subcompositor_interface.name) == 0) {
- registry->wl_subcompositor = (struct wl_subcompositor *)wl_registry_bind(wl_registry, name, &wl_subcompositor_interface, 1);
- registry->wl_subcompositor_name = name;
- return;
- }
-
if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
- registry->wl_data_device_manager = (struct wl_data_device_manager *)wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, 3);
+ registry->wl_data_device_manager = (struct wl_data_device_manager *)wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, CLAMP((int)version, 1, 3));
registry->wl_data_device_manager_name = name;
- // This global creates some seats data. Let's do that for the ones already available.
+ // This global creates some seat data. Let's do that for the ones already available.
for (struct wl_seat *wl_seat : registry->wl_seats) {
SeatState *ss = wl_seat_get_seat_state(wl_seat);
ERR_FAIL_NULL(ss);
@@ -406,7 +400,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
}
if (strcmp(interface, wl_output_interface.name) == 0) {
- struct wl_output *wl_output = (struct wl_output *)wl_registry_bind(wl_registry, name, &wl_output_interface, 2);
+ struct wl_output *wl_output = (struct wl_output *)wl_registry_bind(wl_registry, name, &wl_output_interface, CLAMP((int)version, 1, 4));
wl_proxy_tag_godot((struct wl_proxy *)wl_output);
registry->wl_outputs.push_back(wl_output);
@@ -421,7 +415,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
}
if (strcmp(interface, wl_seat_interface.name) == 0) {
- struct wl_seat *wl_seat = (struct wl_seat *)wl_registry_bind(wl_registry, name, &wl_seat_interface, 5);
+ struct wl_seat *wl_seat = (struct wl_seat *)wl_registry_bind(wl_registry, name, &wl_seat_interface, CLAMP((int)version, 1, 9));
wl_proxy_tag_godot((struct wl_proxy *)wl_seat);
SeatState *ss = memnew(SeatState);
@@ -466,7 +460,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
}
if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
- registry->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, MAX(2, MIN(6, (int)version)));
+ registry->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, CLAMP((int)version, 1, 6));
registry->xdg_wm_base_name = name;
xdg_wm_base_add_listener(registry->xdg_wm_base, &xdg_wm_base_listener, nullptr);
@@ -502,7 +496,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
if (strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0) {
registry->wp_primary_selection_device_manager = (struct zwp_primary_selection_device_manager_v1 *)wl_registry_bind(wl_registry, name, &zwp_primary_selection_device_manager_v1_interface, 1);
- // This global creates some seats data. Let's do that for the ones already available.
+ // This global creates some seat data. Let's do that for the ones already available.
for (struct wl_seat *wl_seat : registry->wl_seats) {
SeatState *ss = wl_seat_get_seat_state(wl_seat);
ERR_FAIL_NULL(ss);
@@ -570,13 +564,13 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry
return;
}
- if (name == registry->wl_exporter_name) {
- if (registry->wl_exporter) {
- zxdg_exporter_v1_destroy(registry->wl_exporter);
- registry->wl_exporter = nullptr;
+ if (name == registry->xdg_exporter_name) {
+ if (registry->xdg_exporter) {
+ zxdg_exporter_v1_destroy(registry->xdg_exporter);
+ registry->xdg_exporter = nullptr;
}
- registry->wl_exporter_name = 0;
+ registry->xdg_exporter_name = 0;
return;
}
@@ -592,17 +586,6 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry
return;
}
- if (name == registry->wl_subcompositor_name) {
- if (registry->wl_subcompositor) {
- wl_subcompositor_destroy(registry->wl_subcompositor);
- registry->wl_subcompositor = nullptr;
- }
-
- registry->wl_subcompositor_name = 0;
-
- return;
- }
-
if (name == registry->wl_data_device_manager_name) {
if (registry->wl_data_device_manager) {
wl_data_device_manager_destroy(registry->wl_data_device_manager);
@@ -1000,6 +983,12 @@ void WaylandThread::_wl_output_on_geometry(void *data, struct wl_output *wl_outp
ss->pending_data.make.parse_utf8(make);
ss->pending_data.model.parse_utf8(model);
+
+ // `wl_output::done` is a version 2 addition. We'll directly update the data
+ // for compatibility.
+ if (wl_output_get_version(wl_output) == 1) {
+ ss->data = ss->pending_data;
+ }
}
void WaylandThread::_wl_output_on_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
@@ -1010,8 +999,17 @@ void WaylandThread::_wl_output_on_mode(void *data, struct wl_output *wl_output,
ss->pending_data.size.height = height;
ss->pending_data.refresh_rate = refresh ? refresh / 1000.0f : -1;
+
+ // `wl_output::done` is a version 2 addition. We'll directly update the data
+ // for compatibility.
+ if (wl_output_get_version(wl_output) == 1) {
+ ss->data = ss->pending_data;
+ }
}
+// NOTE: The following `wl_output` events are only for version 2 onwards, so we
+// can assume that they're "atomic" (i.e. rely on the `wl_output::done` event).
+
void WaylandThread::_wl_output_on_done(void *data, struct wl_output *wl_output) {
ScreenState *ss = (ScreenState *)data;
ERR_FAIL_NULL(ss);
@@ -1523,7 +1521,7 @@ void WaylandThread::_wl_pointer_on_frame(void *data, struct wl_pointer *wl_point
wayland_thread->push_message(msg);
}
- if (pd.discrete_scroll_vector - old_pd.discrete_scroll_vector != Vector2i()) {
+ if (pd.discrete_scroll_vector_120 - old_pd.discrete_scroll_vector_120 != Vector2i()) {
// This is a discrete scroll (eg. from a scroll wheel), so we'll just emit
// scroll wheel buttons.
if (pd.scroll_vector.y != 0) {
@@ -1596,13 +1594,13 @@ void WaylandThread::_wl_pointer_on_frame(void *data, struct wl_pointer *wl_point
if (test_button == MouseButton::WHEEL_UP || test_button == MouseButton::WHEEL_DOWN) {
// If this is a discrete scroll, specify how many "clicks" it did for this
// pointer frame.
- mb->set_factor(abs(pd.discrete_scroll_vector.y));
+ mb->set_factor(Math::abs(pd.discrete_scroll_vector_120.y / (float)120));
}
if (test_button == MouseButton::WHEEL_RIGHT || test_button == MouseButton::WHEEL_LEFT) {
// If this is a discrete scroll, specify how many "clicks" it did for this
// pointer frame.
- mb->set_factor(abs(pd.discrete_scroll_vector.x));
+ mb->set_factor(fabs(pd.discrete_scroll_vector_120.x / (float)120));
}
mb->set_button_mask(pd.pressed_button_mask);
@@ -1661,7 +1659,7 @@ void WaylandThread::_wl_pointer_on_frame(void *data, struct wl_pointer *wl_point
// Reset the scroll vectors as we already handled them.
pd.scroll_vector = Vector2();
- pd.discrete_scroll_vector = Vector2();
+ pd.discrete_scroll_vector_120 = Vector2i();
// Update the data all getters read. Wayland's specification requires us to do
// this, since all pointer actions are sent in individual events.
@@ -1683,6 +1681,9 @@ void WaylandThread::_wl_pointer_on_axis_source(void *data, struct wl_pointer *wl
void WaylandThread::_wl_pointer_on_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) {
}
+// NOTE: This event is deprecated since version 8 and superseded by
+// `wl_pointer::axis_value120`. This thus converts the data to its
+// fraction-of-120 format.
void WaylandThread::_wl_pointer_on_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);
@@ -1694,17 +1695,37 @@ void WaylandThread::_wl_pointer_on_axis_discrete(void *data, struct wl_pointer *
PointerData &pd = ss->pointer_data_buffer;
+ // NOTE: We can allow ourselves to not accumulate this data (and thus just
+ // assign it) as the spec guarantees only one event per axis type.
+
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
- pd.discrete_scroll_vector.y = discrete;
+ pd.discrete_scroll_vector_120.y = discrete * 120;
}
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
- pd.discrete_scroll_vector.x = discrete;
+ pd.discrete_scroll_vector_120.x = discrete * 120;
}
}
-// TODO: Add support to this event.
+// Supersedes `wl_pointer::axis_discrete` Since version 8.
void WaylandThread::_wl_pointer_on_axis_value120(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t value120) {
+ SeatState *ss = (SeatState *)data;
+ ERR_FAIL_NULL(ss);
+
+ if (!ss->pointed_surface) {
+ // We're probably on a decoration or some other third-party thing.
+ return;
+ }
+
+ PointerData &pd = ss->pointer_data_buffer;
+
+ if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
+ pd.discrete_scroll_vector_120.y += value120;
+ }
+
+ if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
+ pd.discrete_scroll_vector_120.x += value120;
+ }
}
// TODO: Add support to this event.
@@ -1999,7 +2020,7 @@ void WaylandThread::_wp_relative_pointer_on_relative_motion(void *data, struct z
pd.relative_motion_time = uptime_lo;
}
-void WaylandThread::_wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers) {
+void WaylandThread::_wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);
@@ -2009,7 +2030,7 @@ void WaylandThread::_wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_po
}
}
-void WaylandThread::_wp_pointer_gesture_pinch_on_update(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) {
+void WaylandThread::_wp_pointer_gesture_pinch_on_update(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);
@@ -2068,7 +2089,7 @@ void WaylandThread::_wp_pointer_gesture_pinch_on_update(void *data, struct zwp_p
}
}
-void WaylandThread::_wp_pointer_gesture_pinch_on_end(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled) {
+void WaylandThread::_wp_pointer_gesture_pinch_on_end(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);
@@ -2093,7 +2114,7 @@ void WaylandThread::_wp_primary_selection_device_on_selection(void *data, struct
ss->wp_primary_selection_offer = id;
}
-void WaylandThread::_wp_primary_selection_offer_on_offer(void *data, struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1, const char *mime_type) {
+void WaylandThread::_wp_primary_selection_offer_on_offer(void *data, struct zwp_primary_selection_offer_v1 *wp_primary_selection_offer_v1, const char *mime_type) {
OfferState *os = (OfferState *)data;
ERR_FAIL_NULL(os);
@@ -2147,10 +2168,10 @@ void WaylandThread::_wp_primary_selection_source_on_cancelled(void *data, struct
}
}
-void WaylandThread::_wp_tablet_seat_on_tablet_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_v2 *id) {
+void WaylandThread::_wp_tablet_seat_on_tablet_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_v2 *id) {
}
-void WaylandThread::_wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_tool_v2 *id) {
+void WaylandThread::_wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_tool_v2 *id) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);
@@ -2163,31 +2184,31 @@ void WaylandThread::_wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_
ss->tablet_tools.push_back(id);
}
-void WaylandThread::_wp_tablet_seat_on_pad_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id) {
+void WaylandThread::_wp_tablet_seat_on_pad_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id) {
}
-void WaylandThread::_wp_tablet_tool_on_type(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t tool_type) {
- TabletToolState *state = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_type(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t tool_type) {
+ TabletToolState *state = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (state && tool_type == ZWP_TABLET_TOOL_V2_TYPE_ERASER) {
state->is_eraser = true;
}
}
-void WaylandThread::_wp_tablet_tool_on_hardware_serial(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t hardware_serial_hi, uint32_t hardware_serial_lo) {
+void WaylandThread::_wp_tablet_tool_on_hardware_serial(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_serial_hi, uint32_t hardware_serial_lo) {
}
-void WaylandThread::_wp_tablet_tool_on_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t hardware_id_hi, uint32_t hardware_id_lo) {
+void WaylandThread::_wp_tablet_tool_on_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_id_hi, uint32_t hardware_id_lo) {
}
-void WaylandThread::_wp_tablet_tool_on_capability(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t capability) {
+void WaylandThread::_wp_tablet_tool_on_capability(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t capability) {
}
-void WaylandThread::_wp_tablet_tool_on_done(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) {
+void WaylandThread::_wp_tablet_tool_on_done(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) {
}
-void WaylandThread::_wp_tablet_tool_on_removed(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_removed(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2199,7 +2220,7 @@ void WaylandThread::_wp_tablet_tool_on_removed(void *data, struct zwp_tablet_too
return;
}
- List<struct zwp_tablet_tool_v2 *>::Element *E = ss->tablet_tools.find(zwp_tablet_tool_v2);
+ List<struct zwp_tablet_tool_v2 *>::Element *E = ss->tablet_tools.find(wp_tablet_tool_v2);
if (E && E->get()) {
struct zwp_tablet_tool_v2 *tool = E->get();
@@ -2213,8 +2234,8 @@ void WaylandThread::_wp_tablet_tool_on_removed(void *data, struct zwp_tablet_too
}
}
-void WaylandThread::_wp_tablet_tool_on_proximity_in(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_proximity_in(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2241,8 +2262,8 @@ void WaylandThread::_wp_tablet_tool_on_proximity_in(void *data, struct zwp_table
DEBUG_LOG_WAYLAND_THREAD("Tablet tool entered window.");
}
-void WaylandThread::_wp_tablet_tool_on_proximity_out(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_proximity_out(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2268,8 +2289,8 @@ void WaylandThread::_wp_tablet_tool_on_proximity_out(void *data, struct zwp_tabl
DEBUG_LOG_WAYLAND_THREAD("Tablet tool left window.");
}
-void WaylandThread::_wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2286,8 +2307,8 @@ void WaylandThread::_wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v
td.button_time = OS::get_singleton()->get_ticks_msec();
}
-void WaylandThread::_wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2302,8 +2323,8 @@ void WaylandThread::_wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2
td.button_time = OS::get_singleton()->get_ticks_msec();
}
-void WaylandThread::_wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2323,8 +2344,8 @@ void WaylandThread::_wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool
td.motion_time = OS::get_singleton()->get_ticks_msec();
}
-void WaylandThread::_wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t pressure) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t pressure) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2333,12 +2354,12 @@ void WaylandThread::_wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_to
ts->data_pending.pressure = pressure;
}
-void WaylandThread::_wp_tablet_tool_on_distance(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t distance) {
+void WaylandThread::_wp_tablet_tool_on_distance(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t distance) {
// Unsupported
}
-void WaylandThread::_wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2350,20 +2371,20 @@ void WaylandThread::_wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v
td.tilt.y = wl_fixed_to_double(tilt_y);
}
-void WaylandThread::_wp_tablet_tool_on_rotation(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t degrees) {
+void WaylandThread::_wp_tablet_tool_on_rotation(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees) {
// Unsupported.
}
-void WaylandThread::_wp_tablet_tool_on_slider(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, int32_t position) {
+void WaylandThread::_wp_tablet_tool_on_slider(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, int32_t position) {
// Unsupported.
}
-void WaylandThread::_wp_tablet_tool_on_wheel(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t degrees, int32_t clicks) {
+void WaylandThread::_wp_tablet_tool_on_wheel(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees, int32_t clicks) {
// TODO
}
-void WaylandThread::_wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2398,8 +2419,8 @@ void WaylandThread::_wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool
}
}
-void WaylandThread::_wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t time) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t time) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -3071,8 +3092,8 @@ void WaylandThread::window_create(DisplayServer::WindowID p_window_id, int p_wid
// "loop".
wl_surface_commit(ws.wl_surface);
- if (registry.wl_exporter) {
- ws.xdg_exported = zxdg_exporter_v1_export(registry.wl_exporter, ws.wl_surface);
+ if (registry.xdg_exporter) {
+ ws.xdg_exported = zxdg_exporter_v1_export(registry.xdg_exporter, ws.wl_surface);
zxdg_exported_v1_add_listener(ws.xdg_exported, &xdg_exported_listener, &ws);
}
@@ -3529,9 +3550,6 @@ Error WaylandThread::init() {
ERR_FAIL_NULL_V_MSG(registry.wl_shm, ERR_UNAVAILABLE, "Can't obtain the Wayland shared memory global.");
ERR_FAIL_NULL_V_MSG(registry.wl_compositor, ERR_UNAVAILABLE, "Can't obtain the Wayland compositor global.");
- ERR_FAIL_NULL_V_MSG(registry.wl_subcompositor, ERR_UNAVAILABLE, "Can't obtain the Wayland subcompositor global.");
- ERR_FAIL_NULL_V_MSG(registry.wl_data_device_manager, ERR_UNAVAILABLE, "Can't obtain the Wayland data device manager global.");
- ERR_FAIL_NULL_V_MSG(registry.wp_pointer_constraints, ERR_UNAVAILABLE, "Can't obtain the Wayland pointer constraints global.");
ERR_FAIL_NULL_V_MSG(registry.xdg_wm_base, ERR_UNAVAILABLE, "Can't obtain the Wayland XDG shell global.");
if (!registry.xdg_decoration_manager) {
@@ -3660,7 +3678,10 @@ void WaylandThread::cursor_shape_set_custom_image(DisplayServer::CursorShape p_c
munmap(cursor.buffer_data, cursor.buffer_data_size);
}
- cursor.buffer_data = (uint32_t *)mmap(nullptr, data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ // NOTE: From `wl_keyboard`s of version 7 or later, the spec requires the mmap
+ // operation to be done with MAP_PRIVATE, as "MAP_SHARED may fail". We'll do it
+ // regardless of global version.
+ cursor.buffer_data = (uint32_t *)mmap(nullptr, data_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (cursor.wl_buffer) {
// Clean up the old Wayland buffer.
@@ -4179,18 +4200,14 @@ void WaylandThread::destroy() {
xdg_wm_base_destroy(registry.xdg_wm_base);
}
- if (registry.wl_exporter) {
- zxdg_exporter_v1_destroy(registry.wl_exporter);
+ if (registry.xdg_exporter) {
+ zxdg_exporter_v1_destroy(registry.xdg_exporter);
}
if (registry.wl_shm) {
wl_shm_destroy(registry.wl_shm);
}
- if (registry.wl_subcompositor) {
- wl_subcompositor_destroy(registry.wl_subcompositor);
- }
-
if (registry.wl_compositor) {
wl_compositor_destroy(registry.wl_compositor);
}
diff --git a/platform/linuxbsd/wayland/wayland_thread.h b/platform/linuxbsd/wayland/wayland_thread.h
index d49f0c9d34..d35a5b7139 100644
--- a/platform/linuxbsd/wayland/wayland_thread.h
+++ b/platform/linuxbsd/wayland/wayland_thread.h
@@ -133,8 +133,8 @@ public:
struct xdg_wm_base *xdg_wm_base = nullptr;
uint32_t xdg_wm_base_name = 0;
- struct zxdg_exporter_v1 *wl_exporter = nullptr;
- uint32_t wl_exporter_name = 0;
+ struct zxdg_exporter_v1 *xdg_exporter = nullptr;
+ uint32_t xdg_exporter_name = 0;
// wayland-protocols globals.
@@ -300,8 +300,8 @@ public:
// The amount "scrolled" in pixels, in each direction.
Vector2 scroll_vector;
- // The amount of scroll "clicks" in each direction.
- Vector2i discrete_scroll_vector;
+ // The amount of scroll "clicks" in each direction, in fractions of 120.
+ Vector2i discrete_scroll_vector_120;
uint32_t pinch_scale = 1;
};
@@ -579,41 +579,41 @@ private:
static void _wp_relative_pointer_on_relative_motion(void *data, struct zwp_relative_pointer_v1 *wp_relative_pointer_v1, uint32_t uptime_hi, uint32_t uptime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel);
- static void _wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers);
- static void _wp_pointer_gesture_pinch_on_update(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation);
- static void _wp_pointer_gesture_pinch_on_end(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled);
+ static void _wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers);
+ static void _wp_pointer_gesture_pinch_on_update(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation);
+ static void _wp_pointer_gesture_pinch_on_end(void *data, struct zwp_pointer_gesture_pinch_v1 *zp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled);
static void _wp_primary_selection_device_on_data_offer(void *data, struct zwp_primary_selection_device_v1 *wp_primary_selection_device_v1, struct zwp_primary_selection_offer_v1 *offer);
static void _wp_primary_selection_device_on_selection(void *data, struct zwp_primary_selection_device_v1 *wp_primary_selection_device_v1, struct zwp_primary_selection_offer_v1 *id);
- static void _wp_primary_selection_offer_on_offer(void *data, struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1, const char *mime_type);
+ static void _wp_primary_selection_offer_on_offer(void *data, struct zwp_primary_selection_offer_v1 *wp_primary_selection_offer_v1, const char *mime_type);
static void _wp_primary_selection_source_on_send(void *data, struct zwp_primary_selection_source_v1 *wp_primary_selection_source_v1, const char *mime_type, int32_t fd);
static void _wp_primary_selection_source_on_cancelled(void *data, struct zwp_primary_selection_source_v1 *wp_primary_selection_source_v1);
- static void _wp_tablet_seat_on_tablet_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_v2 *id);
- static void _wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_tool_v2 *id);
- static void _wp_tablet_seat_on_pad_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id);
-
- static void _wp_tablet_tool_on_type(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t tool_type);
- static void _wp_tablet_tool_on_hardware_serial(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t hardware_serial_hi, uint32_t hardware_serial_lo);
- static void _wp_tablet_tool_on_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t hardware_id_hi, uint32_t hardware_id_lo);
- static void _wp_tablet_tool_on_capability(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t capability);
- static void _wp_tablet_tool_on_done(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2);
- static void _wp_tablet_tool_on_removed(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2);
- static void _wp_tablet_tool_on_proximity_in(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface);
- static void _wp_tablet_tool_on_proximity_out(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2);
- static void _wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial);
- static void _wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2);
- static void _wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y);
- static void _wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t pressure);
- static void _wp_tablet_tool_on_distance(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t distance);
- static void _wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y);
- static void _wp_tablet_tool_on_rotation(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t degrees);
- static void _wp_tablet_tool_on_slider(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, int32_t position);
- static void _wp_tablet_tool_on_wheel(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t degrees, int32_t clicks);
- static void _wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state);
- static void _wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t time);
+ static void _wp_tablet_seat_on_tablet_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_v2 *id);
+ static void _wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_tool_v2 *id);
+ static void _wp_tablet_seat_on_pad_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id);
+
+ static void _wp_tablet_tool_on_type(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t tool_type);
+ static void _wp_tablet_tool_on_hardware_serial(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_serial_hi, uint32_t hardware_serial_lo);
+ static void _wp_tablet_tool_on_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_id_hi, uint32_t hardware_id_lo);
+ static void _wp_tablet_tool_on_capability(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t capability);
+ static void _wp_tablet_tool_on_done(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2);
+ static void _wp_tablet_tool_on_removed(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2);
+ static void _wp_tablet_tool_on_proximity_in(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface);
+ static void _wp_tablet_tool_on_proximity_out(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2);
+ static void _wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial);
+ static void _wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2);
+ static void _wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y);
+ static void _wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t pressure);
+ static void _wp_tablet_tool_on_distance(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t distance);
+ static void _wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y);
+ static void _wp_tablet_tool_on_rotation(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees);
+ static void _wp_tablet_tool_on_slider(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, int32_t position);
+ static void _wp_tablet_tool_on_wheel(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees, int32_t clicks);
+ static void _wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state);
+ static void _wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t time);
static void _xdg_toplevel_decoration_on_configure(void *data, struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration, uint32_t mode);
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 0041b4c7f3..e854fe9274 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -4992,7 +4992,7 @@ void DisplayServerX11::process_events() {
files.write[i] = files[i].replace("file://", "").uri_decode();
}
- if (!windows[window_id].drop_files_callback.is_null()) {
+ if (windows[window_id].drop_files_callback.is_valid()) {
windows[window_id].drop_files_callback.call(files);
}
diff --git a/platform/macos/detect.py b/platform/macos/detect.py
index 3c8b1ebee1..a5ef29e34f 100644
--- a/platform/macos/detect.py
+++ b/platform/macos/detect.py
@@ -1,6 +1,6 @@
import os
import sys
-from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang
+from methods import print_error, detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang
from platform_methods import detect_arch, detect_mvk
from typing import TYPE_CHECKING
@@ -64,11 +64,11 @@ def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_64", "arm64"]
if env["arch"] not in supported_arches:
- print(
+ print_error(
'Unsupported CPU architecture "%s" for macOS. Supported architectures are: %s.'
% (env["arch"], ", ".join(supported_arches))
)
- sys.exit()
+ sys.exit(255)
## Build type
@@ -254,7 +254,7 @@ def configure(env: "SConsEnvironment"):
if mvk_path != "":
env.Append(LINKFLAGS=["-L" + mvk_path])
else:
- print(
+ print_error(
"MoltenVK SDK installation directory not found, use 'vulkan_sdk_path' SCons parameter to specify SDK path."
)
sys.exit(255)
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index cfe925e79b..6461f50818 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -938,7 +938,7 @@ Error DisplayServerMacOS::dialog_show(String p_title, String p_description, Vect
button_pressed = int64_t(2 + (ret - NSAlertThirdButtonReturn));
}
- if (!p_callback.is_null()) {
+ if (p_callback.is_valid()) {
Variant ret;
Callable::CallError ce;
const Variant *args[1] = { &button_pressed };
@@ -1018,7 +1018,7 @@ Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title,
String url;
url.parse_utf8([[[panel URL] path] UTF8String]);
files.push_back(url);
- if (!callback.is_null()) {
+ if (callback.is_valid()) {
if (p_options_in_cb) {
Variant v_result = true;
Variant v_files = files;
@@ -1047,7 +1047,7 @@ Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title,
}
}
} else {
- if (!callback.is_null()) {
+ if (callback.is_valid()) {
if (p_options_in_cb) {
Variant v_result = false;
Variant v_files = Vector<String>();
@@ -1134,7 +1134,7 @@ Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title,
url.parse_utf8([[[urls objectAtIndex:i] path] UTF8String]);
files.push_back(url);
}
- if (!callback.is_null()) {
+ if (callback.is_valid()) {
if (p_options_in_cb) {
Variant v_result = true;
Variant v_files = files;
@@ -1163,7 +1163,7 @@ Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title,
}
}
} else {
- if (!callback.is_null()) {
+ if (callback.is_valid()) {
if (p_options_in_cb) {
Variant v_result = false;
Variant v_files = Vector<String>();
@@ -1222,7 +1222,7 @@ Error DisplayServerMacOS::dialog_input_text(String p_title, String p_description
String ret;
ret.parse_utf8([[input stringValue] UTF8String]);
- if (!p_callback.is_null()) {
+ if (p_callback.is_valid()) {
Variant v_result = ret;
Variant ret;
Callable::CallError ce;
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index d75def9b50..5f52d33318 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -909,7 +909,7 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres
return OK;
}
-Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_warn) {
+Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_warn, bool p_set_id) {
int codesign_tool = p_preset->get("codesign/codesign");
switch (codesign_tool) {
case 1: { // built-in ad-hoc
@@ -953,6 +953,12 @@ Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pre
args.push_back("--code-signature-flags");
args.push_back("runtime");
+ if (p_set_id) {
+ String app_id = p_preset->get("application/bundle_identifier");
+ args.push_back("--binary-identifier");
+ args.push_back(app_id);
+ }
+
args.push_back("-v"); /* provide some more feedback */
args.push_back(p_path);
@@ -1012,6 +1018,12 @@ Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pre
args.push_back(p_preset->get("codesign/identity"));
}
+ if (p_set_id) {
+ String app_id = p_preset->get("application/bundle_identifier");
+ args.push_back("-i");
+ args.push_back(app_id);
+ }
+
args.push_back("-v"); /* provide some more feedback */
args.push_back("-f");
@@ -1043,7 +1055,7 @@ 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) {
+ const String &p_ent_path, const String &p_helper_ent_path, bool p_should_error_on_non_code) {
static Vector<String> extensions_to_sign;
if (extensions_to_sign.is_empty()) {
@@ -1070,7 +1082,8 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres
}
if (extensions_to_sign.find(current_file.get_extension()) > -1) {
- Error code_sign_error{ _code_sign(p_preset, current_file_path, p_ent_path, false) };
+ int ftype = MachO::get_filetype(current_file_path);
+ Error code_sign_error{ _code_sign(p_preset, current_file_path, (ftype == 2 || ftype == 5) ? p_helper_ent_path : p_ent_path, false, (ftype == 2 || ftype == 5)) };
if (code_sign_error != OK) {
return code_sign_error;
}
@@ -1079,7 +1092,7 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres
FileAccess::set_unix_permissions(current_file_path, 0755);
}
} else if (dir_access->current_is_dir()) {
- Error code_sign_error{ _code_sign_directory(p_preset, current_file_path, p_ent_path, p_should_error_on_non_code) };
+ Error code_sign_error{ _code_sign_directory(p_preset, current_file_path, p_ent_path, p_helper_ent_path, p_should_error_on_non_code) };
if (code_sign_error != OK) {
return code_sign_error;
}
@@ -1097,6 +1110,7 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres
Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access, const String &p_src_path,
const String &p_in_app_path, bool p_sign_enabled,
const Ref<EditorExportPreset> &p_preset, const String &p_ent_path,
+ const String &p_helper_ent_path,
bool p_should_error_on_non_code_sign) {
static Vector<String> extensions_to_sign;
@@ -1186,10 +1200,11 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access
if (err == OK && p_sign_enabled) {
if (dir_access->dir_exists(p_src_path) && p_src_path.get_extension().is_empty()) {
// If it is a directory, find and sign all dynamic libraries.
- err = _code_sign_directory(p_preset, p_in_app_path, p_ent_path, p_should_error_on_non_code_sign);
+ err = _code_sign_directory(p_preset, p_in_app_path, p_ent_path, p_helper_ent_path, p_should_error_on_non_code_sign);
} else {
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);
+ int ftype = MachO::get_filetype(p_in_app_path);
+ err = _code_sign(p_preset, p_in_app_path, (ftype == 2 || ftype == 5) ? p_helper_ent_path : p_ent_path, false, (ftype == 2 || ftype == 5));
}
if (dir_access->file_exists(p_in_app_path) && is_executable(p_in_app_path)) {
// chmod with 0755 if the file is executable.
@@ -1203,13 +1218,13 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access
Error EditorExportPlatformMacOS::_export_macos_plugins_for(Ref<EditorExportPlugin> p_editor_export_plugin,
const String &p_app_path_name, Ref<DirAccess> &dir_access,
bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset,
- const String &p_ent_path) {
+ const String &p_ent_path, const String &p_helper_ent_path) {
Error error{ OK };
const Vector<String> &macos_plugins{ p_editor_export_plugin->get_macos_plugin_files() };
for (int i = 0; i < macos_plugins.size(); ++i) {
String src_path{ ProjectSettings::get_singleton()->globalize_path(macos_plugins[i]) };
String path_in_app{ p_app_path_name + "/Contents/PlugIns/" + src_path.get_file() };
- error = _copy_and_sign_files(dir_access, src_path, path_in_app, p_sign_enabled, p_preset, p_ent_path, false);
+ error = _copy_and_sign_files(dir_access, src_path, path_in_app, p_sign_enabled, p_preset, p_ent_path, p_helper_ent_path, false);
if (error != OK) {
break;
}
@@ -1786,8 +1801,9 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("'rcodesign' doesn't support signing applications with embedded dynamic libraries."));
}
+ bool sandbox = p_preset->get("codesign/entitlements/app_sandbox/enabled");
String ent_path = p_preset->get("codesign/entitlements/custom_file");
- String hlp_ent_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name + "_helper.entitlements");
+ String hlp_ent_path = sandbox ? EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name + "_helper.entitlements") : ent_path;
if (sign_enabled && (ent_path.is_empty())) {
ent_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name + ".entitlements");
@@ -1939,7 +1955,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
err = ERR_CANT_CREATE;
}
- if ((err == OK) && helpers.size() > 0) {
+ if ((err == OK) && sandbox && (helpers.size() > 0 || shared_objects.size() > 0)) {
ent_f = FileAccess::open(hlp_ent_path, FileAccess::WRITE);
if (ent_f.is_valid()) {
ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
@@ -1965,7 +1981,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
String hlp_path = helpers[i];
err = da->copy(hlp_path, tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file());
if (err == OK && sign_enabled) {
- err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), hlp_ent_path, false);
+ err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), hlp_ent_path, false, true);
}
FileAccess::set_unix_permissions(tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), 0755);
}
@@ -1977,11 +1993,11 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
String src_path = ProjectSettings::get_singleton()->globalize_path(shared_objects[i].path);
if (shared_objects[i].target.is_empty()) {
String path_in_app = tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file();
- err = _copy_and_sign_files(da, src_path, path_in_app, sign_enabled, p_preset, ent_path, true);
+ err = _copy_and_sign_files(da, src_path, path_in_app, sign_enabled, p_preset, ent_path, hlp_ent_path, true);
} else {
String path_in_app = tmp_app_path_name.path_join(shared_objects[i].target);
tmp_app_dir->make_dir_recursive(path_in_app);
- err = _copy_and_sign_files(da, src_path, path_in_app.path_join(src_path.get_file()), sign_enabled, p_preset, ent_path, false);
+ err = _copy_and_sign_files(da, src_path, path_in_app.path_join(src_path.get_file()), sign_enabled, p_preset, ent_path, hlp_ent_path, false);
}
if (err != OK) {
break;
@@ -1990,7 +2006,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
Vector<Ref<EditorExportPlugin>> export_plugins{ EditorExport::get_singleton()->get_export_plugins() };
for (int i = 0; i < export_plugins.size(); ++i) {
- err = _export_macos_plugins_for(export_plugins[i], tmp_app_path_name, da, sign_enabled, p_preset, ent_path);
+ err = _export_macos_plugins_for(export_plugins[i], tmp_app_path_name, da, sign_enabled, p_preset, ent_path, hlp_ent_path);
if (err != OK) {
break;
}
@@ -2010,7 +2026,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
if (ep.step(TTR("Code signing bundle"), 2)) {
return ERR_SKIP;
}
- err = _code_sign(p_preset, tmp_app_path_name, ent_path);
+ err = _code_sign(p_preset, tmp_app_path_name, ent_path, true, false);
}
String noto_path = p_path;
@@ -2028,7 +2044,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
if (ep.step(TTR("Code signing DMG"), 3)) {
return ERR_SKIP;
}
- err = _code_sign(p_preset, p_path, ent_path, false);
+ err = _code_sign(p_preset, p_path, ent_path, false, false);
}
} else if (export_format == "pkg") {
// Create a Installer.
diff --git a/platform/macos/export/export_plugin.h b/platform/macos/export/export_plugin.h
index 0764b63e8c..2d615abede 100644
--- a/platform/macos/export/export_plugin.h
+++ b/platform/macos/export/export_plugin.h
@@ -89,14 +89,14 @@ class EditorExportPlatformMacOS : public EditorExportPlatform {
void _make_icon(const Ref<EditorExportPreset> &p_preset, const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path);
- Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_warn = true);
- Error _code_sign_directory(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_should_error_on_non_code = true);
+ Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_warn = true, bool p_set_id = false);
+ Error _code_sign_directory(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, const String &p_helper_ent_path, bool p_should_error_on_non_code = true);
Error _copy_and_sign_files(Ref<DirAccess> &dir_access, const String &p_src_path, const String &p_in_app_path,
- bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset, const String &p_ent_path,
+ bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset, const String &p_ent_path, const String &p_helper_ent_path,
bool p_should_error_on_non_code_sign);
Error _export_macos_plugins_for(Ref<EditorExportPlugin> p_editor_export_plugin, const String &p_app_path_name,
Ref<DirAccess> &dir_access, bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset,
- const String &p_ent_path);
+ const String &p_ent_path, const String &p_helper_ent_path);
Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
Error _create_pkg(const Ref<EditorExportPreset> &p_preset, const String &p_pkg_path, const String &p_app_path_name);
Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path);
diff --git a/platform/macos/export/macho.cpp b/platform/macos/export/macho.cpp
index c7556c1964..a829774a88 100644
--- a/platform/macos/export/macho.cpp
+++ b/platform/macos/export/macho.cpp
@@ -105,6 +105,26 @@ bool MachO::is_macho(const String &p_path) {
return (magic == 0xcefaedfe || magic == 0xfeedface || magic == 0xcffaedfe || magic == 0xfeedfacf);
}
+uint32_t MachO::get_filetype(const String &p_path) {
+ Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(fa.is_null(), 0, vformat("MachO: Can't open file: \"%s\".", p_path));
+ uint32_t magic = fa->get_32();
+ MachHeader mach_header;
+
+ // Read MachO header.
+ if (magic == 0xcefaedfe || magic == 0xfeedface) {
+ // Thin 32-bit binary.
+ fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader));
+ } else if (magic == 0xcffaedfe || magic == 0xfeedfacf) {
+ // Thin 64-bit binary.
+ fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader));
+ fa->get_32(); // Skip extra reserved field.
+ } else {
+ ERR_FAIL_V_MSG(0, vformat("MachO: File is not a valid MachO binary: \"%s\".", p_path));
+ }
+ return mach_header.filetype;
+}
+
bool MachO::open_file(const String &p_path) {
fa = FileAccess::open(p_path, FileAccess::READ_WRITE);
ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("MachO: Can't open file: \"%s\".", p_path));
diff --git a/platform/macos/export/macho.h b/platform/macos/export/macho.h
index 37975f0820..a84de7de60 100644
--- a/platform/macos/export/macho.h
+++ b/platform/macos/export/macho.h
@@ -181,6 +181,7 @@ class MachO : public RefCounted {
public:
static bool is_macho(const String &p_path);
+ static uint32_t get_filetype(const String &p_path);
bool open_file(const String &p_path);
diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm
index 93bba84783..68a7288ad4 100644
--- a/platform/macos/godot_content_view.mm
+++ b/platform/macos/godot_content_view.mm
@@ -313,7 +313,7 @@
}
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
- if (!wd.drop_files_callback.is_null()) {
+ if (wd.drop_files_callback.is_valid()) {
Vector<String> files;
NSPasteboard *pboard = [sender draggingPasteboard];
diff --git a/platform/macos/godot_window_delegate.mm b/platform/macos/godot_window_delegate.mm
index 2d83b46007..7749debfd6 100644
--- a/platform/macos/godot_window_delegate.mm
+++ b/platform/macos/godot_window_delegate.mm
@@ -268,7 +268,7 @@
ds->window_resize(window_id, wd.size.width, wd.size.height);
- if (!wd.rect_changed_callback.is_null()) {
+ if (wd.rect_changed_callback.is_valid()) {
wd.rect_changed_callback.call(Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id)));
}
}
@@ -291,7 +291,7 @@
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
ds->release_pressed_events();
- if (!wd.rect_changed_callback.is_null()) {
+ if (wd.rect_changed_callback.is_valid()) {
wd.rect_changed_callback.call(Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id)));
}
}
diff --git a/platform/web/SCsub b/platform/web/SCsub
index 3e0cc9ac4a..bc5893ab3a 100644
--- a/platform/web/SCsub
+++ b/platform/web/SCsub
@@ -1,5 +1,7 @@
#!/usr/bin/env python
+from methods import print_error
+
Import("env")
# The HTTP server "targets". Run with "scons p=web serve", or "scons p=web run"
@@ -11,7 +13,7 @@ if "serve" in COMMAND_LINE_TARGETS or "run" in COMMAND_LINE_TARGETS:
try:
port = int(port)
except Exception:
- print("GODOT_WEB_TEST_PORT must be a valid integer")
+ print_error("GODOT_WEB_TEST_PORT must be a valid integer")
sys.exit(255)
serve(env.Dir(env.GetTemplateZipPath()).abspath, port, "run" in COMMAND_LINE_TARGETS)
sys.exit(0)
diff --git a/platform/web/api/web_tools_editor_plugin.h b/platform/web/api/web_tools_editor_plugin.h
index ac0d5e20ec..2902f60f24 100644
--- a/platform/web/api/web_tools_editor_plugin.h
+++ b/platform/web/api/web_tools_editor_plugin.h
@@ -34,7 +34,7 @@
#if defined(TOOLS_ENABLED) && defined(WEB_ENABLED)
#include "core/io/zip_io.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class WebToolsEditorPlugin : public EditorPlugin {
GDCLASS(WebToolsEditorPlugin, EditorPlugin);
diff --git a/platform/web/detect.py b/platform/web/detect.py
index 2d2cc288a1..ccd884b225 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -10,7 +10,7 @@ from emscripten_helpers import (
create_template_zip,
get_template_zip_path,
)
-from methods import get_compiler_version
+from methods import print_warning, print_error, get_compiler_version
from SCons.Util import WhereIs
from typing import TYPE_CHECKING
@@ -85,16 +85,16 @@ def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["wasm32"]
if env["arch"] not in supported_arches:
- print(
+ print_error(
'Unsupported CPU architecture "%s" for Web. Supported architectures are: %s.'
% (env["arch"], ", ".join(supported_arches))
)
- sys.exit()
+ sys.exit(255)
try:
env["initial_memory"] = int(env["initial_memory"])
except Exception:
- print("Initial memory must be a valid integer")
+ print_error("Initial memory must be a valid integer")
sys.exit(255)
## Build type
@@ -109,7 +109,7 @@ def configure(env: "SConsEnvironment"):
env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"])
if env.editor_build and env["initial_memory"] < 64:
- print('Note: Forcing "initial_memory=64" as it is required for the web editor.')
+ print("Note: Forcing `initial_memory=64` as it is required for the web editor.")
env["initial_memory"] = 64
env.Append(LINKFLAGS=["-s", "INITIAL_MEMORY=%sMB" % env["initial_memory"]])
@@ -227,7 +227,7 @@ def configure(env: "SConsEnvironment"):
env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"])
env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"])
elif env["proxy_to_pthread"]:
- print('"threads=no" support requires "proxy_to_pthread=no", disabling proxy to pthread.')
+ print_warning('"threads=no" support requires "proxy_to_pthread=no", disabling proxy to pthread.')
env["proxy_to_pthread"] = False
if env["lto"] != "none":
@@ -240,11 +240,11 @@ def configure(env: "SConsEnvironment"):
if env["dlink_enabled"]:
if env["proxy_to_pthread"]:
- print("GDExtension support requires proxy_to_pthread=no, disabling proxy to pthread.")
+ print_warning("GDExtension support requires proxy_to_pthread=no, disabling proxy to pthread.")
env["proxy_to_pthread"] = False
if cc_semver < (3, 1, 14):
- print("GDExtension support requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver)
+ print_error("GDExtension support requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver)
sys.exit(255)
env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"])
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index 06f5eb82f7..a51c161b9c 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -58,7 +58,7 @@ DisplayServerWeb *DisplayServerWeb::get_singleton() {
// Window (canvas)
bool DisplayServerWeb::check_size_force_redraw() {
bool size_changed = godot_js_display_size_update() != 0;
- if (size_changed && !rect_changed_callback.is_null()) {
+ if (size_changed && rect_changed_callback.is_valid()) {
Size2i window_size = window_get_size();
Variant size = Rect2i(Point2i(), window_size); // TODO use window_get_position if implemented.
rect_changed_callback.call(size);
@@ -109,7 +109,7 @@ void DisplayServerWeb::_drop_files_js_callback(const Vector<String> &p_files) {
if (!ds) {
ERR_FAIL_MSG("Unable to drop files because the DisplayServer is not active");
}
- if (ds->drop_files_callback.is_null()) {
+ if (!ds->drop_files_callback.is_valid()) {
return;
}
ds->drop_files_callback.call(p_files);
@@ -129,7 +129,7 @@ void DisplayServerWeb::request_quit_callback() {
void DisplayServerWeb::_request_quit_callback() {
DisplayServerWeb *ds = get_singleton();
- if (ds && !ds->window_event_callback.is_null()) {
+ if (ds && ds->window_event_callback.is_valid()) {
Variant event = int(DisplayServer::WINDOW_EVENT_CLOSE_REQUEST);
ds->window_event_callback.call(event);
}
@@ -722,7 +722,7 @@ void DisplayServerWeb::vk_input_text_callback(const char *p_text, int p_cursor)
void DisplayServerWeb::_vk_input_text_callback(const String &p_text, int p_cursor) {
DisplayServerWeb *ds = DisplayServerWeb::get_singleton();
- if (!ds || ds->input_text_callback.is_null()) {
+ if (!ds || !ds->input_text_callback.is_valid()) {
return;
}
// Call input_text
@@ -972,7 +972,7 @@ void DisplayServerWeb::_send_window_event_callback(int p_notification) {
if (godot_js_is_ime_focused() && (p_notification == DisplayServer::WINDOW_EVENT_FOCUS_IN || p_notification == DisplayServer::WINDOW_EVENT_FOCUS_OUT)) {
return;
}
- if (!ds->window_event_callback.is_null()) {
+ if (ds->window_event_callback.is_valid()) {
Variant event = int(p_notification);
ds->window_event_callback.call(event);
}
diff --git a/platform/web/javascript_bridge_singleton.cpp b/platform/web/javascript_bridge_singleton.cpp
index d72ad8331b..a2c83d2f2b 100644
--- a/platform/web/javascript_bridge_singleton.cpp
+++ b/platform/web/javascript_bridge_singleton.cpp
@@ -248,7 +248,7 @@ Variant JavaScriptObjectImpl::callp(const StringName &p_method, const Variant **
void JavaScriptObjectImpl::callback(void *p_ref, int p_args_id, int p_argc) {
const JavaScriptObjectImpl *obj = (JavaScriptObjectImpl *)p_ref;
- ERR_FAIL_COND_MSG(obj->_callable.is_null(), "JavaScript callback failed.");
+ ERR_FAIL_COND_MSG(!obj->_callable.is_valid(), "JavaScript callback failed.");
Vector<const Variant *> argp;
Array arg_arr;
diff --git a/platform/web/js/engine/features.js b/platform/web/js/engine/features.js
index 81bc82f3c6..263ea6ac88 100644
--- a/platform/web/js/engine/features.js
+++ b/platform/web/js/engine/features.js
@@ -72,8 +72,7 @@ const Features = { // eslint-disable-line no-unused-vars
*
* @returns {Array<string>} A list of human-readable missing features.
* @function Engine.getMissingFeatures
- * @typedef {{ threads: boolean }} SupportedFeatures
- * @param {SupportedFeatures} supportedFeatures
+ * @param {{threads: (boolean|undefined)}} supportedFeatures
*/
getMissingFeatures: function (supportedFeatures = {}) {
const {
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 159a273e70..435c501956 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -10,7 +10,6 @@ sources = []
common_win = [
"godot_windows.cpp",
- "crash_handler_windows.cpp",
"os_windows.cpp",
"display_server_windows.cpp",
"key_mapping_windows.cpp",
@@ -25,6 +24,11 @@ common_win = [
"rendering_context_driver_vulkan_windows.cpp",
]
+if env.msvc:
+ common_win += ["crash_handler_windows_seh.cpp"]
+else:
+ common_win += ["crash_handler_windows_signal.cpp"]
+
common_win_wrap = [
"console_wrapper_windows.cpp",
]
diff --git a/platform/windows/console_wrapper_windows.cpp b/platform/windows/console_wrapper_windows.cpp
index de751580b7..133711a9ea 100644
--- a/platform/windows/console_wrapper_windows.cpp
+++ b/platform/windows/console_wrapper_windows.cpp
@@ -136,6 +136,10 @@ int main(int argc, char *argv[]) {
STARTUPINFOW si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
WCHAR new_command_line[32767];
_snwprintf_s(new_command_line, 32767, _TRUNCATE, L"%ls %ls", exe_name, PathGetArgsW(GetCommandLineW()));
diff --git a/platform/windows/crash_handler_windows.h b/platform/windows/crash_handler_windows.h
index 3871210977..a0a0b610d0 100644
--- a/platform/windows/crash_handler_windows.h
+++ b/platform/windows/crash_handler_windows.h
@@ -35,12 +35,15 @@
#include <windows.h>
// Crash handler exception only enabled with MSVC
-#if defined(DEBUG_ENABLED) && defined(_MSC_VER)
+#if defined(DEBUG_ENABLED)
#define CRASH_HANDLER_EXCEPTION 1
+#ifdef _MSC_VER
extern DWORD CrashHandlerException(EXCEPTION_POINTERS *ep);
#endif
+#endif
+
class CrashHandler {
bool disabled;
diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows_seh.cpp
index 133d36aa0d..2abe285d31 100644
--- a/platform/windows/crash_handler_windows.cpp
+++ b/platform/windows/crash_handler_windows_seh.cpp
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* crash_handler_windows.cpp */
+/* crash_handler_windows_seh.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
diff --git a/platform/windows/crash_handler_windows_signal.cpp b/platform/windows/crash_handler_windows_signal.cpp
new file mode 100644
index 0000000000..e11a60bdc7
--- /dev/null
+++ b/platform/windows/crash_handler_windows_signal.cpp
@@ -0,0 +1,205 @@
+/**************************************************************************/
+/* crash_handler_windows_signal.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 "crash_handler_windows.h"
+
+#include "core/config/project_settings.h"
+#include "core/os/os.h"
+#include "core/string/print_string.h"
+#include "core/version.h"
+#include "main/main.h"
+
+#ifdef CRASH_HANDLER_EXCEPTION
+
+#include <cxxabi.h>
+#include <signal.h>
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include <psapi.h>
+
+#include "thirdparty/libbacktrace/backtrace.h"
+
+struct CrashHandlerData {
+ int64_t index = 0;
+ backtrace_state *state = nullptr;
+ int64_t offset = 0;
+};
+
+int symbol_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function) {
+ CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data);
+ if (!function) {
+ return 0;
+ }
+
+ char fname[1024];
+ snprintf(fname, 1024, "%s", function);
+
+ if (function[0] == '_') {
+ int status;
+ char *demangled = abi::__cxa_demangle(function, nullptr, nullptr, &status);
+
+ if (status == 0 && demangled) {
+ snprintf(fname, 1024, "%s", demangled);
+ }
+
+ if (demangled) {
+ free(demangled);
+ }
+ }
+
+ print_error(vformat("[%d] %s (%s:%d)", ch_data->index++, String::utf8(fname), String::utf8(filename), lineno));
+ return 0;
+}
+
+void error_callback(void *data, const char *msg, int errnum) {
+ CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data);
+ if (ch_data->index == 0) {
+ print_error(vformat("Error(%d): %s", errnum, String::utf8(msg)));
+ } else {
+ print_error(vformat("[%d] error(%d): %s", ch_data->index++, errnum, String::utf8(msg)));
+ }
+}
+
+int trace_callback(void *data, uintptr_t pc) {
+ CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data);
+ backtrace_pcinfo(ch_data->state, pc - ch_data->offset, &symbol_callback, &error_callback, data);
+ return 0;
+}
+
+int64_t get_image_base(const String &p_path) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ if (f.is_null()) {
+ return 0;
+ }
+ {
+ f->seek(0x3c);
+ uint32_t pe_pos = f->get_32();
+
+ f->seek(pe_pos);
+ uint32_t magic = f->get_32();
+ if (magic != 0x00004550) {
+ return 0;
+ }
+ }
+ int64_t opt_header_pos = f->get_position() + 0x14;
+ f->seek(opt_header_pos);
+
+ uint16_t opt_header_magic = f->get_16();
+ if (opt_header_magic == 0x10B) {
+ f->seek(opt_header_pos + 0x1C);
+ return f->get_32();
+ } else if (opt_header_magic == 0x20B) {
+ f->seek(opt_header_pos + 0x18);
+ return f->get_64();
+ } else {
+ return 0;
+ }
+}
+
+extern void CrashHandlerException(int signal) {
+ CrashHandlerData data;
+
+ if (OS::get_singleton() == nullptr || OS::get_singleton()->is_disable_crash_handler() || IsDebuggerPresent()) {
+ return;
+ }
+
+ String msg;
+ const ProjectSettings *proj_settings = ProjectSettings::get_singleton();
+ if (proj_settings) {
+ msg = proj_settings->get("debug/settings/crash_handler/message");
+ }
+
+ // Tell MainLoop about the crash. This can be handled by users too in Node.
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
+ }
+
+ print_error("\n================================================================");
+ print_error(vformat("%s: Program crashed with signal %d", __FUNCTION__, signal));
+
+ // Print the engine version just before, so that people are reminded to include the version in backtrace reports.
+ if (String(VERSION_HASH).is_empty()) {
+ print_error(vformat("Engine version: %s", VERSION_FULL_NAME));
+ } else {
+ print_error(vformat("Engine version: %s (%s)", VERSION_FULL_NAME, VERSION_HASH));
+ }
+ print_error(vformat("Dumping the backtrace. %s", msg));
+
+ String _execpath = OS::get_singleton()->get_executable_path();
+
+ // Load process and image info to determine ASLR addresses offset.
+ MODULEINFO mi;
+ GetModuleInformation(GetCurrentProcess(), GetModuleHandle(NULL), &mi, sizeof(mi));
+ int64_t image_mem_base = reinterpret_cast<int64_t>(mi.lpBaseOfDll);
+ int64_t image_file_base = get_image_base(_execpath);
+ data.offset = image_mem_base - image_file_base;
+
+ data.state = backtrace_create_state(_execpath.utf8().get_data(), 0, &error_callback, reinterpret_cast<void *>(&data));
+ if (data.state != nullptr) {
+ data.index = 1;
+ backtrace_simple(data.state, 1, &trace_callback, &error_callback, reinterpret_cast<void *>(&data));
+ }
+
+ print_error("-- END OF BACKTRACE --");
+ print_error("================================================================");
+}
+#endif
+
+CrashHandler::CrashHandler() {
+ disabled = false;
+}
+
+CrashHandler::~CrashHandler() {
+}
+
+void CrashHandler::disable() {
+ if (disabled) {
+ return;
+ }
+
+#if defined(CRASH_HANDLER_EXCEPTION)
+ signal(SIGSEGV, nullptr);
+ signal(SIGFPE, nullptr);
+ signal(SIGILL, nullptr);
+#endif
+
+ disabled = true;
+}
+
+void CrashHandler::initialize() {
+#if defined(CRASH_HANDLER_EXCEPTION)
+ signal(SIGSEGV, CrashHandlerException);
+ signal(SIGFPE, CrashHandlerException);
+ signal(SIGILL, CrashHandlerException);
+#endif
+}
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index f34d479345..93eb34001e 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -2,6 +2,7 @@ import methods
import os
import subprocess
import sys
+from methods import print_warning, print_error
from platform_methods import detect_arch
from typing import TYPE_CHECKING
@@ -293,16 +294,14 @@ def setup_msvc_manual(env: "SConsEnvironment"):
env_arch = detect_build_env_arch()
if env["arch"] != env_arch:
- print(
- """
- Arch argument (%s) is not matching Native/Cross Compile Tools Prompt/Developer Console (or Visual Studio settings) that is being used to run SCons (%s).
- Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler will be executed and inform you.
- """
+ print_error(
+ "Arch argument (%s) is not matching Native/Cross Compile Tools Prompt/Developer Console (or Visual Studio settings) that is being used to run SCons (%s).\n"
+ "Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler will be executed and inform you."
% (env["arch"], env_arch)
)
- sys.exit(200)
+ sys.exit(255)
- print("Found MSVC, arch %s" % (env_arch))
+ print("Using VCVARS-determined MSVC, arch %s" % (env_arch))
def setup_msvc_auto(env: "SConsEnvironment"):
@@ -338,7 +337,7 @@ def setup_msvc_auto(env: "SConsEnvironment"):
env.Tool("mssdk") # we want the MS SDK
# Note: actual compiler version can be found in env['MSVC_VERSION'], e.g. "14.1" for VS2015
- print("Found MSVC version %s, arch %s" % (env["MSVC_VERSION"], env["arch"]))
+ print("Using SCons-detected MSVC version %s, arch %s" % (env["MSVC_VERSION"], env["arch"]))
def setup_mingw(env: "SConsEnvironment"):
@@ -346,32 +345,24 @@ def setup_mingw(env: "SConsEnvironment"):
env_arch = detect_build_env_arch()
if os.getenv("MSYSTEM") == "MSYS":
- print(
- """
- Running from base MSYS2 console/environment, use target specific environment instead (e.g., mingw32, mingw64, clang32, clang64).
- """
+ print_error(
+ "Running from base MSYS2 console/environment, use target specific environment instead (e.g., mingw32, mingw64, clang32, clang64)."
)
- sys.exit(201)
+ sys.exit(255)
if env_arch != "" and env["arch"] != env_arch:
- print(
- """
- Arch argument (%s) is not matching MSYS2 console/environment that is being used to run SCons (%s).
- Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSYS2 compiler will be executed and inform you.
- """
+ print_error(
+ "Arch argument (%s) is not matching MSYS2 console/environment that is being used to run SCons (%s).\n"
+ "Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSYS2 compiler will be executed and inform you."
% (env["arch"], env_arch)
)
- sys.exit(202)
+ sys.exit(255)
if not try_cmd("gcc --version", env["mingw_prefix"], env["arch"]) and not try_cmd(
"clang --version", env["mingw_prefix"], env["arch"]
):
- print(
- """
- No valid compilers found, use MINGW_PREFIX environment variable to set MinGW path.
- """
- )
- sys.exit(202)
+ print_error("No valid compilers found, use MINGW_PREFIX environment variable to set MinGW path.")
+ sys.exit(255)
print("Using MinGW, arch %s" % (env["arch"]))
@@ -454,10 +445,10 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
if os.getenv("WindowsSdkDir") is not None:
env.Prepend(CPPPATH=[str(os.getenv("WindowsSdkDir")) + "/Include"])
else:
- print("Missing environment variable: WindowsSdkDir")
+ print_warning("Missing environment variable: WindowsSdkDir")
if int(env["target_win_version"], 16) < 0x0601:
- print("`target_win_version` should be 0x0601 or higher (Windows 7).")
+ print_error("`target_win_version` should be 0x0601 or higher (Windows 7).")
sys.exit(255)
env.AppendUnique(
@@ -515,10 +506,10 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
if env["d3d12"]:
# Check whether we have d3d12 dependencies installed.
if not os.path.exists(env["mesa_libs"]):
- print("The Direct3D 12 rendering driver requires dependencies to be installed.")
- print(r"You can install them by running `python misc\scripts\install_d3d12_sdk_windows.py`.")
- print("See the documentation for more information:")
- print(
+ print_error(
+ "The Direct3D 12 rendering driver requires dependencies to be installed.\n"
+ "You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n"
+ "See the documentation for more information:\n\t"
"https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html"
)
sys.exit(255)
@@ -557,13 +548,16 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
LIBS += ["dxgi", "d3d9", "d3d11"]
env.Prepend(CPPPATH=["#thirdparty/angle/include"])
+ if env["target"] in ["editor", "template_debug"]:
+ LIBS += ["psapi", "dbghelp"]
+
env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS])
if vcvars_msvc_config:
if os.getenv("WindowsSdkDir") is not None:
env.Append(LIBPATH=[str(os.getenv("WindowsSdkDir")) + "/Lib"])
else:
- print("Missing environment variable: WindowsSdkDir")
+ print_warning("Missing environment variable: WindowsSdkDir")
## LTO
@@ -572,7 +566,7 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
if env["lto"] != "none":
if env["lto"] == "thin":
- print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
+ print_error("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
sys.exit(255)
env.AppendUnique(CCFLAGS=["/GL"])
env.AppendUnique(ARFLAGS=["/LTCG"])
@@ -690,7 +684,7 @@ def configure_mingw(env: "SConsEnvironment"):
## Compile flags
if int(env["target_win_version"], 16) < 0x0601:
- print("`target_win_version` should be 0x0601 or higher (Windows 7).")
+ print_error("`target_win_version` should be 0x0601 or higher (Windows 7).")
sys.exit(255)
if not env["use_llvm"]:
@@ -744,10 +738,10 @@ def configure_mingw(env: "SConsEnvironment"):
if env["d3d12"]:
# Check whether we have d3d12 dependencies installed.
if not os.path.exists(env["mesa_libs"]):
- print("The Direct3D 12 rendering driver requires dependencies to be installed.")
- print(r"You can install them by running `python misc\scripts\install_d3d12_sdk_windows.py`.")
- print("See the documentation for more information:")
- print(
+ print_error(
+ "The Direct3D 12 rendering driver requires dependencies to be installed.\n"
+ "You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n"
+ "See the documentation for more information:\n\t"
"https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html"
)
sys.exit(255)
@@ -794,11 +788,11 @@ def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
if env["arch"] not in supported_arches:
- print(
+ print_error(
'Unsupported CPU architecture "%s" for Windows. Supported architectures are: %s.'
% (env["arch"], ", ".join(supported_arches))
)
- sys.exit()
+ sys.exit(255)
# At this point the env has been set up with basic tools/compilers.
env.Prepend(CPPPATH=["#platform/windows"])
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index b6b713687f..ebae00017b 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -545,7 +545,7 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title
result->Release();
}
}
- if (!p_callback.is_null()) {
+ if (p_callback.is_valid()) {
if (p_options_in_cb) {
Variant v_result = true;
Variant v_files = file_names;
@@ -574,7 +574,7 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title
}
}
} else {
- if (!p_callback.is_null()) {
+ if (p_callback.is_valid()) {
if (p_options_in_cb) {
Variant v_result = false;
Variant v_files = Vector<String>();
@@ -2556,7 +2556,7 @@ Error DisplayServerWindows::dialog_show(String p_title, String p_description, Ve
int button_pressed;
if (task_dialog_indirect && SUCCEEDED(task_dialog_indirect(&config, &button_pressed, nullptr, nullptr))) {
- if (!p_callback.is_null()) {
+ if (p_callback.is_valid()) {
Variant button = button_pressed;
const Variant *args[1] = { &button };
Variant ret;
@@ -4591,7 +4591,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
if (rect_changed) {
- if (!window.rect_changed_callback.is_null()) {
+ if (window.rect_changed_callback.is_valid()) {
window.rect_changed_callback.call(Rect2i(window.last_pos.x, window.last_pos.y, window.width, window.height));
}
@@ -4803,7 +4803,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
files.push_back(file);
}
- if (files.size() && !windows[window_id].drop_files_callback.is_null()) {
+ if (files.size() && windows[window_id].drop_files_callback.is_valid()) {
windows[window_id].drop_files_callback.call(files);
}
} break;
diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp
index 5f41b4e568..486c3120fc 100644
--- a/platform/windows/godot_windows.cpp
+++ b/platform/windows/godot_windows.cpp
@@ -215,7 +215,7 @@ int main(int argc, char **argv) {
// _argc and _argv are ignored
// we are going to use the WideChar version of them instead
-#ifdef CRASH_HANDLER_EXCEPTION
+#if defined(CRASH_HANDLER_EXCEPTION) && defined(_MSC_VER)
__try {
return _main();
} __except (CrashHandlerException(GetExceptionInformation())) {
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index abed93d414..157702655e 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -115,7 +115,24 @@ void RedirectStream(const char *p_file_name, const char *p_mode, FILE *p_cpp_str
}
void RedirectIOToConsole() {
+ // Save current handles.
+ HANDLE h_stdin = GetStdHandle(STD_INPUT_HANDLE);
+ HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
+ HANDLE h_stderr = GetStdHandle(STD_ERROR_HANDLE);
+
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
+ // Restore redirection (Note: if not redirected it's NULL handles not INVALID_HANDLE_VALUE).
+ if (h_stdin != 0) {
+ SetStdHandle(STD_INPUT_HANDLE, h_stdin);
+ }
+ if (h_stdout != 0) {
+ SetStdHandle(STD_OUTPUT_HANDLE, h_stdout);
+ }
+ if (h_stderr != 0) {
+ SetStdHandle(STD_ERROR_HANDLE, h_stderr);
+ }
+
+ // Update file handles.
RedirectStream("CONIN$", "r", stdin, STD_INPUT_HANDLE);
RedirectStream("CONOUT$", "w", stdout, STD_OUTPUT_HANDLE);
RedirectStream("CONOUT$", "w", stderr, STD_ERROR_HANDLE);
@@ -173,10 +190,6 @@ void OS_Windows::initialize() {
add_error_handler(&error_handlers);
#endif
-#ifndef WINDOWS_SUBSYSTEM_CONSOLE
- RedirectIOToConsole();
-#endif
-
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA);
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM);
@@ -1521,10 +1534,10 @@ void OS_Windows::unset_environment(const String &p_var) const {
}
String OS_Windows::get_stdin_string() {
- WCHAR buff[1024];
+ char buff[1024];
DWORD count = 0;
- if (ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), buff, 1024, &count, nullptr)) {
- return String::utf16((const char16_t *)buff, count);
+ if (ReadFile(GetStdHandle(STD_INPUT_HANDLE), buff, 1024, &count, nullptr)) {
+ return String::utf8((const char *)buff, count);
}
return String();
@@ -1908,6 +1921,13 @@ String OS_Windows::get_system_ca_certificates() {
OS_Windows::OS_Windows(HINSTANCE _hInstance) {
hInstance = _hInstance;
+#ifndef WINDOWS_SUBSYSTEM_CONSOLE
+ RedirectIOToConsole();
+#endif
+
+ SetConsoleOutputCP(CP_UTF8);
+ SetConsoleCP(CP_UTF8);
+
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
#ifdef WASAPI_ENABLED
diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp
index 6881a75596..6fc33afe99 100644
--- a/platform/windows/windows_terminal_logger.cpp
+++ b/platform/windows/windows_terminal_logger.cpp
@@ -53,26 +53,12 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er
}
buf[len] = 0;
- int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, len, nullptr, 0);
- if (wlen < 0) {
- return;
- }
-
- wchar_t *wbuf = (wchar_t *)memalloc((len + 1) * sizeof(wchar_t));
- ERR_FAIL_NULL_MSG(wbuf, "Out of memory.");
- MultiByteToWideChar(CP_UTF8, 0, buf, len, wbuf, wlen);
- wbuf[wlen] = 0;
-
- if (p_err) {
- fwprintf(stderr, L"%ls", wbuf);
- } else {
- wprintf(L"%ls", wbuf);
- }
-
- memfree(wbuf);
+ DWORD written = 0;
+ HANDLE h = p_err ? GetStdHandle(STD_ERROR_HANDLE) : GetStdHandle(STD_OUTPUT_HANDLE);
+ WriteFile(h, &buf[0], len, &written, nullptr);
#ifdef DEBUG_ENABLED
- fflush(stdout);
+ FlushFileBuffers(h);
#endif
}
diff --git a/platform_methods.py b/platform_methods.py
index 56115db4a4..57b11d1a47 100644
--- a/platform_methods.py
+++ b/platform_methods.py
@@ -39,8 +39,7 @@ def detect_arch():
# Catches x86, i386, i486, i586, i686, etc.
return "x86_32"
else:
- print("Unsupported CPU architecture: " + host_machine)
- print("Falling back to x86_64.")
+ methods.print_warning(f'Unsupported CPU architecture: "{host_machine}". Falling back to x86_64.')
return "x86_64"
diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp
index 9f71c881a9..54370f42da 100644
--- a/scene/3d/label_3d.cpp
+++ b/scene/3d/label_3d.cpp
@@ -636,6 +636,10 @@ void Label3D::_shape() {
}
void Label3D::set_text(const String &p_string) {
+ if (text == p_string) {
+ return;
+ }
+
text = p_string;
xl_text = atr(p_string);
dirty_text = true;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index d46470282f..7140161eca 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -149,7 +149,7 @@ void AnimationPlayer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
if (!Engine::get_singleton()->is_editor_hint() && animation_set.has(autoplay)) {
- set_active(true);
+ set_active(active);
play(autoplay);
_check_immediately_after_start();
}
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index e177bed20a..0773181239 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -931,9 +931,12 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
RID rid = l.text_buf->get_line_rid(line);
- //draw_rect(Rect2(p_ofs + off, TS->shaped_text_get_size(rid)), Color(1,0,0), false, 2); //DEBUG_RECTS
+ double l_ascent = TS->shaped_text_get_ascent(rid);
+ Size2 l_size = TS->shaped_text_get_size(rid);
+ double upos = TS->shaped_text_get_underline_position(rid);
+ double uth = TS->shaped_text_get_underline_thickness(rid);
- off.y += TS->shaped_text_get_ascent(rid);
+ off.y += l_ascent;
// Draw inlined objects.
Array objects = TS->shaped_text_get_objects(rid);
for (int i = 0; i < objects.size(); i++) {
@@ -950,7 +953,6 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
}
Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]);
- //draw_rect(rect, Color(1,0,0), false, 2); //DEBUG_RECTS
switch (it->type) {
case ITEM_IMAGE: {
ItemImage *img = static_cast<ItemImage *>(it);
@@ -1015,514 +1017,416 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
const Glyph *glyphs = TS->shaped_text_get_glyphs(rid);
int gl_size = TS->shaped_text_get_glyph_count(rid);
+ Vector2i chr_range = TS->shaped_text_get_range(rid);
- Vector2 gloff = off;
- // Draw outlines and shadow.
- int processed_glyphs_ol = r_processed_glyphs;
- for (int i = 0; i < gl_size; i++) {
- Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
- int size = _find_outline_size(it, p_outline_size);
- Color font_color = _find_color(it, p_base_color);
- Color font_outline_color = _find_outline_color(it, p_outline_color);
- Color font_shadow_color = p_font_shadow_color;
- if ((size <= 0 || font_outline_color.a == 0) && (font_shadow_color.a == 0)) {
- gloff.x += glyphs[i].advance;
- continue;
- }
-
- // Get FX.
- ItemFade *fade = nullptr;
- Item *fade_item = it;
- while (fade_item) {
- if (fade_item->type == ITEM_FADE) {
- fade = static_cast<ItemFade *>(fade_item);
- break;
- }
- fade_item = fade_item->parent;
- }
-
- Vector<ItemFX *> fx_stack;
- _fetch_item_fx_stack(it, fx_stack);
- bool custom_fx_ok = true;
-
- Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off);
- RID frid = glyphs[i].font_rid;
- uint32_t gl = glyphs[i].index;
- uint16_t gl_fl = glyphs[i].flags;
- uint8_t gl_cn = glyphs[i].count;
- bool cprev_cluster = false;
- bool cprev_conn = false;
- if (gl_cn == 0) { // Parts of the same cluster, always connected.
- cprev_cluster = true;
- }
- if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected.
- if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) {
- cprev_conn = true;
- }
- } else {
- if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) {
- cprev_conn = true;
- }
- }
+ int sel_start = -1;
+ int sel_end = -1;
- //Apply fx.
- if (fade) {
- float faded_visibility = 1.0f;
- if (glyphs[i].start >= fade->starting_index) {
- faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length;
- faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
- }
- font_outline_color.a = faded_visibility;
- font_shadow_color.a = faded_visibility;
- }
-
- bool txt_visible = (font_outline_color.a != 0) || (font_shadow_color.a != 0);
- Transform2D char_xform;
- char_xform.set_origin(gloff + p_ofs);
-
- for (int j = 0; j < fx_stack.size(); j++) {
- ItemFX *item_fx = fx_stack[j];
- bool cn = cprev_cluster || (cprev_conn && item_fx->connected);
-
- if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) {
- ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx);
-
- Ref<CharFXTransform> charfx = item_custom->char_fx_transform;
- Ref<RichTextEffect> custom_effect = item_custom->custom_effect;
-
- if (!custom_effect.is_null()) {
- charfx->elapsed_time = item_custom->elapsed_time;
- charfx->range = Vector2i(l.char_offset + glyphs[i].start, l.char_offset + glyphs[i].end);
- charfx->relative_index = l.char_offset + glyphs[i].start - item_fx->char_ofs;
- charfx->visibility = txt_visible;
- charfx->outline = true;
- charfx->font = frid;
- charfx->glyph_index = gl;
- charfx->glyph_flags = gl_fl;
- charfx->glyph_count = gl_cn;
- charfx->offset = fx_offset;
- charfx->color = font_color;
- charfx->transform = char_xform;
-
- bool effect_status = custom_effect->_process_effect_impl(charfx);
- custom_fx_ok = effect_status;
-
- char_xform = charfx->transform;
- fx_offset += charfx->offset;
- font_color = charfx->color;
- frid = charfx->font;
- gl = charfx->glyph_index;
- txt_visible &= charfx->visibility;
+ if (selection.active && (selection.from_frame->lines[selection.from_line].char_offset + selection.from_char) <= (l.char_offset + chr_range.y) && (selection.to_frame->lines[selection.to_line].char_offset + selection.to_char) >= (l.char_offset + chr_range.x)) {
+ sel_start = MAX(chr_range.x, (selection.from_frame->lines[selection.from_line].char_offset + selection.from_char) - l.char_offset);
+ sel_end = MIN(chr_range.y, (selection.to_frame->lines[selection.to_line].char_offset + selection.to_char) - l.char_offset);
+ }
+
+ int processed_glyphs_step = 0;
+ for (int step = DRAW_STEP_BACKGROUND; step < DRAW_STEP_MAX; step++) {
+ Vector2 off_step = off;
+ processed_glyphs_step = r_processed_glyphs;
+
+ Vector2 ul_start;
+ bool ul_started = false;
+ Color ul_color_prev;
+ Color ul_color;
+
+ Vector2 dot_ul_start;
+ bool dot_ul_started = false;
+ Color dot_ul_color_prev;
+ Color dot_ul_color;
+
+ Vector2 st_start;
+ bool st_started = false;
+ Color st_color_prev;
+ Color st_color;
+
+ float box_start = 0.0;
+ Color last_color = Color(0, 0, 0, 0);
+
+ for (int i = 0; i < gl_size; i++) {
+ bool selected = selection.active && (sel_start != -1) && (glyphs[i].start >= sel_start) && (glyphs[i].end <= sel_end);
+ Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
+
+ Color font_color = (step == DRAW_STEP_SHADOW || step == DRAW_STEP_OUTLINE || step == DRAW_STEP_TEXT) ? _find_color(it, p_base_color) : Color();
+ int outline_size = (step == DRAW_STEP_OUTLINE) ? _find_outline_size(it, p_outline_size) : 0;
+ Color font_outline_color = (step == DRAW_STEP_OUTLINE) ? _find_outline_color(it, p_outline_color) : Color();
+ Color font_shadow_color = p_font_shadow_color;
+ bool txt_visible = false;
+ if (step == DRAW_STEP_OUTLINE) {
+ txt_visible = (font_outline_color.a != 0 && outline_size > 0);
+ } else if (step == DRAW_STEP_SHADOW) {
+ txt_visible = (font_shadow_color.a != 0);
+ } else if (step == DRAW_STEP_TEXT) {
+ txt_visible = (font_color.a != 0);
+ bool has_ul = _find_underline(it);
+ if (!has_ul && underline_meta) {
+ ItemMeta *meta = nullptr;
+ if (_find_meta(it, nullptr, &meta) && meta) {
+ switch (meta->underline) {
+ case META_UNDERLINE_ALWAYS: {
+ has_ul = true;
+ } break;
+ case META_UNDERLINE_NEVER: {
+ has_ul = false;
+ } break;
+ case META_UNDERLINE_ON_HOVER: {
+ has_ul = (meta == meta_hovering);
+ } break;
+ }
+ }
}
- } else if (item_fx->type == ITEM_SHAKE) {
- ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
-
- if (!cn) {
- uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
- uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
- uint64_t max_rand = 2147483647;
- double current_offset = Math::remap(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
- double previous_offset = Math::remap(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
- double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate));
- n_time = (n_time > 1.0) ? 1.0 : n_time;
- item_shake->prev_off = Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f;
+ if (has_ul) {
+ if (ul_started && font_color != ul_color_prev) {
+ float y_off = upos;
+ float underline_width = MAX(1.0, uth * theme_cache.base_scale);
+ draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), ul_color, underline_width);
+ ul_start = p_ofs + Vector2(off_step.x, off_step.y);
+ ul_color_prev = font_color;
+ ul_color = font_color;
+ ul_color.a *= 0.5;
+ } else if (!ul_started) {
+ ul_started = true;
+ ul_start = p_ofs + Vector2(off_step.x, off_step.y);
+ ul_color_prev = font_color;
+ ul_color = font_color;
+ ul_color.a *= 0.5;
+ }
+ } else if (ul_started) {
+ ul_started = false;
+ float y_off = upos;
+ float underline_width = MAX(1.0, uth * theme_cache.base_scale);
+ draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), ul_color, underline_width);
}
- fx_offset += item_shake->prev_off;
- } else if (item_fx->type == ITEM_WAVE) {
- ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
-
- if (!cn) {
- double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_wave->amplitude / 10.0f);
- item_wave->prev_off = Point2(0, 1) * value;
+ if (_find_hint(it, nullptr) && underline_hint) {
+ if (dot_ul_started && font_color != dot_ul_color_prev) {
+ float y_off = upos;
+ float underline_width = MAX(1.0, uth * theme_cache.base_scale);
+ draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
+ dot_ul_start = p_ofs + Vector2(off_step.x, off_step.y);
+ dot_ul_color_prev = font_color;
+ dot_ul_color = font_color;
+ dot_ul_color.a *= 0.5;
+ } else if (!dot_ul_started) {
+ dot_ul_started = true;
+ dot_ul_start = p_ofs + Vector2(off_step.x, off_step.y);
+ dot_ul_color_prev = font_color;
+ dot_ul_color = font_color;
+ dot_ul_color.a *= 0.5;
+ }
+ } else if (dot_ul_started) {
+ dot_ul_started = false;
+ float y_off = upos;
+ float underline_width = MAX(1.0, uth * theme_cache.base_scale);
+ draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
}
- fx_offset += item_wave->prev_off;
- } else if (item_fx->type == ITEM_TORNADO) {
- ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
-
- if (!cn) {
- double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius);
- double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius);
- item_tornado->prev_off = Point2(torn_x, torn_y);
+ if (_find_strikethrough(it)) {
+ if (st_started && font_color != st_color_prev) {
+ float y_off = -l_ascent + l_size.y / 2;
+ float underline_width = MAX(1.0, uth * theme_cache.base_scale);
+ draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), st_color, underline_width);
+ st_start = p_ofs + Vector2(off_step.x, off_step.y);
+ st_color_prev = font_color;
+ st_color = font_color;
+ st_color.a *= 0.5;
+ } else if (!st_started) {
+ st_started = true;
+ st_start = p_ofs + Vector2(off_step.x, off_step.y);
+ st_color_prev = font_color;
+ st_color = font_color;
+ st_color.a *= 0.5;
+ }
+ } else if (st_started) {
+ st_started = false;
+ float y_off = -l_ascent + l_size.y / 2;
+ float underline_width = MAX(1.0, uth * theme_cache.base_scale);
+ draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), st_color, underline_width);
}
- fx_offset += item_tornado->prev_off;
- } else if (item_fx->type == ITEM_RAINBOW) {
- ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
-
- font_color = font_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + gloff.x) / 50)), item_rainbow->saturation, item_rainbow->value, font_color.a);
- } else if (item_fx->type == ITEM_PULSE) {
- ItemPulse *item_pulse = static_cast<ItemPulse *>(item_fx);
-
- const float sined_time = (Math::ease(Math::pingpong(item_pulse->elapsed_time, 1.0 / item_pulse->frequency) * item_pulse->frequency, item_pulse->ease));
- font_color = font_color.lerp(font_color * item_pulse->color, sined_time);
}
- }
+ if (step == DRAW_STEP_SHADOW || step == DRAW_STEP_OUTLINE || step == DRAW_STEP_TEXT) {
+ ItemFade *fade = nullptr;
+ Item *fade_item = it;
+ while (fade_item) {
+ if (fade_item->type == ITEM_FADE) {
+ fade = static_cast<ItemFade *>(fade_item);
+ break;
+ }
+ fade_item = fade_item->parent;
+ }
- if (is_inside_tree() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) {
- fx_offset = fx_offset.round();
- }
- Vector2 char_off = char_xform.get_origin();
-
- // Draw glyph outlines.
- const Color modulated_outline_color = font_outline_color * Color(1, 1, 1, font_color.a);
- const Color modulated_shadow_color = font_shadow_color * Color(1, 1, 1, font_color.a);
- for (int j = 0; j < glyphs[i].repeat; j++) {
- if (txt_visible) {
- bool skip = (trim_chars && l.char_offset + glyphs[i].end > visible_characters) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
- if (!skip && frid != RID()) {
- if (modulated_shadow_color.a > 0) {
- Transform2D char_reverse_xform;
- char_reverse_xform.set_origin(-char_off - p_shadow_ofs);
- Transform2D char_final_xform = char_xform * char_reverse_xform;
- char_final_xform.columns[2] += p_shadow_ofs;
- draw_set_transform_matrix(char_final_xform);
-
- TS->font_draw_glyph(frid, ci, glyphs[i].font_size, fx_offset + char_off + p_shadow_ofs, gl, modulated_shadow_color);
- if (p_shadow_outline_size > 0) {
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, p_shadow_outline_size, fx_offset + char_off + p_shadow_ofs, gl, modulated_shadow_color);
- }
+ Vector<ItemFX *> fx_stack;
+ _fetch_item_fx_stack(it, fx_stack);
+ bool custom_fx_ok = true;
+
+ Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off);
+ RID frid = glyphs[i].font_rid;
+ uint32_t gl = glyphs[i].index;
+ uint16_t gl_fl = glyphs[i].flags;
+ uint8_t gl_cn = glyphs[i].count;
+ bool cprev_cluster = false;
+ bool cprev_conn = false;
+ if (gl_cn == 0) { // Parts of the same grapheme cluster, always connected.
+ cprev_cluster = true;
+ }
+ if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected.
+ if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) {
+ cprev_conn = true;
}
- if (modulated_outline_color.a != 0.0 && size > 0) {
- Transform2D char_reverse_xform;
- char_reverse_xform.set_origin(-char_off);
- Transform2D char_final_xform = char_xform * char_reverse_xform;
- draw_set_transform_matrix(char_final_xform);
+ } else {
+ if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) {
+ cprev_conn = true;
+ }
+ }
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, fx_offset + char_off, gl, modulated_outline_color);
+ //Apply fx.
+ if (fade) {
+ float faded_visibility = 1.0f;
+ if (glyphs[i].start >= fade->starting_index) {
+ faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length;
+ faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
}
+ font_color.a = faded_visibility;
}
- processed_glyphs_ol++;
- }
- gloff.x += glyphs[i].advance;
- }
- }
- draw_set_transform_matrix(Transform2D());
- Vector2 fbg_line_off = off + p_ofs;
- // Draw background color box
- Vector2i chr_range = TS->shaped_text_get_range(rid);
- _draw_fbg_boxes(ci, rid, fbg_line_off, it_from, it_to, chr_range.x, chr_range.y, 0);
+ Transform2D char_xform;
+ char_xform.set_origin(p_ofs + off_step);
+
+ for (int j = 0; j < fx_stack.size(); j++) {
+ ItemFX *item_fx = fx_stack[j];
+ bool cn = cprev_cluster || (cprev_conn && item_fx->connected);
+
+ if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) {
+ ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx);
+
+ Ref<CharFXTransform> charfx = item_custom->char_fx_transform;
+ Ref<RichTextEffect> custom_effect = item_custom->custom_effect;
+
+ if (!custom_effect.is_null()) {
+ charfx->elapsed_time = item_custom->elapsed_time;
+ charfx->range = Vector2i(l.char_offset + glyphs[i].start, l.char_offset + glyphs[i].end);
+ charfx->relative_index = l.char_offset + glyphs[i].start - item_fx->char_ofs;
+ charfx->visibility = txt_visible;
+ charfx->outline = (step == DRAW_STEP_SHADOW) || (step == DRAW_STEP_OUTLINE);
+ charfx->font = frid;
+ charfx->glyph_index = gl;
+ charfx->glyph_flags = gl_fl;
+ charfx->glyph_count = gl_cn;
+ charfx->offset = fx_offset;
+ charfx->color = font_color;
+ charfx->transform = char_xform;
+
+ bool effect_status = custom_effect->_process_effect_impl(charfx);
+ custom_fx_ok = effect_status;
+
+ char_xform = charfx->transform;
+ fx_offset += charfx->offset;
+ font_color = charfx->color;
+ frid = charfx->font;
+ gl = charfx->glyph_index;
+ txt_visible &= charfx->visibility;
+ }
+ } else if (item_fx->type == ITEM_SHAKE) {
+ ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
+
+ if (!cn) {
+ uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
+ uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
+ uint64_t max_rand = 2147483647;
+ double current_offset = Math::remap(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
+ double previous_offset = Math::remap(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
+ double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate));
+ n_time = (n_time > 1.0) ? 1.0 : n_time;
+ item_shake->prev_off = Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f;
+ }
+ fx_offset += item_shake->prev_off;
+ } else if (item_fx->type == ITEM_WAVE) {
+ ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
- // Draw main text.
- Color selection_bg = theme_cache.selection_color;
+ if (!cn) {
+ double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off_step.x) / 50)) * (item_wave->amplitude / 10.0f);
+ item_wave->prev_off = Point2(0, 1) * value;
+ }
+ fx_offset += item_wave->prev_off;
+ } else if (item_fx->type == ITEM_TORNADO) {
+ ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
+
+ if (!cn) {
+ double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off_step.x) / 50)) * (item_tornado->radius);
+ double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off_step.x) / 50)) * (item_tornado->radius);
+ item_tornado->prev_off = Point2(torn_x, torn_y);
+ }
+ fx_offset += item_tornado->prev_off;
+ } else if (item_fx->type == ITEM_RAINBOW) {
+ ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
- int sel_start = -1;
- int sel_end = -1;
+ font_color = font_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + off_step.x) / 50)), item_rainbow->saturation, item_rainbow->value, font_color.a);
+ } else if (item_fx->type == ITEM_PULSE) {
+ ItemPulse *item_pulse = static_cast<ItemPulse *>(item_fx);
- if (selection.active && (selection.from_frame->lines[selection.from_line].char_offset + selection.from_char) <= (l.char_offset + TS->shaped_text_get_range(rid).y) && (selection.to_frame->lines[selection.to_line].char_offset + selection.to_char) >= (l.char_offset + TS->shaped_text_get_range(rid).x)) {
- sel_start = MAX(TS->shaped_text_get_range(rid).x, (selection.from_frame->lines[selection.from_line].char_offset + selection.from_char) - l.char_offset);
- sel_end = MIN(TS->shaped_text_get_range(rid).y, (selection.to_frame->lines[selection.to_line].char_offset + selection.to_char) - l.char_offset);
-
- Vector<Vector2> sel = TS->shaped_text_get_selection(rid, sel_start, sel_end);
- for (int i = 0; i < sel.size(); i++) {
- Rect2 rect = Rect2(sel[i].x + p_ofs.x + off.x, p_ofs.y + off.y - TS->shaped_text_get_ascent(rid), sel[i].y - sel[i].x, TS->shaped_text_get_size(rid).y);
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_bg);
- }
- }
-
- Vector2 ul_start;
- bool ul_started = false;
- Color ul_color_prev;
- Color ul_color;
-
- Vector2 dot_ul_start;
- bool dot_ul_started = false;
- Color dot_ul_color_prev;
- Color dot_ul_color;
-
- Vector2 st_start;
- bool st_started = false;
- Color st_color_prev;
- Color st_color;
-
- for (int i = 0; i < gl_size; i++) {
- bool selected = selection.active && (sel_start != -1) && (glyphs[i].start >= sel_start) && (glyphs[i].end <= sel_end);
- Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
- bool has_ul = _find_underline(it);
- if (!has_ul && underline_meta) {
- ItemMeta *meta = nullptr;
- if (_find_meta(it, nullptr, &meta) && meta) {
- switch (meta->underline) {
- case META_UNDERLINE_ALWAYS: {
- has_ul = true;
- } break;
- case META_UNDERLINE_NEVER: {
- has_ul = false;
- } break;
- case META_UNDERLINE_ON_HOVER: {
- has_ul = (meta == meta_hovering);
- } break;
+ const float sined_time = (Math::ease(Math::pingpong(item_pulse->elapsed_time, 1.0 / item_pulse->frequency) * item_pulse->frequency, item_pulse->ease));
+ font_color = font_color.lerp(font_color * item_pulse->color, sined_time);
+ }
}
- }
- }
- Color font_color = _find_color(it, p_base_color);
- if (has_ul) {
- if (ul_started && font_color != ul_color_prev) {
- float y_off = TS->shaped_text_get_underline_position(rid);
- float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
- draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), ul_color, underline_width);
- ul_start = p_ofs + Vector2(off.x, off.y);
- ul_color_prev = font_color;
- ul_color = font_color;
- ul_color.a *= 0.5;
- } else if (!ul_started) {
- ul_started = true;
- ul_start = p_ofs + Vector2(off.x, off.y);
- ul_color_prev = font_color;
- ul_color = font_color;
- ul_color.a *= 0.5;
- }
- } else if (ul_started) {
- ul_started = false;
- float y_off = TS->shaped_text_get_underline_position(rid);
- float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
- draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), ul_color, underline_width);
- }
- if (_find_hint(it, nullptr) && underline_hint) {
- if (dot_ul_started && font_color != dot_ul_color_prev) {
- float y_off = TS->shaped_text_get_underline_position(rid);
- float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
- draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
- dot_ul_start = p_ofs + Vector2(off.x, off.y);
- dot_ul_color_prev = font_color;
- dot_ul_color = font_color;
- dot_ul_color.a *= 0.5;
- } else if (!dot_ul_started) {
- dot_ul_started = true;
- dot_ul_start = p_ofs + Vector2(off.x, off.y);
- dot_ul_color_prev = font_color;
- dot_ul_color = font_color;
- dot_ul_color.a *= 0.5;
- }
- } else if (dot_ul_started) {
- dot_ul_started = false;
- float y_off = TS->shaped_text_get_underline_position(rid);
- float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
- draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
- }
- if (_find_strikethrough(it)) {
- if (st_started && font_color != st_color_prev) {
- float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2;
- float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
- draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), st_color, underline_width);
- st_start = p_ofs + Vector2(off.x, off.y);
- st_color_prev = font_color;
- st_color = font_color;
- st_color.a *= 0.5;
- } else if (!st_started) {
- st_started = true;
- st_start = p_ofs + Vector2(off.x, off.y);
- st_color_prev = font_color;
- st_color = font_color;
- st_color.a *= 0.5;
- }
- } else if (st_started) {
- st_started = false;
- float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2;
- float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
- draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), st_color, underline_width);
- }
-
- // Get FX.
- ItemFade *fade = nullptr;
- Item *fade_item = it;
- while (fade_item) {
- if (fade_item->type == ITEM_FADE) {
- fade = static_cast<ItemFade *>(fade_item);
- break;
- }
- fade_item = fade_item->parent;
- }
-
- Vector<ItemFX *> fx_stack;
- _fetch_item_fx_stack(it, fx_stack);
- bool custom_fx_ok = true;
-
- Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off);
- RID frid = glyphs[i].font_rid;
- uint32_t gl = glyphs[i].index;
- uint16_t gl_fl = glyphs[i].flags;
- uint8_t gl_cn = glyphs[i].count;
- bool cprev_cluster = false;
- bool cprev_conn = false;
- if (gl_cn == 0) { // Parts of the same grapheme cluster, always connected.
- cprev_cluster = true;
- }
- if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected.
- if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) {
- cprev_conn = true;
- }
- } else {
- if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) {
- cprev_conn = true;
- }
- }
- //Apply fx.
- if (fade) {
- float faded_visibility = 1.0f;
- if (glyphs[i].start >= fade->starting_index) {
- faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length;
- faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
- }
- font_color.a = faded_visibility;
- }
-
- bool txt_visible = (font_color.a != 0);
-
- Transform2D char_xform;
- char_xform.set_origin(p_ofs + off);
-
- for (int j = 0; j < fx_stack.size(); j++) {
- ItemFX *item_fx = fx_stack[j];
- bool cn = cprev_cluster || (cprev_conn && item_fx->connected);
-
- if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) {
- ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx);
-
- Ref<CharFXTransform> charfx = item_custom->char_fx_transform;
- Ref<RichTextEffect> custom_effect = item_custom->custom_effect;
-
- if (!custom_effect.is_null()) {
- charfx->elapsed_time = item_custom->elapsed_time;
- charfx->range = Vector2i(l.char_offset + glyphs[i].start, l.char_offset + glyphs[i].end);
- charfx->relative_index = l.char_offset + glyphs[i].start - item_fx->char_ofs;
- charfx->visibility = txt_visible;
- charfx->outline = false;
- charfx->font = frid;
- charfx->glyph_index = gl;
- charfx->glyph_flags = gl_fl;
- charfx->glyph_count = gl_cn;
- charfx->offset = fx_offset;
- charfx->color = font_color;
- charfx->transform = char_xform;
-
- bool effect_status = custom_effect->_process_effect_impl(charfx);
- custom_fx_ok = effect_status;
-
- char_xform = charfx->transform;
- fx_offset += charfx->offset;
- font_color = charfx->color;
- frid = charfx->font;
- gl = charfx->glyph_index;
- txt_visible &= charfx->visibility;
+ if (is_inside_tree() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) {
+ fx_offset = fx_offset.round();
}
- } else if (item_fx->type == ITEM_SHAKE) {
- ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
-
- if (!cn) {
- uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
- uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
- uint64_t max_rand = 2147483647;
- double current_offset = Math::remap(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
- double previous_offset = Math::remap(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
- double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate));
- n_time = (n_time > 1.0) ? 1.0 : n_time;
- item_shake->prev_off = Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f;
+
+ Vector2 char_off = char_xform.get_origin();
+ Transform2D char_reverse_xform;
+ if (step == DRAW_STEP_TEXT) {
+ if (selected && use_selected_font_color) {
+ font_color = theme_cache.font_selected_color;
+ }
+
+ char_reverse_xform.set_origin(-char_off);
+ Transform2D char_final_xform = char_xform * char_reverse_xform;
+ draw_set_transform_matrix(char_final_xform);
+ } else if (step == DRAW_STEP_SHADOW) {
+ font_color = font_shadow_color * Color(1, 1, 1, font_color.a);
+
+ char_reverse_xform.set_origin(-char_off - p_shadow_ofs);
+ Transform2D char_final_xform = char_xform * char_reverse_xform;
+ char_final_xform.columns[2] += p_shadow_ofs;
+ draw_set_transform_matrix(char_final_xform);
+ } else if (step == DRAW_STEP_OUTLINE) {
+ font_color = font_outline_color * Color(1, 1, 1, font_color.a);
+
+ char_reverse_xform.set_origin(-char_off);
+ Transform2D char_final_xform = char_xform * char_reverse_xform;
+ draw_set_transform_matrix(char_final_xform);
}
- fx_offset += item_shake->prev_off;
- } else if (item_fx->type == ITEM_WAVE) {
- ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
- if (!cn) {
- double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_wave->amplitude / 10.0f);
- item_wave->prev_off = Point2(0, 1) * value;
+ // Draw glyphs.
+ for (int j = 0; j < glyphs[i].repeat; j++) {
+ bool skip = (trim_chars && l.char_offset + glyphs[i].end > visible_characters) || (trim_glyphs_ltr && (processed_glyphs_step >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_step < total_glyphs - visible_glyphs));
+ if (!skip) {
+ if (txt_visible) {
+ if (step == DRAW_STEP_TEXT) {
+ if (frid != RID()) {
+ TS->font_draw_glyph(frid, ci, glyphs[i].font_size, fx_offset + char_off, gl, font_color);
+ } else if (((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((glyphs[i].flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) {
+ TS->draw_hex_code_box(ci, glyphs[i].font_size, fx_offset + char_off, gl, font_color);
+ }
+ } else if (step == DRAW_STEP_SHADOW && frid != RID()) {
+ TS->font_draw_glyph(frid, ci, glyphs[i].font_size, fx_offset + char_off + p_shadow_ofs, gl, font_color);
+ if (p_shadow_outline_size > 0) {
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, p_shadow_outline_size, fx_offset + char_off + p_shadow_ofs, gl, font_color);
+ }
+ } else if (step == DRAW_STEP_OUTLINE && frid != RID() && outline_size > 0) {
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, outline_size, fx_offset + char_off, gl, font_color);
+ }
+ }
+ processed_glyphs_step++;
+ }
+ if (step == DRAW_STEP_TEXT && skip) {
+ // Finish underline/overline/strikethrough is previous glyph is skipped.
+ if (ul_started) {
+ ul_started = false;
+ float y_off = upos;
+ float underline_width = MAX(1.0, uth * theme_cache.base_scale);
+ draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), ul_color, underline_width);
+ }
+ if (dot_ul_started) {
+ dot_ul_started = false;
+ float y_off = upos;
+ float underline_width = MAX(1.0, uth * theme_cache.base_scale);
+ draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
+ }
+ if (st_started) {
+ st_started = false;
+ float y_off = -l_ascent + l_size.y / 2;
+ float underline_width = MAX(1.0, uth * theme_cache.base_scale);
+ draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), st_color, underline_width);
+ }
+ }
+ off_step.x += glyphs[i].advance;
}
- fx_offset += item_wave->prev_off;
- } else if (item_fx->type == ITEM_TORNADO) {
- ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
-
- if (!cn) {
- double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius);
- double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius);
- item_tornado->prev_off = Point2(torn_x, torn_y);
+ draw_set_transform_matrix(Transform2D());
+ }
+ // Draw boxes.
+ if (step == DRAW_STEP_BACKGROUND || step == DRAW_STEP_FOREGROUND) {
+ for (int j = 0; j < glyphs[i].repeat; j++) {
+ bool skip = (trim_chars && l.char_offset + glyphs[i].end > visible_characters) || (trim_glyphs_ltr && (processed_glyphs_step >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_step < total_glyphs - visible_glyphs));
+ if (!skip) {
+ Color color;
+ if (step == DRAW_STEP_BACKGROUND) {
+ color = _find_bgcolor(it);
+ } else if (step == DRAW_STEP_FOREGROUND) {
+ color = _find_fgcolor(it);
+ }
+ if (color != last_color) {
+ if (last_color.a > 0.0) {
+ Vector2 rect_off = p_ofs + Vector2(box_start - theme_cache.text_highlight_h_padding, off_step.y - l_ascent - theme_cache.text_highlight_v_padding);
+ Vector2 rect_size = Vector2(off_step.x - box_start + 2 * theme_cache.text_highlight_h_padding, l_size.y + 2 * theme_cache.text_highlight_v_padding);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(rect_off, rect_size), last_color);
+ }
+ if (color.a > 0.0) {
+ box_start = off_step.x;
+ }
+ }
+ last_color = color;
+ processed_glyphs_step++;
+ } else {
+ // Finish box is previous glyph is skipped.
+ if (last_color.a > 0.0) {
+ Vector2 rect_off = p_ofs + Vector2(box_start - theme_cache.text_highlight_h_padding, off_step.y - l_ascent - theme_cache.text_highlight_v_padding);
+ Vector2 rect_size = Vector2(off_step.x - box_start + 2 * theme_cache.text_highlight_h_padding, l_size.y + 2 * theme_cache.text_highlight_v_padding);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(rect_off, rect_size), last_color);
+ }
+ last_color = Color(0, 0, 0, 0);
+ }
+ off_step.x += glyphs[i].advance;
}
- fx_offset += item_tornado->prev_off;
- } else if (item_fx->type == ITEM_RAINBOW) {
- ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
-
- font_color = font_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + off.x) / 50)), item_rainbow->saturation, item_rainbow->value, font_color.a);
- } else if (item_fx->type == ITEM_PULSE) {
- ItemPulse *item_pulse = static_cast<ItemPulse *>(item_fx);
-
- const float sined_time = (Math::ease(Math::pingpong(item_pulse->elapsed_time, 1.0 / item_pulse->frequency) * item_pulse->frequency, item_pulse->ease));
- font_color = font_color.lerp(font_color * item_pulse->color, sined_time);
}
}
-
- if (is_inside_tree() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) {
- fx_offset = fx_offset.round();
+ // Finish lines and boxes.
+ if (step == DRAW_STEP_BACKGROUND) {
+ if (sel_start != -1) {
+ Color selection_bg = theme_cache.selection_color;
+ Vector<Vector2> sel = TS->shaped_text_get_selection(rid, sel_start, sel_end);
+ for (int i = 0; i < sel.size(); i++) {
+ Rect2 rect = Rect2(sel[i].x + p_ofs.x + off.x, p_ofs.y + off.y - l_ascent, sel[i].y - sel[i].x, l_size.y); // Note: use "off" not "off_step", selection is relative to the line start.
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_bg);
+ }
+ }
}
- Vector2 char_off = char_xform.get_origin();
-
- Transform2D char_reverse_xform;
- char_reverse_xform.set_origin(-char_off);
- char_xform = char_xform * char_reverse_xform;
- draw_set_transform_matrix(char_xform);
-
- if (selected && use_selected_font_color) {
- font_color = theme_cache.font_selected_color;
+ if (step == DRAW_STEP_BACKGROUND || step == DRAW_STEP_FOREGROUND) {
+ if (last_color.a > 0.0) {
+ Vector2 rect_off = p_ofs + Vector2(box_start - theme_cache.text_highlight_h_padding, off_step.y - l_ascent - theme_cache.text_highlight_v_padding);
+ Vector2 rect_size = Vector2(off_step.x - box_start + 2 * theme_cache.text_highlight_h_padding, l_size.y + 2 * theme_cache.text_highlight_v_padding);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(rect_off, rect_size), last_color);
+ }
}
-
- // Draw glyphs.
- for (int j = 0; j < glyphs[i].repeat; j++) {
- bool skip = (trim_chars && l.char_offset + glyphs[i].end > visible_characters) || (trim_glyphs_ltr && (r_processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (r_processed_glyphs < total_glyphs - visible_glyphs));
- if (txt_visible) {
- if (!skip) {
- if (frid != RID()) {
- TS->font_draw_glyph(frid, ci, glyphs[i].font_size, fx_offset + char_off, gl, font_color);
- } else if (((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((glyphs[i].flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) {
- TS->draw_hex_code_box(ci, glyphs[i].font_size, fx_offset + char_off, gl, font_color);
- }
- }
- r_processed_glyphs++;
+ if (step == DRAW_STEP_TEXT) {
+ if (ul_started) {
+ ul_started = false;
+ float y_off = upos;
+ float underline_width = MAX(1.0, uth * theme_cache.base_scale);
+ draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), ul_color, underline_width);
}
- if (skip) {
- // End underline/overline/strikethrough is previous glyph is skipped.
- if (ul_started) {
- ul_started = false;
- float y_off = TS->shaped_text_get_underline_position(rid);
- float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
- draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), ul_color, underline_width);
- }
- if (dot_ul_started) {
- dot_ul_started = false;
- float y_off = TS->shaped_text_get_underline_position(rid);
- float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
- draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
- }
- if (st_started) {
- st_started = false;
- float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2;
- float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
- draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), st_color, underline_width);
- }
+ if (dot_ul_started) {
+ dot_ul_started = false;
+ float y_off = upos;
+ float underline_width = MAX(1.0, uth * theme_cache.base_scale);
+ draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
+ }
+ if (st_started) {
+ st_started = false;
+ float y_off = -l_ascent + l_size.y / 2;
+ float underline_width = MAX(1.0, uth * theme_cache.base_scale);
+ draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), st_color, underline_width);
}
- off.x += glyphs[i].advance;
}
-
- draw_set_transform_matrix(Transform2D());
- }
- if (ul_started) {
- ul_started = false;
- float y_off = TS->shaped_text_get_underline_position(rid);
- float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
- draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), ul_color, underline_width);
}
- if (dot_ul_started) {
- dot_ul_started = false;
- float y_off = TS->shaped_text_get_underline_position(rid);
- float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
- draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
- }
- if (st_started) {
- st_started = false;
- float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2;
- float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
- draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), st_color, underline_width);
- }
- // Draw foreground color box
- _draw_fbg_boxes(ci, rid, fbg_line_off, it_from, it_to, chr_range.x, chr_range.y, 1);
+ r_processed_glyphs = processed_glyphs_step;
off.y += TS->shaped_text_get_descent(rid);
}
@@ -1803,7 +1707,7 @@ void RichTextLabel::_scroll_changed(double) {
return;
}
- if (scroll_follow && vscroll->get_value() >= (vscroll->get_max() - Math::round(vscroll->get_page()))) {
+ if (scroll_follow && vscroll->get_value() > (vscroll->get_max() - vscroll->get_page() - 1)) {
scroll_following = true;
} else {
scroll_following = false;
@@ -4121,7 +4025,7 @@ bool RichTextLabel::is_scroll_active() const {
void RichTextLabel::set_scroll_follow(bool p_follow) {
scroll_follow = p_follow;
- if (!vscroll->is_visible_in_tree() || vscroll->get_value() >= (vscroll->get_max() - vscroll->get_page())) {
+ if (!vscroll->is_visible_in_tree() || vscroll->get_value() > (vscroll->get_max() - vscroll->get_page() - 1)) {
scroll_following = true;
}
}
@@ -6404,65 +6308,6 @@ void RichTextLabel::menu_option(int p_option) {
}
}
-void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item *it_from, Item *it_to, int start, int end, int fbg_flag) {
- Vector2i fbg_index = Vector2i(end, start);
- Color last_color = Color(0, 0, 0, 0);
- bool draw_box = false;
- // Draw a box based on color tags associated with glyphs
- for (int i = start; i < end; i++) {
- Item *it = _get_item_at_pos(it_from, it_to, i);
- Color color;
-
- if (fbg_flag == 0) {
- color = _find_bgcolor(it);
- } else {
- color = _find_fgcolor(it);
- }
-
- bool change_to_color = ((color.a > 0) && ((last_color.a - 0.0) < 0.01));
- bool change_from_color = (((color.a - 0.0) < 0.01) && (last_color.a > 0.0));
- bool change_color = (((color.a > 0) == (last_color.a > 0)) && (color != last_color));
-
- if (change_to_color) {
- fbg_index.x = MIN(i, fbg_index.x);
- fbg_index.y = MAX(i, fbg_index.y);
- }
-
- if (change_from_color || change_color) {
- fbg_index.x = MIN(i, fbg_index.x);
- fbg_index.y = MAX(i, fbg_index.y);
- draw_box = true;
- }
-
- if (draw_box) {
- Vector<Vector2> sel = TS->shaped_text_get_selection(p_rid, fbg_index.x, fbg_index.y);
- for (int j = 0; j < sel.size(); j++) {
- Vector2 rect_off = line_off + Vector2(sel[j].x - theme_cache.text_highlight_h_padding, -TS->shaped_text_get_ascent(p_rid) - theme_cache.text_highlight_v_padding);
- Vector2 rect_size = Vector2(sel[j].y - sel[j].x + 2 * theme_cache.text_highlight_h_padding, TS->shaped_text_get_size(p_rid).y + 2 * theme_cache.text_highlight_v_padding);
- RenderingServer::get_singleton()->canvas_item_add_rect(p_ci, Rect2(rect_off, rect_size), last_color);
- }
- fbg_index = Vector2i(end, start);
- draw_box = false;
- }
-
- if (change_color) {
- fbg_index.x = MIN(i, fbg_index.x);
- fbg_index.y = MAX(i, fbg_index.y);
- }
-
- last_color = color;
- }
-
- if (last_color.a > 0) {
- Vector<Vector2> sel = TS->shaped_text_get_selection(p_rid, fbg_index.x, end);
- for (int i = 0; i < sel.size(); i++) {
- Vector2 rect_off = line_off + Vector2(sel[i].x - theme_cache.text_highlight_h_padding, -TS->shaped_text_get_ascent(p_rid) - theme_cache.text_highlight_v_padding);
- Vector2 rect_size = Vector2(sel[i].y - sel[i].x + 2 * theme_cache.text_highlight_h_padding, TS->shaped_text_get_size(p_rid).y + 2 * theme_cache.text_highlight_v_padding);
- RenderingServer::get_singleton()->canvas_item_add_rect(p_ci, Rect2(rect_off, rect_size), last_color);
- }
- }
-}
-
Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_identifier) {
for (int i = 0; i < custom_effects.size(); i++) {
Ref<RichTextEffect> effect = custom_effects[i];
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index a993d922d2..371f6724d7 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -43,6 +43,15 @@ class RichTextEffect;
class RichTextLabel : public Control {
GDCLASS(RichTextLabel, Control);
+ enum RTLDrawStep {
+ DRAW_STEP_BACKGROUND,
+ DRAW_STEP_SHADOW,
+ DRAW_STEP_OUTLINE,
+ DRAW_STEP_TEXT,
+ DRAW_STEP_FOREGROUND,
+ DRAW_STEP_MAX,
+ };
+
public:
enum ListType {
LIST_NUMBERS,
@@ -596,7 +605,6 @@ private:
Size2 _get_image_size(const Ref<Texture2D> &p_image, int p_width = 0, int p_height = 0, const Rect2 &p_region = Rect2());
- void _draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item *it_from, Item *it_to, int start, int end, int fbg_flag);
#ifndef DISABLE_DEPRECATED
// Kept for compatibility from 3.x to 4.0.
bool _set(const StringName &p_name, const Variant &p_value);
diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp
index 5261cbe3eb..fc9ac2ab18 100644
--- a/scene/gui/texture_progress_bar.cpp
+++ b/scene/gui/texture_progress_bar.cpp
@@ -494,7 +494,7 @@ void TextureProgressBar::_notification(int p_what) {
Rect2 source = Rect2(Point2(), progress->get_size());
draw_texture_rect_region(progress, region, source, tint_progress);
} else if (val != 0) {
- Array pts;
+ LocalVector<float> pts;
float direction = mode == FILL_COUNTER_CLOCKWISE ? -1 : 1;
float start;
@@ -507,11 +507,11 @@ void TextureProgressBar::_notification(int p_what) {
float end = start + direction * val;
float from = MIN(start, end);
float to = MAX(start, end);
- pts.append(from);
+ pts.push_back(from);
for (float corner = Math::floor(from * 4 + 0.5) * 0.25 + 0.125; corner < to; corner += 0.25) {
- pts.append(corner);
+ pts.push_back(corner);
}
- pts.append(to);
+ pts.push_back(to);
Ref<AtlasTexture> atlas_progress = progress;
bool valid_atlas_progress = atlas_progress.is_valid() && atlas_progress->get_atlas().is_valid();
@@ -524,8 +524,8 @@ void TextureProgressBar::_notification(int p_what) {
Vector<Point2> uvs;
Vector<Point2> points;
- for (int i = 0; i < pts.size(); i++) {
- Point2 uv = unit_val_to_uv(pts[i]);
+ for (const float &f : pts) {
+ Point2 uv = unit_val_to_uv(f);
if (uvs.find(uv) >= 0) {
continue;
}
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 5b375905cc..0087a5e7f2 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -37,6 +37,15 @@
#include "servers/rendering_server.h"
#include "texture.h"
+#ifdef TOOLS_ENABLED
+#include "editor/editor_help.h"
+
+#include "modules/modules_enabled.gen.h" // For regex.
+#ifdef MODULE_REGEX_ENABLED
+#include "modules/regex/regex.h"
+#endif
+#endif
+
Shader::Mode Shader::get_mode() const {
return mode;
}
@@ -121,6 +130,12 @@ void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_gr
List<PropertyInfo> local;
RenderingServer::get_singleton()->get_shader_parameter_list(shader, &local);
+#ifdef TOOLS_ENABLED
+ DocData::ClassDoc class_doc;
+ class_doc.name = get_path();
+ class_doc.is_script_doc = true;
+#endif
+
for (PropertyInfo &pi : local) {
bool is_group = pi.usage == PROPERTY_USAGE_GROUP || pi.usage == PROPERTY_USAGE_SUBGROUP;
if (!p_get_groups && is_group) {
@@ -136,9 +151,33 @@ void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_gr
if (pi.type == Variant::RID) {
pi.type = Variant::OBJECT;
}
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ DocData::PropertyDoc prop_doc;
+ prop_doc.name = "shader_parameter/" + pi.name;
+#ifdef MODULE_REGEX_ENABLED
+ const RegEx pattern("/\\*\\*([^*]|[\\r\\n]|(\\*+([^*/]|[\\r\\n])))*\\*+/\\s*uniform\\s+\\w+\\s+" + pi.name + "(?=[\\s:;=])");
+ Ref<RegExMatch> pattern_ref = pattern.search(code);
+ if (pattern_ref != nullptr) {
+ RegExMatch *match = pattern_ref.ptr();
+ const RegEx pattern_tip("\\/\\*\\*([\\s\\S]*?)\\*/");
+ Ref<RegExMatch> pattern_tip_ref = pattern_tip.search(match->get_string(0));
+ RegExMatch *match_tip = pattern_tip_ref.ptr();
+ const RegEx pattern_stripped("\\n\\s*\\*\\s*");
+ prop_doc.description = pattern_stripped.sub(match_tip->get_string(1), "\n", true);
+ }
+#endif
+ class_doc.properties.push_back(prop_doc);
+ }
+#endif
p_params->push_back(pi);
}
}
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint() && !class_doc.name.is_empty() && p_params) {
+ EditorHelp::get_doc_data()->add_doc(class_doc);
+ }
+#endif
}
RID Shader::get_rid() const {
diff --git a/scu_builders.py b/scu_builders.py
index e6adf6543c..a9ae428222 100644
--- a/scu_builders.py
+++ b/scu_builders.py
@@ -3,6 +3,7 @@
import glob, os
import math
+from methods import print_error
from pathlib import Path
from os.path import normpath, basename
@@ -38,7 +39,7 @@ def find_files_in_folder(folder, sub_folder, include_list, extension, sought_exc
abs_folder = base_folder_path + folder + "/" + sub_folder
if not os.path.isdir(abs_folder):
- print("SCU: ERROR: %s not found." % abs_folder)
+ print_error(f'SCU: "{abs_folder}" not found.')
return include_list, found_exceptions
os.chdir(abs_folder)
@@ -70,7 +71,7 @@ def write_output_file(file_count, include_list, start_line, end_line, output_fol
# create
os.mkdir(output_folder)
if not os.path.isdir(output_folder):
- print("SCU: ERROR: %s could not be created." % output_folder)
+ print_error(f'SCU: "{output_folder}" could not be created.')
return
if _verbose:
print("SCU: Creating folder: %s" % output_folder)
@@ -104,7 +105,7 @@ def write_output_file(file_count, include_list, start_line, end_line, output_fol
def write_exception_output_file(file_count, exception_string, output_folder, output_filename_prefix, extension):
output_folder = os.path.abspath(output_folder)
if not os.path.isdir(output_folder):
- print("SCU: ERROR: %s does not exist." % output_folder)
+ print_error(f"SCU: {output_folder} does not exist.")
return
file_text = exception_string + "\n"
diff --git a/servers/physics_2d/godot_area_2d.h b/servers/physics_2d/godot_area_2d.h
index d14ddb6a2e..e6c3b45d6c 100644
--- a/servers/physics_2d/godot_area_2d.h
+++ b/servers/physics_2d/godot_area_2d.h
@@ -101,10 +101,10 @@ class GodotArea2D : public GodotCollisionObject2D {
public:
void set_monitor_callback(const Callable &p_callback);
- _FORCE_INLINE_ bool has_monitor_callback() const { return !monitor_callback.is_null(); }
+ _FORCE_INLINE_ bool has_monitor_callback() const { return monitor_callback.is_valid(); }
void set_area_monitor_callback(const Callable &p_callback);
- _FORCE_INLINE_ bool has_area_monitor_callback() const { return !area_monitor_callback.is_null(); }
+ _FORCE_INLINE_ bool has_area_monitor_callback() const { return area_monitor_callback.is_valid(); }
_FORCE_INLINE_ void add_body_to_query(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
_FORCE_INLINE_ void remove_body_from_query(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
diff --git a/servers/physics_3d/godot_area_3d.h b/servers/physics_3d/godot_area_3d.h
index c3c9e494a4..701dc73917 100644
--- a/servers/physics_3d/godot_area_3d.h
+++ b/servers/physics_3d/godot_area_3d.h
@@ -107,10 +107,10 @@ class GodotArea3D : public GodotCollisionObject3D {
public:
void set_monitor_callback(const Callable &p_callback);
- _FORCE_INLINE_ bool has_monitor_callback() const { return !monitor_callback.is_null(); }
+ _FORCE_INLINE_ bool has_monitor_callback() const { return monitor_callback.is_valid(); }
void set_area_monitor_callback(const Callable &p_callback);
- _FORCE_INLINE_ bool has_area_monitor_callback() const { return !area_monitor_callback.is_null(); }
+ _FORCE_INLINE_ bool has_area_monitor_callback() const { return area_monitor_callback.is_valid(); }
_FORCE_INLINE_ void add_body_to_query(GodotBody3D *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
_FORCE_INLINE_ void remove_body_from_query(GodotBody3D *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index 34f9069649..e48c72cec7 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -2106,7 +2106,7 @@ void RendererCanvasCull::update_visibility_notifiers() {
if (visibility_notifier->just_visible) {
visibility_notifier->just_visible = false;
- if (!visibility_notifier->enter_callable.is_null()) {
+ if (visibility_notifier->enter_callable.is_valid()) {
if (RSG::threaded) {
visibility_notifier->enter_callable.call_deferred();
} else {
@@ -2117,7 +2117,7 @@ void RendererCanvasCull::update_visibility_notifiers() {
if (visibility_notifier->visible_in_frame != RSG::rasterizer->get_frame_number()) {
visibility_notifier_list.remove(E);
- if (!visibility_notifier->exit_callable.is_null()) {
+ if (visibility_notifier->exit_callable.is_valid()) {
if (RSG::threaded) {
visibility_notifier->exit_callable.call_deferred();
} else {
diff --git a/servers/rendering/renderer_rd/shaders/particles.glsl b/servers/rendering/renderer_rd/shaders/particles.glsl
index 5fa4154727..efdf1c2278 100644
--- a/servers/rendering/renderer_rd/shaders/particles.glsl
+++ b/servers/rendering/renderer_rd/shaders/particles.glsl
@@ -612,14 +612,14 @@ void main() {
vec3 uvw_pos = vec3(local_pos_bottom / FRAME.colliders[i].extents) * 0.5 + 0.5;
- float y = 1.0 - texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz).r;
+ float y = texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz).r;
if (y > uvw_pos.y) {
//inside heightfield
vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
- vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
- vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * FRAME.colliders[i].extents;
+ vec3 pos2 = (vec3(uvw_pos.x + DELTA, texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
+ vec3 pos3 = (vec3(uvw_pos.x, texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * FRAME.colliders[i].extents;
normal = normalize(cross(pos1 - pos2, pos1 - pos3));
float local_y = (vec3(local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5).y;
diff --git a/servers/rendering/renderer_rd/storage_rd/utilities.cpp b/servers/rendering/renderer_rd/storage_rd/utilities.cpp
index 8b780a6f7b..8ff1d2bc46 100644
--- a/servers/rendering/renderer_rd/storage_rd/utilities.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/utilities.cpp
@@ -198,7 +198,7 @@ void Utilities::visibility_notifier_call(RID p_notifier, bool p_enter, bool p_de
ERR_FAIL_NULL(vn);
if (p_enter) {
- if (!vn->enter_callback.is_null()) {
+ if (vn->enter_callback.is_valid()) {
if (p_deferred) {
vn->enter_callback.call_deferred();
} else {
@@ -206,7 +206,7 @@ void Utilities::visibility_notifier_call(RID p_notifier, bool p_enter, bool p_de
}
}
} else {
- if (!vn->exit_callback.is_null()) {
+ if (vn->exit_callback.is_valid()) {
if (p_deferred) {
vn->exit_callback.call_deferred();
} else {
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index f94323f198..8de76c7dbc 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -105,10 +105,6 @@ public:
_changes_changed();
}
-#define DISPLAY_CHANGED \
- changes++; \
- _changes_changed();
-
#else
_FORCE_INLINE_ static void redraw_request() {
changes++;
diff --git a/tests/display_server_mock.h b/tests/display_server_mock.h
index 9cce95f9b5..fd79a46c5c 100644
--- a/tests/display_server_mock.h
+++ b/tests/display_server_mock.h
@@ -89,7 +89,7 @@ private:
}
void _send_window_event(WindowEvent p_event) {
- if (!event_callback.is_null()) {
+ if (event_callback.is_valid()) {
Variant event = int(p_event);
event_callback.call(event);
}
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 4c44a9b6f6..f2ddd5ebf9 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -318,7 +318,7 @@ Files extracted from upstream source:
## glad
- Upstream: https://github.com/Dav1dde/glad
-- Version: 2.0.6 (658f48e72aee3c6582e80b05ac0f8787a64fe6bb, 2024)
+- Version: 2.0.4 (d08b1aa01f8fe57498f04d47b5fa8c48725be877, 2023)
- License: CC0 1.0 and Apache 2.0
Files extracted from upstream source:
@@ -334,11 +334,11 @@ Files generated from [upstream web instance](https://gen.glad.sh/):
- `glx.c`
- `glad/glx.h`
-See the permalinks in `glad/egl.h`, `glad/gl.h`, and `glad/glx.h` to regenrate
-the files with a new version of the web instance.
+See the permalinks in `glad/gl.h` and `glad/glx.h` to regenrate the files with
+a new version of the web instance.
-Some changes have been made in order to allow loading OpenGL and OpenGLES APIs
-at the same time. See the patches in the `patches` directory.
+Some changes have been made in order to allow loading OpenGL and OpenGLES APIs at the same time.
+See the patches in the `patches` directory.
## glslang
@@ -428,6 +428,18 @@ Files extracted from upstream source:
- `jpge*.{c,h}`
+## libbacktrace
+
+- Upstream: https://github.com/ianlancetaylor/libbacktrace
+- Version: git (4d2dd0b172f2c9192f83ba93425f868f2a13c553, 2022)
+- License: BSD-3-Clause
+
+Files extracted from upstream source:
+
+- `*.{c,h}` files for Windows platform
+- `LICENSE`
+
+
## libktx
- Upstream: https://github.com/KhronosGroup/KTX-Software
diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
index a77cdad5f4..0de7c3720e 100644
--- a/thirdparty/clipper2/include/clipper2/clipper.core.h
+++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
@@ -138,7 +138,7 @@ namespace Clipper2Lib
}
template <typename T2>
- explicit Point<T>(const Point<T2>& p)
+ explicit Point(const Point<T2>& p)
{
Init(p.x, p.y, p.z);
}
@@ -180,7 +180,7 @@ namespace Clipper2Lib
Point(const T2 x_, const T2 y_) { Init(x_, y_); }
template <typename T2>
- explicit Point<T>(const Point<T2>& p) { Init(p.x, p.y); }
+ explicit Point(const Point<T2>& p) { Init(p.x, p.y); }
Point operator * (const double scale) const
{
diff --git a/thirdparty/clipper2/patches/gcc14-warning.patch b/thirdparty/clipper2/patches/gcc14-warning.patch
new file mode 100644
index 0000000000..a4f06ef37e
--- /dev/null
+++ b/thirdparty/clipper2/patches/gcc14-warning.patch
@@ -0,0 +1,22 @@
+diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
+index a77cdad5f4..0de7c3720e 100644
+--- a/thirdparty/clipper2/include/clipper2/clipper.core.h
++++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
+@@ -138,7 +138,7 @@ namespace Clipper2Lib
+ }
+
+ template <typename T2>
+- explicit Point<T>(const Point<T2>& p)
++ explicit Point(const Point<T2>& p)
+ {
+ Init(p.x, p.y, p.z);
+ }
+@@ -180,7 +180,7 @@ namespace Clipper2Lib
+ Point(const T2 x_, const T2 y_) { Init(x_, y_); }
+
+ template <typename T2>
+- explicit Point<T>(const Point<T2>& p) { Init(p.x, p.y); }
++ explicit Point(const Point<T2>& p) { Init(p.x, p.y); }
+
+ Point operator * (const double scale) const
+ {
diff --git a/thirdparty/glad/EGL/eglplatform.h b/thirdparty/glad/EGL/eglplatform.h
index 6786afd90b..99362a23de 100644
--- a/thirdparty/glad/EGL/eglplatform.h
+++ b/thirdparty/glad/EGL/eglplatform.h
@@ -64,12 +64,6 @@ typedef HDC EGLNativeDisplayType;
typedef HBITMAP EGLNativePixmapType;
typedef HWND EGLNativeWindowType;
-#elif defined(__QNX__)
-
-typedef khronos_uintptr_t EGLNativeDisplayType;
-typedef struct _screen_pixmap* EGLNativePixmapType; /* screen_pixmap_t */
-typedef struct _screen_window* EGLNativeWindowType; /* screen_window_t */
-
#elif defined(__EMSCRIPTEN__)
typedef int EGLNativeDisplayType;
diff --git a/thirdparty/glad/gl.c b/thirdparty/glad/gl.c
index 38ecb514bd..ee0cc188fc 100644
--- a/thirdparty/glad/gl.c
+++ b/thirdparty/glad/gl.c
@@ -453,7 +453,6 @@ PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui = NULL;
PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv = NULL;
PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui = NULL;
PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv = NULL;
-PFNGLNAMEDFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glad_glNamedFramebufferTextureMultiviewOVR = NULL;
PFNGLNEWLISTPROC glad_glNewList = NULL;
PFNGLNORMAL3BPROC glad_glNormal3b = NULL;
PFNGLNORMAL3BVPROC glad_glNormal3bv = NULL;
@@ -2109,29 +2108,40 @@ static void glad_gl_load_GL_EXT_framebuffer_object( GLADuserptrloadfunc load, vo
static void glad_gl_load_GL_OVR_multiview( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GL_OVR_multiview) return;
glad_glFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC) load(userptr, "glFramebufferTextureMultiviewOVR");
- glad_glNamedFramebufferTextureMultiviewOVR = (PFNGLNAMEDFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC) load(userptr, "glNamedFramebufferTextureMultiviewOVR");
}
-static void glad_gl_free_extensions(char **exts_i) {
- if (exts_i != NULL) {
- unsigned int index;
- for(index = 0; exts_i[index]; index++) {
- free((void *) (exts_i[index]));
- }
- free((void *)exts_i);
- exts_i = NULL;
- }
-}
-static int glad_gl_get_extensions( const char **out_exts, char ***out_exts_i) {
#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0)
- if (glad_glGetStringi != NULL && glad_glGetIntegerv != NULL) {
+#define GLAD_GL_IS_SOME_NEW_VERSION 1
+#else
+#define GLAD_GL_IS_SOME_NEW_VERSION 0
+#endif
+
+static int glad_gl_get_extensions( int version, const char **out_exts, unsigned int *out_num_exts_i, char ***out_exts_i) {
+#if GLAD_GL_IS_SOME_NEW_VERSION
+ if(GLAD_VERSION_MAJOR(version) < 3) {
+#else
+ GLAD_UNUSED(version);
+ GLAD_UNUSED(out_num_exts_i);
+ GLAD_UNUSED(out_exts_i);
+#endif
+ if (glad_glGetString == NULL) {
+ return 0;
+ }
+ *out_exts = (const char *)glad_glGetString(GL_EXTENSIONS);
+#if GLAD_GL_IS_SOME_NEW_VERSION
+ } else {
unsigned int index = 0;
unsigned int num_exts_i = 0;
char **exts_i = NULL;
+ if (glad_glGetStringi == NULL || glad_glGetIntegerv == NULL) {
+ return 0;
+ }
glad_glGetIntegerv(GL_NUM_EXTENSIONS, (int*) &num_exts_i);
- exts_i = (char **) malloc((num_exts_i + 1) * (sizeof *exts_i));
+ if (num_exts_i > 0) {
+ exts_i = (char **) malloc(num_exts_i * (sizeof *exts_i));
+ }
if (exts_i == NULL) {
return 0;
}
@@ -2140,40 +2150,31 @@ static int glad_gl_get_extensions( const char **out_exts, char ***out_exts_i) {
size_t len = strlen(gl_str_tmp) + 1;
char *local_str = (char*) malloc(len * sizeof(char));
- if(local_str == NULL) {
- exts_i[index] = NULL;
- glad_gl_free_extensions(exts_i);
- return 0;
+ if(local_str != NULL) {
+ memcpy(local_str, gl_str_tmp, len * sizeof(char));
}
- memcpy(local_str, gl_str_tmp, len * sizeof(char));
exts_i[index] = local_str;
}
- exts_i[index] = NULL;
+ *out_num_exts_i = num_exts_i;
*out_exts_i = exts_i;
-
- return 1;
}
-#else
- GLAD_UNUSED(out_exts_i);
#endif
- if (glad_glGetString == NULL) {
- return 0;
- }
- *out_exts = (const char *)glad_glGetString(GL_EXTENSIONS);
return 1;
}
-static int glad_gl_has_extension(const char *exts, char **exts_i, const char *ext) {
- if(exts_i) {
+static void glad_gl_free_extensions(char **exts_i, unsigned int num_exts_i) {
+ if (exts_i != NULL) {
unsigned int index;
- for(index = 0; exts_i[index]; index++) {
- const char *e = exts_i[index];
- if(strcmp(e, ext) == 0) {
- return 1;
- }
+ for(index = 0; index < num_exts_i; index++) {
+ free((void *) (exts_i[index]));
}
- } else {
+ free((void *)exts_i);
+ exts_i = NULL;
+ }
+}
+static int glad_gl_has_extension(int version, const char *exts, unsigned int num_exts_i, char **exts_i, const char *ext) {
+ if(GLAD_VERSION_MAJOR(version) < 3 || !GLAD_GL_IS_SOME_NEW_VERSION) {
const char *extensions;
const char *loc;
const char *terminator;
@@ -2193,6 +2194,14 @@ static int glad_gl_has_extension(const char *exts, char **exts_i, const char *ex
}
extensions = terminator;
}
+ } else {
+ unsigned int index;
+ for(index = 0; index < num_exts_i; index++) {
+ const char *e = exts_i[index];
+ if(strcmp(e, ext) == 0) {
+ return 1;
+ }
+ }
}
return 0;
}
@@ -2201,21 +2210,22 @@ static GLADapiproc glad_gl_get_proc_from_userptr(void *userptr, const char* name
return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name);
}
-static int glad_gl_find_extensions_gl(void) {
+static int glad_gl_find_extensions_gl( int version) {
const char *exts = NULL;
+ unsigned int num_exts_i = 0;
char **exts_i = NULL;
- if (!glad_gl_get_extensions(&exts, &exts_i)) return 0;
+ if (!glad_gl_get_extensions(version, &exts, &num_exts_i, &exts_i)) return 0;
- GLAD_GL_ARB_debug_output = glad_gl_has_extension(exts, exts_i, "GL_ARB_debug_output");
- GLAD_GL_ARB_framebuffer_object = glad_gl_has_extension(exts, exts_i, "GL_ARB_framebuffer_object");
- GLAD_GL_ARB_get_program_binary = glad_gl_has_extension(exts, exts_i, "GL_ARB_get_program_binary");
- GLAD_GL_EXT_framebuffer_blit = glad_gl_has_extension(exts, exts_i, "GL_EXT_framebuffer_blit");
- GLAD_GL_EXT_framebuffer_multisample = glad_gl_has_extension(exts, exts_i, "GL_EXT_framebuffer_multisample");
- GLAD_GL_EXT_framebuffer_object = glad_gl_has_extension(exts, exts_i, "GL_EXT_framebuffer_object");
- GLAD_GL_OVR_multiview = glad_gl_has_extension(exts, exts_i, "GL_OVR_multiview");
- GLAD_GL_OVR_multiview2 = glad_gl_has_extension(exts, exts_i, "GL_OVR_multiview2");
+ GLAD_GL_ARB_debug_output = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_debug_output");
+ GLAD_GL_ARB_framebuffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_framebuffer_object");
+ GLAD_GL_ARB_get_program_binary = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_get_program_binary");
+ GLAD_GL_EXT_framebuffer_blit = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_blit");
+ GLAD_GL_EXT_framebuffer_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_multisample");
+ GLAD_GL_EXT_framebuffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_object");
+ GLAD_GL_OVR_multiview = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OVR_multiview");
+ GLAD_GL_OVR_multiview2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OVR_multiview2");
- glad_gl_free_extensions(exts_i);
+ glad_gl_free_extensions(exts_i, num_exts_i);
return 1;
}
@@ -2265,6 +2275,7 @@ int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) {
glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString");
if(glad_glGetString == NULL) return 0;
+ if(glad_glGetString(GL_VERSION) == NULL) return 0;
version = glad_gl_find_core_gl();
glad_gl_load_GL_VERSION_1_0(load, userptr);
@@ -2280,7 +2291,7 @@ int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) {
glad_gl_load_GL_VERSION_3_2(load, userptr);
glad_gl_load_GL_VERSION_3_3(load, userptr);
- if (!glad_gl_find_extensions_gl()) return 0;
+ if (!glad_gl_find_extensions_gl(version)) return 0;
glad_gl_load_GL_ARB_debug_output(load, userptr);
glad_gl_load_GL_ARB_framebuffer_object(load, userptr);
glad_gl_load_GL_ARB_get_program_binary(load, userptr);
@@ -2299,15 +2310,16 @@ int gladLoadGL( GLADloadfunc load) {
return gladLoadGLUserPtr( glad_gl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load);
}
-static int glad_gl_find_extensions_gles2(void) {
+static int glad_gl_find_extensions_gles2( int version) {
const char *exts = NULL;
+ unsigned int num_exts_i = 0;
char **exts_i = NULL;
- if (!glad_gl_get_extensions(&exts, &exts_i)) return 0;
+ if (!glad_gl_get_extensions(version, &exts, &num_exts_i, &exts_i)) return 0;
- GLAD_GL_OVR_multiview = glad_gl_has_extension(exts, exts_i, "GL_OVR_multiview");
- GLAD_GL_OVR_multiview2 = glad_gl_has_extension(exts, exts_i, "GL_OVR_multiview2");
+ GLAD_GL_OVR_multiview = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OVR_multiview");
+ GLAD_GL_OVR_multiview2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OVR_multiview2");
- glad_gl_free_extensions(exts_i);
+ glad_gl_free_extensions(exts_i, num_exts_i);
return 1;
}
@@ -2349,6 +2361,7 @@ int gladLoadGLES2UserPtr( GLADuserptrloadfunc load, void *userptr) {
glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString");
if(glad_glGetString == NULL) return 0;
+ if(glad_glGetString(GL_VERSION) == NULL) return 0;
version = glad_gl_find_core_gles2();
glad_gl_load_GL_ES_VERSION_2_0(load, userptr);
@@ -2356,7 +2369,7 @@ int gladLoadGLES2UserPtr( GLADuserptrloadfunc load, void *userptr) {
glad_gl_load_GL_ES_VERSION_3_1(load, userptr);
glad_gl_load_GL_ES_VERSION_3_2(load, userptr);
- if (!glad_gl_find_extensions_gles2()) return 0;
+ if (!glad_gl_find_extensions_gles2(version)) return 0;
glad_gl_load_GL_OVR_multiview(load, userptr);
@@ -2614,9 +2627,10 @@ static GLADapiproc glad_dlsym_handle(void* handle, const char *name) {
typedef __eglMustCastToProperFunctionPointerType (GLAD_API_PTR *PFNEGLGETPROCADDRESSPROC)(const char *name);
#endif
extern __eglMustCastToProperFunctionPointerType emscripten_GetProcAddress(const char *name);
-#elif defined(GLAD_GLES2_USE_SYSTEM_EGL)
- #include <EGL/egl.h>
+#elif EGL_STATIC
+ typedef void (*__eglMustCastToProperFunctionPointerType)(void);
typedef __eglMustCastToProperFunctionPointerType (GLAD_API_PTR *PFNEGLGETPROCADDRESSPROC)(const char *name);
+ extern __eglMustCastToProperFunctionPointerType GLAD_API_PTR eglGetProcAddress(const char *name);
#else
#include <glad/egl.h>
#endif
@@ -2644,7 +2658,7 @@ static GLADapiproc glad_gles2_get_proc(void *vuserptr, const char* name) {
return result;
}
-static void* _glad_GLES2_loader_handle = NULL;
+static void* _glad_GL_loader_handle = NULL;
static void* glad_gles2_dlopen_handle(void) {
#if GLAD_PLATFORM_EMSCRIPTEN
@@ -2660,11 +2674,11 @@ static void* glad_gles2_dlopen_handle(void) {
GLAD_UNUSED(glad_get_dlopen_handle);
return NULL;
#else
- if (_glad_GLES2_loader_handle == NULL) {
- _glad_GLES2_loader_handle = glad_get_dlopen_handle(NAMES, sizeof(NAMES) / sizeof(NAMES[0]));
+ if (_glad_GL_loader_handle == NULL) {
+ _glad_GL_loader_handle = glad_get_dlopen_handle(NAMES, sizeof(NAMES) / sizeof(NAMES[0]));
}
- return _glad_GLES2_loader_handle;
+ return _glad_GL_loader_handle;
#endif
}
@@ -2694,12 +2708,11 @@ int gladLoaderLoadGLES2(void) {
userptr.get_proc_address_ptr = emscripten_GetProcAddress;
version = gladLoadGLES2UserPtr(glad_gles2_get_proc, &userptr);
#else
-#ifndef GLAD_GLES2_USE_SYSTEM_EGL
if (eglGetProcAddress == NULL) {
return 0;
}
-#endif
- did_load = _glad_GLES2_loader_handle == NULL;
+
+ did_load = _glad_GL_loader_handle == NULL;
handle = glad_gles2_dlopen_handle();
if (handle != NULL) {
userptr = glad_gles2_build_userptr(handle);
@@ -2718,9 +2731,9 @@ int gladLoaderLoadGLES2(void) {
void gladLoaderUnloadGLES2(void) {
- if (_glad_GLES2_loader_handle != NULL) {
- glad_close_dlopen_handle(_glad_GLES2_loader_handle);
- _glad_GLES2_loader_handle = NULL;
+ if (_glad_GL_loader_handle != NULL) {
+ glad_close_dlopen_handle(_glad_GL_loader_handle);
+ _glad_GL_loader_handle = NULL;
}
}
diff --git a/thirdparty/glad/glad/egl.h b/thirdparty/glad/glad/egl.h
index 053c5853a7..1bf35c1404 100644
--- a/thirdparty/glad/glad/egl.h
+++ b/thirdparty/glad/glad/egl.h
@@ -1,5 +1,5 @@
/**
- * Loader generated by glad 2.0.6 on Fri Apr 5 08:17:09 2024
+ * Loader generated by glad 2.0.3 on Fri Feb 3 07:06:48 2023
*
* SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0
*
@@ -141,7 +141,7 @@ extern "C" {
#define GLAD_VERSION_MAJOR(version) (version / 10000)
#define GLAD_VERSION_MINOR(version) (version % 10000)
-#define GLAD_GENERATOR_VERSION "2.0.6"
+#define GLAD_GENERATOR_VERSION "2.0.3"
typedef void (*GLADapiproc)(void);
diff --git a/thirdparty/glad/glad/gl.h b/thirdparty/glad/glad/gl.h
index 1301d10b65..307ea4dbb8 100644
--- a/thirdparty/glad/glad/gl.h
+++ b/thirdparty/glad/glad/gl.h
@@ -1,5 +1,5 @@
/**
- * Loader generated by glad 2.0.6 on Fri Apr 5 08:14:44 2024
+ * Loader generated by glad 2.0.4 on Mon May 22 13:18:29 2023
*
* SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0
*
@@ -178,7 +178,7 @@ extern "C" {
#define GLAD_VERSION_MAJOR(version) (version / 10000)
#define GLAD_VERSION_MINOR(version) (version % 10000)
-#define GLAD_GENERATOR_VERSION "2.0.6"
+#define GLAD_GENERATOR_VERSION "2.0.4"
typedef void (*GLADapiproc)(void);
@@ -2394,7 +2394,6 @@ typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP3UIPROC)(GLenum texture, GLenum t
typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP3UIVPROC)(GLenum texture, GLenum type, const GLuint * coords);
typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP4UIPROC)(GLenum texture, GLenum type, GLuint coords);
typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP4UIVPROC)(GLenum texture, GLenum type, const GLuint * coords);
-typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews);
typedef void (GLAD_API_PTR *PFNGLNEWLISTPROC)(GLuint list, GLenum mode);
typedef void (GLAD_API_PTR *PFNGLNORMAL3BPROC)(GLbyte nx, GLbyte ny, GLbyte nz);
typedef void (GLAD_API_PTR *PFNGLNORMAL3BVPROC)(const GLbyte * v);
@@ -3655,8 +3654,6 @@ GLAD_API_CALL PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui;
#define glMultiTexCoordP4ui glad_glMultiTexCoordP4ui
GLAD_API_CALL PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv;
#define glMultiTexCoordP4uiv glad_glMultiTexCoordP4uiv
-GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glad_glNamedFramebufferTextureMultiviewOVR;
-#define glNamedFramebufferTextureMultiviewOVR glad_glNamedFramebufferTextureMultiviewOVR
GLAD_API_CALL PFNGLNEWLISTPROC glad_glNewList;
#define glNewList glad_glNewList
GLAD_API_CALL PFNGLNORMAL3BPROC glad_glNormal3b;
diff --git a/thirdparty/glad/glad/glx.h b/thirdparty/glad/glad/glx.h
index a2fa0dadee..cf7663a3af 100644
--- a/thirdparty/glad/glad/glx.h
+++ b/thirdparty/glad/glad/glx.h
@@ -1,5 +1,5 @@
/**
- * Loader generated by glad 2.0.6 on Fri Apr 5 08:14:31 2024
+ * Loader generated by glad 2.0.4 on Mon May 22 13:18:29 2023
*
* SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0
*
@@ -152,7 +152,7 @@ extern "C" {
#define GLAD_VERSION_MAJOR(version) (version / 10000)
#define GLAD_VERSION_MINOR(version) (version % 10000)
-#define GLAD_GENERATOR_VERSION "2.0.6"
+#define GLAD_GENERATOR_VERSION "2.0.4"
typedef void (*GLADapiproc)(void);
diff --git a/thirdparty/glad/patches/patch_enable_both_gl_and_gles.diff b/thirdparty/glad/patches/patch_enable_both_gl_and_gles.diff
index 88c5510166..a98efe51d8 100644
--- a/thirdparty/glad/patches/patch_enable_both_gl_and_gles.diff
+++ b/thirdparty/glad/patches/patch_enable_both_gl_and_gles.diff
@@ -1,8 +1,8 @@
diff --git a/thirdparty/glad/gl.c b/thirdparty/glad/gl.c
-index 3f0884a3dc..38ecb514bd 100644
+index a0b59dbbfb..9f10f6544a 100644
--- a/thirdparty/glad/gl.c
+++ b/thirdparty/glad/gl.c
-@@ -2462,7 +2462,7 @@ static GLADapiproc glad_gl_get_proc(void *vuserptr, const char *name) {
+@@ -2475,7 +2475,7 @@ static GLADapiproc glad_gl_get_proc(void *vuserptr, const char *name) {
return result;
}
@@ -11,7 +11,7 @@ index 3f0884a3dc..38ecb514bd 100644
static void* glad_gl_dlopen_handle(void) {
#if GLAD_PLATFORM_APPLE
-@@ -2484,11 +2484,11 @@ static void* glad_gl_dlopen_handle(void) {
+@@ -2497,11 +2497,11 @@ static void* glad_gl_dlopen_handle(void) {
};
#endif
@@ -26,7 +26,7 @@ index 3f0884a3dc..38ecb514bd 100644
}
static struct _glad_gl_userptr glad_gl_build_userptr(void *handle) {
-@@ -2514,7 +2514,7 @@ int gladLoaderLoadGL(void) {
+@@ -2527,7 +2527,7 @@ int gladLoaderLoadGL(void) {
int did_load = 0;
struct _glad_gl_userptr userptr;
@@ -35,7 +35,7 @@ index 3f0884a3dc..38ecb514bd 100644
handle = glad_gl_dlopen_handle();
if (handle) {
userptr = glad_gl_build_userptr(handle);
-@@ -2532,9 +2532,9 @@ int gladLoaderLoadGL(void) {
+@@ -2545,9 +2545,9 @@ int gladLoaderLoadGL(void) {
void gladLoaderUnloadGL(void) {
@@ -49,7 +49,7 @@ index 3f0884a3dc..38ecb514bd 100644
}
diff --git a/thirdparty/glad/glad/gl.h b/thirdparty/glad/glad/gl.h
-index 77c6f33cab..1301d10b65 100644
+index 905c16aeed..f3cb7d8cb5 100644
--- a/thirdparty/glad/glad/gl.h
+++ b/thirdparty/glad/glad/gl.h
@@ -67,6 +67,7 @@
diff --git a/thirdparty/libbacktrace/LICENSE b/thirdparty/libbacktrace/LICENSE
new file mode 100644
index 0000000000..097d2774e5
--- /dev/null
+++ b/thirdparty/libbacktrace/LICENSE
@@ -0,0 +1,29 @@
+# Copyright (C) 2012-2016 Free Software Foundation, Inc.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+
+# (1) Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+
+# (2) Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+
+# (3) The name of the author may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
diff --git a/thirdparty/libbacktrace/alloc.c b/thirdparty/libbacktrace/alloc.c
new file mode 100644
index 0000000000..ff2c8677c0
--- /dev/null
+++ b/thirdparty/libbacktrace/alloc.c
@@ -0,0 +1,167 @@
+/* alloc.c -- Memory allocation without mmap.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* Allocation routines to use on systems that do not support anonymous
+ mmap. This implementation just uses malloc, which means that the
+ backtrace functions may not be safely invoked from a signal
+ handler. */
+
+/* Allocate memory like malloc. If ERROR_CALLBACK is NULL, don't
+ report an error. */
+
+void *
+backtrace_alloc (struct backtrace_state *state ATTRIBUTE_UNUSED,
+ size_t size, backtrace_error_callback error_callback,
+ void *data)
+{
+ void *ret;
+
+ ret = malloc (size);
+ if (ret == NULL)
+ {
+ if (error_callback)
+ error_callback (data, "malloc", errno);
+ }
+ return ret;
+}
+
+/* Free memory. */
+
+void
+backtrace_free (struct backtrace_state *state ATTRIBUTE_UNUSED,
+ void *p, size_t size ATTRIBUTE_UNUSED,
+ backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED)
+{
+ free (p);
+}
+
+/* Grow VEC by SIZE bytes. */
+
+void *
+backtrace_vector_grow (struct backtrace_state *state ATTRIBUTE_UNUSED,
+ size_t size, backtrace_error_callback error_callback,
+ void *data, struct backtrace_vector *vec)
+{
+ void *ret;
+
+ if (size > vec->alc)
+ {
+ size_t alc;
+ void *base;
+
+ if (vec->size == 0)
+ alc = 32 * size;
+ else if (vec->size >= 4096)
+ alc = vec->size + 4096;
+ else
+ alc = 2 * vec->size;
+
+ if (alc < vec->size + size)
+ alc = vec->size + size;
+
+ base = realloc (vec->base, alc);
+ if (base == NULL)
+ {
+ error_callback (data, "realloc", errno);
+ return NULL;
+ }
+
+ vec->base = base;
+ vec->alc = alc - vec->size;
+ }
+
+ ret = (char *) vec->base + vec->size;
+ vec->size += size;
+ vec->alc -= size;
+ return ret;
+}
+
+/* Finish the current allocation on VEC. */
+
+void *
+backtrace_vector_finish (struct backtrace_state *state,
+ struct backtrace_vector *vec,
+ backtrace_error_callback error_callback,
+ void *data)
+{
+ void *ret;
+
+ /* With this allocator we call realloc in backtrace_vector_grow,
+ which means we can't easily reuse the memory here. So just
+ release it. */
+ if (!backtrace_vector_release (state, vec, error_callback, data))
+ return NULL;
+ ret = vec->base;
+ vec->base = NULL;
+ vec->size = 0;
+ vec->alc = 0;
+ return ret;
+}
+
+/* Release any extra space allocated for VEC. */
+
+int
+backtrace_vector_release (struct backtrace_state *state ATTRIBUTE_UNUSED,
+ struct backtrace_vector *vec,
+ backtrace_error_callback error_callback,
+ void *data)
+{
+ vec->alc = 0;
+
+ if (vec->size == 0)
+ {
+ /* As of C17, realloc with size 0 is marked as an obsolescent feature, use
+ free instead. */
+ free (vec->base);
+ vec->base = NULL;
+ return 1;
+ }
+
+ vec->base = realloc (vec->base, vec->size);
+ if (vec->base == NULL)
+ {
+ error_callback (data, "realloc", errno);
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/thirdparty/libbacktrace/atomic.c b/thirdparty/libbacktrace/atomic.c
new file mode 100644
index 0000000000..fcac485b23
--- /dev/null
+++ b/thirdparty/libbacktrace/atomic.c
@@ -0,0 +1,113 @@
+/* atomic.c -- Support for atomic functions if not present.
+ Copyright (C) 2013-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "backtrace-supported.h"
+#include "internal.h"
+
+/* This file holds implementations of the atomic functions that are
+ used if the host compiler has the sync functions but not the atomic
+ functions, as is true of versions of GCC before 4.7. */
+
+#if !defined (HAVE_ATOMIC_FUNCTIONS) && defined (HAVE_SYNC_FUNCTIONS)
+
+/* Do an atomic load of a pointer. */
+
+void *
+backtrace_atomic_load_pointer (void *arg)
+{
+ void **pp;
+ void *p;
+
+ pp = (void **) arg;
+ p = *pp;
+ while (!__sync_bool_compare_and_swap (pp, p, p))
+ p = *pp;
+ return p;
+}
+
+/* Do an atomic load of an int. */
+
+int
+backtrace_atomic_load_int (int *p)
+{
+ int i;
+
+ i = *p;
+ while (!__sync_bool_compare_and_swap (p, i, i))
+ i = *p;
+ return i;
+}
+
+/* Do an atomic store of a pointer. */
+
+void
+backtrace_atomic_store_pointer (void *arg, void *p)
+{
+ void **pp;
+ void *old;
+
+ pp = (void **) arg;
+ old = *pp;
+ while (!__sync_bool_compare_and_swap (pp, old, p))
+ old = *pp;
+}
+
+/* Do an atomic store of a size_t value. */
+
+void
+backtrace_atomic_store_size_t (size_t *p, size_t v)
+{
+ size_t old;
+
+ old = *p;
+ while (!__sync_bool_compare_and_swap (p, old, v))
+ old = *p;
+}
+
+/* Do an atomic store of a int value. */
+
+void
+backtrace_atomic_store_int (int *p, int v)
+{
+ size_t old;
+
+ old = *p;
+ while (!__sync_bool_compare_and_swap (p, old, v))
+ old = *p;
+}
+
+#endif
diff --git a/thirdparty/libbacktrace/backtrace-supported.h b/thirdparty/libbacktrace/backtrace-supported.h
new file mode 100644
index 0000000000..f597195f13
--- /dev/null
+++ b/thirdparty/libbacktrace/backtrace-supported.h
@@ -0,0 +1,66 @@
+/* backtrace-supported.h.in -- Whether stack backtrace is supported.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+/* The file backtrace-supported.h.in is used by configure to generate
+ the file backtrace-supported.h. The file backtrace-supported.h may
+ be #include'd to see whether the backtrace library will be able to
+ get a backtrace and produce symbolic information. */
+
+
+/* BACKTRACE_SUPPORTED will be #define'd as 1 if the backtrace library
+ should work, 0 if it will not. Libraries may #include this to make
+ other arrangements. */
+
+#define BACKTRACE_SUPPORTED 1
+
+/* BACKTRACE_USES_MALLOC will be #define'd as 1 if the backtrace
+ library will call malloc as it works, 0 if it will call mmap
+ instead. This may be used to determine whether it is safe to call
+ the backtrace functions from a signal handler. In general this
+ only applies to calls like backtrace and backtrace_pcinfo. It does
+ not apply to backtrace_simple, which never calls malloc. It does
+ not apply to backtrace_print, which always calls fprintf and
+ therefore malloc. */
+
+#define BACKTRACE_USES_MALLOC 1
+
+/* BACKTRACE_SUPPORTS_THREADS will be #define'd as 1 if the backtrace
+ library is configured with threading support, 0 if not. If this is
+ 0, the threaded parameter to backtrace_create_state must be passed
+ as 0. */
+
+#define BACKTRACE_SUPPORTS_THREADS 1
+
+/* BACKTRACE_SUPPORTS_DATA will be #defined'd as 1 if the backtrace_syminfo
+ will work for variables. It will always work for functions. */
+
+#define BACKTRACE_SUPPORTS_DATA 0
diff --git a/thirdparty/libbacktrace/backtrace.c b/thirdparty/libbacktrace/backtrace.c
new file mode 100644
index 0000000000..7b62900852
--- /dev/null
+++ b/thirdparty/libbacktrace/backtrace.c
@@ -0,0 +1,129 @@
+/* backtrace.c -- Entry point for stack backtrace library.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include "unwind.h"
+#include "backtrace.h"
+#include "internal.h"
+
+/* The main backtrace_full routine. */
+
+/* Data passed through _Unwind_Backtrace. */
+
+struct backtrace_data
+{
+ /* Number of frames to skip. */
+ int skip;
+ /* Library state. */
+ struct backtrace_state *state;
+ /* Callback routine. */
+ backtrace_full_callback callback;
+ /* Error callback routine. */
+ backtrace_error_callback error_callback;
+ /* Data to pass to callback routines. */
+ void *data;
+ /* Value to return from backtrace_full. */
+ int ret;
+ /* Whether there is any memory available. */
+ int can_alloc;
+};
+
+/* Unwind library callback routine. This is passed to
+ _Unwind_Backtrace. */
+
+static _Unwind_Reason_Code
+unwind (struct _Unwind_Context *context, void *vdata)
+{
+ struct backtrace_data *bdata = (struct backtrace_data *) vdata;
+ uintptr_t pc;
+ int ip_before_insn = 0;
+
+#ifdef HAVE_GETIPINFO
+ pc = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
+ pc = _Unwind_GetIP (context);
+#endif
+
+ if (bdata->skip > 0)
+ {
+ --bdata->skip;
+ return _URC_NO_REASON;
+ }
+
+ if (!ip_before_insn)
+ --pc;
+
+ if (!bdata->can_alloc)
+ bdata->ret = bdata->callback (bdata->data, pc, NULL, 0, NULL);
+ else
+ bdata->ret = backtrace_pcinfo (bdata->state, pc, bdata->callback,
+ bdata->error_callback, bdata->data);
+ if (bdata->ret != 0)
+ return _URC_END_OF_STACK;
+
+ return _URC_NO_REASON;
+}
+
+/* Get a stack backtrace. */
+
+int __attribute__((noinline))
+backtrace_full (struct backtrace_state *state, int skip,
+ backtrace_full_callback callback,
+ backtrace_error_callback error_callback, void *data)
+{
+ struct backtrace_data bdata;
+ void *p;
+
+ bdata.skip = skip + 1;
+ bdata.state = state;
+ bdata.callback = callback;
+ bdata.error_callback = error_callback;
+ bdata.data = data;
+ bdata.ret = 0;
+
+ /* If we can't allocate any memory at all, don't try to produce
+ file/line information. */
+ p = backtrace_alloc (state, 4096, NULL, NULL);
+ if (p == NULL)
+ bdata.can_alloc = 0;
+ else
+ {
+ backtrace_free (state, p, 4096, NULL, NULL);
+ bdata.can_alloc = 1;
+ }
+
+ _Unwind_Backtrace (unwind, &bdata);
+ return bdata.ret;
+}
diff --git a/thirdparty/libbacktrace/backtrace.h b/thirdparty/libbacktrace/backtrace.h
new file mode 100644
index 0000000000..69cea4ca1e
--- /dev/null
+++ b/thirdparty/libbacktrace/backtrace.h
@@ -0,0 +1,189 @@
+/* backtrace.h -- Public header file for stack backtrace library.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#ifndef BACKTRACE_H
+#define BACKTRACE_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The backtrace state. This struct is intentionally not defined in
+ the public interface. */
+
+struct backtrace_state;
+
+/* The type of the error callback argument to backtrace functions.
+ This function, if not NULL, will be called for certain error cases.
+ The DATA argument is passed to the function that calls this one.
+ The MSG argument is an error message. The ERRNUM argument, if
+ greater than 0, holds an errno value. The MSG buffer may become
+ invalid after this function returns.
+
+ As a special case, the ERRNUM argument will be passed as -1 if no
+ debug info can be found for the executable, or if the debug info
+ exists but has an unsupported version, but the function requires
+ debug info (e.g., backtrace_full, backtrace_pcinfo). The MSG in
+ this case will be something along the lines of "no debug info".
+ Similarly, ERRNUM will be passed as -1 if there is no symbol table,
+ but the function requires a symbol table (e.g., backtrace_syminfo).
+ This may be used as a signal that some other approach should be
+ tried. */
+
+typedef void (*backtrace_error_callback) (void *data, const char *msg,
+ int errnum);
+
+/* Create state information for the backtrace routines. This must be
+ called before any of the other routines, and its return value must
+ be passed to all of the other routines. FILENAME is the path name
+ of the executable file; if it is NULL the library will try
+ system-specific path names. If not NULL, FILENAME must point to a
+ permanent buffer. If THREADED is non-zero the state may be
+ accessed by multiple threads simultaneously, and the library will
+ use appropriate atomic operations. If THREADED is zero the state
+ may only be accessed by one thread at a time. This returns a state
+ pointer on success, NULL on error. If an error occurs, this will
+ call the ERROR_CALLBACK routine.
+
+ Calling this function allocates resources that cannot be freed.
+ There is no backtrace_free_state function. The state is used to
+ cache information that is expensive to recompute. Programs are
+ expected to call this function at most once and to save the return
+ value for all later calls to backtrace functions. */
+
+extern struct backtrace_state *backtrace_create_state (
+ const char *filename, int threaded,
+ backtrace_error_callback error_callback, void *data);
+
+/* The type of the callback argument to the backtrace_full function.
+ DATA is the argument passed to backtrace_full. PC is the program
+ counter. FILENAME is the name of the file containing PC, or NULL
+ if not available. LINENO is the line number in FILENAME containing
+ PC, or 0 if not available. FUNCTION is the name of the function
+ containing PC, or NULL if not available. This should return 0 to
+ continuing tracing. The FILENAME and FUNCTION buffers may become
+ invalid after this function returns. */
+
+typedef int (*backtrace_full_callback) (void *data, uintptr_t pc,
+ const char *filename, int lineno,
+ const char *function);
+
+/* Get a full stack backtrace. SKIP is the number of frames to skip;
+ passing 0 will start the trace with the function calling
+ backtrace_full. DATA is passed to the callback routine. If any
+ call to CALLBACK returns a non-zero value, the stack backtrace
+ stops, and backtrace returns that value; this may be used to limit
+ the number of stack frames desired. If all calls to CALLBACK
+ return 0, backtrace returns 0. The backtrace_full function will
+ make at least one call to either CALLBACK or ERROR_CALLBACK. This
+ function requires debug info for the executable. */
+
+extern int backtrace_full (struct backtrace_state *state, int skip,
+ backtrace_full_callback callback,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* The type of the callback argument to the backtrace_simple function.
+ DATA is the argument passed to simple_backtrace. PC is the program
+ counter. This should return 0 to continue tracing. */
+
+typedef int (*backtrace_simple_callback) (void *data, uintptr_t pc);
+
+/* Get a simple backtrace. SKIP is the number of frames to skip, as
+ in backtrace. DATA is passed to the callback routine. If any call
+ to CALLBACK returns a non-zero value, the stack backtrace stops,
+ and backtrace_simple returns that value. Otherwise
+ backtrace_simple returns 0. The backtrace_simple function will
+ make at least one call to either CALLBACK or ERROR_CALLBACK. This
+ function does not require any debug info for the executable. */
+
+extern int backtrace_simple (struct backtrace_state *state, int skip,
+ backtrace_simple_callback callback,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* Print the current backtrace in a user readable format to a FILE.
+ SKIP is the number of frames to skip, as in backtrace_full. Any
+ error messages are printed to stderr. This function requires debug
+ info for the executable. */
+
+extern void backtrace_print (struct backtrace_state *state, int skip, FILE *);
+
+/* Given PC, a program counter in the current program, call the
+ callback function with filename, line number, and function name
+ information. This will normally call the callback function exactly
+ once. However, if the PC happens to describe an inlined call, and
+ the debugging information contains the necessary information, then
+ this may call the callback function multiple times. This will make
+ at least one call to either CALLBACK or ERROR_CALLBACK. This
+ returns the first non-zero value returned by CALLBACK, or 0. */
+
+extern int backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc,
+ backtrace_full_callback callback,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* The type of the callback argument to backtrace_syminfo. DATA and
+ PC are the arguments passed to backtrace_syminfo. SYMNAME is the
+ name of the symbol for the corresponding code. SYMVAL is the
+ value and SYMSIZE is the size of the symbol. SYMNAME will be NULL
+ if no error occurred but the symbol could not be found. */
+
+typedef void (*backtrace_syminfo_callback) (void *data, uintptr_t pc,
+ const char *symname,
+ uintptr_t symval,
+ uintptr_t symsize);
+
+/* Given ADDR, an address or program counter in the current program,
+ call the callback information with the symbol name and value
+ describing the function or variable in which ADDR may be found.
+ This will call either CALLBACK or ERROR_CALLBACK exactly once.
+ This returns 1 on success, 0 on failure. This function requires
+ the symbol table but does not require the debug info. Note that if
+ the symbol table is present but ADDR could not be found in the
+ table, CALLBACK will be called with a NULL SYMNAME argument.
+ Returns 1 on success, 0 on error. */
+
+extern int backtrace_syminfo (struct backtrace_state *state, uintptr_t addr,
+ backtrace_syminfo_callback callback,
+ backtrace_error_callback error_callback,
+ void *data);
+
+#ifdef __cplusplus
+} /* End extern "C". */
+#endif
+
+#endif
diff --git a/thirdparty/libbacktrace/config.h b/thirdparty/libbacktrace/config.h
new file mode 100644
index 0000000000..0c745c191b
--- /dev/null
+++ b/thirdparty/libbacktrace/config.h
@@ -0,0 +1,170 @@
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* ELF size: 32 or 64 */
+#define BACKTRACE_ELF_SIZE unused
+
+/* XCOFF size: 32 or 64 */
+#define BACKTRACE_XCOFF_SIZE unused
+
+/* Define to 1 if you have the __atomic functions */
+#define HAVE_ATOMIC_FUNCTIONS 1
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#define HAVE_CLOCK_GETTIME 1
+
+/* Define to 1 if you have the declaration of `getpagesize', and to 0 if you
+ don't. */
+#define HAVE_DECL_GETPAGESIZE 0
+
+/* Define to 1 if you have the declaration of `strnlen', and to 0 if you
+ don't. */
+#define HAVE_DECL_STRNLEN 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+/* #undef HAVE_DLFCN_H */
+
+/* Define if dl_iterate_phdr is available. */
+/* #undef HAVE_DL_ITERATE_PHDR */
+
+/* Define to 1 if you have the fcntl function */
+/* #undef HAVE_FCNTL */
+
+/* Define if getexecname is available. */
+/* #undef HAVE_GETEXECNAME */
+
+/* Define if _Unwind_GetIPInfo is available. */
+#define HAVE_GETIPINFO 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have KERN_PROC and KERN_PROC_PATHNAME in <sys/sysctl.h>.
+ */
+/* #undef HAVE_KERN_PROC */
+
+/* Define to 1 if you have KERN_PROCARGS and KERN_PROC_PATHNAME in
+ <sys/sysctl.h>. */
+/* #undef HAVE_KERN_PROC_ARGS */
+
+/* Define if -llzma is available. */
+#define HAVE_LIBLZMA 1
+
+/* Define to 1 if you have the <link.h> header file. */
+/* #undef HAVE_LINK_H */
+
+/* Define if AIX loadquery is available. */
+/* #undef HAVE_LOADQUERY */
+
+/* Define to 1 if you have the `lstat' function. */
+/* #undef HAVE_LSTAT */
+
+/* Define to 1 if you have the <mach-o/dyld.h> header file. */
+/* #undef HAVE_MACH_O_DYLD_H */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `readlink' function. */
+/* #undef HAVE_READLINK */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the __sync functions */
+#define HAVE_SYNC_FUNCTIONS 1
+
+/* Define to 1 if you have the <sys/ldr.h> header file. */
+/* #undef HAVE_SYS_LDR_H */
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+/* #undef HAVE_SYS_MMAN_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define if -lz is available. */
+#define HAVE_ZLIB 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+#define LT_OBJDIR ".libs/"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "package-unused"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "package-unused version-unused"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libbacktrace"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "version-unused"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+
+
+/* Enable large inode numbers on Mac OS X 10.5. */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#define _FILE_OFFSET_BITS 64
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
diff --git a/thirdparty/libbacktrace/dwarf.c b/thirdparty/libbacktrace/dwarf.c
new file mode 100644
index 0000000000..5b2724e6a7
--- /dev/null
+++ b/thirdparty/libbacktrace/dwarf.c
@@ -0,0 +1,4402 @@
+/* dwarf.c -- Get file/line information from DWARF for backtraces.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "filenames.h"
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* DWARF constants. */
+
+enum dwarf_tag {
+ DW_TAG_entry_point = 0x3,
+ DW_TAG_compile_unit = 0x11,
+ DW_TAG_inlined_subroutine = 0x1d,
+ DW_TAG_subprogram = 0x2e,
+ DW_TAG_skeleton_unit = 0x4a,
+};
+
+enum dwarf_form {
+ DW_FORM_addr = 0x01,
+ DW_FORM_block2 = 0x03,
+ DW_FORM_block4 = 0x04,
+ DW_FORM_data2 = 0x05,
+ DW_FORM_data4 = 0x06,
+ DW_FORM_data8 = 0x07,
+ DW_FORM_string = 0x08,
+ DW_FORM_block = 0x09,
+ DW_FORM_block1 = 0x0a,
+ DW_FORM_data1 = 0x0b,
+ DW_FORM_flag = 0x0c,
+ DW_FORM_sdata = 0x0d,
+ DW_FORM_strp = 0x0e,
+ DW_FORM_udata = 0x0f,
+ DW_FORM_ref_addr = 0x10,
+ DW_FORM_ref1 = 0x11,
+ DW_FORM_ref2 = 0x12,
+ DW_FORM_ref4 = 0x13,
+ DW_FORM_ref8 = 0x14,
+ DW_FORM_ref_udata = 0x15,
+ DW_FORM_indirect = 0x16,
+ DW_FORM_sec_offset = 0x17,
+ DW_FORM_exprloc = 0x18,
+ DW_FORM_flag_present = 0x19,
+ DW_FORM_ref_sig8 = 0x20,
+ DW_FORM_strx = 0x1a,
+ DW_FORM_addrx = 0x1b,
+ DW_FORM_ref_sup4 = 0x1c,
+ DW_FORM_strp_sup = 0x1d,
+ DW_FORM_data16 = 0x1e,
+ DW_FORM_line_strp = 0x1f,
+ DW_FORM_implicit_const = 0x21,
+ DW_FORM_loclistx = 0x22,
+ DW_FORM_rnglistx = 0x23,
+ DW_FORM_ref_sup8 = 0x24,
+ DW_FORM_strx1 = 0x25,
+ DW_FORM_strx2 = 0x26,
+ DW_FORM_strx3 = 0x27,
+ DW_FORM_strx4 = 0x28,
+ DW_FORM_addrx1 = 0x29,
+ DW_FORM_addrx2 = 0x2a,
+ DW_FORM_addrx3 = 0x2b,
+ DW_FORM_addrx4 = 0x2c,
+ DW_FORM_GNU_addr_index = 0x1f01,
+ DW_FORM_GNU_str_index = 0x1f02,
+ DW_FORM_GNU_ref_alt = 0x1f20,
+ DW_FORM_GNU_strp_alt = 0x1f21
+};
+
+enum dwarf_attribute {
+ DW_AT_sibling = 0x01,
+ DW_AT_location = 0x02,
+ DW_AT_name = 0x03,
+ DW_AT_ordering = 0x09,
+ DW_AT_subscr_data = 0x0a,
+ DW_AT_byte_size = 0x0b,
+ DW_AT_bit_offset = 0x0c,
+ DW_AT_bit_size = 0x0d,
+ DW_AT_element_list = 0x0f,
+ DW_AT_stmt_list = 0x10,
+ DW_AT_low_pc = 0x11,
+ DW_AT_high_pc = 0x12,
+ DW_AT_language = 0x13,
+ DW_AT_member = 0x14,
+ DW_AT_discr = 0x15,
+ DW_AT_discr_value = 0x16,
+ DW_AT_visibility = 0x17,
+ DW_AT_import = 0x18,
+ DW_AT_string_length = 0x19,
+ DW_AT_common_reference = 0x1a,
+ DW_AT_comp_dir = 0x1b,
+ DW_AT_const_value = 0x1c,
+ DW_AT_containing_type = 0x1d,
+ DW_AT_default_value = 0x1e,
+ DW_AT_inline = 0x20,
+ DW_AT_is_optional = 0x21,
+ DW_AT_lower_bound = 0x22,
+ DW_AT_producer = 0x25,
+ DW_AT_prototyped = 0x27,
+ DW_AT_return_addr = 0x2a,
+ DW_AT_start_scope = 0x2c,
+ DW_AT_bit_stride = 0x2e,
+ DW_AT_upper_bound = 0x2f,
+ DW_AT_abstract_origin = 0x31,
+ DW_AT_accessibility = 0x32,
+ DW_AT_address_class = 0x33,
+ DW_AT_artificial = 0x34,
+ DW_AT_base_types = 0x35,
+ DW_AT_calling_convention = 0x36,
+ DW_AT_count = 0x37,
+ DW_AT_data_member_location = 0x38,
+ DW_AT_decl_column = 0x39,
+ DW_AT_decl_file = 0x3a,
+ DW_AT_decl_line = 0x3b,
+ DW_AT_declaration = 0x3c,
+ DW_AT_discr_list = 0x3d,
+ DW_AT_encoding = 0x3e,
+ DW_AT_external = 0x3f,
+ DW_AT_frame_base = 0x40,
+ DW_AT_friend = 0x41,
+ DW_AT_identifier_case = 0x42,
+ DW_AT_macro_info = 0x43,
+ DW_AT_namelist_items = 0x44,
+ DW_AT_priority = 0x45,
+ DW_AT_segment = 0x46,
+ DW_AT_specification = 0x47,
+ DW_AT_static_link = 0x48,
+ DW_AT_type = 0x49,
+ DW_AT_use_location = 0x4a,
+ DW_AT_variable_parameter = 0x4b,
+ DW_AT_virtuality = 0x4c,
+ DW_AT_vtable_elem_location = 0x4d,
+ DW_AT_allocated = 0x4e,
+ DW_AT_associated = 0x4f,
+ DW_AT_data_location = 0x50,
+ DW_AT_byte_stride = 0x51,
+ DW_AT_entry_pc = 0x52,
+ DW_AT_use_UTF8 = 0x53,
+ DW_AT_extension = 0x54,
+ DW_AT_ranges = 0x55,
+ DW_AT_trampoline = 0x56,
+ DW_AT_call_column = 0x57,
+ DW_AT_call_file = 0x58,
+ DW_AT_call_line = 0x59,
+ DW_AT_description = 0x5a,
+ DW_AT_binary_scale = 0x5b,
+ DW_AT_decimal_scale = 0x5c,
+ DW_AT_small = 0x5d,
+ DW_AT_decimal_sign = 0x5e,
+ DW_AT_digit_count = 0x5f,
+ DW_AT_picture_string = 0x60,
+ DW_AT_mutable = 0x61,
+ DW_AT_threads_scaled = 0x62,
+ DW_AT_explicit = 0x63,
+ DW_AT_object_pointer = 0x64,
+ DW_AT_endianity = 0x65,
+ DW_AT_elemental = 0x66,
+ DW_AT_pure = 0x67,
+ DW_AT_recursive = 0x68,
+ DW_AT_signature = 0x69,
+ DW_AT_main_subprogram = 0x6a,
+ DW_AT_data_bit_offset = 0x6b,
+ DW_AT_const_expr = 0x6c,
+ DW_AT_enum_class = 0x6d,
+ DW_AT_linkage_name = 0x6e,
+ DW_AT_string_length_bit_size = 0x6f,
+ DW_AT_string_length_byte_size = 0x70,
+ DW_AT_rank = 0x71,
+ DW_AT_str_offsets_base = 0x72,
+ DW_AT_addr_base = 0x73,
+ DW_AT_rnglists_base = 0x74,
+ DW_AT_dwo_name = 0x76,
+ DW_AT_reference = 0x77,
+ DW_AT_rvalue_reference = 0x78,
+ DW_AT_macros = 0x79,
+ DW_AT_call_all_calls = 0x7a,
+ DW_AT_call_all_source_calls = 0x7b,
+ DW_AT_call_all_tail_calls = 0x7c,
+ DW_AT_call_return_pc = 0x7d,
+ DW_AT_call_value = 0x7e,
+ DW_AT_call_origin = 0x7f,
+ DW_AT_call_parameter = 0x80,
+ DW_AT_call_pc = 0x81,
+ DW_AT_call_tail_call = 0x82,
+ DW_AT_call_target = 0x83,
+ DW_AT_call_target_clobbered = 0x84,
+ DW_AT_call_data_location = 0x85,
+ DW_AT_call_data_value = 0x86,
+ DW_AT_noreturn = 0x87,
+ DW_AT_alignment = 0x88,
+ DW_AT_export_symbols = 0x89,
+ DW_AT_deleted = 0x8a,
+ DW_AT_defaulted = 0x8b,
+ DW_AT_loclists_base = 0x8c,
+ DW_AT_lo_user = 0x2000,
+ DW_AT_hi_user = 0x3fff,
+ DW_AT_MIPS_fde = 0x2001,
+ DW_AT_MIPS_loop_begin = 0x2002,
+ DW_AT_MIPS_tail_loop_begin = 0x2003,
+ DW_AT_MIPS_epilog_begin = 0x2004,
+ DW_AT_MIPS_loop_unroll_factor = 0x2005,
+ DW_AT_MIPS_software_pipeline_depth = 0x2006,
+ DW_AT_MIPS_linkage_name = 0x2007,
+ DW_AT_MIPS_stride = 0x2008,
+ DW_AT_MIPS_abstract_name = 0x2009,
+ DW_AT_MIPS_clone_origin = 0x200a,
+ DW_AT_MIPS_has_inlines = 0x200b,
+ DW_AT_HP_block_index = 0x2000,
+ DW_AT_HP_unmodifiable = 0x2001,
+ DW_AT_HP_prologue = 0x2005,
+ DW_AT_HP_epilogue = 0x2008,
+ DW_AT_HP_actuals_stmt_list = 0x2010,
+ DW_AT_HP_proc_per_section = 0x2011,
+ DW_AT_HP_raw_data_ptr = 0x2012,
+ DW_AT_HP_pass_by_reference = 0x2013,
+ DW_AT_HP_opt_level = 0x2014,
+ DW_AT_HP_prof_version_id = 0x2015,
+ DW_AT_HP_opt_flags = 0x2016,
+ DW_AT_HP_cold_region_low_pc = 0x2017,
+ DW_AT_HP_cold_region_high_pc = 0x2018,
+ DW_AT_HP_all_variables_modifiable = 0x2019,
+ DW_AT_HP_linkage_name = 0x201a,
+ DW_AT_HP_prof_flags = 0x201b,
+ DW_AT_HP_unit_name = 0x201f,
+ DW_AT_HP_unit_size = 0x2020,
+ DW_AT_HP_widened_byte_size = 0x2021,
+ DW_AT_HP_definition_points = 0x2022,
+ DW_AT_HP_default_location = 0x2023,
+ DW_AT_HP_is_result_param = 0x2029,
+ DW_AT_sf_names = 0x2101,
+ DW_AT_src_info = 0x2102,
+ DW_AT_mac_info = 0x2103,
+ DW_AT_src_coords = 0x2104,
+ DW_AT_body_begin = 0x2105,
+ DW_AT_body_end = 0x2106,
+ DW_AT_GNU_vector = 0x2107,
+ DW_AT_GNU_guarded_by = 0x2108,
+ DW_AT_GNU_pt_guarded_by = 0x2109,
+ DW_AT_GNU_guarded = 0x210a,
+ DW_AT_GNU_pt_guarded = 0x210b,
+ DW_AT_GNU_locks_excluded = 0x210c,
+ DW_AT_GNU_exclusive_locks_required = 0x210d,
+ DW_AT_GNU_shared_locks_required = 0x210e,
+ DW_AT_GNU_odr_signature = 0x210f,
+ DW_AT_GNU_template_name = 0x2110,
+ DW_AT_GNU_call_site_value = 0x2111,
+ DW_AT_GNU_call_site_data_value = 0x2112,
+ DW_AT_GNU_call_site_target = 0x2113,
+ DW_AT_GNU_call_site_target_clobbered = 0x2114,
+ DW_AT_GNU_tail_call = 0x2115,
+ DW_AT_GNU_all_tail_call_sites = 0x2116,
+ DW_AT_GNU_all_call_sites = 0x2117,
+ DW_AT_GNU_all_source_call_sites = 0x2118,
+ DW_AT_GNU_macros = 0x2119,
+ DW_AT_GNU_deleted = 0x211a,
+ DW_AT_GNU_dwo_name = 0x2130,
+ DW_AT_GNU_dwo_id = 0x2131,
+ DW_AT_GNU_ranges_base = 0x2132,
+ DW_AT_GNU_addr_base = 0x2133,
+ DW_AT_GNU_pubnames = 0x2134,
+ DW_AT_GNU_pubtypes = 0x2135,
+ DW_AT_GNU_discriminator = 0x2136,
+ DW_AT_GNU_locviews = 0x2137,
+ DW_AT_GNU_entry_view = 0x2138,
+ DW_AT_VMS_rtnbeg_pd_address = 0x2201,
+ DW_AT_use_GNAT_descriptive_type = 0x2301,
+ DW_AT_GNAT_descriptive_type = 0x2302,
+ DW_AT_GNU_numerator = 0x2303,
+ DW_AT_GNU_denominator = 0x2304,
+ DW_AT_GNU_bias = 0x2305,
+ DW_AT_upc_threads_scaled = 0x3210,
+ DW_AT_PGI_lbase = 0x3a00,
+ DW_AT_PGI_soffset = 0x3a01,
+ DW_AT_PGI_lstride = 0x3a02,
+ DW_AT_APPLE_optimized = 0x3fe1,
+ DW_AT_APPLE_flags = 0x3fe2,
+ DW_AT_APPLE_isa = 0x3fe3,
+ DW_AT_APPLE_block = 0x3fe4,
+ DW_AT_APPLE_major_runtime_vers = 0x3fe5,
+ DW_AT_APPLE_runtime_class = 0x3fe6,
+ DW_AT_APPLE_omit_frame_ptr = 0x3fe7,
+ DW_AT_APPLE_property_name = 0x3fe8,
+ DW_AT_APPLE_property_getter = 0x3fe9,
+ DW_AT_APPLE_property_setter = 0x3fea,
+ DW_AT_APPLE_property_attribute = 0x3feb,
+ DW_AT_APPLE_objc_complete_type = 0x3fec,
+ DW_AT_APPLE_property = 0x3fed
+};
+
+enum dwarf_line_number_op {
+ DW_LNS_extended_op = 0x0,
+ DW_LNS_copy = 0x1,
+ DW_LNS_advance_pc = 0x2,
+ DW_LNS_advance_line = 0x3,
+ DW_LNS_set_file = 0x4,
+ DW_LNS_set_column = 0x5,
+ DW_LNS_negate_stmt = 0x6,
+ DW_LNS_set_basic_block = 0x7,
+ DW_LNS_const_add_pc = 0x8,
+ DW_LNS_fixed_advance_pc = 0x9,
+ DW_LNS_set_prologue_end = 0xa,
+ DW_LNS_set_epilogue_begin = 0xb,
+ DW_LNS_set_isa = 0xc,
+};
+
+enum dwarf_extended_line_number_op {
+ DW_LNE_end_sequence = 0x1,
+ DW_LNE_set_address = 0x2,
+ DW_LNE_define_file = 0x3,
+ DW_LNE_set_discriminator = 0x4,
+};
+
+enum dwarf_line_number_content_type {
+ DW_LNCT_path = 0x1,
+ DW_LNCT_directory_index = 0x2,
+ DW_LNCT_timestamp = 0x3,
+ DW_LNCT_size = 0x4,
+ DW_LNCT_MD5 = 0x5,
+ DW_LNCT_lo_user = 0x2000,
+ DW_LNCT_hi_user = 0x3fff
+};
+
+enum dwarf_range_list_entry {
+ DW_RLE_end_of_list = 0x00,
+ DW_RLE_base_addressx = 0x01,
+ DW_RLE_startx_endx = 0x02,
+ DW_RLE_startx_length = 0x03,
+ DW_RLE_offset_pair = 0x04,
+ DW_RLE_base_address = 0x05,
+ DW_RLE_start_end = 0x06,
+ DW_RLE_start_length = 0x07
+};
+
+enum dwarf_unit_type {
+ DW_UT_compile = 0x01,
+ DW_UT_type = 0x02,
+ DW_UT_partial = 0x03,
+ DW_UT_skeleton = 0x04,
+ DW_UT_split_compile = 0x05,
+ DW_UT_split_type = 0x06,
+ DW_UT_lo_user = 0x80,
+ DW_UT_hi_user = 0xff
+};
+
+#if !defined(HAVE_DECL_STRNLEN) || !HAVE_DECL_STRNLEN
+
+/* If strnlen is not declared, provide our own version. */
+
+static size_t
+xstrnlen (const char *s, size_t maxlen)
+{
+ size_t i;
+
+ for (i = 0; i < maxlen; ++i)
+ if (s[i] == '\0')
+ break;
+ return i;
+}
+
+#define strnlen xstrnlen
+
+#endif
+
+/* A buffer to read DWARF info. */
+
+struct dwarf_buf
+{
+ /* Buffer name for error messages. */
+ const char *name;
+ /* Start of the buffer. */
+ const unsigned char *start;
+ /* Next byte to read. */
+ const unsigned char *buf;
+ /* The number of bytes remaining. */
+ size_t left;
+ /* Whether the data is big-endian. */
+ int is_bigendian;
+ /* Error callback routine. */
+ backtrace_error_callback error_callback;
+ /* Data for error_callback. */
+ void *data;
+ /* Non-zero if we've reported an underflow error. */
+ int reported_underflow;
+};
+
+/* A single attribute in a DWARF abbreviation. */
+
+struct attr
+{
+ /* The attribute name. */
+ enum dwarf_attribute name;
+ /* The attribute form. */
+ enum dwarf_form form;
+ /* The attribute value, for DW_FORM_implicit_const. */
+ int64_t val;
+};
+
+/* A single DWARF abbreviation. */
+
+struct abbrev
+{
+ /* The abbrev code--the number used to refer to the abbrev. */
+ uint64_t code;
+ /* The entry tag. */
+ enum dwarf_tag tag;
+ /* Non-zero if this abbrev has child entries. */
+ int has_children;
+ /* The number of attributes. */
+ size_t num_attrs;
+ /* The attributes. */
+ struct attr *attrs;
+};
+
+/* The DWARF abbreviations for a compilation unit. This structure
+ only exists while reading the compilation unit. Most DWARF readers
+ seem to a hash table to map abbrev ID's to abbrev entries.
+ However, we primarily care about GCC, and GCC simply issues ID's in
+ numerical order starting at 1. So we simply keep a sorted vector,
+ and try to just look up the code. */
+
+struct abbrevs
+{
+ /* The number of abbrevs in the vector. */
+ size_t num_abbrevs;
+ /* The abbrevs, sorted by the code field. */
+ struct abbrev *abbrevs;
+};
+
+/* The different kinds of attribute values. */
+
+enum attr_val_encoding
+{
+ /* No attribute value. */
+ ATTR_VAL_NONE,
+ /* An address. */
+ ATTR_VAL_ADDRESS,
+ /* An index into the .debug_addr section, whose value is relative to
+ * the DW_AT_addr_base attribute of the compilation unit. */
+ ATTR_VAL_ADDRESS_INDEX,
+ /* A unsigned integer. */
+ ATTR_VAL_UINT,
+ /* A sigd integer. */
+ ATTR_VAL_SINT,
+ /* A string. */
+ ATTR_VAL_STRING,
+ /* An index into the .debug_str_offsets section. */
+ ATTR_VAL_STRING_INDEX,
+ /* An offset to other data in the containing unit. */
+ ATTR_VAL_REF_UNIT,
+ /* An offset to other data within the .debug_info section. */
+ ATTR_VAL_REF_INFO,
+ /* An offset to other data within the alt .debug_info section. */
+ ATTR_VAL_REF_ALT_INFO,
+ /* An offset to data in some other section. */
+ ATTR_VAL_REF_SECTION,
+ /* A type signature. */
+ ATTR_VAL_REF_TYPE,
+ /* An index into the .debug_rnglists section. */
+ ATTR_VAL_RNGLISTS_INDEX,
+ /* A block of data (not represented). */
+ ATTR_VAL_BLOCK,
+ /* An expression (not represented). */
+ ATTR_VAL_EXPR,
+};
+
+/* An attribute value. */
+
+struct attr_val
+{
+ /* How the value is stored in the field u. */
+ enum attr_val_encoding encoding;
+ union
+ {
+ /* ATTR_VAL_ADDRESS*, ATTR_VAL_UINT, ATTR_VAL_REF*. */
+ uint64_t uint;
+ /* ATTR_VAL_SINT. */
+ int64_t sint;
+ /* ATTR_VAL_STRING. */
+ const char *string;
+ /* ATTR_VAL_BLOCK not stored. */
+ } u;
+};
+
+/* The line number program header. */
+
+struct line_header
+{
+ /* The version of the line number information. */
+ int version;
+ /* Address size. */
+ int addrsize;
+ /* The minimum instruction length. */
+ unsigned int min_insn_len;
+ /* The maximum number of ops per instruction. */
+ unsigned int max_ops_per_insn;
+ /* The line base for special opcodes. */
+ int line_base;
+ /* The line range for special opcodes. */
+ unsigned int line_range;
+ /* The opcode base--the first special opcode. */
+ unsigned int opcode_base;
+ /* Opcode lengths, indexed by opcode - 1. */
+ const unsigned char *opcode_lengths;
+ /* The number of directory entries. */
+ size_t dirs_count;
+ /* The directory entries. */
+ const char **dirs;
+ /* The number of filenames. */
+ size_t filenames_count;
+ /* The filenames. */
+ const char **filenames;
+};
+
+/* A format description from a line header. */
+
+struct line_header_format
+{
+ int lnct; /* LNCT code. */
+ enum dwarf_form form; /* Form of entry data. */
+};
+
+/* Map a single PC value to a file/line. We will keep a vector of
+ these sorted by PC value. Each file/line will be correct from the
+ PC up to the PC of the next entry if there is one. We allocate one
+ extra entry at the end so that we can use bsearch. */
+
+struct line
+{
+ /* PC. */
+ uintptr_t pc;
+ /* File name. Many entries in the array are expected to point to
+ the same file name. */
+ const char *filename;
+ /* Line number. */
+ int lineno;
+ /* Index of the object in the original array read from the DWARF
+ section, before it has been sorted. The index makes it possible
+ to use Quicksort and maintain stability. */
+ int idx;
+};
+
+/* A growable vector of line number information. This is used while
+ reading the line numbers. */
+
+struct line_vector
+{
+ /* Memory. This is an array of struct line. */
+ struct backtrace_vector vec;
+ /* Number of valid mappings. */
+ size_t count;
+};
+
+/* A function described in the debug info. */
+
+struct function
+{
+ /* The name of the function. */
+ const char *name;
+ /* If this is an inlined function, the filename of the call
+ site. */
+ const char *caller_filename;
+ /* If this is an inlined function, the line number of the call
+ site. */
+ int caller_lineno;
+ /* Map PC ranges to inlined functions. */
+ struct function_addrs *function_addrs;
+ size_t function_addrs_count;
+};
+
+/* An address range for a function. This maps a PC value to a
+ specific function. */
+
+struct function_addrs
+{
+ /* Range is LOW <= PC < HIGH. */
+ uint64_t low;
+ uint64_t high;
+ /* Function for this address range. */
+ struct function *function;
+};
+
+/* A growable vector of function address ranges. */
+
+struct function_vector
+{
+ /* Memory. This is an array of struct function_addrs. */
+ struct backtrace_vector vec;
+ /* Number of address ranges present. */
+ size_t count;
+};
+
+/* A DWARF compilation unit. This only holds the information we need
+ to map a PC to a file and line. */
+
+struct unit
+{
+ /* The first entry for this compilation unit. */
+ const unsigned char *unit_data;
+ /* The length of the data for this compilation unit. */
+ size_t unit_data_len;
+ /* The offset of UNIT_DATA from the start of the information for
+ this compilation unit. */
+ size_t unit_data_offset;
+ /* Offset of the start of the compilation unit from the start of the
+ .debug_info section. */
+ size_t low_offset;
+ /* Offset of the end of the compilation unit from the start of the
+ .debug_info section. */
+ size_t high_offset;
+ /* DWARF version. */
+ int version;
+ /* Whether unit is DWARF64. */
+ int is_dwarf64;
+ /* Address size. */
+ int addrsize;
+ /* Offset into line number information. */
+ off_t lineoff;
+ /* Offset of compilation unit in .debug_str_offsets. */
+ uint64_t str_offsets_base;
+ /* Offset of compilation unit in .debug_addr. */
+ uint64_t addr_base;
+ /* Offset of compilation unit in .debug_rnglists. */
+ uint64_t rnglists_base;
+ /* Primary source file. */
+ const char *filename;
+ /* Compilation command working directory. */
+ const char *comp_dir;
+ /* Absolute file name, only set if needed. */
+ const char *abs_filename;
+ /* The abbreviations for this unit. */
+ struct abbrevs abbrevs;
+
+ /* The fields above this point are read in during initialization and
+ may be accessed freely. The fields below this point are read in
+ as needed, and therefore require care, as different threads may
+ try to initialize them simultaneously. */
+
+ /* PC to line number mapping. This is NULL if the values have not
+ been read. This is (struct line *) -1 if there was an error
+ reading the values. */
+ struct line *lines;
+ /* Number of entries in lines. */
+ size_t lines_count;
+ /* PC ranges to function. */
+ struct function_addrs *function_addrs;
+ size_t function_addrs_count;
+};
+
+/* An address range for a compilation unit. This maps a PC value to a
+ specific compilation unit. Note that we invert the representation
+ in DWARF: instead of listing the units and attaching a list of
+ ranges, we list the ranges and have each one point to the unit.
+ This lets us do a binary search to find the unit. */
+
+struct unit_addrs
+{
+ /* Range is LOW <= PC < HIGH. */
+ uint64_t low;
+ uint64_t high;
+ /* Compilation unit for this address range. */
+ struct unit *u;
+};
+
+/* A growable vector of compilation unit address ranges. */
+
+struct unit_addrs_vector
+{
+ /* Memory. This is an array of struct unit_addrs. */
+ struct backtrace_vector vec;
+ /* Number of address ranges present. */
+ size_t count;
+};
+
+/* A growable vector of compilation unit pointer. */
+
+struct unit_vector
+{
+ struct backtrace_vector vec;
+ size_t count;
+};
+
+/* The information we need to map a PC to a file and line. */
+
+struct dwarf_data
+{
+ /* The data for the next file we know about. */
+ struct dwarf_data *next;
+ /* The data for .gnu_debugaltlink. */
+ struct dwarf_data *altlink;
+ /* The base address for this file. */
+ uintptr_t base_address;
+ /* A sorted list of address ranges. */
+ struct unit_addrs *addrs;
+ /* Number of address ranges in list. */
+ size_t addrs_count;
+ /* A sorted list of units. */
+ struct unit **units;
+ /* Number of units in the list. */
+ size_t units_count;
+ /* The unparsed DWARF debug data. */
+ struct dwarf_sections dwarf_sections;
+ /* Whether the data is big-endian or not. */
+ int is_bigendian;
+ /* A vector used for function addresses. We keep this here so that
+ we can grow the vector as we read more functions. */
+ struct function_vector fvec;
+};
+
+/* Report an error for a DWARF buffer. */
+
+static void
+dwarf_buf_error (struct dwarf_buf *buf, const char *msg, int errnum)
+{
+ char b[200];
+
+ snprintf (b, sizeof b, "%s in %s at %d",
+ msg, buf->name, (int) (buf->buf - buf->start));
+ buf->error_callback (buf->data, b, errnum);
+}
+
+/* Require at least COUNT bytes in BUF. Return 1 if all is well, 0 on
+ error. */
+
+static int
+require (struct dwarf_buf *buf, size_t count)
+{
+ if (buf->left >= count)
+ return 1;
+
+ if (!buf->reported_underflow)
+ {
+ dwarf_buf_error (buf, "DWARF underflow", 0);
+ buf->reported_underflow = 1;
+ }
+
+ return 0;
+}
+
+/* Advance COUNT bytes in BUF. Return 1 if all is well, 0 on
+ error. */
+
+static int
+advance (struct dwarf_buf *buf, size_t count)
+{
+ if (!require (buf, count))
+ return 0;
+ buf->buf += count;
+ buf->left -= count;
+ return 1;
+}
+
+/* Read one zero-terminated string from BUF and advance past the string. */
+
+static const char *
+read_string (struct dwarf_buf *buf)
+{
+ const char *p = (const char *)buf->buf;
+ size_t len = strnlen (p, buf->left);
+
+ /* - If len == left, we ran out of buffer before finding the zero terminator.
+ Generate an error by advancing len + 1.
+ - If len < left, advance by len + 1 to skip past the zero terminator. */
+ size_t count = len + 1;
+
+ if (!advance (buf, count))
+ return NULL;
+
+ return p;
+}
+
+/* Read one byte from BUF and advance 1 byte. */
+
+static unsigned char
+read_byte (struct dwarf_buf *buf)
+{
+ const unsigned char *p = buf->buf;
+
+ if (!advance (buf, 1))
+ return 0;
+ return p[0];
+}
+
+/* Read a signed char from BUF and advance 1 byte. */
+
+static signed char
+read_sbyte (struct dwarf_buf *buf)
+{
+ const unsigned char *p = buf->buf;
+
+ if (!advance (buf, 1))
+ return 0;
+ return (*p ^ 0x80) - 0x80;
+}
+
+/* Read a uint16 from BUF and advance 2 bytes. */
+
+static uint16_t
+read_uint16 (struct dwarf_buf *buf)
+{
+ const unsigned char *p = buf->buf;
+
+ if (!advance (buf, 2))
+ return 0;
+ if (buf->is_bigendian)
+ return ((uint16_t) p[0] << 8) | (uint16_t) p[1];
+ else
+ return ((uint16_t) p[1] << 8) | (uint16_t) p[0];
+}
+
+/* Read a 24 bit value from BUF and advance 3 bytes. */
+
+static uint32_t
+read_uint24 (struct dwarf_buf *buf)
+{
+ const unsigned char *p = buf->buf;
+
+ if (!advance (buf, 3))
+ return 0;
+ if (buf->is_bigendian)
+ return (((uint32_t) p[0] << 16) | ((uint32_t) p[1] << 8)
+ | (uint32_t) p[2]);
+ else
+ return (((uint32_t) p[2] << 16) | ((uint32_t) p[1] << 8)
+ | (uint32_t) p[0]);
+}
+
+/* Read a uint32 from BUF and advance 4 bytes. */
+
+static uint32_t
+read_uint32 (struct dwarf_buf *buf)
+{
+ const unsigned char *p = buf->buf;
+
+ if (!advance (buf, 4))
+ return 0;
+ if (buf->is_bigendian)
+ return (((uint32_t) p[0] << 24) | ((uint32_t) p[1] << 16)
+ | ((uint32_t) p[2] << 8) | (uint32_t) p[3]);
+ else
+ return (((uint32_t) p[3] << 24) | ((uint32_t) p[2] << 16)
+ | ((uint32_t) p[1] << 8) | (uint32_t) p[0]);
+}
+
+/* Read a uint64 from BUF and advance 8 bytes. */
+
+static uint64_t
+read_uint64 (struct dwarf_buf *buf)
+{
+ const unsigned char *p = buf->buf;
+
+ if (!advance (buf, 8))
+ return 0;
+ if (buf->is_bigendian)
+ return (((uint64_t) p[0] << 56) | ((uint64_t) p[1] << 48)
+ | ((uint64_t) p[2] << 40) | ((uint64_t) p[3] << 32)
+ | ((uint64_t) p[4] << 24) | ((uint64_t) p[5] << 16)
+ | ((uint64_t) p[6] << 8) | (uint64_t) p[7]);
+ else
+ return (((uint64_t) p[7] << 56) | ((uint64_t) p[6] << 48)
+ | ((uint64_t) p[5] << 40) | ((uint64_t) p[4] << 32)
+ | ((uint64_t) p[3] << 24) | ((uint64_t) p[2] << 16)
+ | ((uint64_t) p[1] << 8) | (uint64_t) p[0]);
+}
+
+/* Read an offset from BUF and advance the appropriate number of
+ bytes. */
+
+static uint64_t
+read_offset (struct dwarf_buf *buf, int is_dwarf64)
+{
+ if (is_dwarf64)
+ return read_uint64 (buf);
+ else
+ return read_uint32 (buf);
+}
+
+/* Read an address from BUF and advance the appropriate number of
+ bytes. */
+
+static uint64_t
+read_address (struct dwarf_buf *buf, int addrsize)
+{
+ switch (addrsize)
+ {
+ case 1:
+ return read_byte (buf);
+ case 2:
+ return read_uint16 (buf);
+ case 4:
+ return read_uint32 (buf);
+ case 8:
+ return read_uint64 (buf);
+ default:
+ dwarf_buf_error (buf, "unrecognized address size", 0);
+ return 0;
+ }
+}
+
+/* Return whether a value is the highest possible address, given the
+ address size. */
+
+static int
+is_highest_address (uint64_t address, int addrsize)
+{
+ switch (addrsize)
+ {
+ case 1:
+ return address == (unsigned char) -1;
+ case 2:
+ return address == (uint16_t) -1;
+ case 4:
+ return address == (uint32_t) -1;
+ case 8:
+ return address == (uint64_t) -1;
+ default:
+ return 0;
+ }
+}
+
+/* Read an unsigned LEB128 number. */
+
+static uint64_t
+read_uleb128 (struct dwarf_buf *buf)
+{
+ uint64_t ret;
+ unsigned int shift;
+ int overflow;
+ unsigned char b;
+
+ ret = 0;
+ shift = 0;
+ overflow = 0;
+ do
+ {
+ const unsigned char *p;
+
+ p = buf->buf;
+ if (!advance (buf, 1))
+ return 0;
+ b = *p;
+ if (shift < 64)
+ ret |= ((uint64_t) (b & 0x7f)) << shift;
+ else if (!overflow)
+ {
+ dwarf_buf_error (buf, "LEB128 overflows uint64_t", 0);
+ overflow = 1;
+ }
+ shift += 7;
+ }
+ while ((b & 0x80) != 0);
+
+ return ret;
+}
+
+/* Read a signed LEB128 number. */
+
+static int64_t
+read_sleb128 (struct dwarf_buf *buf)
+{
+ uint64_t val;
+ unsigned int shift;
+ int overflow;
+ unsigned char b;
+
+ val = 0;
+ shift = 0;
+ overflow = 0;
+ do
+ {
+ const unsigned char *p;
+
+ p = buf->buf;
+ if (!advance (buf, 1))
+ return 0;
+ b = *p;
+ if (shift < 64)
+ val |= ((uint64_t) (b & 0x7f)) << shift;
+ else if (!overflow)
+ {
+ dwarf_buf_error (buf, "signed LEB128 overflows uint64_t", 0);
+ overflow = 1;
+ }
+ shift += 7;
+ }
+ while ((b & 0x80) != 0);
+
+ if ((b & 0x40) != 0 && shift < 64)
+ val |= ((uint64_t) -1) << shift;
+
+ return (int64_t) val;
+}
+
+/* Return the length of an LEB128 number. */
+
+static size_t
+leb128_len (const unsigned char *p)
+{
+ size_t ret;
+
+ ret = 1;
+ while ((*p & 0x80) != 0)
+ {
+ ++p;
+ ++ret;
+ }
+ return ret;
+}
+
+/* Read initial_length from BUF and advance the appropriate number of bytes. */
+
+static uint64_t
+read_initial_length (struct dwarf_buf *buf, int *is_dwarf64)
+{
+ uint64_t len;
+
+ len = read_uint32 (buf);
+ if (len == 0xffffffff)
+ {
+ len = read_uint64 (buf);
+ *is_dwarf64 = 1;
+ }
+ else
+ *is_dwarf64 = 0;
+
+ return len;
+}
+
+/* Free an abbreviations structure. */
+
+static void
+free_abbrevs (struct backtrace_state *state, struct abbrevs *abbrevs,
+ backtrace_error_callback error_callback, void *data)
+{
+ size_t i;
+
+ for (i = 0; i < abbrevs->num_abbrevs; ++i)
+ backtrace_free (state, abbrevs->abbrevs[i].attrs,
+ abbrevs->abbrevs[i].num_attrs * sizeof (struct attr),
+ error_callback, data);
+ backtrace_free (state, abbrevs->abbrevs,
+ abbrevs->num_abbrevs * sizeof (struct abbrev),
+ error_callback, data);
+ abbrevs->num_abbrevs = 0;
+ abbrevs->abbrevs = NULL;
+}
+
+/* Read an attribute value. Returns 1 on success, 0 on failure. If
+ the value can be represented as a uint64_t, sets *VAL and sets
+ *IS_VALID to 1. We don't try to store the value of other attribute
+ forms, because we don't care about them. */
+
+static int
+read_attribute (enum dwarf_form form, uint64_t implicit_val,
+ struct dwarf_buf *buf, int is_dwarf64, int version,
+ int addrsize, const struct dwarf_sections *dwarf_sections,
+ struct dwarf_data *altlink, struct attr_val *val)
+{
+ /* Avoid warnings about val.u.FIELD may be used uninitialized if
+ this function is inlined. The warnings aren't valid but can
+ occur because the different fields are set and used
+ conditionally. */
+ memset (val, 0, sizeof *val);
+
+ switch (form)
+ {
+ case DW_FORM_addr:
+ val->encoding = ATTR_VAL_ADDRESS;
+ val->u.uint = read_address (buf, addrsize);
+ return 1;
+ case DW_FORM_block2:
+ val->encoding = ATTR_VAL_BLOCK;
+ return advance (buf, read_uint16 (buf));
+ case DW_FORM_block4:
+ val->encoding = ATTR_VAL_BLOCK;
+ return advance (buf, read_uint32 (buf));
+ case DW_FORM_data2:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = read_uint16 (buf);
+ return 1;
+ case DW_FORM_data4:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = read_uint32 (buf);
+ return 1;
+ case DW_FORM_data8:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = read_uint64 (buf);
+ return 1;
+ case DW_FORM_data16:
+ val->encoding = ATTR_VAL_BLOCK;
+ return advance (buf, 16);
+ case DW_FORM_string:
+ val->encoding = ATTR_VAL_STRING;
+ val->u.string = read_string (buf);
+ return val->u.string == NULL ? 0 : 1;
+ case DW_FORM_block:
+ val->encoding = ATTR_VAL_BLOCK;
+ return advance (buf, read_uleb128 (buf));
+ case DW_FORM_block1:
+ val->encoding = ATTR_VAL_BLOCK;
+ return advance (buf, read_byte (buf));
+ case DW_FORM_data1:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = read_byte (buf);
+ return 1;
+ case DW_FORM_flag:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = read_byte (buf);
+ return 1;
+ case DW_FORM_sdata:
+ val->encoding = ATTR_VAL_SINT;
+ val->u.sint = read_sleb128 (buf);
+ return 1;
+ case DW_FORM_strp:
+ {
+ uint64_t offset;
+
+ offset = read_offset (buf, is_dwarf64);
+ if (offset >= dwarf_sections->size[DEBUG_STR])
+ {
+ dwarf_buf_error (buf, "DW_FORM_strp out of range", 0);
+ return 0;
+ }
+ val->encoding = ATTR_VAL_STRING;
+ val->u.string =
+ (const char *) dwarf_sections->data[DEBUG_STR] + offset;
+ return 1;
+ }
+ case DW_FORM_line_strp:
+ {
+ uint64_t offset;
+
+ offset = read_offset (buf, is_dwarf64);
+ if (offset >= dwarf_sections->size[DEBUG_LINE_STR])
+ {
+ dwarf_buf_error (buf, "DW_FORM_line_strp out of range", 0);
+ return 0;
+ }
+ val->encoding = ATTR_VAL_STRING;
+ val->u.string =
+ (const char *) dwarf_sections->data[DEBUG_LINE_STR] + offset;
+ return 1;
+ }
+ case DW_FORM_udata:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = read_uleb128 (buf);
+ return 1;
+ case DW_FORM_ref_addr:
+ val->encoding = ATTR_VAL_REF_INFO;
+ if (version == 2)
+ val->u.uint = read_address (buf, addrsize);
+ else
+ val->u.uint = read_offset (buf, is_dwarf64);
+ return 1;
+ case DW_FORM_ref1:
+ val->encoding = ATTR_VAL_REF_UNIT;
+ val->u.uint = read_byte (buf);
+ return 1;
+ case DW_FORM_ref2:
+ val->encoding = ATTR_VAL_REF_UNIT;
+ val->u.uint = read_uint16 (buf);
+ return 1;
+ case DW_FORM_ref4:
+ val->encoding = ATTR_VAL_REF_UNIT;
+ val->u.uint = read_uint32 (buf);
+ return 1;
+ case DW_FORM_ref8:
+ val->encoding = ATTR_VAL_REF_UNIT;
+ val->u.uint = read_uint64 (buf);
+ return 1;
+ case DW_FORM_ref_udata:
+ val->encoding = ATTR_VAL_REF_UNIT;
+ val->u.uint = read_uleb128 (buf);
+ return 1;
+ case DW_FORM_indirect:
+ {
+ uint64_t form;
+
+ form = read_uleb128 (buf);
+ if (form == DW_FORM_implicit_const)
+ {
+ dwarf_buf_error (buf,
+ "DW_FORM_indirect to DW_FORM_implicit_const",
+ 0);
+ return 0;
+ }
+ return read_attribute ((enum dwarf_form) form, 0, buf, is_dwarf64,
+ version, addrsize, dwarf_sections, altlink,
+ val);
+ }
+ case DW_FORM_sec_offset:
+ val->encoding = ATTR_VAL_REF_SECTION;
+ val->u.uint = read_offset (buf, is_dwarf64);
+ return 1;
+ case DW_FORM_exprloc:
+ val->encoding = ATTR_VAL_EXPR;
+ return advance (buf, read_uleb128 (buf));
+ case DW_FORM_flag_present:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = 1;
+ return 1;
+ case DW_FORM_ref_sig8:
+ val->encoding = ATTR_VAL_REF_TYPE;
+ val->u.uint = read_uint64 (buf);
+ return 1;
+ case DW_FORM_strx: case DW_FORM_strx1: case DW_FORM_strx2:
+ case DW_FORM_strx3: case DW_FORM_strx4:
+ {
+ uint64_t offset;
+
+ switch (form)
+ {
+ case DW_FORM_strx:
+ offset = read_uleb128 (buf);
+ break;
+ case DW_FORM_strx1:
+ offset = read_byte (buf);
+ break;
+ case DW_FORM_strx2:
+ offset = read_uint16 (buf);
+ break;
+ case DW_FORM_strx3:
+ offset = read_uint24 (buf);
+ break;
+ case DW_FORM_strx4:
+ offset = read_uint32 (buf);
+ break;
+ default:
+ /* This case can't happen. */
+ return 0;
+ }
+ val->encoding = ATTR_VAL_STRING_INDEX;
+ val->u.uint = offset;
+ return 1;
+ }
+ case DW_FORM_addrx: case DW_FORM_addrx1: case DW_FORM_addrx2:
+ case DW_FORM_addrx3: case DW_FORM_addrx4:
+ {
+ uint64_t offset;
+
+ switch (form)
+ {
+ case DW_FORM_addrx:
+ offset = read_uleb128 (buf);
+ break;
+ case DW_FORM_addrx1:
+ offset = read_byte (buf);
+ break;
+ case DW_FORM_addrx2:
+ offset = read_uint16 (buf);
+ break;
+ case DW_FORM_addrx3:
+ offset = read_uint24 (buf);
+ break;
+ case DW_FORM_addrx4:
+ offset = read_uint32 (buf);
+ break;
+ default:
+ /* This case can't happen. */
+ return 0;
+ }
+ val->encoding = ATTR_VAL_ADDRESS_INDEX;
+ val->u.uint = offset;
+ return 1;
+ }
+ case DW_FORM_ref_sup4:
+ val->encoding = ATTR_VAL_REF_SECTION;
+ val->u.uint = read_uint32 (buf);
+ return 1;
+ case DW_FORM_ref_sup8:
+ val->encoding = ATTR_VAL_REF_SECTION;
+ val->u.uint = read_uint64 (buf);
+ return 1;
+ case DW_FORM_implicit_const:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = implicit_val;
+ return 1;
+ case DW_FORM_loclistx:
+ /* We don't distinguish this from DW_FORM_sec_offset. It
+ * shouldn't matter since we don't care about loclists. */
+ val->encoding = ATTR_VAL_REF_SECTION;
+ val->u.uint = read_uleb128 (buf);
+ return 1;
+ case DW_FORM_rnglistx:
+ val->encoding = ATTR_VAL_RNGLISTS_INDEX;
+ val->u.uint = read_uleb128 (buf);
+ return 1;
+ case DW_FORM_GNU_addr_index:
+ val->encoding = ATTR_VAL_REF_SECTION;
+ val->u.uint = read_uleb128 (buf);
+ return 1;
+ case DW_FORM_GNU_str_index:
+ val->encoding = ATTR_VAL_REF_SECTION;
+ val->u.uint = read_uleb128 (buf);
+ return 1;
+ case DW_FORM_GNU_ref_alt:
+ val->u.uint = read_offset (buf, is_dwarf64);
+ if (altlink == NULL)
+ {
+ val->encoding = ATTR_VAL_NONE;
+ return 1;
+ }
+ val->encoding = ATTR_VAL_REF_ALT_INFO;
+ return 1;
+ case DW_FORM_strp_sup: case DW_FORM_GNU_strp_alt:
+ {
+ uint64_t offset;
+
+ offset = read_offset (buf, is_dwarf64);
+ if (altlink == NULL)
+ {
+ val->encoding = ATTR_VAL_NONE;
+ return 1;
+ }
+ if (offset >= altlink->dwarf_sections.size[DEBUG_STR])
+ {
+ dwarf_buf_error (buf, "DW_FORM_strp_sup out of range", 0);
+ return 0;
+ }
+ val->encoding = ATTR_VAL_STRING;
+ val->u.string =
+ (const char *) altlink->dwarf_sections.data[DEBUG_STR] + offset;
+ return 1;
+ }
+ default:
+ dwarf_buf_error (buf, "unrecognized DWARF form", -1);
+ return 0;
+ }
+}
+
+/* If we can determine the value of a string attribute, set *STRING to
+ point to the string. Return 1 on success, 0 on error. If we don't
+ know the value, we consider that a success, and we don't change
+ *STRING. An error is only reported for some sort of out of range
+ offset. */
+
+static int
+resolve_string (const struct dwarf_sections *dwarf_sections, int is_dwarf64,
+ int is_bigendian, uint64_t str_offsets_base,
+ const struct attr_val *val,
+ backtrace_error_callback error_callback, void *data,
+ const char **string)
+{
+ switch (val->encoding)
+ {
+ case ATTR_VAL_STRING:
+ *string = val->u.string;
+ return 1;
+
+ case ATTR_VAL_STRING_INDEX:
+ {
+ uint64_t offset;
+ struct dwarf_buf offset_buf;
+
+ offset = val->u.uint * (is_dwarf64 ? 8 : 4) + str_offsets_base;
+ if (offset + (is_dwarf64 ? 8 : 4)
+ > dwarf_sections->size[DEBUG_STR_OFFSETS])
+ {
+ error_callback (data, "DW_FORM_strx value out of range", 0);
+ return 0;
+ }
+
+ offset_buf.name = ".debug_str_offsets";
+ offset_buf.start = dwarf_sections->data[DEBUG_STR_OFFSETS];
+ offset_buf.buf = dwarf_sections->data[DEBUG_STR_OFFSETS] + offset;
+ offset_buf.left = dwarf_sections->size[DEBUG_STR_OFFSETS] - offset;
+ offset_buf.is_bigendian = is_bigendian;
+ offset_buf.error_callback = error_callback;
+ offset_buf.data = data;
+ offset_buf.reported_underflow = 0;
+
+ offset = read_offset (&offset_buf, is_dwarf64);
+ if (offset >= dwarf_sections->size[DEBUG_STR])
+ {
+ dwarf_buf_error (&offset_buf,
+ "DW_FORM_strx offset out of range",
+ 0);
+ return 0;
+ }
+ *string = (const char *) dwarf_sections->data[DEBUG_STR] + offset;
+ return 1;
+ }
+
+ default:
+ return 1;
+ }
+}
+
+/* Set *ADDRESS to the real address for a ATTR_VAL_ADDRESS_INDEX.
+ Return 1 on success, 0 on error. */
+
+static int
+resolve_addr_index (const struct dwarf_sections *dwarf_sections,
+ uint64_t addr_base, int addrsize, int is_bigendian,
+ uint64_t addr_index,
+ backtrace_error_callback error_callback, void *data,
+ uint64_t *address)
+{
+ uint64_t offset;
+ struct dwarf_buf addr_buf;
+
+ offset = addr_index * addrsize + addr_base;
+ if (offset + addrsize > dwarf_sections->size[DEBUG_ADDR])
+ {
+ error_callback (data, "DW_FORM_addrx value out of range", 0);
+ return 0;
+ }
+
+ addr_buf.name = ".debug_addr";
+ addr_buf.start = dwarf_sections->data[DEBUG_ADDR];
+ addr_buf.buf = dwarf_sections->data[DEBUG_ADDR] + offset;
+ addr_buf.left = dwarf_sections->size[DEBUG_ADDR] - offset;
+ addr_buf.is_bigendian = is_bigendian;
+ addr_buf.error_callback = error_callback;
+ addr_buf.data = data;
+ addr_buf.reported_underflow = 0;
+
+ *address = read_address (&addr_buf, addrsize);
+ return 1;
+}
+
+/* Compare a unit offset against a unit for bsearch. */
+
+static int
+units_search (const void *vkey, const void *ventry)
+{
+ const size_t *key = (const size_t *) vkey;
+ const struct unit *entry = *((const struct unit *const *) ventry);
+ size_t offset;
+
+ offset = *key;
+ if (offset < entry->low_offset)
+ return -1;
+ else if (offset >= entry->high_offset)
+ return 1;
+ else
+ return 0;
+}
+
+/* Find a unit in PU containing OFFSET. */
+
+static struct unit *
+find_unit (struct unit **pu, size_t units_count, size_t offset)
+{
+ struct unit **u;
+ u = bsearch (&offset, pu, units_count, sizeof (struct unit *), units_search);
+ return u == NULL ? NULL : *u;
+}
+
+/* Compare function_addrs for qsort. When ranges are nested, make the
+ smallest one sort last. */
+
+static int
+function_addrs_compare (const void *v1, const void *v2)
+{
+ const struct function_addrs *a1 = (const struct function_addrs *) v1;
+ const struct function_addrs *a2 = (const struct function_addrs *) v2;
+
+ if (a1->low < a2->low)
+ return -1;
+ if (a1->low > a2->low)
+ return 1;
+ if (a1->high < a2->high)
+ return 1;
+ if (a1->high > a2->high)
+ return -1;
+ return strcmp (a1->function->name, a2->function->name);
+}
+
+/* Compare a PC against a function_addrs for bsearch. We always
+ allocate an entra entry at the end of the vector, so that this
+ routine can safely look at the next entry. Note that if there are
+ multiple ranges containing PC, which one will be returned is
+ unpredictable. We compensate for that in dwarf_fileline. */
+
+static int
+function_addrs_search (const void *vkey, const void *ventry)
+{
+ const uintptr_t *key = (const uintptr_t *) vkey;
+ const struct function_addrs *entry = (const struct function_addrs *) ventry;
+ uintptr_t pc;
+
+ pc = *key;
+ if (pc < entry->low)
+ return -1;
+ else if (pc > (entry + 1)->low)
+ return 1;
+ else
+ return 0;
+}
+
+/* Add a new compilation unit address range to a vector. This is
+ called via add_ranges. Returns 1 on success, 0 on failure. */
+
+static int
+add_unit_addr (struct backtrace_state *state, void *rdata,
+ uint64_t lowpc, uint64_t highpc,
+ backtrace_error_callback error_callback, void *data,
+ void *pvec)
+{
+ struct unit *u = (struct unit *) rdata;
+ struct unit_addrs_vector *vec = (struct unit_addrs_vector *) pvec;
+ struct unit_addrs *p;
+
+ /* Try to merge with the last entry. */
+ if (vec->count > 0)
+ {
+ p = (struct unit_addrs *) vec->vec.base + (vec->count - 1);
+ if ((lowpc == p->high || lowpc == p->high + 1)
+ && u == p->u)
+ {
+ if (highpc > p->high)
+ p->high = highpc;
+ return 1;
+ }
+ }
+
+ p = ((struct unit_addrs *)
+ backtrace_vector_grow (state, sizeof (struct unit_addrs),
+ error_callback, data, &vec->vec));
+ if (p == NULL)
+ return 0;
+
+ p->low = lowpc;
+ p->high = highpc;
+ p->u = u;
+
+ ++vec->count;
+
+ return 1;
+}
+
+/* Compare unit_addrs for qsort. When ranges are nested, make the
+ smallest one sort last. */
+
+static int
+unit_addrs_compare (const void *v1, const void *v2)
+{
+ const struct unit_addrs *a1 = (const struct unit_addrs *) v1;
+ const struct unit_addrs *a2 = (const struct unit_addrs *) v2;
+
+ if (a1->low < a2->low)
+ return -1;
+ if (a1->low > a2->low)
+ return 1;
+ if (a1->high < a2->high)
+ return 1;
+ if (a1->high > a2->high)
+ return -1;
+ if (a1->u->lineoff < a2->u->lineoff)
+ return -1;
+ if (a1->u->lineoff > a2->u->lineoff)
+ return 1;
+ return 0;
+}
+
+/* Compare a PC against a unit_addrs for bsearch. We always allocate
+ an entry entry at the end of the vector, so that this routine can
+ safely look at the next entry. Note that if there are multiple
+ ranges containing PC, which one will be returned is unpredictable.
+ We compensate for that in dwarf_fileline. */
+
+static int
+unit_addrs_search (const void *vkey, const void *ventry)
+{
+ const uintptr_t *key = (const uintptr_t *) vkey;
+ const struct unit_addrs *entry = (const struct unit_addrs *) ventry;
+ uintptr_t pc;
+
+ pc = *key;
+ if (pc < entry->low)
+ return -1;
+ else if (pc > (entry + 1)->low)
+ return 1;
+ else
+ return 0;
+}
+
+/* Sort the line vector by PC. We want a stable sort here to maintain
+ the order of lines for the same PC values. Since the sequence is
+ being sorted in place, their addresses cannot be relied on to
+ maintain stability. That is the purpose of the index member. */
+
+static int
+line_compare (const void *v1, const void *v2)
+{
+ const struct line *ln1 = (const struct line *) v1;
+ const struct line *ln2 = (const struct line *) v2;
+
+ if (ln1->pc < ln2->pc)
+ return -1;
+ else if (ln1->pc > ln2->pc)
+ return 1;
+ else if (ln1->idx < ln2->idx)
+ return -1;
+ else if (ln1->idx > ln2->idx)
+ return 1;
+ else
+ return 0;
+}
+
+/* Find a PC in a line vector. We always allocate an extra entry at
+ the end of the lines vector, so that this routine can safely look
+ at the next entry. Note that when there are multiple mappings for
+ the same PC value, this will return the last one. */
+
+static int
+line_search (const void *vkey, const void *ventry)
+{
+ const uintptr_t *key = (const uintptr_t *) vkey;
+ const struct line *entry = (const struct line *) ventry;
+ uintptr_t pc;
+
+ pc = *key;
+ if (pc < entry->pc)
+ return -1;
+ else if (pc >= (entry + 1)->pc)
+ return 1;
+ else
+ return 0;
+}
+
+/* Sort the abbrevs by the abbrev code. This function is passed to
+ both qsort and bsearch. */
+
+static int
+abbrev_compare (const void *v1, const void *v2)
+{
+ const struct abbrev *a1 = (const struct abbrev *) v1;
+ const struct abbrev *a2 = (const struct abbrev *) v2;
+
+ if (a1->code < a2->code)
+ return -1;
+ else if (a1->code > a2->code)
+ return 1;
+ else
+ {
+ /* This really shouldn't happen. It means there are two
+ different abbrevs with the same code, and that means we don't
+ know which one lookup_abbrev should return. */
+ return 0;
+ }
+}
+
+/* Read the abbreviation table for a compilation unit. Returns 1 on
+ success, 0 on failure. */
+
+static int
+read_abbrevs (struct backtrace_state *state, uint64_t abbrev_offset,
+ const unsigned char *dwarf_abbrev, size_t dwarf_abbrev_size,
+ int is_bigendian, backtrace_error_callback error_callback,
+ void *data, struct abbrevs *abbrevs)
+{
+ struct dwarf_buf abbrev_buf;
+ struct dwarf_buf count_buf;
+ size_t num_abbrevs;
+
+ abbrevs->num_abbrevs = 0;
+ abbrevs->abbrevs = NULL;
+
+ if (abbrev_offset >= dwarf_abbrev_size)
+ {
+ error_callback (data, "abbrev offset out of range", 0);
+ return 0;
+ }
+
+ abbrev_buf.name = ".debug_abbrev";
+ abbrev_buf.start = dwarf_abbrev;
+ abbrev_buf.buf = dwarf_abbrev + abbrev_offset;
+ abbrev_buf.left = dwarf_abbrev_size - abbrev_offset;
+ abbrev_buf.is_bigendian = is_bigendian;
+ abbrev_buf.error_callback = error_callback;
+ abbrev_buf.data = data;
+ abbrev_buf.reported_underflow = 0;
+
+ /* Count the number of abbrevs in this list. */
+
+ count_buf = abbrev_buf;
+ num_abbrevs = 0;
+ while (read_uleb128 (&count_buf) != 0)
+ {
+ if (count_buf.reported_underflow)
+ return 0;
+ ++num_abbrevs;
+ // Skip tag.
+ read_uleb128 (&count_buf);
+ // Skip has_children.
+ read_byte (&count_buf);
+ // Skip attributes.
+ while (read_uleb128 (&count_buf) != 0)
+ {
+ uint64_t form;
+
+ form = read_uleb128 (&count_buf);
+ if ((enum dwarf_form) form == DW_FORM_implicit_const)
+ read_sleb128 (&count_buf);
+ }
+ // Skip form of last attribute.
+ read_uleb128 (&count_buf);
+ }
+
+ if (count_buf.reported_underflow)
+ return 0;
+
+ if (num_abbrevs == 0)
+ return 1;
+
+ abbrevs->abbrevs = ((struct abbrev *)
+ backtrace_alloc (state,
+ num_abbrevs * sizeof (struct abbrev),
+ error_callback, data));
+ if (abbrevs->abbrevs == NULL)
+ return 0;
+ abbrevs->num_abbrevs = num_abbrevs;
+ memset (abbrevs->abbrevs, 0, num_abbrevs * sizeof (struct abbrev));
+
+ num_abbrevs = 0;
+ while (1)
+ {
+ uint64_t code;
+ struct abbrev a;
+ size_t num_attrs;
+ struct attr *attrs;
+
+ if (abbrev_buf.reported_underflow)
+ goto fail;
+
+ code = read_uleb128 (&abbrev_buf);
+ if (code == 0)
+ break;
+
+ a.code = code;
+ a.tag = (enum dwarf_tag) read_uleb128 (&abbrev_buf);
+ a.has_children = read_byte (&abbrev_buf);
+
+ count_buf = abbrev_buf;
+ num_attrs = 0;
+ while (read_uleb128 (&count_buf) != 0)
+ {
+ uint64_t form;
+
+ ++num_attrs;
+ form = read_uleb128 (&count_buf);
+ if ((enum dwarf_form) form == DW_FORM_implicit_const)
+ read_sleb128 (&count_buf);
+ }
+
+ if (num_attrs == 0)
+ {
+ attrs = NULL;
+ read_uleb128 (&abbrev_buf);
+ read_uleb128 (&abbrev_buf);
+ }
+ else
+ {
+ attrs = ((struct attr *)
+ backtrace_alloc (state, num_attrs * sizeof *attrs,
+ error_callback, data));
+ if (attrs == NULL)
+ goto fail;
+ num_attrs = 0;
+ while (1)
+ {
+ uint64_t name;
+ uint64_t form;
+
+ name = read_uleb128 (&abbrev_buf);
+ form = read_uleb128 (&abbrev_buf);
+ if (name == 0)
+ break;
+ attrs[num_attrs].name = (enum dwarf_attribute) name;
+ attrs[num_attrs].form = (enum dwarf_form) form;
+ if ((enum dwarf_form) form == DW_FORM_implicit_const)
+ attrs[num_attrs].val = read_sleb128 (&abbrev_buf);
+ else
+ attrs[num_attrs].val = 0;
+ ++num_attrs;
+ }
+ }
+
+ a.num_attrs = num_attrs;
+ a.attrs = attrs;
+
+ abbrevs->abbrevs[num_abbrevs] = a;
+ ++num_abbrevs;
+ }
+
+ backtrace_qsort (abbrevs->abbrevs, abbrevs->num_abbrevs,
+ sizeof (struct abbrev), abbrev_compare);
+
+ return 1;
+
+ fail:
+ free_abbrevs (state, abbrevs, error_callback, data);
+ return 0;
+}
+
+/* Return the abbrev information for an abbrev code. */
+
+static const struct abbrev *
+lookup_abbrev (struct abbrevs *abbrevs, uint64_t code,
+ backtrace_error_callback error_callback, void *data)
+{
+ struct abbrev key;
+ void *p;
+
+ /* With GCC, where abbrevs are simply numbered in order, we should
+ be able to just look up the entry. */
+ if (code - 1 < abbrevs->num_abbrevs
+ && abbrevs->abbrevs[code - 1].code == code)
+ return &abbrevs->abbrevs[code - 1];
+
+ /* Otherwise we have to search. */
+ memset (&key, 0, sizeof key);
+ key.code = code;
+ p = bsearch (&key, abbrevs->abbrevs, abbrevs->num_abbrevs,
+ sizeof (struct abbrev), abbrev_compare);
+ if (p == NULL)
+ {
+ error_callback (data, "invalid abbreviation code", 0);
+ return NULL;
+ }
+ return (const struct abbrev *) p;
+}
+
+/* This struct is used to gather address range information while
+ reading attributes. We use this while building a mapping from
+ address ranges to compilation units and then again while mapping
+ from address ranges to function entries. Normally either
+ lowpc/highpc is set or ranges is set. */
+
+struct pcrange {
+ uint64_t lowpc; /* The low PC value. */
+ int have_lowpc; /* Whether a low PC value was found. */
+ int lowpc_is_addr_index; /* Whether lowpc is in .debug_addr. */
+ uint64_t highpc; /* The high PC value. */
+ int have_highpc; /* Whether a high PC value was found. */
+ int highpc_is_relative; /* Whether highpc is relative to lowpc. */
+ int highpc_is_addr_index; /* Whether highpc is in .debug_addr. */
+ uint64_t ranges; /* Offset in ranges section. */
+ int have_ranges; /* Whether ranges is valid. */
+ int ranges_is_index; /* Whether ranges is DW_FORM_rnglistx. */
+};
+
+/* Update PCRANGE from an attribute value. */
+
+static void
+update_pcrange (const struct attr* attr, const struct attr_val* val,
+ struct pcrange *pcrange)
+{
+ switch (attr->name)
+ {
+ case DW_AT_low_pc:
+ if (val->encoding == ATTR_VAL_ADDRESS)
+ {
+ pcrange->lowpc = val->u.uint;
+ pcrange->have_lowpc = 1;
+ }
+ else if (val->encoding == ATTR_VAL_ADDRESS_INDEX)
+ {
+ pcrange->lowpc = val->u.uint;
+ pcrange->have_lowpc = 1;
+ pcrange->lowpc_is_addr_index = 1;
+ }
+ break;
+
+ case DW_AT_high_pc:
+ if (val->encoding == ATTR_VAL_ADDRESS)
+ {
+ pcrange->highpc = val->u.uint;
+ pcrange->have_highpc = 1;
+ }
+ else if (val->encoding == ATTR_VAL_UINT)
+ {
+ pcrange->highpc = val->u.uint;
+ pcrange->have_highpc = 1;
+ pcrange->highpc_is_relative = 1;
+ }
+ else if (val->encoding == ATTR_VAL_ADDRESS_INDEX)
+ {
+ pcrange->highpc = val->u.uint;
+ pcrange->have_highpc = 1;
+ pcrange->highpc_is_addr_index = 1;
+ }
+ break;
+
+ case DW_AT_ranges:
+ if (val->encoding == ATTR_VAL_UINT
+ || val->encoding == ATTR_VAL_REF_SECTION)
+ {
+ pcrange->ranges = val->u.uint;
+ pcrange->have_ranges = 1;
+ }
+ else if (val->encoding == ATTR_VAL_RNGLISTS_INDEX)
+ {
+ pcrange->ranges = val->u.uint;
+ pcrange->have_ranges = 1;
+ pcrange->ranges_is_index = 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Call ADD_RANGE for a low/high PC pair. Returns 1 on success, 0 on
+ error. */
+
+static int
+add_low_high_range (struct backtrace_state *state,
+ const struct dwarf_sections *dwarf_sections,
+ uintptr_t base_address, int is_bigendian,
+ struct unit *u, const struct pcrange *pcrange,
+ int (*add_range) (struct backtrace_state *state,
+ void *rdata, uint64_t lowpc,
+ uint64_t highpc,
+ backtrace_error_callback error_callback,
+ void *data, void *vec),
+ void *rdata,
+ backtrace_error_callback error_callback, void *data,
+ void *vec)
+{
+ uint64_t lowpc;
+ uint64_t highpc;
+
+ lowpc = pcrange->lowpc;
+ if (pcrange->lowpc_is_addr_index)
+ {
+ if (!resolve_addr_index (dwarf_sections, u->addr_base, u->addrsize,
+ is_bigendian, lowpc, error_callback, data,
+ &lowpc))
+ return 0;
+ }
+
+ highpc = pcrange->highpc;
+ if (pcrange->highpc_is_addr_index)
+ {
+ if (!resolve_addr_index (dwarf_sections, u->addr_base, u->addrsize,
+ is_bigendian, highpc, error_callback, data,
+ &highpc))
+ return 0;
+ }
+ if (pcrange->highpc_is_relative)
+ highpc += lowpc;
+
+ /* Add in the base address of the module when recording PC values,
+ so that we can look up the PC directly. */
+ lowpc += base_address;
+ highpc += base_address;
+
+ return add_range (state, rdata, lowpc, highpc, error_callback, data, vec);
+}
+
+/* Call ADD_RANGE for each range read from .debug_ranges, as used in
+ DWARF versions 2 through 4. */
+
+static int
+add_ranges_from_ranges (
+ struct backtrace_state *state,
+ const struct dwarf_sections *dwarf_sections,
+ uintptr_t base_address, int is_bigendian,
+ struct unit *u, uint64_t base,
+ const struct pcrange *pcrange,
+ int (*add_range) (struct backtrace_state *state, void *rdata,
+ uint64_t lowpc, uint64_t highpc,
+ backtrace_error_callback error_callback, void *data,
+ void *vec),
+ void *rdata,
+ backtrace_error_callback error_callback, void *data,
+ void *vec)
+{
+ struct dwarf_buf ranges_buf;
+
+ if (pcrange->ranges >= dwarf_sections->size[DEBUG_RANGES])
+ {
+ error_callback (data, "ranges offset out of range", 0);
+ return 0;
+ }
+
+ ranges_buf.name = ".debug_ranges";
+ ranges_buf.start = dwarf_sections->data[DEBUG_RANGES];
+ ranges_buf.buf = dwarf_sections->data[DEBUG_RANGES] + pcrange->ranges;
+ ranges_buf.left = dwarf_sections->size[DEBUG_RANGES] - pcrange->ranges;
+ ranges_buf.is_bigendian = is_bigendian;
+ ranges_buf.error_callback = error_callback;
+ ranges_buf.data = data;
+ ranges_buf.reported_underflow = 0;
+
+ while (1)
+ {
+ uint64_t low;
+ uint64_t high;
+
+ if (ranges_buf.reported_underflow)
+ return 0;
+
+ low = read_address (&ranges_buf, u->addrsize);
+ high = read_address (&ranges_buf, u->addrsize);
+
+ if (low == 0 && high == 0)
+ break;
+
+ if (is_highest_address (low, u->addrsize))
+ base = high;
+ else
+ {
+ if (!add_range (state, rdata,
+ low + base + base_address,
+ high + base + base_address,
+ error_callback, data, vec))
+ return 0;
+ }
+ }
+
+ if (ranges_buf.reported_underflow)
+ return 0;
+
+ return 1;
+}
+
+/* Call ADD_RANGE for each range read from .debug_rnglists, as used in
+ DWARF version 5. */
+
+static int
+add_ranges_from_rnglists (
+ struct backtrace_state *state,
+ const struct dwarf_sections *dwarf_sections,
+ uintptr_t base_address, int is_bigendian,
+ struct unit *u, uint64_t base,
+ const struct pcrange *pcrange,
+ int (*add_range) (struct backtrace_state *state, void *rdata,
+ uint64_t lowpc, uint64_t highpc,
+ backtrace_error_callback error_callback, void *data,
+ void *vec),
+ void *rdata,
+ backtrace_error_callback error_callback, void *data,
+ void *vec)
+{
+ uint64_t offset;
+ struct dwarf_buf rnglists_buf;
+
+ if (!pcrange->ranges_is_index)
+ offset = pcrange->ranges;
+ else
+ offset = u->rnglists_base + pcrange->ranges * (u->is_dwarf64 ? 8 : 4);
+ if (offset >= dwarf_sections->size[DEBUG_RNGLISTS])
+ {
+ error_callback (data, "rnglists offset out of range", 0);
+ return 0;
+ }
+
+ rnglists_buf.name = ".debug_rnglists";
+ rnglists_buf.start = dwarf_sections->data[DEBUG_RNGLISTS];
+ rnglists_buf.buf = dwarf_sections->data[DEBUG_RNGLISTS] + offset;
+ rnglists_buf.left = dwarf_sections->size[DEBUG_RNGLISTS] - offset;
+ rnglists_buf.is_bigendian = is_bigendian;
+ rnglists_buf.error_callback = error_callback;
+ rnglists_buf.data = data;
+ rnglists_buf.reported_underflow = 0;
+
+ if (pcrange->ranges_is_index)
+ {
+ offset = read_offset (&rnglists_buf, u->is_dwarf64);
+ offset += u->rnglists_base;
+ if (offset >= dwarf_sections->size[DEBUG_RNGLISTS])
+ {
+ error_callback (data, "rnglists index offset out of range", 0);
+ return 0;
+ }
+ rnglists_buf.buf = dwarf_sections->data[DEBUG_RNGLISTS] + offset;
+ rnglists_buf.left = dwarf_sections->size[DEBUG_RNGLISTS] - offset;
+ }
+
+ while (1)
+ {
+ unsigned char rle;
+
+ rle = read_byte (&rnglists_buf);
+ if (rle == DW_RLE_end_of_list)
+ break;
+ switch (rle)
+ {
+ case DW_RLE_base_addressx:
+ {
+ uint64_t index;
+
+ index = read_uleb128 (&rnglists_buf);
+ if (!resolve_addr_index (dwarf_sections, u->addr_base,
+ u->addrsize, is_bigendian, index,
+ error_callback, data, &base))
+ return 0;
+ }
+ break;
+
+ case DW_RLE_startx_endx:
+ {
+ uint64_t index;
+ uint64_t low;
+ uint64_t high;
+
+ index = read_uleb128 (&rnglists_buf);
+ if (!resolve_addr_index (dwarf_sections, u->addr_base,
+ u->addrsize, is_bigendian, index,
+ error_callback, data, &low))
+ return 0;
+ index = read_uleb128 (&rnglists_buf);
+ if (!resolve_addr_index (dwarf_sections, u->addr_base,
+ u->addrsize, is_bigendian, index,
+ error_callback, data, &high))
+ return 0;
+ if (!add_range (state, rdata, low + base_address,
+ high + base_address, error_callback, data,
+ vec))
+ return 0;
+ }
+ break;
+
+ case DW_RLE_startx_length:
+ {
+ uint64_t index;
+ uint64_t low;
+ uint64_t length;
+
+ index = read_uleb128 (&rnglists_buf);
+ if (!resolve_addr_index (dwarf_sections, u->addr_base,
+ u->addrsize, is_bigendian, index,
+ error_callback, data, &low))
+ return 0;
+ length = read_uleb128 (&rnglists_buf);
+ low += base_address;
+ if (!add_range (state, rdata, low, low + length,
+ error_callback, data, vec))
+ return 0;
+ }
+ break;
+
+ case DW_RLE_offset_pair:
+ {
+ uint64_t low;
+ uint64_t high;
+
+ low = read_uleb128 (&rnglists_buf);
+ high = read_uleb128 (&rnglists_buf);
+ if (!add_range (state, rdata, low + base + base_address,
+ high + base + base_address,
+ error_callback, data, vec))
+ return 0;
+ }
+ break;
+
+ case DW_RLE_base_address:
+ base = read_address (&rnglists_buf, u->addrsize);
+ break;
+
+ case DW_RLE_start_end:
+ {
+ uint64_t low;
+ uint64_t high;
+
+ low = read_address (&rnglists_buf, u->addrsize);
+ high = read_address (&rnglists_buf, u->addrsize);
+ if (!add_range (state, rdata, low + base_address,
+ high + base_address, error_callback, data,
+ vec))
+ return 0;
+ }
+ break;
+
+ case DW_RLE_start_length:
+ {
+ uint64_t low;
+ uint64_t length;
+
+ low = read_address (&rnglists_buf, u->addrsize);
+ length = read_uleb128 (&rnglists_buf);
+ low += base_address;
+ if (!add_range (state, rdata, low, low + length,
+ error_callback, data, vec))
+ return 0;
+ }
+ break;
+
+ default:
+ dwarf_buf_error (&rnglists_buf, "unrecognized DW_RLE value", -1);
+ return 0;
+ }
+ }
+
+ if (rnglists_buf.reported_underflow)
+ return 0;
+
+ return 1;
+}
+
+/* Call ADD_RANGE for each lowpc/highpc pair in PCRANGE. RDATA is
+ passed to ADD_RANGE, and is either a struct unit * or a struct
+ function *. VEC is the vector we are adding ranges to, and is
+ either a struct unit_addrs_vector * or a struct function_vector *.
+ Returns 1 on success, 0 on error. */
+
+static int
+add_ranges (struct backtrace_state *state,
+ const struct dwarf_sections *dwarf_sections,
+ uintptr_t base_address, int is_bigendian,
+ struct unit *u, uint64_t base, const struct pcrange *pcrange,
+ int (*add_range) (struct backtrace_state *state, void *rdata,
+ uint64_t lowpc, uint64_t highpc,
+ backtrace_error_callback error_callback,
+ void *data, void *vec),
+ void *rdata,
+ backtrace_error_callback error_callback, void *data,
+ void *vec)
+{
+ if (pcrange->have_lowpc && pcrange->have_highpc)
+ return add_low_high_range (state, dwarf_sections, base_address,
+ is_bigendian, u, pcrange, add_range, rdata,
+ error_callback, data, vec);
+
+ if (!pcrange->have_ranges)
+ {
+ /* Did not find any address ranges to add. */
+ return 1;
+ }
+
+ if (u->version < 5)
+ return add_ranges_from_ranges (state, dwarf_sections, base_address,
+ is_bigendian, u, base, pcrange, add_range,
+ rdata, error_callback, data, vec);
+ else
+ return add_ranges_from_rnglists (state, dwarf_sections, base_address,
+ is_bigendian, u, base, pcrange, add_range,
+ rdata, error_callback, data, vec);
+}
+
+/* Find the address range covered by a compilation unit, reading from
+ UNIT_BUF and adding values to U. Returns 1 if all data could be
+ read, 0 if there is some error. */
+
+static int
+find_address_ranges (struct backtrace_state *state, uintptr_t base_address,
+ struct dwarf_buf *unit_buf,
+ const struct dwarf_sections *dwarf_sections,
+ int is_bigendian, struct dwarf_data *altlink,
+ backtrace_error_callback error_callback, void *data,
+ struct unit *u, struct unit_addrs_vector *addrs,
+ enum dwarf_tag *unit_tag)
+{
+ while (unit_buf->left > 0)
+ {
+ uint64_t code;
+ const struct abbrev *abbrev;
+ struct pcrange pcrange;
+ struct attr_val name_val;
+ int have_name_val;
+ struct attr_val comp_dir_val;
+ int have_comp_dir_val;
+ size_t i;
+
+ code = read_uleb128 (unit_buf);
+ if (code == 0)
+ return 1;
+
+ abbrev = lookup_abbrev (&u->abbrevs, code, error_callback, data);
+ if (abbrev == NULL)
+ return 0;
+
+ if (unit_tag != NULL)
+ *unit_tag = abbrev->tag;
+
+ memset (&pcrange, 0, sizeof pcrange);
+ memset (&name_val, 0, sizeof name_val);
+ have_name_val = 0;
+ memset (&comp_dir_val, 0, sizeof comp_dir_val);
+ have_comp_dir_val = 0;
+ for (i = 0; i < abbrev->num_attrs; ++i)
+ {
+ struct attr_val val;
+
+ if (!read_attribute (abbrev->attrs[i].form, abbrev->attrs[i].val,
+ unit_buf, u->is_dwarf64, u->version,
+ u->addrsize, dwarf_sections, altlink, &val))
+ return 0;
+
+ switch (abbrev->attrs[i].name)
+ {
+ case DW_AT_low_pc: case DW_AT_high_pc: case DW_AT_ranges:
+ update_pcrange (&abbrev->attrs[i], &val, &pcrange);
+ break;
+
+ case DW_AT_stmt_list:
+ if ((abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ && (val.encoding == ATTR_VAL_UINT
+ || val.encoding == ATTR_VAL_REF_SECTION))
+ u->lineoff = val.u.uint;
+ break;
+
+ case DW_AT_name:
+ if (abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ {
+ name_val = val;
+ have_name_val = 1;
+ }
+ break;
+
+ case DW_AT_comp_dir:
+ if (abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ {
+ comp_dir_val = val;
+ have_comp_dir_val = 1;
+ }
+ break;
+
+ case DW_AT_str_offsets_base:
+ if ((abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ && val.encoding == ATTR_VAL_REF_SECTION)
+ u->str_offsets_base = val.u.uint;
+ break;
+
+ case DW_AT_addr_base:
+ if ((abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ && val.encoding == ATTR_VAL_REF_SECTION)
+ u->addr_base = val.u.uint;
+ break;
+
+ case DW_AT_rnglists_base:
+ if ((abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ && val.encoding == ATTR_VAL_REF_SECTION)
+ u->rnglists_base = val.u.uint;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Resolve strings after we're sure that we have seen
+ // DW_AT_str_offsets_base.
+ if (have_name_val)
+ {
+ if (!resolve_string (dwarf_sections, u->is_dwarf64, is_bigendian,
+ u->str_offsets_base, &name_val,
+ error_callback, data, &u->filename))
+ return 0;
+ }
+ if (have_comp_dir_val)
+ {
+ if (!resolve_string (dwarf_sections, u->is_dwarf64, is_bigendian,
+ u->str_offsets_base, &comp_dir_val,
+ error_callback, data, &u->comp_dir))
+ return 0;
+ }
+
+ if (abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_subprogram
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ {
+ if (!add_ranges (state, dwarf_sections, base_address,
+ is_bigendian, u, pcrange.lowpc, &pcrange,
+ add_unit_addr, (void *) u, error_callback, data,
+ (void *) addrs))
+ return 0;
+
+ /* If we found the PC range in the DW_TAG_compile_unit or
+ DW_TAG_skeleton_unit, we can stop now. */
+ if ((abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ && (pcrange.have_ranges
+ || (pcrange.have_lowpc && pcrange.have_highpc)))
+ return 1;
+ }
+
+ if (abbrev->has_children)
+ {
+ if (!find_address_ranges (state, base_address, unit_buf,
+ dwarf_sections, is_bigendian, altlink,
+ error_callback, data, u, addrs, NULL))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Build a mapping from address ranges to the compilation units where
+ the line number information for that range can be found. Returns 1
+ on success, 0 on failure. */
+
+static int
+build_address_map (struct backtrace_state *state, uintptr_t base_address,
+ const struct dwarf_sections *dwarf_sections,
+ int is_bigendian, struct dwarf_data *altlink,
+ backtrace_error_callback error_callback, void *data,
+ struct unit_addrs_vector *addrs,
+ struct unit_vector *unit_vec)
+{
+ struct dwarf_buf info;
+ struct backtrace_vector units;
+ size_t units_count;
+ size_t i;
+ struct unit **pu;
+ size_t unit_offset = 0;
+ struct unit_addrs *pa;
+
+ memset (&addrs->vec, 0, sizeof addrs->vec);
+ memset (&unit_vec->vec, 0, sizeof unit_vec->vec);
+ addrs->count = 0;
+ unit_vec->count = 0;
+
+ /* Read through the .debug_info section. FIXME: Should we use the
+ .debug_aranges section? gdb and addr2line don't use it, but I'm
+ not sure why. */
+
+ info.name = ".debug_info";
+ info.start = dwarf_sections->data[DEBUG_INFO];
+ info.buf = info.start;
+ info.left = dwarf_sections->size[DEBUG_INFO];
+ info.is_bigendian = is_bigendian;
+ info.error_callback = error_callback;
+ info.data = data;
+ info.reported_underflow = 0;
+
+ memset (&units, 0, sizeof units);
+ units_count = 0;
+
+ while (info.left > 0)
+ {
+ const unsigned char *unit_data_start;
+ uint64_t len;
+ int is_dwarf64;
+ struct dwarf_buf unit_buf;
+ int version;
+ int unit_type;
+ uint64_t abbrev_offset;
+ int addrsize;
+ struct unit *u;
+ enum dwarf_tag unit_tag;
+
+ if (info.reported_underflow)
+ goto fail;
+
+ unit_data_start = info.buf;
+
+ len = read_initial_length (&info, &is_dwarf64);
+ unit_buf = info;
+ unit_buf.left = len;
+
+ if (!advance (&info, len))
+ goto fail;
+
+ version = read_uint16 (&unit_buf);
+ if (version < 2 || version > 5)
+ {
+ dwarf_buf_error (&unit_buf, "unrecognized DWARF version", -1);
+ goto fail;
+ }
+
+ if (version < 5)
+ unit_type = 0;
+ else
+ {
+ unit_type = read_byte (&unit_buf);
+ if (unit_type == DW_UT_type || unit_type == DW_UT_split_type)
+ {
+ /* This unit doesn't have anything we need. */
+ continue;
+ }
+ }
+
+ pu = ((struct unit **)
+ backtrace_vector_grow (state, sizeof (struct unit *),
+ error_callback, data, &units));
+ if (pu == NULL)
+ goto fail;
+
+ u = ((struct unit *)
+ backtrace_alloc (state, sizeof *u, error_callback, data));
+ if (u == NULL)
+ goto fail;
+
+ *pu = u;
+ ++units_count;
+
+ if (version < 5)
+ addrsize = 0; /* Set below. */
+ else
+ addrsize = read_byte (&unit_buf);
+
+ memset (&u->abbrevs, 0, sizeof u->abbrevs);
+ abbrev_offset = read_offset (&unit_buf, is_dwarf64);
+ if (!read_abbrevs (state, abbrev_offset,
+ dwarf_sections->data[DEBUG_ABBREV],
+ dwarf_sections->size[DEBUG_ABBREV],
+ is_bigendian, error_callback, data, &u->abbrevs))
+ goto fail;
+
+ if (version < 5)
+ addrsize = read_byte (&unit_buf);
+
+ switch (unit_type)
+ {
+ case 0:
+ break;
+ case DW_UT_compile: case DW_UT_partial:
+ break;
+ case DW_UT_skeleton: case DW_UT_split_compile:
+ read_uint64 (&unit_buf); /* dwo_id */
+ break;
+ default:
+ break;
+ }
+
+ u->low_offset = unit_offset;
+ unit_offset += len + (is_dwarf64 ? 12 : 4);
+ u->high_offset = unit_offset;
+ u->unit_data = unit_buf.buf;
+ u->unit_data_len = unit_buf.left;
+ u->unit_data_offset = unit_buf.buf - unit_data_start;
+ u->version = version;
+ u->is_dwarf64 = is_dwarf64;
+ u->addrsize = addrsize;
+ u->filename = NULL;
+ u->comp_dir = NULL;
+ u->abs_filename = NULL;
+ u->lineoff = 0;
+ u->str_offsets_base = 0;
+ u->addr_base = 0;
+ u->rnglists_base = 0;
+
+ /* The actual line number mappings will be read as needed. */
+ u->lines = NULL;
+ u->lines_count = 0;
+ u->function_addrs = NULL;
+ u->function_addrs_count = 0;
+
+ if (!find_address_ranges (state, base_address, &unit_buf, dwarf_sections,
+ is_bigendian, altlink, error_callback, data,
+ u, addrs, &unit_tag))
+ goto fail;
+
+ if (unit_buf.reported_underflow)
+ goto fail;
+ }
+ if (info.reported_underflow)
+ goto fail;
+
+ /* Add a trailing addrs entry, but don't include it in addrs->count. */
+ pa = ((struct unit_addrs *)
+ backtrace_vector_grow (state, sizeof (struct unit_addrs),
+ error_callback, data, &addrs->vec));
+ if (pa == NULL)
+ goto fail;
+ pa->low = 0;
+ --pa->low;
+ pa->high = pa->low;
+ pa->u = NULL;
+
+ unit_vec->vec = units;
+ unit_vec->count = units_count;
+ return 1;
+
+ fail:
+ if (units_count > 0)
+ {
+ pu = (struct unit **) units.base;
+ for (i = 0; i < units_count; i++)
+ {
+ free_abbrevs (state, &pu[i]->abbrevs, error_callback, data);
+ backtrace_free (state, pu[i], sizeof **pu, error_callback, data);
+ }
+ backtrace_vector_free (state, &units, error_callback, data);
+ }
+ if (addrs->count > 0)
+ {
+ backtrace_vector_free (state, &addrs->vec, error_callback, data);
+ addrs->count = 0;
+ }
+ return 0;
+}
+
+/* Add a new mapping to the vector of line mappings that we are
+ building. Returns 1 on success, 0 on failure. */
+
+static int
+add_line (struct backtrace_state *state, struct dwarf_data *ddata,
+ uintptr_t pc, const char *filename, int lineno,
+ backtrace_error_callback error_callback, void *data,
+ struct line_vector *vec)
+{
+ struct line *ln;
+
+ /* If we are adding the same mapping, ignore it. This can happen
+ when using discriminators. */
+ if (vec->count > 0)
+ {
+ ln = (struct line *) vec->vec.base + (vec->count - 1);
+ if (pc == ln->pc && filename == ln->filename && lineno == ln->lineno)
+ return 1;
+ }
+
+ ln = ((struct line *)
+ backtrace_vector_grow (state, sizeof (struct line), error_callback,
+ data, &vec->vec));
+ if (ln == NULL)
+ return 0;
+
+ /* Add in the base address here, so that we can look up the PC
+ directly. */
+ ln->pc = pc + ddata->base_address;
+
+ ln->filename = filename;
+ ln->lineno = lineno;
+ ln->idx = vec->count;
+
+ ++vec->count;
+
+ return 1;
+}
+
+/* Free the line header information. */
+
+static void
+free_line_header (struct backtrace_state *state, struct line_header *hdr,
+ backtrace_error_callback error_callback, void *data)
+{
+ if (hdr->dirs_count != 0)
+ backtrace_free (state, hdr->dirs, hdr->dirs_count * sizeof (const char *),
+ error_callback, data);
+ backtrace_free (state, hdr->filenames,
+ hdr->filenames_count * sizeof (char *),
+ error_callback, data);
+}
+
+/* Read the directories and file names for a line header for version
+ 2, setting fields in HDR. Return 1 on success, 0 on failure. */
+
+static int
+read_v2_paths (struct backtrace_state *state, struct unit *u,
+ struct dwarf_buf *hdr_buf, struct line_header *hdr)
+{
+ const unsigned char *p;
+ const unsigned char *pend;
+ size_t i;
+
+ /* Count the number of directory entries. */
+ hdr->dirs_count = 0;
+ p = hdr_buf->buf;
+ pend = p + hdr_buf->left;
+ while (p < pend && *p != '\0')
+ {
+ p += strnlen((const char *) p, pend - p) + 1;
+ ++hdr->dirs_count;
+ }
+
+ /* The index of the first entry in the list of directories is 1. Index 0 is
+ used for the current directory of the compilation. To simplify index
+ handling, we set entry 0 to the compilation unit directory. */
+ ++hdr->dirs_count;
+ hdr->dirs = ((const char **)
+ backtrace_alloc (state,
+ hdr->dirs_count * sizeof (const char *),
+ hdr_buf->error_callback,
+ hdr_buf->data));
+ if (hdr->dirs == NULL)
+ return 0;
+
+ hdr->dirs[0] = u->comp_dir;
+ i = 1;
+ while (*hdr_buf->buf != '\0')
+ {
+ if (hdr_buf->reported_underflow)
+ return 0;
+
+ hdr->dirs[i] = read_string (hdr_buf);
+ if (hdr->dirs[i] == NULL)
+ return 0;
+ ++i;
+ }
+ if (!advance (hdr_buf, 1))
+ return 0;
+
+ /* Count the number of file entries. */
+ hdr->filenames_count = 0;
+ p = hdr_buf->buf;
+ pend = p + hdr_buf->left;
+ while (p < pend && *p != '\0')
+ {
+ p += strnlen ((const char *) p, pend - p) + 1;
+ p += leb128_len (p);
+ p += leb128_len (p);
+ p += leb128_len (p);
+ ++hdr->filenames_count;
+ }
+
+ /* The index of the first entry in the list of file names is 1. Index 0 is
+ used for the DW_AT_name of the compilation unit. To simplify index
+ handling, we set entry 0 to the compilation unit file name. */
+ ++hdr->filenames_count;
+ hdr->filenames = ((const char **)
+ backtrace_alloc (state,
+ hdr->filenames_count * sizeof (char *),
+ hdr_buf->error_callback,
+ hdr_buf->data));
+ if (hdr->filenames == NULL)
+ return 0;
+ hdr->filenames[0] = u->filename;
+ i = 1;
+ while (*hdr_buf->buf != '\0')
+ {
+ const char *filename;
+ uint64_t dir_index;
+
+ if (hdr_buf->reported_underflow)
+ return 0;
+
+ filename = read_string (hdr_buf);
+ if (filename == NULL)
+ return 0;
+ dir_index = read_uleb128 (hdr_buf);
+ if (IS_ABSOLUTE_PATH (filename)
+ || (dir_index < hdr->dirs_count && hdr->dirs[dir_index] == NULL))
+ hdr->filenames[i] = filename;
+ else
+ {
+ const char *dir;
+ size_t dir_len;
+ size_t filename_len;
+ char *s;
+
+ if (dir_index < hdr->dirs_count)
+ dir = hdr->dirs[dir_index];
+ else
+ {
+ dwarf_buf_error (hdr_buf,
+ ("invalid directory index in "
+ "line number program header"),
+ 0);
+ return 0;
+ }
+ dir_len = strlen (dir);
+ filename_len = strlen (filename);
+ s = ((char *) backtrace_alloc (state, dir_len + filename_len + 2,
+ hdr_buf->error_callback,
+ hdr_buf->data));
+ if (s == NULL)
+ return 0;
+ memcpy (s, dir, dir_len);
+ /* FIXME: If we are on a DOS-based file system, and the
+ directory or the file name use backslashes, then we
+ should use a backslash here. */
+ s[dir_len] = '/';
+ memcpy (s + dir_len + 1, filename, filename_len + 1);
+ hdr->filenames[i] = s;
+ }
+
+ /* Ignore the modification time and size. */
+ read_uleb128 (hdr_buf);
+ read_uleb128 (hdr_buf);
+
+ ++i;
+ }
+
+ return 1;
+}
+
+/* Read a single version 5 LNCT entry for a directory or file name in a
+ line header. Sets *STRING to the resulting name, ignoring other
+ data. Return 1 on success, 0 on failure. */
+
+static int
+read_lnct (struct backtrace_state *state, struct dwarf_data *ddata,
+ struct unit *u, struct dwarf_buf *hdr_buf,
+ const struct line_header *hdr, size_t formats_count,
+ const struct line_header_format *formats, const char **string)
+{
+ size_t i;
+ const char *dir;
+ const char *path;
+
+ dir = NULL;
+ path = NULL;
+ for (i = 0; i < formats_count; i++)
+ {
+ struct attr_val val;
+
+ if (!read_attribute (formats[i].form, 0, hdr_buf, u->is_dwarf64,
+ u->version, hdr->addrsize, &ddata->dwarf_sections,
+ ddata->altlink, &val))
+ return 0;
+ switch (formats[i].lnct)
+ {
+ case DW_LNCT_path:
+ if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
+ ddata->is_bigendian, u->str_offsets_base,
+ &val, hdr_buf->error_callback, hdr_buf->data,
+ &path))
+ return 0;
+ break;
+ case DW_LNCT_directory_index:
+ if (val.encoding == ATTR_VAL_UINT)
+ {
+ if (val.u.uint >= hdr->dirs_count)
+ {
+ dwarf_buf_error (hdr_buf,
+ ("invalid directory index in "
+ "line number program header"),
+ 0);
+ return 0;
+ }
+ dir = hdr->dirs[val.u.uint];
+ }
+ break;
+ default:
+ /* We don't care about timestamps or sizes or hashes. */
+ break;
+ }
+ }
+
+ if (path == NULL)
+ {
+ dwarf_buf_error (hdr_buf,
+ "missing file name in line number program header",
+ 0);
+ return 0;
+ }
+
+ if (dir == NULL)
+ *string = path;
+ else
+ {
+ size_t dir_len;
+ size_t path_len;
+ char *s;
+
+ dir_len = strlen (dir);
+ path_len = strlen (path);
+ s = (char *) backtrace_alloc (state, dir_len + path_len + 2,
+ hdr_buf->error_callback, hdr_buf->data);
+ if (s == NULL)
+ return 0;
+ memcpy (s, dir, dir_len);
+ /* FIXME: If we are on a DOS-based file system, and the
+ directory or the path name use backslashes, then we should
+ use a backslash here. */
+ s[dir_len] = '/';
+ memcpy (s + dir_len + 1, path, path_len + 1);
+ *string = s;
+ }
+
+ return 1;
+}
+
+/* Read a set of DWARF 5 line header format entries, setting *PCOUNT
+ and *PPATHS. Return 1 on success, 0 on failure. */
+
+static int
+read_line_header_format_entries (struct backtrace_state *state,
+ struct dwarf_data *ddata,
+ struct unit *u,
+ struct dwarf_buf *hdr_buf,
+ struct line_header *hdr,
+ size_t *pcount,
+ const char ***ppaths)
+{
+ size_t formats_count;
+ struct line_header_format *formats;
+ size_t paths_count;
+ const char **paths;
+ size_t i;
+ int ret;
+
+ formats_count = read_byte (hdr_buf);
+ if (formats_count == 0)
+ formats = NULL;
+ else
+ {
+ formats = ((struct line_header_format *)
+ backtrace_alloc (state,
+ (formats_count
+ * sizeof (struct line_header_format)),
+ hdr_buf->error_callback,
+ hdr_buf->data));
+ if (formats == NULL)
+ return 0;
+
+ for (i = 0; i < formats_count; i++)
+ {
+ formats[i].lnct = (int) read_uleb128(hdr_buf);
+ formats[i].form = (enum dwarf_form) read_uleb128 (hdr_buf);
+ }
+ }
+
+ paths_count = read_uleb128 (hdr_buf);
+ if (paths_count == 0)
+ {
+ *pcount = 0;
+ *ppaths = NULL;
+ ret = 1;
+ goto exit;
+ }
+
+ paths = ((const char **)
+ backtrace_alloc (state, paths_count * sizeof (const char *),
+ hdr_buf->error_callback, hdr_buf->data));
+ if (paths == NULL)
+ {
+ ret = 0;
+ goto exit;
+ }
+ for (i = 0; i < paths_count; i++)
+ {
+ if (!read_lnct (state, ddata, u, hdr_buf, hdr, formats_count,
+ formats, &paths[i]))
+ {
+ backtrace_free (state, paths,
+ paths_count * sizeof (const char *),
+ hdr_buf->error_callback, hdr_buf->data);
+ ret = 0;
+ goto exit;
+ }
+ }
+
+ *pcount = paths_count;
+ *ppaths = paths;
+
+ ret = 1;
+
+ exit:
+ if (formats != NULL)
+ backtrace_free (state, formats,
+ formats_count * sizeof (struct line_header_format),
+ hdr_buf->error_callback, hdr_buf->data);
+
+ return ret;
+}
+
+/* Read the line header. Return 1 on success, 0 on failure. */
+
+static int
+read_line_header (struct backtrace_state *state, struct dwarf_data *ddata,
+ struct unit *u, int is_dwarf64, struct dwarf_buf *line_buf,
+ struct line_header *hdr)
+{
+ uint64_t hdrlen;
+ struct dwarf_buf hdr_buf;
+
+ hdr->version = read_uint16 (line_buf);
+ if (hdr->version < 2 || hdr->version > 5)
+ {
+ dwarf_buf_error (line_buf, "unsupported line number version", -1);
+ return 0;
+ }
+
+ if (hdr->version < 5)
+ hdr->addrsize = u->addrsize;
+ else
+ {
+ hdr->addrsize = read_byte (line_buf);
+ /* We could support a non-zero segment_selector_size but I doubt
+ we'll ever see it. */
+ if (read_byte (line_buf) != 0)
+ {
+ dwarf_buf_error (line_buf,
+ "non-zero segment_selector_size not supported",
+ -1);
+ return 0;
+ }
+ }
+
+ hdrlen = read_offset (line_buf, is_dwarf64);
+
+ hdr_buf = *line_buf;
+ hdr_buf.left = hdrlen;
+
+ if (!advance (line_buf, hdrlen))
+ return 0;
+
+ hdr->min_insn_len = read_byte (&hdr_buf);
+ if (hdr->version < 4)
+ hdr->max_ops_per_insn = 1;
+ else
+ hdr->max_ops_per_insn = read_byte (&hdr_buf);
+
+ /* We don't care about default_is_stmt. */
+ read_byte (&hdr_buf);
+
+ hdr->line_base = read_sbyte (&hdr_buf);
+ hdr->line_range = read_byte (&hdr_buf);
+
+ hdr->opcode_base = read_byte (&hdr_buf);
+ hdr->opcode_lengths = hdr_buf.buf;
+ if (!advance (&hdr_buf, hdr->opcode_base - 1))
+ return 0;
+
+ if (hdr->version < 5)
+ {
+ if (!read_v2_paths (state, u, &hdr_buf, hdr))
+ return 0;
+ }
+ else
+ {
+ if (!read_line_header_format_entries (state, ddata, u, &hdr_buf, hdr,
+ &hdr->dirs_count,
+ &hdr->dirs))
+ return 0;
+ if (!read_line_header_format_entries (state, ddata, u, &hdr_buf, hdr,
+ &hdr->filenames_count,
+ &hdr->filenames))
+ return 0;
+ }
+
+ if (hdr_buf.reported_underflow)
+ return 0;
+
+ return 1;
+}
+
+/* Read the line program, adding line mappings to VEC. Return 1 on
+ success, 0 on failure. */
+
+static int
+read_line_program (struct backtrace_state *state, struct dwarf_data *ddata,
+ const struct line_header *hdr, struct dwarf_buf *line_buf,
+ struct line_vector *vec)
+{
+ uint64_t address;
+ unsigned int op_index;
+ const char *reset_filename;
+ const char *filename;
+ int lineno;
+
+ address = 0;
+ op_index = 0;
+ if (hdr->filenames_count > 1)
+ reset_filename = hdr->filenames[1];
+ else
+ reset_filename = "";
+ filename = reset_filename;
+ lineno = 1;
+ while (line_buf->left > 0)
+ {
+ unsigned int op;
+
+ op = read_byte (line_buf);
+ if (op >= hdr->opcode_base)
+ {
+ unsigned int advance;
+
+ /* Special opcode. */
+ op -= hdr->opcode_base;
+ advance = op / hdr->line_range;
+ address += (hdr->min_insn_len * (op_index + advance)
+ / hdr->max_ops_per_insn);
+ op_index = (op_index + advance) % hdr->max_ops_per_insn;
+ lineno += hdr->line_base + (int) (op % hdr->line_range);
+ add_line (state, ddata, address, filename, lineno,
+ line_buf->error_callback, line_buf->data, vec);
+ }
+ else if (op == DW_LNS_extended_op)
+ {
+ uint64_t len;
+
+ len = read_uleb128 (line_buf);
+ op = read_byte (line_buf);
+ switch (op)
+ {
+ case DW_LNE_end_sequence:
+ /* FIXME: Should we mark the high PC here? It seems
+ that we already have that information from the
+ compilation unit. */
+ address = 0;
+ op_index = 0;
+ filename = reset_filename;
+ lineno = 1;
+ break;
+ case DW_LNE_set_address:
+ address = read_address (line_buf, hdr->addrsize);
+ break;
+ case DW_LNE_define_file:
+ {
+ const char *f;
+ unsigned int dir_index;
+
+ f = read_string (line_buf);
+ if (f == NULL)
+ return 0;
+ dir_index = read_uleb128 (line_buf);
+ /* Ignore that time and length. */
+ read_uleb128 (line_buf);
+ read_uleb128 (line_buf);
+ if (IS_ABSOLUTE_PATH (f))
+ filename = f;
+ else
+ {
+ const char *dir;
+ size_t dir_len;
+ size_t f_len;
+ char *p;
+
+ if (dir_index < hdr->dirs_count)
+ dir = hdr->dirs[dir_index];
+ else
+ {
+ dwarf_buf_error (line_buf,
+ ("invalid directory index "
+ "in line number program"),
+ 0);
+ return 0;
+ }
+ dir_len = strlen (dir);
+ f_len = strlen (f);
+ p = ((char *)
+ backtrace_alloc (state, dir_len + f_len + 2,
+ line_buf->error_callback,
+ line_buf->data));
+ if (p == NULL)
+ return 0;
+ memcpy (p, dir, dir_len);
+ /* FIXME: If we are on a DOS-based file system,
+ and the directory or the file name use
+ backslashes, then we should use a backslash
+ here. */
+ p[dir_len] = '/';
+ memcpy (p + dir_len + 1, f, f_len + 1);
+ filename = p;
+ }
+ }
+ break;
+ case DW_LNE_set_discriminator:
+ /* We don't care about discriminators. */
+ read_uleb128 (line_buf);
+ break;
+ default:
+ if (!advance (line_buf, len - 1))
+ return 0;
+ break;
+ }
+ }
+ else
+ {
+ switch (op)
+ {
+ case DW_LNS_copy:
+ add_line (state, ddata, address, filename, lineno,
+ line_buf->error_callback, line_buf->data, vec);
+ break;
+ case DW_LNS_advance_pc:
+ {
+ uint64_t advance;
+
+ advance = read_uleb128 (line_buf);
+ address += (hdr->min_insn_len * (op_index + advance)
+ / hdr->max_ops_per_insn);
+ op_index = (op_index + advance) % hdr->max_ops_per_insn;
+ }
+ break;
+ case DW_LNS_advance_line:
+ lineno += (int) read_sleb128 (line_buf);
+ break;
+ case DW_LNS_set_file:
+ {
+ uint64_t fileno;
+
+ fileno = read_uleb128 (line_buf);
+ if (fileno >= hdr->filenames_count)
+ {
+ dwarf_buf_error (line_buf,
+ ("invalid file number in "
+ "line number program"),
+ 0);
+ return 0;
+ }
+ filename = hdr->filenames[fileno];
+ }
+ break;
+ case DW_LNS_set_column:
+ read_uleb128 (line_buf);
+ break;
+ case DW_LNS_negate_stmt:
+ break;
+ case DW_LNS_set_basic_block:
+ break;
+ case DW_LNS_const_add_pc:
+ {
+ unsigned int advance;
+
+ op = 255 - hdr->opcode_base;
+ advance = op / hdr->line_range;
+ address += (hdr->min_insn_len * (op_index + advance)
+ / hdr->max_ops_per_insn);
+ op_index = (op_index + advance) % hdr->max_ops_per_insn;
+ }
+ break;
+ case DW_LNS_fixed_advance_pc:
+ address += read_uint16 (line_buf);
+ op_index = 0;
+ break;
+ case DW_LNS_set_prologue_end:
+ break;
+ case DW_LNS_set_epilogue_begin:
+ break;
+ case DW_LNS_set_isa:
+ read_uleb128 (line_buf);
+ break;
+ default:
+ {
+ unsigned int i;
+
+ for (i = hdr->opcode_lengths[op - 1]; i > 0; --i)
+ read_uleb128 (line_buf);
+ }
+ break;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/* Read the line number information for a compilation unit. Returns 1
+ on success, 0 on failure. */
+
+static int
+read_line_info (struct backtrace_state *state, struct dwarf_data *ddata,
+ backtrace_error_callback error_callback, void *data,
+ struct unit *u, struct line_header *hdr, struct line **lines,
+ size_t *lines_count)
+{
+ struct line_vector vec;
+ struct dwarf_buf line_buf;
+ uint64_t len;
+ int is_dwarf64;
+ struct line *ln;
+
+ memset (&vec.vec, 0, sizeof vec.vec);
+ vec.count = 0;
+
+ memset (hdr, 0, sizeof *hdr);
+
+ if (u->lineoff != (off_t) (size_t) u->lineoff
+ || (size_t) u->lineoff >= ddata->dwarf_sections.size[DEBUG_LINE])
+ {
+ error_callback (data, "unit line offset out of range", 0);
+ goto fail;
+ }
+
+ line_buf.name = ".debug_line";
+ line_buf.start = ddata->dwarf_sections.data[DEBUG_LINE];
+ line_buf.buf = ddata->dwarf_sections.data[DEBUG_LINE] + u->lineoff;
+ line_buf.left = ddata->dwarf_sections.size[DEBUG_LINE] - u->lineoff;
+ line_buf.is_bigendian = ddata->is_bigendian;
+ line_buf.error_callback = error_callback;
+ line_buf.data = data;
+ line_buf.reported_underflow = 0;
+
+ len = read_initial_length (&line_buf, &is_dwarf64);
+ line_buf.left = len;
+
+ if (!read_line_header (state, ddata, u, is_dwarf64, &line_buf, hdr))
+ goto fail;
+
+ if (!read_line_program (state, ddata, hdr, &line_buf, &vec))
+ goto fail;
+
+ if (line_buf.reported_underflow)
+ goto fail;
+
+ if (vec.count == 0)
+ {
+ /* This is not a failure in the sense of a generating an error,
+ but it is a failure in that sense that we have no useful
+ information. */
+ goto fail;
+ }
+
+ /* Allocate one extra entry at the end. */
+ ln = ((struct line *)
+ backtrace_vector_grow (state, sizeof (struct line), error_callback,
+ data, &vec.vec));
+ if (ln == NULL)
+ goto fail;
+ ln->pc = (uintptr_t) -1;
+ ln->filename = NULL;
+ ln->lineno = 0;
+ ln->idx = 0;
+
+ if (!backtrace_vector_release (state, &vec.vec, error_callback, data))
+ goto fail;
+
+ ln = (struct line *) vec.vec.base;
+ backtrace_qsort (ln, vec.count, sizeof (struct line), line_compare);
+
+ *lines = ln;
+ *lines_count = vec.count;
+
+ return 1;
+
+ fail:
+ backtrace_vector_free (state, &vec.vec, error_callback, data);
+ free_line_header (state, hdr, error_callback, data);
+ *lines = (struct line *) (uintptr_t) -1;
+ *lines_count = 0;
+ return 0;
+}
+
+static const char *read_referenced_name (struct dwarf_data *, struct unit *,
+ uint64_t, backtrace_error_callback,
+ void *);
+
+/* Read the name of a function from a DIE referenced by ATTR with VAL. */
+
+static const char *
+read_referenced_name_from_attr (struct dwarf_data *ddata, struct unit *u,
+ struct attr *attr, struct attr_val *val,
+ backtrace_error_callback error_callback,
+ void *data)
+{
+ switch (attr->name)
+ {
+ case DW_AT_abstract_origin:
+ case DW_AT_specification:
+ break;
+ default:
+ return NULL;
+ }
+
+ if (attr->form == DW_FORM_ref_sig8)
+ return NULL;
+
+ if (val->encoding == ATTR_VAL_REF_INFO)
+ {
+ struct unit *unit
+ = find_unit (ddata->units, ddata->units_count,
+ val->u.uint);
+ if (unit == NULL)
+ return NULL;
+
+ uint64_t offset = val->u.uint - unit->low_offset;
+ return read_referenced_name (ddata, unit, offset, error_callback, data);
+ }
+
+ if (val->encoding == ATTR_VAL_UINT
+ || val->encoding == ATTR_VAL_REF_UNIT)
+ return read_referenced_name (ddata, u, val->u.uint, error_callback, data);
+
+ if (val->encoding == ATTR_VAL_REF_ALT_INFO)
+ {
+ struct unit *alt_unit
+ = find_unit (ddata->altlink->units, ddata->altlink->units_count,
+ val->u.uint);
+ if (alt_unit == NULL)
+ return NULL;
+
+ uint64_t offset = val->u.uint - alt_unit->low_offset;
+ return read_referenced_name (ddata->altlink, alt_unit, offset,
+ error_callback, data);
+ }
+
+ return NULL;
+}
+
+/* Read the name of a function from a DIE referenced by a
+ DW_AT_abstract_origin or DW_AT_specification tag. OFFSET is within
+ the same compilation unit. */
+
+static const char *
+read_referenced_name (struct dwarf_data *ddata, struct unit *u,
+ uint64_t offset, backtrace_error_callback error_callback,
+ void *data)
+{
+ struct dwarf_buf unit_buf;
+ uint64_t code;
+ const struct abbrev *abbrev;
+ const char *ret;
+ size_t i;
+
+ /* OFFSET is from the start of the data for this compilation unit.
+ U->unit_data is the data, but it starts U->unit_data_offset bytes
+ from the beginning. */
+
+ if (offset < u->unit_data_offset
+ || offset - u->unit_data_offset >= u->unit_data_len)
+ {
+ error_callback (data,
+ "abstract origin or specification out of range",
+ 0);
+ return NULL;
+ }
+
+ offset -= u->unit_data_offset;
+
+ unit_buf.name = ".debug_info";
+ unit_buf.start = ddata->dwarf_sections.data[DEBUG_INFO];
+ unit_buf.buf = u->unit_data + offset;
+ unit_buf.left = u->unit_data_len - offset;
+ unit_buf.is_bigendian = ddata->is_bigendian;
+ unit_buf.error_callback = error_callback;
+ unit_buf.data = data;
+ unit_buf.reported_underflow = 0;
+
+ code = read_uleb128 (&unit_buf);
+ if (code == 0)
+ {
+ dwarf_buf_error (&unit_buf,
+ "invalid abstract origin or specification",
+ 0);
+ return NULL;
+ }
+
+ abbrev = lookup_abbrev (&u->abbrevs, code, error_callback, data);
+ if (abbrev == NULL)
+ return NULL;
+
+ ret = NULL;
+ for (i = 0; i < abbrev->num_attrs; ++i)
+ {
+ struct attr_val val;
+
+ if (!read_attribute (abbrev->attrs[i].form, abbrev->attrs[i].val,
+ &unit_buf, u->is_dwarf64, u->version, u->addrsize,
+ &ddata->dwarf_sections, ddata->altlink, &val))
+ return NULL;
+
+ switch (abbrev->attrs[i].name)
+ {
+ case DW_AT_name:
+ /* Third name preference: don't override. A name we found in some
+ other way, will normally be more useful -- e.g., this name is
+ normally not mangled. */
+ if (ret != NULL)
+ break;
+ if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
+ ddata->is_bigendian, u->str_offsets_base,
+ &val, error_callback, data, &ret))
+ return NULL;
+ break;
+
+ case DW_AT_linkage_name:
+ case DW_AT_MIPS_linkage_name:
+ /* First name preference: override all. */
+ {
+ const char *s;
+
+ s = NULL;
+ if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
+ ddata->is_bigendian, u->str_offsets_base,
+ &val, error_callback, data, &s))
+ return NULL;
+ if (s != NULL)
+ return s;
+ }
+ break;
+
+ case DW_AT_specification:
+ /* Second name preference: override DW_AT_name, don't override
+ DW_AT_linkage_name. */
+ {
+ const char *name;
+
+ name = read_referenced_name_from_attr (ddata, u, &abbrev->attrs[i],
+ &val, error_callback, data);
+ if (name != NULL)
+ ret = name;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/* Add a range to a unit that maps to a function. This is called via
+ add_ranges. Returns 1 on success, 0 on error. */
+
+static int
+add_function_range (struct backtrace_state *state, void *rdata,
+ uint64_t lowpc, uint64_t highpc,
+ backtrace_error_callback error_callback, void *data,
+ void *pvec)
+{
+ struct function *function = (struct function *) rdata;
+ struct function_vector *vec = (struct function_vector *) pvec;
+ struct function_addrs *p;
+
+ if (vec->count > 0)
+ {
+ p = (struct function_addrs *) vec->vec.base + (vec->count - 1);
+ if ((lowpc == p->high || lowpc == p->high + 1)
+ && function == p->function)
+ {
+ if (highpc > p->high)
+ p->high = highpc;
+ return 1;
+ }
+ }
+
+ p = ((struct function_addrs *)
+ backtrace_vector_grow (state, sizeof (struct function_addrs),
+ error_callback, data, &vec->vec));
+ if (p == NULL)
+ return 0;
+
+ p->low = lowpc;
+ p->high = highpc;
+ p->function = function;
+
+ ++vec->count;
+
+ return 1;
+}
+
+/* Read one entry plus all its children. Add function addresses to
+ VEC. Returns 1 on success, 0 on error. */
+
+static int
+read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata,
+ struct unit *u, uint64_t base, struct dwarf_buf *unit_buf,
+ const struct line_header *lhdr,
+ backtrace_error_callback error_callback, void *data,
+ struct function_vector *vec_function,
+ struct function_vector *vec_inlined)
+{
+ while (unit_buf->left > 0)
+ {
+ uint64_t code;
+ const struct abbrev *abbrev;
+ int is_function;
+ struct function *function;
+ struct function_vector *vec;
+ size_t i;
+ struct pcrange pcrange;
+ int have_linkage_name;
+
+ code = read_uleb128 (unit_buf);
+ if (code == 0)
+ return 1;
+
+ abbrev = lookup_abbrev (&u->abbrevs, code, error_callback, data);
+ if (abbrev == NULL)
+ return 0;
+
+ is_function = (abbrev->tag == DW_TAG_subprogram
+ || abbrev->tag == DW_TAG_entry_point
+ || abbrev->tag == DW_TAG_inlined_subroutine);
+
+ if (abbrev->tag == DW_TAG_inlined_subroutine)
+ vec = vec_inlined;
+ else
+ vec = vec_function;
+
+ function = NULL;
+ if (is_function)
+ {
+ function = ((struct function *)
+ backtrace_alloc (state, sizeof *function,
+ error_callback, data));
+ if (function == NULL)
+ return 0;
+ memset (function, 0, sizeof *function);
+ }
+
+ memset (&pcrange, 0, sizeof pcrange);
+ have_linkage_name = 0;
+ for (i = 0; i < abbrev->num_attrs; ++i)
+ {
+ struct attr_val val;
+
+ if (!read_attribute (abbrev->attrs[i].form, abbrev->attrs[i].val,
+ unit_buf, u->is_dwarf64, u->version,
+ u->addrsize, &ddata->dwarf_sections,
+ ddata->altlink, &val))
+ return 0;
+
+ /* The compile unit sets the base address for any address
+ ranges in the function entries. */
+ if ((abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ && abbrev->attrs[i].name == DW_AT_low_pc)
+ {
+ if (val.encoding == ATTR_VAL_ADDRESS)
+ base = val.u.uint;
+ else if (val.encoding == ATTR_VAL_ADDRESS_INDEX)
+ {
+ if (!resolve_addr_index (&ddata->dwarf_sections,
+ u->addr_base, u->addrsize,
+ ddata->is_bigendian, val.u.uint,
+ error_callback, data, &base))
+ return 0;
+ }
+ }
+
+ if (is_function)
+ {
+ switch (abbrev->attrs[i].name)
+ {
+ case DW_AT_call_file:
+ if (val.encoding == ATTR_VAL_UINT)
+ {
+ if (val.u.uint >= lhdr->filenames_count)
+ {
+ dwarf_buf_error (unit_buf,
+ ("invalid file number in "
+ "DW_AT_call_file attribute"),
+ 0);
+ return 0;
+ }
+ function->caller_filename = lhdr->filenames[val.u.uint];
+ }
+ break;
+
+ case DW_AT_call_line:
+ if (val.encoding == ATTR_VAL_UINT)
+ function->caller_lineno = val.u.uint;
+ break;
+
+ case DW_AT_abstract_origin:
+ case DW_AT_specification:
+ /* Second name preference: override DW_AT_name, don't override
+ DW_AT_linkage_name. */
+ if (have_linkage_name)
+ break;
+ {
+ const char *name;
+
+ name
+ = read_referenced_name_from_attr (ddata, u,
+ &abbrev->attrs[i], &val,
+ error_callback, data);
+ if (name != NULL)
+ function->name = name;
+ }
+ break;
+
+ case DW_AT_name:
+ /* Third name preference: don't override. */
+ if (function->name != NULL)
+ break;
+ if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
+ ddata->is_bigendian,
+ u->str_offsets_base, &val,
+ error_callback, data, &function->name))
+ return 0;
+ break;
+
+ case DW_AT_linkage_name:
+ case DW_AT_MIPS_linkage_name:
+ /* First name preference: override all. */
+ {
+ const char *s;
+
+ s = NULL;
+ if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
+ ddata->is_bigendian,
+ u->str_offsets_base, &val,
+ error_callback, data, &s))
+ return 0;
+ if (s != NULL)
+ {
+ function->name = s;
+ have_linkage_name = 1;
+ }
+ }
+ break;
+
+ case DW_AT_low_pc: case DW_AT_high_pc: case DW_AT_ranges:
+ update_pcrange (&abbrev->attrs[i], &val, &pcrange);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /* If we couldn't find a name for the function, we have no use
+ for it. */
+ if (is_function && function->name == NULL)
+ {
+ backtrace_free (state, function, sizeof *function,
+ error_callback, data);
+ is_function = 0;
+ }
+
+ if (is_function)
+ {
+ if (pcrange.have_ranges
+ || (pcrange.have_lowpc && pcrange.have_highpc))
+ {
+ if (!add_ranges (state, &ddata->dwarf_sections,
+ ddata->base_address, ddata->is_bigendian,
+ u, base, &pcrange, add_function_range,
+ (void *) function, error_callback, data,
+ (void *) vec))
+ return 0;
+ }
+ else
+ {
+ backtrace_free (state, function, sizeof *function,
+ error_callback, data);
+ is_function = 0;
+ }
+ }
+
+ if (abbrev->has_children)
+ {
+ if (!is_function)
+ {
+ if (!read_function_entry (state, ddata, u, base, unit_buf, lhdr,
+ error_callback, data, vec_function,
+ vec_inlined))
+ return 0;
+ }
+ else
+ {
+ struct function_vector fvec;
+
+ /* Gather any information for inlined functions in
+ FVEC. */
+
+ memset (&fvec, 0, sizeof fvec);
+
+ if (!read_function_entry (state, ddata, u, base, unit_buf, lhdr,
+ error_callback, data, vec_function,
+ &fvec))
+ return 0;
+
+ if (fvec.count > 0)
+ {
+ struct function_addrs *p;
+ struct function_addrs *faddrs;
+
+ /* Allocate a trailing entry, but don't include it
+ in fvec.count. */
+ p = ((struct function_addrs *)
+ backtrace_vector_grow (state,
+ sizeof (struct function_addrs),
+ error_callback, data,
+ &fvec.vec));
+ if (p == NULL)
+ return 0;
+ p->low = 0;
+ --p->low;
+ p->high = p->low;
+ p->function = NULL;
+
+ if (!backtrace_vector_release (state, &fvec.vec,
+ error_callback, data))
+ return 0;
+
+ faddrs = (struct function_addrs *) fvec.vec.base;
+ backtrace_qsort (faddrs, fvec.count,
+ sizeof (struct function_addrs),
+ function_addrs_compare);
+
+ function->function_addrs = faddrs;
+ function->function_addrs_count = fvec.count;
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+/* Read function name information for a compilation unit. We look
+ through the whole unit looking for function tags. */
+
+static void
+read_function_info (struct backtrace_state *state, struct dwarf_data *ddata,
+ const struct line_header *lhdr,
+ backtrace_error_callback error_callback, void *data,
+ struct unit *u, struct function_vector *fvec,
+ struct function_addrs **ret_addrs,
+ size_t *ret_addrs_count)
+{
+ struct function_vector lvec;
+ struct function_vector *pfvec;
+ struct dwarf_buf unit_buf;
+ struct function_addrs *p;
+ struct function_addrs *addrs;
+ size_t addrs_count;
+
+ /* Use FVEC if it is not NULL. Otherwise use our own vector. */
+ if (fvec != NULL)
+ pfvec = fvec;
+ else
+ {
+ memset (&lvec, 0, sizeof lvec);
+ pfvec = &lvec;
+ }
+
+ unit_buf.name = ".debug_info";
+ unit_buf.start = ddata->dwarf_sections.data[DEBUG_INFO];
+ unit_buf.buf = u->unit_data;
+ unit_buf.left = u->unit_data_len;
+ unit_buf.is_bigendian = ddata->is_bigendian;
+ unit_buf.error_callback = error_callback;
+ unit_buf.data = data;
+ unit_buf.reported_underflow = 0;
+
+ while (unit_buf.left > 0)
+ {
+ if (!read_function_entry (state, ddata, u, 0, &unit_buf, lhdr,
+ error_callback, data, pfvec, pfvec))
+ return;
+ }
+
+ if (pfvec->count == 0)
+ return;
+
+ /* Allocate a trailing entry, but don't include it in
+ pfvec->count. */
+ p = ((struct function_addrs *)
+ backtrace_vector_grow (state, sizeof (struct function_addrs),
+ error_callback, data, &pfvec->vec));
+ if (p == NULL)
+ return;
+ p->low = 0;
+ --p->low;
+ p->high = p->low;
+ p->function = NULL;
+
+ addrs_count = pfvec->count;
+
+ if (fvec == NULL)
+ {
+ if (!backtrace_vector_release (state, &lvec.vec, error_callback, data))
+ return;
+ addrs = (struct function_addrs *) pfvec->vec.base;
+ }
+ else
+ {
+ /* Finish this list of addresses, but leave the remaining space in
+ the vector available for the next function unit. */
+ addrs = ((struct function_addrs *)
+ backtrace_vector_finish (state, &fvec->vec,
+ error_callback, data));
+ if (addrs == NULL)
+ return;
+ fvec->count = 0;
+ }
+
+ backtrace_qsort (addrs, addrs_count, sizeof (struct function_addrs),
+ function_addrs_compare);
+
+ *ret_addrs = addrs;
+ *ret_addrs_count = addrs_count;
+}
+
+/* See if PC is inlined in FUNCTION. If it is, print out the inlined
+ information, and update FILENAME and LINENO for the caller.
+ Returns whatever CALLBACK returns, or 0 to keep going. */
+
+static int
+report_inlined_functions (uintptr_t pc, struct function *function,
+ backtrace_full_callback callback, void *data,
+ const char **filename, int *lineno)
+{
+ struct function_addrs *p;
+ struct function_addrs *match;
+ struct function *inlined;
+ int ret;
+
+ if (function->function_addrs_count == 0)
+ return 0;
+
+ /* Our search isn't safe if pc == -1, as that is the sentinel
+ value. */
+ if (pc + 1 == 0)
+ return 0;
+
+ p = ((struct function_addrs *)
+ bsearch (&pc, function->function_addrs,
+ function->function_addrs_count,
+ sizeof (struct function_addrs),
+ function_addrs_search));
+ if (p == NULL)
+ return 0;
+
+ /* Here pc >= p->low && pc < (p + 1)->low. The function_addrs are
+ sorted by low, so if pc > p->low we are at the end of a range of
+ function_addrs with the same low value. If pc == p->low walk
+ forward to the end of the range with that low value. Then walk
+ backward and use the first range that includes pc. */
+ while (pc == (p + 1)->low)
+ ++p;
+ match = NULL;
+ while (1)
+ {
+ if (pc < p->high)
+ {
+ match = p;
+ break;
+ }
+ if (p == function->function_addrs)
+ break;
+ if ((p - 1)->low < p->low)
+ break;
+ --p;
+ }
+ if (match == NULL)
+ return 0;
+
+ /* We found an inlined call. */
+
+ inlined = match->function;
+
+ /* Report any calls inlined into this one. */
+ ret = report_inlined_functions (pc, inlined, callback, data,
+ filename, lineno);
+ if (ret != 0)
+ return ret;
+
+ /* Report this inlined call. */
+ ret = callback (data, pc, *filename, *lineno, inlined->name);
+ if (ret != 0)
+ return ret;
+
+ /* Our caller will report the caller of the inlined function; tell
+ it the appropriate filename and line number. */
+ *filename = inlined->caller_filename;
+ *lineno = inlined->caller_lineno;
+
+ return 0;
+}
+
+/* Look for a PC in the DWARF mapping for one module. On success,
+ call CALLBACK and return whatever it returns. On error, call
+ ERROR_CALLBACK and return 0. Sets *FOUND to 1 if the PC is found,
+ 0 if not. */
+
+static int
+dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
+ uintptr_t pc, backtrace_full_callback callback,
+ backtrace_error_callback error_callback, void *data,
+ int *found)
+{
+ struct unit_addrs *entry;
+ int found_entry;
+ struct unit *u;
+ int new_data;
+ struct line *lines;
+ struct line *ln;
+ struct function_addrs *p;
+ struct function_addrs *fmatch;
+ struct function *function;
+ const char *filename;
+ int lineno;
+ int ret;
+
+ *found = 1;
+
+ /* Find an address range that includes PC. Our search isn't safe if
+ PC == -1, as we use that as a sentinel value, so skip the search
+ in that case. */
+ entry = (ddata->addrs_count == 0 || pc + 1 == 0
+ ? NULL
+ : bsearch (&pc, ddata->addrs, ddata->addrs_count,
+ sizeof (struct unit_addrs), unit_addrs_search));
+
+ if (entry == NULL)
+ {
+ *found = 0;
+ return 0;
+ }
+
+ /* Here pc >= entry->low && pc < (entry + 1)->low. The unit_addrs
+ are sorted by low, so if pc > p->low we are at the end of a range
+ of unit_addrs with the same low value. If pc == p->low walk
+ forward to the end of the range with that low value. Then walk
+ backward and use the first range that includes pc. */
+ while (pc == (entry + 1)->low)
+ ++entry;
+ found_entry = 0;
+ while (1)
+ {
+ if (pc < entry->high)
+ {
+ found_entry = 1;
+ break;
+ }
+ if (entry == ddata->addrs)
+ break;
+ if ((entry - 1)->low < entry->low)
+ break;
+ --entry;
+ }
+ if (!found_entry)
+ {
+ *found = 0;
+ return 0;
+ }
+
+ /* We need the lines, lines_count, function_addrs,
+ function_addrs_count fields of u. If they are not set, we need
+ to set them. When running in threaded mode, we need to allow for
+ the possibility that some other thread is setting them
+ simultaneously. */
+
+ u = entry->u;
+ lines = u->lines;
+
+ /* Skip units with no useful line number information by walking
+ backward. Useless line number information is marked by setting
+ lines == -1. */
+ while (entry > ddata->addrs
+ && pc >= (entry - 1)->low
+ && pc < (entry - 1)->high)
+ {
+ if (state->threaded)
+ lines = (struct line *) backtrace_atomic_load_pointer (&u->lines);
+
+ if (lines != (struct line *) (uintptr_t) -1)
+ break;
+
+ --entry;
+
+ u = entry->u;
+ lines = u->lines;
+ }
+
+ if (state->threaded)
+ lines = backtrace_atomic_load_pointer (&u->lines);
+
+ new_data = 0;
+ if (lines == NULL)
+ {
+ struct function_addrs *function_addrs;
+ size_t function_addrs_count;
+ struct line_header lhdr;
+ size_t count;
+
+ /* We have never read the line information for this unit. Read
+ it now. */
+
+ function_addrs = NULL;
+ function_addrs_count = 0;
+ if (read_line_info (state, ddata, error_callback, data, entry->u, &lhdr,
+ &lines, &count))
+ {
+ struct function_vector *pfvec;
+
+ /* If not threaded, reuse DDATA->FVEC for better memory
+ consumption. */
+ if (state->threaded)
+ pfvec = NULL;
+ else
+ pfvec = &ddata->fvec;
+ read_function_info (state, ddata, &lhdr, error_callback, data,
+ entry->u, pfvec, &function_addrs,
+ &function_addrs_count);
+ free_line_header (state, &lhdr, error_callback, data);
+ new_data = 1;
+ }
+
+ /* Atomically store the information we just read into the unit.
+ If another thread is simultaneously writing, it presumably
+ read the same information, and we don't care which one we
+ wind up with; we just leak the other one. We do have to
+ write the lines field last, so that the acquire-loads above
+ ensure that the other fields are set. */
+
+ if (!state->threaded)
+ {
+ u->lines_count = count;
+ u->function_addrs = function_addrs;
+ u->function_addrs_count = function_addrs_count;
+ u->lines = lines;
+ }
+ else
+ {
+ backtrace_atomic_store_size_t (&u->lines_count, count);
+ backtrace_atomic_store_pointer (&u->function_addrs, function_addrs);
+ backtrace_atomic_store_size_t (&u->function_addrs_count,
+ function_addrs_count);
+ backtrace_atomic_store_pointer (&u->lines, lines);
+ }
+ }
+
+ /* Now all fields of U have been initialized. */
+
+ if (lines == (struct line *) (uintptr_t) -1)
+ {
+ /* If reading the line number information failed in some way,
+ try again to see if there is a better compilation unit for
+ this PC. */
+ if (new_data)
+ return dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
+ data, found);
+ return callback (data, pc, NULL, 0, NULL);
+ }
+
+ /* Search for PC within this unit. */
+
+ ln = (struct line *) bsearch (&pc, lines, entry->u->lines_count,
+ sizeof (struct line), line_search);
+ if (ln == NULL)
+ {
+ /* The PC is between the low_pc and high_pc attributes of the
+ compilation unit, but no entry in the line table covers it.
+ This implies that the start of the compilation unit has no
+ line number information. */
+
+ if (entry->u->abs_filename == NULL)
+ {
+ const char *filename;
+
+ filename = entry->u->filename;
+ if (filename != NULL
+ && !IS_ABSOLUTE_PATH (filename)
+ && entry->u->comp_dir != NULL)
+ {
+ size_t filename_len;
+ const char *dir;
+ size_t dir_len;
+ char *s;
+
+ filename_len = strlen (filename);
+ dir = entry->u->comp_dir;
+ dir_len = strlen (dir);
+ s = (char *) backtrace_alloc (state, dir_len + filename_len + 2,
+ error_callback, data);
+ if (s == NULL)
+ {
+ *found = 0;
+ return 0;
+ }
+ memcpy (s, dir, dir_len);
+ /* FIXME: Should use backslash if DOS file system. */
+ s[dir_len] = '/';
+ memcpy (s + dir_len + 1, filename, filename_len + 1);
+ filename = s;
+ }
+ entry->u->abs_filename = filename;
+ }
+
+ return callback (data, pc, entry->u->abs_filename, 0, NULL);
+ }
+
+ /* Search for function name within this unit. */
+
+ if (entry->u->function_addrs_count == 0)
+ return callback (data, pc, ln->filename, ln->lineno, NULL);
+
+ p = ((struct function_addrs *)
+ bsearch (&pc, entry->u->function_addrs,
+ entry->u->function_addrs_count,
+ sizeof (struct function_addrs),
+ function_addrs_search));
+ if (p == NULL)
+ return callback (data, pc, ln->filename, ln->lineno, NULL);
+
+ /* Here pc >= p->low && pc < (p + 1)->low. The function_addrs are
+ sorted by low, so if pc > p->low we are at the end of a range of
+ function_addrs with the same low value. If pc == p->low walk
+ forward to the end of the range with that low value. Then walk
+ backward and use the first range that includes pc. */
+ while (pc == (p + 1)->low)
+ ++p;
+ fmatch = NULL;
+ while (1)
+ {
+ if (pc < p->high)
+ {
+ fmatch = p;
+ break;
+ }
+ if (p == entry->u->function_addrs)
+ break;
+ if ((p - 1)->low < p->low)
+ break;
+ --p;
+ }
+ if (fmatch == NULL)
+ return callback (data, pc, ln->filename, ln->lineno, NULL);
+
+ function = fmatch->function;
+
+ filename = ln->filename;
+ lineno = ln->lineno;
+
+ ret = report_inlined_functions (pc, function, callback, data,
+ &filename, &lineno);
+ if (ret != 0)
+ return ret;
+
+ return callback (data, pc, filename, lineno, function->name);
+}
+
+
+/* Return the file/line information for a PC using the DWARF mapping
+ we built earlier. */
+
+static int
+dwarf_fileline (struct backtrace_state *state, uintptr_t pc,
+ backtrace_full_callback callback,
+ backtrace_error_callback error_callback, void *data)
+{
+ struct dwarf_data *ddata;
+ int found;
+ int ret;
+
+ if (!state->threaded)
+ {
+ for (ddata = (struct dwarf_data *) state->fileline_data;
+ ddata != NULL;
+ ddata = ddata->next)
+ {
+ ret = dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
+ data, &found);
+ if (ret != 0 || found)
+ return ret;
+ }
+ }
+ else
+ {
+ struct dwarf_data **pp;
+
+ pp = (struct dwarf_data **) (void *) &state->fileline_data;
+ while (1)
+ {
+ ddata = backtrace_atomic_load_pointer (pp);
+ if (ddata == NULL)
+ break;
+
+ ret = dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
+ data, &found);
+ if (ret != 0 || found)
+ return ret;
+
+ pp = &ddata->next;
+ }
+ }
+
+ /* FIXME: See if any libraries have been dlopen'ed. */
+
+ return callback (data, pc, NULL, 0, NULL);
+}
+
+/* Initialize our data structures from the DWARF debug info for a
+ file. Return NULL on failure. */
+
+static struct dwarf_data *
+build_dwarf_data (struct backtrace_state *state,
+ uintptr_t base_address,
+ const struct dwarf_sections *dwarf_sections,
+ int is_bigendian,
+ struct dwarf_data *altlink,
+ backtrace_error_callback error_callback,
+ void *data)
+{
+ struct unit_addrs_vector addrs_vec;
+ struct unit_addrs *addrs;
+ size_t addrs_count;
+ struct unit_vector units_vec;
+ struct unit **units;
+ size_t units_count;
+ struct dwarf_data *fdata;
+
+ if (!build_address_map (state, base_address, dwarf_sections, is_bigendian,
+ altlink, error_callback, data, &addrs_vec,
+ &units_vec))
+ return NULL;
+
+ if (!backtrace_vector_release (state, &addrs_vec.vec, error_callback, data))
+ return NULL;
+ if (!backtrace_vector_release (state, &units_vec.vec, error_callback, data))
+ return NULL;
+ addrs = (struct unit_addrs *) addrs_vec.vec.base;
+ units = (struct unit **) units_vec.vec.base;
+ addrs_count = addrs_vec.count;
+ units_count = units_vec.count;
+ backtrace_qsort (addrs, addrs_count, sizeof (struct unit_addrs),
+ unit_addrs_compare);
+ /* No qsort for units required, already sorted. */
+
+ fdata = ((struct dwarf_data *)
+ backtrace_alloc (state, sizeof (struct dwarf_data),
+ error_callback, data));
+ if (fdata == NULL)
+ return NULL;
+
+ fdata->next = NULL;
+ fdata->altlink = altlink;
+ fdata->base_address = base_address;
+ fdata->addrs = addrs;
+ fdata->addrs_count = addrs_count;
+ fdata->units = units;
+ fdata->units_count = units_count;
+ fdata->dwarf_sections = *dwarf_sections;
+ fdata->is_bigendian = is_bigendian;
+ memset (&fdata->fvec, 0, sizeof fdata->fvec);
+
+ return fdata;
+}
+
+/* Build our data structures from the DWARF sections for a module.
+ Set FILELINE_FN and STATE->FILELINE_DATA. Return 1 on success, 0
+ on failure. */
+
+int
+backtrace_dwarf_add (struct backtrace_state *state,
+ uintptr_t base_address,
+ const struct dwarf_sections *dwarf_sections,
+ int is_bigendian,
+ struct dwarf_data *fileline_altlink,
+ backtrace_error_callback error_callback,
+ void *data, fileline *fileline_fn,
+ struct dwarf_data **fileline_entry)
+{
+ struct dwarf_data *fdata;
+
+ fdata = build_dwarf_data (state, base_address, dwarf_sections, is_bigendian,
+ fileline_altlink, error_callback, data);
+ if (fdata == NULL)
+ return 0;
+
+ if (fileline_entry != NULL)
+ *fileline_entry = fdata;
+
+ if (!state->threaded)
+ {
+ struct dwarf_data **pp;
+
+ for (pp = (struct dwarf_data **) (void *) &state->fileline_data;
+ *pp != NULL;
+ pp = &(*pp)->next)
+ ;
+ *pp = fdata;
+ }
+ else
+ {
+ while (1)
+ {
+ struct dwarf_data **pp;
+
+ pp = (struct dwarf_data **) (void *) &state->fileline_data;
+
+ while (1)
+ {
+ struct dwarf_data *p;
+
+ p = backtrace_atomic_load_pointer (pp);
+
+ if (p == NULL)
+ break;
+
+ pp = &p->next;
+ }
+
+ if (__sync_bool_compare_and_swap (pp, NULL, fdata))
+ break;
+ }
+ }
+
+ *fileline_fn = dwarf_fileline;
+
+ return 1;
+}
diff --git a/thirdparty/libbacktrace/fileline.c b/thirdparty/libbacktrace/fileline.c
new file mode 100644
index 0000000000..0472f4721a
--- /dev/null
+++ b/thirdparty/libbacktrace/fileline.c
@@ -0,0 +1,346 @@
+/* fileline.c -- Get file and line number information in a backtrace.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#if defined (HAVE_KERN_PROC_ARGS) || defined (HAVE_KERN_PROC)
+#include <sys/sysctl.h>
+#endif
+
+#ifdef HAVE_MACH_O_DYLD_H
+#include <mach-o/dyld.h>
+#endif
+
+#include "backtrace.h"
+#include "internal.h"
+
+#ifndef HAVE_GETEXECNAME
+#define getexecname() NULL
+#endif
+
+#if !defined (HAVE_KERN_PROC_ARGS) && !defined (HAVE_KERN_PROC)
+
+#define sysctl_exec_name1(state, error_callback, data) NULL
+#define sysctl_exec_name2(state, error_callback, data) NULL
+
+#else /* defined (HAVE_KERN_PROC_ARGS) || |defined (HAVE_KERN_PROC) */
+
+static char *
+sysctl_exec_name (struct backtrace_state *state,
+ int mib0, int mib1, int mib2, int mib3,
+ backtrace_error_callback error_callback, void *data)
+{
+ int mib[4];
+ size_t len;
+ char *name;
+ size_t rlen;
+
+ mib[0] = mib0;
+ mib[1] = mib1;
+ mib[2] = mib2;
+ mib[3] = mib3;
+
+ if (sysctl (mib, 4, NULL, &len, NULL, 0) < 0)
+ return NULL;
+ name = (char *) backtrace_alloc (state, len, error_callback, data);
+ if (name == NULL)
+ return NULL;
+ rlen = len;
+ if (sysctl (mib, 4, name, &rlen, NULL, 0) < 0)
+ {
+ backtrace_free (state, name, len, error_callback, data);
+ return NULL;
+ }
+ return name;
+}
+
+#ifdef HAVE_KERN_PROC_ARGS
+
+static char *
+sysctl_exec_name1 (struct backtrace_state *state,
+ backtrace_error_callback error_callback, void *data)
+{
+ /* This variant is used on NetBSD. */
+ return sysctl_exec_name (state, CTL_KERN, KERN_PROC_ARGS, -1,
+ KERN_PROC_PATHNAME, error_callback, data);
+}
+
+#else
+
+#define sysctl_exec_name1(state, error_callback, data) NULL
+
+#endif
+
+#ifdef HAVE_KERN_PROC
+
+static char *
+sysctl_exec_name2 (struct backtrace_state *state,
+ backtrace_error_callback error_callback, void *data)
+{
+ /* This variant is used on FreeBSD. */
+ return sysctl_exec_name (state, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1,
+ error_callback, data);
+}
+
+#else
+
+#define sysctl_exec_name2(state, error_callback, data) NULL
+
+#endif
+
+#endif /* defined (HAVE_KERN_PROC_ARGS) || |defined (HAVE_KERN_PROC) */
+
+#ifdef HAVE_MACH_O_DYLD_H
+
+static char *
+macho_get_executable_path (struct backtrace_state *state,
+ backtrace_error_callback error_callback, void *data)
+{
+ uint32_t len;
+ char *name;
+
+ len = 0;
+ if (_NSGetExecutablePath (NULL, &len) == 0)
+ return NULL;
+ name = (char *) backtrace_alloc (state, len, error_callback, data);
+ if (name == NULL)
+ return NULL;
+ if (_NSGetExecutablePath (name, &len) != 0)
+ {
+ backtrace_free (state, name, len, error_callback, data);
+ return NULL;
+ }
+ return name;
+}
+
+#else /* !defined (HAVE_MACH_O_DYLD_H) */
+
+#define macho_get_executable_path(state, error_callback, data) NULL
+
+#endif /* !defined (HAVE_MACH_O_DYLD_H) */
+
+/* Initialize the fileline information from the executable. Returns 1
+ on success, 0 on failure. */
+
+static int
+fileline_initialize (struct backtrace_state *state,
+ backtrace_error_callback error_callback, void *data)
+{
+ int failed;
+ fileline fileline_fn;
+ int pass;
+ int called_error_callback;
+ int descriptor;
+ const char *filename;
+ char buf[64];
+
+ if (!state->threaded)
+ failed = state->fileline_initialization_failed;
+ else
+ failed = backtrace_atomic_load_int (&state->fileline_initialization_failed);
+
+ if (failed)
+ {
+ error_callback (data, "failed to read executable information", -1);
+ return 0;
+ }
+
+ if (!state->threaded)
+ fileline_fn = state->fileline_fn;
+ else
+ fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
+ if (fileline_fn != NULL)
+ return 1;
+
+ /* We have not initialized the information. Do it now. */
+
+ descriptor = -1;
+ called_error_callback = 0;
+ for (pass = 0; pass < 8; ++pass)
+ {
+ int does_not_exist;
+
+ switch (pass)
+ {
+ case 0:
+ filename = state->filename;
+ break;
+ case 1:
+ filename = getexecname ();
+ break;
+ case 2:
+ filename = "/proc/self/exe";
+ break;
+ case 3:
+ filename = "/proc/curproc/file";
+ break;
+ case 4:
+ snprintf (buf, sizeof (buf), "/proc/%ld/object/a.out",
+ (long) getpid ());
+ filename = buf;
+ break;
+ case 5:
+ filename = sysctl_exec_name1 (state, error_callback, data);
+ break;
+ case 6:
+ filename = sysctl_exec_name2 (state, error_callback, data);
+ break;
+ case 7:
+ filename = macho_get_executable_path (state, error_callback, data);
+ break;
+ default:
+ abort ();
+ }
+
+ if (filename == NULL)
+ continue;
+
+ descriptor = backtrace_open (filename, error_callback, data,
+ &does_not_exist);
+ if (descriptor < 0 && !does_not_exist)
+ {
+ called_error_callback = 1;
+ break;
+ }
+ if (descriptor >= 0)
+ break;
+ }
+
+ if (descriptor < 0)
+ {
+ if (!called_error_callback)
+ {
+ if (state->filename != NULL)
+ error_callback (data, state->filename, ENOENT);
+ else
+ error_callback (data,
+ "libbacktrace could not find executable to open",
+ 0);
+ }
+ failed = 1;
+ }
+
+ if (!failed)
+ {
+ if (!backtrace_initialize (state, filename, descriptor, error_callback,
+ data, &fileline_fn))
+ failed = 1;
+ }
+
+ if (failed)
+ {
+ if (!state->threaded)
+ state->fileline_initialization_failed = 1;
+ else
+ backtrace_atomic_store_int (&state->fileline_initialization_failed, 1);
+ return 0;
+ }
+
+ if (!state->threaded)
+ state->fileline_fn = fileline_fn;
+ else
+ {
+ backtrace_atomic_store_pointer (&state->fileline_fn, fileline_fn);
+
+ /* Note that if two threads initialize at once, one of the data
+ sets may be leaked. */
+ }
+
+ return 1;
+}
+
+/* Given a PC, find the file name, line number, and function name. */
+
+int
+backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc,
+ backtrace_full_callback callback,
+ backtrace_error_callback error_callback, void *data)
+{
+ if (!fileline_initialize (state, error_callback, data))
+ return 0;
+
+ if (state->fileline_initialization_failed)
+ return 0;
+
+ return state->fileline_fn (state, pc, callback, error_callback, data);
+}
+
+/* Given a PC, find the symbol for it, and its value. */
+
+int
+backtrace_syminfo (struct backtrace_state *state, uintptr_t pc,
+ backtrace_syminfo_callback callback,
+ backtrace_error_callback error_callback, void *data)
+{
+ if (!fileline_initialize (state, error_callback, data))
+ return 0;
+
+ if (state->fileline_initialization_failed)
+ return 0;
+
+ state->syminfo_fn (state, pc, callback, error_callback, data);
+ return 1;
+}
+
+/* A backtrace_syminfo_callback that can call into a
+ backtrace_full_callback, used when we have a symbol table but no
+ debug info. */
+
+void
+backtrace_syminfo_to_full_callback (void *data, uintptr_t pc,
+ const char *symname,
+ uintptr_t symval ATTRIBUTE_UNUSED,
+ uintptr_t symsize ATTRIBUTE_UNUSED)
+{
+ struct backtrace_call_full *bdata = (struct backtrace_call_full *) data;
+
+ bdata->ret = bdata->full_callback (bdata->full_data, pc, NULL, 0, symname);
+}
+
+/* An error callback that corresponds to
+ backtrace_syminfo_to_full_callback. */
+
+void
+backtrace_syminfo_to_full_error_callback (void *data, const char *msg,
+ int errnum)
+{
+ struct backtrace_call_full *bdata = (struct backtrace_call_full *) data;
+
+ bdata->full_error_callback (bdata->full_data, msg, errnum);
+}
diff --git a/thirdparty/libbacktrace/filenames.h b/thirdparty/libbacktrace/filenames.h
new file mode 100644
index 0000000000..aa7bd7adff
--- /dev/null
+++ b/thirdparty/libbacktrace/filenames.h
@@ -0,0 +1,52 @@
+/* btest.c -- Filename header for libbacktrace library
+ Copyright (C) 2012-2018 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#ifndef GCC_VERSION
+# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)
+#endif
+
+#if (GCC_VERSION < 2007)
+# define __attribute__(x)
+#endif
+
+#ifndef ATTRIBUTE_UNUSED
+# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#endif
+
+#if defined(__MSDOS__) || defined(_WIN32) || defined(__OS2__) || defined (__CYGWIN__)
+# define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\')
+# define HAS_DRIVE_SPEC(f) ((f)[0] != '\0' && (f)[1] == ':')
+# define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || HAS_DRIVE_SPEC(f))
+#else
+# define IS_DIR_SEPARATOR(c) ((c) == '/')
+# define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]))
+#endif
diff --git a/thirdparty/libbacktrace/internal.h b/thirdparty/libbacktrace/internal.h
new file mode 100644
index 0000000000..bb481f373b
--- /dev/null
+++ b/thirdparty/libbacktrace/internal.h
@@ -0,0 +1,380 @@
+/* internal.h -- Internal header file for stack backtrace library.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#ifndef BACKTRACE_INTERNAL_H
+#define BACKTRACE_INTERNAL_H
+
+/* We assume that <sys/types.h> and "backtrace.h" have already been
+ included. */
+
+#ifndef GCC_VERSION
+# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)
+#endif
+
+#if (GCC_VERSION < 2007)
+# define __attribute__(x)
+#endif
+
+#ifndef ATTRIBUTE_UNUSED
+# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#endif
+
+#ifndef ATTRIBUTE_MALLOC
+# if (GCC_VERSION >= 2096)
+# define ATTRIBUTE_MALLOC __attribute__ ((__malloc__))
+# else
+# define ATTRIBUTE_MALLOC
+# endif
+#endif
+
+#ifndef ATTRIBUTE_FALLTHROUGH
+# if (GCC_VERSION >= 7000)
+# define ATTRIBUTE_FALLTHROUGH __attribute__ ((__fallthrough__))
+# else
+# define ATTRIBUTE_FALLTHROUGH
+# endif
+#endif
+
+#ifndef HAVE_SYNC_FUNCTIONS
+
+/* Define out the sync functions. These should never be called if
+ they are not available. */
+
+#define __sync_bool_compare_and_swap(A, B, C) (abort(), 1)
+#define __sync_lock_test_and_set(A, B) (abort(), 0)
+#define __sync_lock_release(A) abort()
+
+#endif /* !defined (HAVE_SYNC_FUNCTIONS) */
+
+#ifdef HAVE_ATOMIC_FUNCTIONS
+
+/* We have the atomic builtin functions. */
+
+#define backtrace_atomic_load_pointer(p) \
+ __atomic_load_n ((p), __ATOMIC_ACQUIRE)
+#define backtrace_atomic_load_int(p) \
+ __atomic_load_n ((p), __ATOMIC_ACQUIRE)
+#define backtrace_atomic_store_pointer(p, v) \
+ __atomic_store_n ((p), (v), __ATOMIC_RELEASE)
+#define backtrace_atomic_store_size_t(p, v) \
+ __atomic_store_n ((p), (v), __ATOMIC_RELEASE)
+#define backtrace_atomic_store_int(p, v) \
+ __atomic_store_n ((p), (v), __ATOMIC_RELEASE)
+
+#else /* !defined (HAVE_ATOMIC_FUNCTIONS) */
+#ifdef HAVE_SYNC_FUNCTIONS
+
+/* We have the sync functions but not the atomic functions. Define
+ the atomic ones in terms of the sync ones. */
+
+extern void *backtrace_atomic_load_pointer (void *);
+extern int backtrace_atomic_load_int (int *);
+extern void backtrace_atomic_store_pointer (void *, void *);
+extern void backtrace_atomic_store_size_t (size_t *, size_t);
+extern void backtrace_atomic_store_int (int *, int);
+
+#else /* !defined (HAVE_SYNC_FUNCTIONS) */
+
+/* We have neither the sync nor the atomic functions. These will
+ never be called. */
+
+#define backtrace_atomic_load_pointer(p) (abort(), (void *) NULL)
+#define backtrace_atomic_load_int(p) (abort(), 0)
+#define backtrace_atomic_store_pointer(p, v) abort()
+#define backtrace_atomic_store_size_t(p, v) abort()
+#define backtrace_atomic_store_int(p, v) abort()
+
+#endif /* !defined (HAVE_SYNC_FUNCTIONS) */
+#endif /* !defined (HAVE_ATOMIC_FUNCTIONS) */
+
+/* The type of the function that collects file/line information. This
+ is like backtrace_pcinfo. */
+
+typedef int (*fileline) (struct backtrace_state *state, uintptr_t pc,
+ backtrace_full_callback callback,
+ backtrace_error_callback error_callback, void *data);
+
+/* The type of the function that collects symbol information. This is
+ like backtrace_syminfo. */
+
+typedef void (*syminfo) (struct backtrace_state *state, uintptr_t pc,
+ backtrace_syminfo_callback callback,
+ backtrace_error_callback error_callback, void *data);
+
+/* What the backtrace state pointer points to. */
+
+struct backtrace_state
+{
+ /* The name of the executable. */
+ const char *filename;
+ /* Non-zero if threaded. */
+ int threaded;
+ /* The master lock for fileline_fn, fileline_data, syminfo_fn,
+ syminfo_data, fileline_initialization_failed and everything the
+ data pointers point to. */
+ void *lock;
+ /* The function that returns file/line information. */
+ fileline fileline_fn;
+ /* The data to pass to FILELINE_FN. */
+ void *fileline_data;
+ /* The function that returns symbol information. */
+ syminfo syminfo_fn;
+ /* The data to pass to SYMINFO_FN. */
+ void *syminfo_data;
+ /* Whether initializing the file/line information failed. */
+ int fileline_initialization_failed;
+ /* The lock for the freelist. */
+ int lock_alloc;
+ /* The freelist when using mmap. */
+ struct backtrace_freelist_struct *freelist;
+};
+
+/* Open a file for reading. Returns -1 on error. If DOES_NOT_EXIST
+ is not NULL, *DOES_NOT_EXIST will be set to 0 normally and set to 1
+ if the file does not exist. If the file does not exist and
+ DOES_NOT_EXIST is not NULL, the function will return -1 and will
+ not call ERROR_CALLBACK. On other errors, or if DOES_NOT_EXIST is
+ NULL, the function will call ERROR_CALLBACK before returning. */
+extern int backtrace_open (const char *filename,
+ backtrace_error_callback error_callback,
+ void *data,
+ int *does_not_exist);
+
+/* A view of the contents of a file. This supports mmap when
+ available. A view will remain in memory even after backtrace_close
+ is called on the file descriptor from which the view was
+ obtained. */
+
+struct backtrace_view
+{
+ /* The data that the caller requested. */
+ const void *data;
+ /* The base of the view. */
+ void *base;
+ /* The total length of the view. */
+ size_t len;
+};
+
+/* Create a view of SIZE bytes from DESCRIPTOR at OFFSET. Store the
+ result in *VIEW. Returns 1 on success, 0 on error. */
+extern int backtrace_get_view (struct backtrace_state *state, int descriptor,
+ off_t offset, uint64_t size,
+ backtrace_error_callback error_callback,
+ void *data, struct backtrace_view *view);
+
+/* Release a view created by backtrace_get_view. */
+extern void backtrace_release_view (struct backtrace_state *state,
+ struct backtrace_view *view,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* Close a file opened by backtrace_open. Returns 1 on success, 0 on
+ error. */
+
+extern int backtrace_close (int descriptor,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* Sort without using memory. */
+
+extern void backtrace_qsort (void *base, size_t count, size_t size,
+ int (*compar) (const void *, const void *));
+
+/* Allocate memory. This is like malloc. If ERROR_CALLBACK is NULL,
+ this does not report an error, it just returns NULL. */
+
+extern void *backtrace_alloc (struct backtrace_state *state, size_t size,
+ backtrace_error_callback error_callback,
+ void *data) ATTRIBUTE_MALLOC;
+
+/* Free memory allocated by backtrace_alloc. If ERROR_CALLBACK is
+ NULL, this does not report an error. */
+
+extern void backtrace_free (struct backtrace_state *state, void *mem,
+ size_t size,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* A growable vector of some struct. This is used for more efficient
+ allocation when we don't know the final size of some group of data
+ that we want to represent as an array. */
+
+struct backtrace_vector
+{
+ /* The base of the vector. */
+ void *base;
+ /* The number of bytes in the vector. */
+ size_t size;
+ /* The number of bytes available at the current allocation. */
+ size_t alc;
+};
+
+/* Grow VEC by SIZE bytes. Return a pointer to the newly allocated
+ bytes. Note that this may move the entire vector to a new memory
+ location. Returns NULL on failure. */
+
+extern void *backtrace_vector_grow (struct backtrace_state *state, size_t size,
+ backtrace_error_callback error_callback,
+ void *data,
+ struct backtrace_vector *vec);
+
+/* Finish the current allocation on VEC. Prepare to start a new
+ allocation. The finished allocation will never be freed. Returns
+ a pointer to the base of the finished entries, or NULL on
+ failure. */
+
+extern void* backtrace_vector_finish (struct backtrace_state *state,
+ struct backtrace_vector *vec,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* Release any extra space allocated for VEC. This may change
+ VEC->base. Returns 1 on success, 0 on failure. */
+
+extern int backtrace_vector_release (struct backtrace_state *state,
+ struct backtrace_vector *vec,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* Free the space managed by VEC. This will reset VEC. */
+
+static inline void
+backtrace_vector_free (struct backtrace_state *state,
+ struct backtrace_vector *vec,
+ backtrace_error_callback error_callback, void *data)
+{
+ vec->alc += vec->size;
+ vec->size = 0;
+ backtrace_vector_release (state, vec, error_callback, data);
+}
+
+/* Read initial debug data from a descriptor, and set the
+ fileline_data, syminfo_fn, and syminfo_data fields of STATE.
+ Return the fileln_fn field in *FILELN_FN--this is done this way so
+ that the synchronization code is only implemented once. This is
+ called after the descriptor has first been opened. It will close
+ the descriptor if it is no longer needed. Returns 1 on success, 0
+ on error. There will be multiple implementations of this function,
+ for different file formats. Each system will compile the
+ appropriate one. */
+
+extern int backtrace_initialize (struct backtrace_state *state,
+ const char *filename,
+ int descriptor,
+ backtrace_error_callback error_callback,
+ void *data,
+ fileline *fileline_fn);
+
+/* An enum for the DWARF sections we care about. */
+
+enum dwarf_section
+{
+ DEBUG_INFO,
+ DEBUG_LINE,
+ DEBUG_ABBREV,
+ DEBUG_RANGES,
+ DEBUG_STR,
+ DEBUG_ADDR,
+ DEBUG_STR_OFFSETS,
+ DEBUG_LINE_STR,
+ DEBUG_RNGLISTS,
+
+ DEBUG_MAX
+};
+
+/* Data for the DWARF sections we care about. */
+
+struct dwarf_sections
+{
+ const unsigned char *data[DEBUG_MAX];
+ size_t size[DEBUG_MAX];
+};
+
+/* DWARF data read from a file, used for .gnu_debugaltlink. */
+
+struct dwarf_data;
+
+/* Add file/line information for a DWARF module. */
+
+extern int backtrace_dwarf_add (struct backtrace_state *state,
+ uintptr_t base_address,
+ const struct dwarf_sections *dwarf_sections,
+ int is_bigendian,
+ struct dwarf_data *fileline_altlink,
+ backtrace_error_callback error_callback,
+ void *data, fileline *fileline_fn,
+ struct dwarf_data **fileline_entry);
+
+/* A data structure to pass to backtrace_syminfo_to_full. */
+
+struct backtrace_call_full
+{
+ backtrace_full_callback full_callback;
+ backtrace_error_callback full_error_callback;
+ void *full_data;
+ int ret;
+};
+
+/* A backtrace_syminfo_callback that can call into a
+ backtrace_full_callback, used when we have a symbol table but no
+ debug info. */
+
+extern void backtrace_syminfo_to_full_callback (void *data, uintptr_t pc,
+ const char *symname,
+ uintptr_t symval,
+ uintptr_t symsize);
+
+/* An error callback that corresponds to
+ backtrace_syminfo_to_full_callback. */
+
+extern void backtrace_syminfo_to_full_error_callback (void *, const char *,
+ int);
+
+/* A test-only hook for elf_uncompress_zdebug. */
+
+extern int backtrace_uncompress_zdebug (struct backtrace_state *,
+ const unsigned char *compressed,
+ size_t compressed_size,
+ backtrace_error_callback, void *data,
+ unsigned char **uncompressed,
+ size_t *uncompressed_size);
+
+/* A test-only hook for elf_uncompress_lzma. */
+
+extern int backtrace_uncompress_lzma (struct backtrace_state *,
+ const unsigned char *compressed,
+ size_t compressed_size,
+ backtrace_error_callback, void *data,
+ unsigned char **uncompressed,
+ size_t *uncompressed_size);
+
+#endif
diff --git a/thirdparty/libbacktrace/pecoff.c b/thirdparty/libbacktrace/pecoff.c
new file mode 100644
index 0000000000..720251900b
--- /dev/null
+++ b/thirdparty/libbacktrace/pecoff.c
@@ -0,0 +1,935 @@
+/* pecoff.c -- Get debug data from a PE/COFFF file for backtraces.
+ Copyright (C) 2015-2021 Free Software Foundation, Inc.
+ Adapted from elf.c by Tristan Gingold, AdaCore.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* Coff file header. */
+
+typedef struct {
+ uint16_t machine;
+ uint16_t number_of_sections;
+ uint32_t time_date_stamp;
+ uint32_t pointer_to_symbol_table;
+ uint32_t number_of_symbols;
+ uint16_t size_of_optional_header;
+ uint16_t characteristics;
+} b_coff_file_header;
+
+/* Coff optional header. */
+
+typedef struct {
+ uint16_t magic;
+ uint8_t major_linker_version;
+ uint8_t minor_linker_version;
+ uint32_t size_of_code;
+ uint32_t size_of_initialized_data;
+ uint32_t size_of_uninitialized_data;
+ uint32_t address_of_entry_point;
+ uint32_t base_of_code;
+ union {
+ struct {
+ uint32_t base_of_data;
+ uint32_t image_base;
+ } pe;
+ struct {
+ uint64_t image_base;
+ } pep;
+ } u;
+} b_coff_optional_header;
+
+/* Values of magic in optional header. */
+
+#define PE_MAGIC 0x10b /* PE32 executable. */
+#define PEP_MAGIC 0x20b /* PE32+ executable (for 64bit targets). */
+
+/* Coff section header. */
+
+typedef struct {
+ char name[8];
+ uint32_t virtual_size;
+ uint32_t virtual_address;
+ uint32_t size_of_raw_data;
+ uint32_t pointer_to_raw_data;
+ uint32_t pointer_to_relocations;
+ uint32_t pointer_to_line_numbers;
+ uint16_t number_of_relocations;
+ uint16_t number_of_line_numbers;
+ uint32_t characteristics;
+} b_coff_section_header;
+
+/* Coff symbol name. */
+
+typedef union {
+ char short_name[8];
+ struct {
+ unsigned char zeroes[4];
+ unsigned char off[4];
+ } long_name;
+} b_coff_name;
+
+/* Coff symbol (external representation which is unaligned). */
+
+typedef struct {
+ b_coff_name name;
+ unsigned char value[4];
+ unsigned char section_number[2];
+ unsigned char type[2];
+ unsigned char storage_class;
+ unsigned char number_of_aux_symbols;
+} b_coff_external_symbol;
+
+/* Symbol types. */
+
+#define N_TBSHFT 4 /* Shift for the derived type. */
+#define IMAGE_SYM_DTYPE_FUNCTION 2 /* Function derived type. */
+
+/* Size of a coff symbol. */
+
+#define SYM_SZ 18
+
+/* Coff symbol, internal representation (aligned). */
+
+typedef struct {
+ const char *name;
+ uint32_t value;
+ int16_t sec;
+ uint16_t type;
+ uint16_t sc;
+} b_coff_internal_symbol;
+
+/* Names of sections, indexed by enum dwarf_section in internal.h. */
+
+static const char * const debug_section_names[DEBUG_MAX] =
+{
+ ".debug_info",
+ ".debug_line",
+ ".debug_abbrev",
+ ".debug_ranges",
+ ".debug_str",
+ ".debug_addr",
+ ".debug_str_offsets",
+ ".debug_line_str",
+ ".debug_rnglists"
+};
+
+/* Information we gather for the sections we care about. */
+
+struct debug_section_info
+{
+ /* Section file offset. */
+ off_t offset;
+ /* Section size. */
+ size_t size;
+};
+
+/* Information we keep for an coff symbol. */
+
+struct coff_symbol
+{
+ /* The name of the symbol. */
+ const char *name;
+ /* The address of the symbol. */
+ uintptr_t address;
+};
+
+/* Information to pass to coff_syminfo. */
+
+struct coff_syminfo_data
+{
+ /* Symbols for the next module. */
+ struct coff_syminfo_data *next;
+ /* The COFF symbols, sorted by address. */
+ struct coff_symbol *symbols;
+ /* The number of symbols. */
+ size_t count;
+};
+
+/* A dummy callback function used when we can't find any debug info. */
+
+static int
+coff_nodebug (struct backtrace_state *state ATTRIBUTE_UNUSED,
+ uintptr_t pc ATTRIBUTE_UNUSED,
+ backtrace_full_callback callback ATTRIBUTE_UNUSED,
+ backtrace_error_callback error_callback, void *data)
+{
+ error_callback (data, "no debug info in PE/COFF executable", -1);
+ return 0;
+}
+
+/* A dummy callback function used when we can't find a symbol
+ table. */
+
+static void
+coff_nosyms (struct backtrace_state *state ATTRIBUTE_UNUSED,
+ uintptr_t addr ATTRIBUTE_UNUSED,
+ backtrace_syminfo_callback callback ATTRIBUTE_UNUSED,
+ backtrace_error_callback error_callback, void *data)
+{
+ error_callback (data, "no symbol table in PE/COFF executable", -1);
+}
+
+/* Read a potentially unaligned 4 byte word at P, using native endianness. */
+
+static uint32_t
+coff_read4 (const unsigned char *p)
+{
+ uint32_t res;
+
+ memcpy (&res, p, 4);
+ return res;
+}
+
+/* Read a potentially unaligned 2 byte word at P, using native endianness.
+ All 2 byte word in symbols are always aligned, but for coherency all
+ fields are declared as char arrays. */
+
+static uint16_t
+coff_read2 (const unsigned char *p)
+{
+ uint16_t res;
+
+ memcpy (&res, p, sizeof (res));
+ return res;
+}
+
+/* Return the length (without the trailing 0) of a COFF short name. */
+
+static size_t
+coff_short_name_len (const char *name)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ if (name[i] == 0)
+ return i;
+ return 8;
+}
+
+/* Return true iff COFF short name CNAME is the same as NAME (a NUL-terminated
+ string). */
+
+static int
+coff_short_name_eq (const char *name, const char *cname)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ {
+ if (name[i] != cname[i])
+ return 0;
+ if (name[i] == 0)
+ return 1;
+ }
+ return name[8] == 0;
+}
+
+/* Return true iff NAME is the same as string at offset OFF. */
+
+static int
+coff_long_name_eq (const char *name, unsigned int off,
+ struct backtrace_view *str_view)
+{
+ if (off >= str_view->len)
+ return 0;
+ return strcmp (name, (const char *)str_view->data + off) == 0;
+}
+
+/* Compare struct coff_symbol for qsort. */
+
+static int
+coff_symbol_compare (const void *v1, const void *v2)
+{
+ const struct coff_symbol *e1 = (const struct coff_symbol *) v1;
+ const struct coff_symbol *e2 = (const struct coff_symbol *) v2;
+
+ if (e1->address < e2->address)
+ return -1;
+ else if (e1->address > e2->address)
+ return 1;
+ else
+ return 0;
+}
+
+/* Convert SYM to internal (and aligned) format ISYM, using string table
+ from STRTAB and STRTAB_SIZE, and number of sections SECTS_NUM.
+ Return -1 in case of error (invalid section number or string index). */
+
+static int
+coff_expand_symbol (b_coff_internal_symbol *isym,
+ const b_coff_external_symbol *sym,
+ uint16_t sects_num,
+ const unsigned char *strtab, size_t strtab_size)
+{
+ isym->type = coff_read2 (sym->type);
+ isym->sec = coff_read2 (sym->section_number);
+ isym->sc = sym->storage_class;
+
+ if (isym->sec > 0 && (uint16_t) isym->sec > sects_num)
+ return -1;
+ if (sym->name.short_name[0] != 0)
+ isym->name = sym->name.short_name;
+ else
+ {
+ uint32_t off = coff_read4 (sym->name.long_name.off);
+
+ if (off >= strtab_size)
+ return -1;
+ isym->name = (const char *) strtab + off;
+ }
+ return 0;
+}
+
+/* Return true iff SYM is a defined symbol for a function. Data symbols
+ aren't considered because they aren't easily identified (same type as
+ section names, presence of symbols defined by the linker script). */
+
+static int
+coff_is_function_symbol (const b_coff_internal_symbol *isym)
+{
+ return (isym->type >> N_TBSHFT) == IMAGE_SYM_DTYPE_FUNCTION
+ && isym->sec > 0;
+}
+
+/* Initialize the symbol table info for coff_syminfo. */
+
+static int
+coff_initialize_syminfo (struct backtrace_state *state,
+ uintptr_t base_address, int is_64,
+ const b_coff_section_header *sects, size_t sects_num,
+ const b_coff_external_symbol *syms, size_t syms_size,
+ const unsigned char *strtab, size_t strtab_size,
+ backtrace_error_callback error_callback,
+ void *data, struct coff_syminfo_data *sdata)
+{
+ size_t syms_count;
+ char *coff_symstr;
+ size_t coff_symstr_len;
+ size_t coff_symbol_count;
+ size_t coff_symbol_size;
+ struct coff_symbol *coff_symbols;
+ struct coff_symbol *coff_sym;
+ char *coff_str;
+ size_t i;
+
+ syms_count = syms_size / SYM_SZ;
+
+ /* We only care about function symbols. Count them. Also count size of
+ strings for in-symbol names. */
+ coff_symbol_count = 0;
+ coff_symstr_len = 0;
+ for (i = 0; i < syms_count; ++i)
+ {
+ const b_coff_external_symbol *asym = &syms[i];
+ b_coff_internal_symbol isym;
+
+ if (coff_expand_symbol (&isym, asym, sects_num, strtab, strtab_size) < 0)
+ {
+ error_callback (data, "invalid section or offset in coff symbol", 0);
+ return 0;
+ }
+ if (coff_is_function_symbol (&isym))
+ {
+ ++coff_symbol_count;
+ if (asym->name.short_name[0] != 0)
+ coff_symstr_len += coff_short_name_len (asym->name.short_name) + 1;
+ }
+
+ i += asym->number_of_aux_symbols;
+ }
+
+ coff_symbol_size = (coff_symbol_count + 1) * sizeof (struct coff_symbol);
+ coff_symbols = ((struct coff_symbol *)
+ backtrace_alloc (state, coff_symbol_size, error_callback,
+ data));
+ if (coff_symbols == NULL)
+ return 0;
+
+ /* Allocate memory for symbols strings. */
+ if (coff_symstr_len > 0)
+ {
+ coff_symstr = ((char *)
+ backtrace_alloc (state, coff_symstr_len, error_callback,
+ data));
+ if (coff_symstr == NULL)
+ {
+ backtrace_free (state, coff_symbols, coff_symbol_size,
+ error_callback, data);
+ return 0;
+ }
+ }
+ else
+ coff_symstr = NULL;
+
+ /* Copy symbols. */
+ coff_sym = coff_symbols;
+ coff_str = coff_symstr;
+ for (i = 0; i < syms_count; ++i)
+ {
+ const b_coff_external_symbol *asym = &syms[i];
+ b_coff_internal_symbol isym;
+
+ if (coff_expand_symbol (&isym, asym, sects_num, strtab, strtab_size))
+ {
+ /* Should not fail, as it was already tested in the previous
+ loop. */
+ abort ();
+ }
+ if (coff_is_function_symbol (&isym))
+ {
+ const char *name;
+ int16_t secnum;
+
+ if (asym->name.short_name[0] != 0)
+ {
+ size_t len = coff_short_name_len (isym.name);
+ name = coff_str;
+ memcpy (coff_str, isym.name, len);
+ coff_str[len] = 0;
+ coff_str += len + 1;
+ }
+ else
+ name = isym.name;
+
+ if (!is_64)
+ {
+ /* Strip leading '_'. */
+ if (name[0] == '_')
+ name++;
+ }
+
+ /* Symbol value is section relative, so we need to read the address
+ of its section. */
+ secnum = coff_read2 (asym->section_number);
+
+ coff_sym->name = name;
+ coff_sym->address = (coff_read4 (asym->value)
+ + sects[secnum - 1].virtual_address
+ + base_address);
+ coff_sym++;
+ }
+
+ i += asym->number_of_aux_symbols;
+ }
+
+ /* End of symbols marker. */
+ coff_sym->name = NULL;
+ coff_sym->address = -1;
+
+ backtrace_qsort (coff_symbols, coff_symbol_count,
+ sizeof (struct coff_symbol), coff_symbol_compare);
+
+ sdata->next = NULL;
+ sdata->symbols = coff_symbols;
+ sdata->count = coff_symbol_count;
+
+ return 1;
+}
+
+/* Add EDATA to the list in STATE. */
+
+static void
+coff_add_syminfo_data (struct backtrace_state *state,
+ struct coff_syminfo_data *sdata)
+{
+ if (!state->threaded)
+ {
+ struct coff_syminfo_data **pp;
+
+ for (pp = (struct coff_syminfo_data **) (void *) &state->syminfo_data;
+ *pp != NULL;
+ pp = &(*pp)->next)
+ ;
+ *pp = sdata;
+ }
+ else
+ {
+ while (1)
+ {
+ struct coff_syminfo_data **pp;
+
+ pp = (struct coff_syminfo_data **) (void *) &state->syminfo_data;
+
+ while (1)
+ {
+ struct coff_syminfo_data *p;
+
+ p = backtrace_atomic_load_pointer (pp);
+
+ if (p == NULL)
+ break;
+
+ pp = &p->next;
+ }
+
+ if (__sync_bool_compare_and_swap (pp, NULL, sdata))
+ break;
+ }
+ }
+}
+
+/* Compare an ADDR against an elf_symbol for bsearch. We allocate one
+ extra entry in the array so that this can look safely at the next
+ entry. */
+
+static int
+coff_symbol_search (const void *vkey, const void *ventry)
+{
+ const uintptr_t *key = (const uintptr_t *) vkey;
+ const struct coff_symbol *entry = (const struct coff_symbol *) ventry;
+ uintptr_t addr;
+
+ addr = *key;
+ if (addr < entry->address)
+ return -1;
+ else if (addr >= entry[1].address)
+ return 1;
+ else
+ return 0;
+}
+
+/* Return the symbol name and value for an ADDR. */
+
+static void
+coff_syminfo (struct backtrace_state *state, uintptr_t addr,
+ backtrace_syminfo_callback callback,
+ backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+ void *data)
+{
+ struct coff_syminfo_data *sdata;
+ struct coff_symbol *sym = NULL;
+
+ if (!state->threaded)
+ {
+ for (sdata = (struct coff_syminfo_data *) state->syminfo_data;
+ sdata != NULL;
+ sdata = sdata->next)
+ {
+ sym = ((struct coff_symbol *)
+ bsearch (&addr, sdata->symbols, sdata->count,
+ sizeof (struct coff_symbol), coff_symbol_search));
+ if (sym != NULL)
+ break;
+ }
+ }
+ else
+ {
+ struct coff_syminfo_data **pp;
+
+ pp = (struct coff_syminfo_data **) (void *) &state->syminfo_data;
+ while (1)
+ {
+ sdata = backtrace_atomic_load_pointer (pp);
+ if (sdata == NULL)
+ break;
+
+ sym = ((struct coff_symbol *)
+ bsearch (&addr, sdata->symbols, sdata->count,
+ sizeof (struct coff_symbol), coff_symbol_search));
+ if (sym != NULL)
+ break;
+
+ pp = &sdata->next;
+ }
+ }
+
+ if (sym == NULL)
+ callback (data, addr, NULL, 0, 0);
+ else
+ callback (data, addr, sym->name, sym->address, 0);
+}
+
+/* Add the backtrace data for one PE/COFF file. Returns 1 on success,
+ 0 on failure (in both cases descriptor is closed). */
+
+static int
+coff_add (struct backtrace_state *state, int descriptor,
+ backtrace_error_callback error_callback, void *data,
+ fileline *fileline_fn, int *found_sym, int *found_dwarf)
+{
+ struct backtrace_view fhdr_view;
+ off_t fhdr_off;
+ int magic_ok;
+ b_coff_file_header fhdr;
+ off_t opt_sects_off;
+ size_t opt_sects_size;
+ unsigned int sects_num;
+ struct backtrace_view sects_view;
+ int sects_view_valid;
+ const b_coff_optional_header *opt_hdr;
+ const b_coff_section_header *sects;
+ struct backtrace_view str_view;
+ int str_view_valid;
+ size_t str_size;
+ off_t str_off;
+ struct backtrace_view syms_view;
+ off_t syms_off;
+ size_t syms_size;
+ int syms_view_valid;
+ unsigned int syms_num;
+ unsigned int i;
+ struct debug_section_info sections[DEBUG_MAX];
+ off_t min_offset;
+ off_t max_offset;
+ struct backtrace_view debug_view;
+ int debug_view_valid;
+ int is_64;
+ uintptr_t image_base;
+ struct dwarf_sections dwarf_sections;
+
+ *found_sym = 0;
+ *found_dwarf = 0;
+
+ sects_view_valid = 0;
+ syms_view_valid = 0;
+ str_view_valid = 0;
+ debug_view_valid = 0;
+
+ /* Map the MS-DOS stub (if any) and extract file header offset. */
+ if (!backtrace_get_view (state, descriptor, 0, 0x40, error_callback,
+ data, &fhdr_view))
+ goto fail;
+
+ {
+ const unsigned char *vptr = fhdr_view.data;
+
+ if (vptr[0] == 'M' && vptr[1] == 'Z')
+ fhdr_off = coff_read4 (vptr + 0x3c);
+ else
+ fhdr_off = 0;
+ }
+
+ backtrace_release_view (state, &fhdr_view, error_callback, data);
+
+ /* Map the coff file header. */
+ if (!backtrace_get_view (state, descriptor, fhdr_off,
+ sizeof (b_coff_file_header) + 4,
+ error_callback, data, &fhdr_view))
+ goto fail;
+
+ if (fhdr_off != 0)
+ {
+ const char *magic = (const char *) fhdr_view.data;
+ magic_ok = memcmp (magic, "PE\0", 4) == 0;
+ fhdr_off += 4;
+
+ memcpy (&fhdr, fhdr_view.data + 4, sizeof fhdr);
+ }
+ else
+ {
+ memcpy (&fhdr, fhdr_view.data, sizeof fhdr);
+ /* TODO: test fhdr.machine for coff but non-PE platforms. */
+ magic_ok = 0;
+ }
+ backtrace_release_view (state, &fhdr_view, error_callback, data);
+
+ if (!magic_ok)
+ {
+ error_callback (data, "executable file is not COFF", 0);
+ goto fail;
+ }
+
+ sects_num = fhdr.number_of_sections;
+ syms_num = fhdr.number_of_symbols;
+
+ opt_sects_off = fhdr_off + sizeof (fhdr);
+ opt_sects_size = (fhdr.size_of_optional_header
+ + sects_num * sizeof (b_coff_section_header));
+
+ /* To translate PC to file/line when using DWARF, we need to find
+ the .debug_info and .debug_line sections. */
+
+ /* Read the optional header and the section headers. */
+
+ if (!backtrace_get_view (state, descriptor, opt_sects_off, opt_sects_size,
+ error_callback, data, &sects_view))
+ goto fail;
+ sects_view_valid = 1;
+ opt_hdr = (const b_coff_optional_header *) sects_view.data;
+ sects = (const b_coff_section_header *)
+ (sects_view.data + fhdr.size_of_optional_header);
+
+ is_64 = 0;
+ if (fhdr.size_of_optional_header > sizeof (*opt_hdr))
+ {
+ if (opt_hdr->magic == PE_MAGIC)
+ image_base = opt_hdr->u.pe.image_base;
+ else if (opt_hdr->magic == PEP_MAGIC)
+ {
+ image_base = opt_hdr->u.pep.image_base;
+ is_64 = 1;
+ }
+ else
+ {
+ error_callback (data, "bad magic in PE optional header", 0);
+ goto fail;
+ }
+ }
+ else
+ image_base = 0;
+
+ /* Read the symbol table and the string table. */
+
+ if (fhdr.pointer_to_symbol_table == 0)
+ {
+ /* No symbol table, no string table. */
+ str_off = 0;
+ str_size = 0;
+ syms_num = 0;
+ syms_size = 0;
+ }
+ else
+ {
+ /* Symbol table is followed by the string table. The string table
+ starts with its length (on 4 bytes).
+ Map the symbol table and the length of the string table. */
+ syms_off = fhdr.pointer_to_symbol_table;
+ syms_size = syms_num * SYM_SZ;
+
+ if (!backtrace_get_view (state, descriptor, syms_off, syms_size + 4,
+ error_callback, data, &syms_view))
+ goto fail;
+ syms_view_valid = 1;
+
+ str_size = coff_read4 (syms_view.data + syms_size);
+
+ str_off = syms_off + syms_size;
+
+ if (str_size > 4)
+ {
+ /* Map string table (including the length word). */
+
+ if (!backtrace_get_view (state, descriptor, str_off, str_size,
+ error_callback, data, &str_view))
+ goto fail;
+ str_view_valid = 1;
+ }
+ }
+
+ memset (sections, 0, sizeof sections);
+
+ /* Look for the symbol table. */
+ for (i = 0; i < sects_num; ++i)
+ {
+ const b_coff_section_header *s = sects + i;
+ unsigned int str_off;
+ int j;
+
+ if (s->name[0] == '/')
+ {
+ /* Extended section name. */
+ str_off = atoi (s->name + 1);
+ }
+ else
+ str_off = 0;
+
+ for (j = 0; j < (int) DEBUG_MAX; ++j)
+ {
+ const char *dbg_name = debug_section_names[j];
+ int match;
+
+ if (str_off != 0)
+ match = coff_long_name_eq (dbg_name, str_off, &str_view);
+ else
+ match = coff_short_name_eq (dbg_name, s->name);
+ if (match)
+ {
+ sections[j].offset = s->pointer_to_raw_data;
+ sections[j].size = s->virtual_size <= s->size_of_raw_data ?
+ s->virtual_size : s->size_of_raw_data;
+ break;
+ }
+ }
+ }
+
+ if (syms_num != 0)
+ {
+ struct coff_syminfo_data *sdata;
+
+ sdata = ((struct coff_syminfo_data *)
+ backtrace_alloc (state, sizeof *sdata, error_callback, data));
+ if (sdata == NULL)
+ goto fail;
+
+ if (!coff_initialize_syminfo (state, image_base, is_64,
+ sects, sects_num,
+ syms_view.data, syms_size,
+ str_view.data, str_size,
+ error_callback, data, sdata))
+ {
+ backtrace_free (state, sdata, sizeof *sdata, error_callback, data);
+ goto fail;
+ }
+
+ *found_sym = 1;
+
+ coff_add_syminfo_data (state, sdata);
+ }
+
+ backtrace_release_view (state, &sects_view, error_callback, data);
+ sects_view_valid = 0;
+ if (syms_view_valid)
+ {
+ backtrace_release_view (state, &syms_view, error_callback, data);
+ syms_view_valid = 0;
+ }
+
+ /* Read all the debug sections in a single view, since they are
+ probably adjacent in the file. We never release this view. */
+
+ min_offset = 0;
+ max_offset = 0;
+ for (i = 0; i < (int) DEBUG_MAX; ++i)
+ {
+ off_t end;
+
+ if (sections[i].size == 0)
+ continue;
+ if (min_offset == 0 || sections[i].offset < min_offset)
+ min_offset = sections[i].offset;
+ end = sections[i].offset + sections[i].size;
+ if (end > max_offset)
+ max_offset = end;
+ }
+ if (min_offset == 0 || max_offset == 0)
+ {
+ if (!backtrace_close (descriptor, error_callback, data))
+ goto fail;
+ *fileline_fn = coff_nodebug;
+ return 1;
+ }
+
+ if (!backtrace_get_view (state, descriptor, min_offset,
+ max_offset - min_offset,
+ error_callback, data, &debug_view))
+ goto fail;
+ debug_view_valid = 1;
+
+ /* We've read all we need from the executable. */
+ if (!backtrace_close (descriptor, error_callback, data))
+ goto fail;
+ descriptor = -1;
+
+ for (i = 0; i < (int) DEBUG_MAX; ++i)
+ {
+ size_t size = sections[i].size;
+ dwarf_sections.size[i] = size;
+ if (size == 0)
+ dwarf_sections.data[i] = NULL;
+ else
+ dwarf_sections.data[i] = ((const unsigned char *) debug_view.data
+ + (sections[i].offset - min_offset));
+ }
+
+ if (!backtrace_dwarf_add (state, /* base_address */ 0, &dwarf_sections,
+ 0, /* FIXME: is_bigendian */
+ NULL, /* altlink */
+ error_callback, data, fileline_fn,
+ NULL /* returned fileline_entry */))
+ goto fail;
+
+ *found_dwarf = 1;
+
+ return 1;
+
+ fail:
+ if (sects_view_valid)
+ backtrace_release_view (state, &sects_view, error_callback, data);
+ if (str_view_valid)
+ backtrace_release_view (state, &str_view, error_callback, data);
+ if (syms_view_valid)
+ backtrace_release_view (state, &syms_view, error_callback, data);
+ if (debug_view_valid)
+ backtrace_release_view (state, &debug_view, error_callback, data);
+ if (descriptor != -1)
+ backtrace_close (descriptor, error_callback, data);
+ return 0;
+}
+
+/* Initialize the backtrace data we need from an ELF executable. At
+ the ELF level, all we need to do is find the debug info
+ sections. */
+
+int
+backtrace_initialize (struct backtrace_state *state,
+ const char *filename ATTRIBUTE_UNUSED, int descriptor,
+ backtrace_error_callback error_callback,
+ void *data, fileline *fileline_fn)
+{
+ int ret;
+ int found_sym;
+ int found_dwarf;
+ fileline coff_fileline_fn;
+
+ ret = coff_add (state, descriptor, error_callback, data,
+ &coff_fileline_fn, &found_sym, &found_dwarf);
+ if (!ret)
+ return 0;
+
+ if (!state->threaded)
+ {
+ if (found_sym)
+ state->syminfo_fn = coff_syminfo;
+ else if (state->syminfo_fn == NULL)
+ state->syminfo_fn = coff_nosyms;
+ }
+ else
+ {
+ if (found_sym)
+ backtrace_atomic_store_pointer (&state->syminfo_fn, coff_syminfo);
+ else
+ (void) __sync_bool_compare_and_swap (&state->syminfo_fn, NULL,
+ coff_nosyms);
+ }
+
+ if (!state->threaded)
+ {
+ if (state->fileline_fn == NULL || state->fileline_fn == coff_nodebug)
+ *fileline_fn = coff_fileline_fn;
+ }
+ else
+ {
+ fileline current_fn;
+
+ current_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
+ if (current_fn == NULL || current_fn == coff_nodebug)
+ *fileline_fn = coff_fileline_fn;
+ }
+
+ return 1;
+}
diff --git a/thirdparty/libbacktrace/posix.c b/thirdparty/libbacktrace/posix.c
new file mode 100644
index 0000000000..924631d2e6
--- /dev/null
+++ b/thirdparty/libbacktrace/posix.c
@@ -0,0 +1,104 @@
+/* posix.c -- POSIX file I/O routines for the backtrace library.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifndef FD_CLOEXEC
+#define FD_CLOEXEC 1
+#endif
+
+/* Open a file for reading. */
+
+int
+backtrace_open (const char *filename, backtrace_error_callback error_callback,
+ void *data, int *does_not_exist)
+{
+ int descriptor;
+
+ if (does_not_exist != NULL)
+ *does_not_exist = 0;
+
+ descriptor = open (filename, (int) (O_RDONLY | O_BINARY | O_CLOEXEC));
+ if (descriptor < 0)
+ {
+ /* If DOES_NOT_EXIST is not NULL, then don't call ERROR_CALLBACK
+ if the file does not exist. We treat lacking permission to
+ open the file as the file not existing; this case arises when
+ running the libgo syscall package tests as root. */
+ if (does_not_exist != NULL && (errno == ENOENT || errno == EACCES))
+ *does_not_exist = 1;
+ else
+ error_callback (data, filename, errno);
+ return -1;
+ }
+
+#ifdef HAVE_FCNTL
+ /* Set FD_CLOEXEC just in case the kernel does not support
+ O_CLOEXEC. It doesn't matter if this fails for some reason.
+ FIXME: At some point it should be safe to only do this if
+ O_CLOEXEC == 0. */
+ fcntl (descriptor, F_SETFD, FD_CLOEXEC);
+#endif
+
+ return descriptor;
+}
+
+/* Close DESCRIPTOR. */
+
+int
+backtrace_close (int descriptor, backtrace_error_callback error_callback,
+ void *data)
+{
+ if (close (descriptor) < 0)
+ {
+ error_callback (data, "close", errno);
+ return 0;
+ }
+ return 1;
+}
diff --git a/thirdparty/libbacktrace/print.c b/thirdparty/libbacktrace/print.c
new file mode 100644
index 0000000000..93d0d3abb4
--- /dev/null
+++ b/thirdparty/libbacktrace/print.c
@@ -0,0 +1,92 @@
+/* print.c -- Print the current backtrace.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* Passed to callbacks. */
+
+struct print_data
+{
+ struct backtrace_state *state;
+ FILE *f;
+};
+
+/* Print one level of a backtrace. */
+
+static int
+print_callback (void *data, uintptr_t pc, const char *filename, int lineno,
+ const char *function)
+{
+ struct print_data *pdata = (struct print_data *) data;
+
+ fprintf (pdata->f, "0x%lx %s\n\t%s:%d\n",
+ (unsigned long) pc,
+ function == NULL ? "???" : function,
+ filename == NULL ? "???" : filename,
+ lineno);
+ return 0;
+}
+
+/* Print errors to stderr. */
+
+static void
+error_callback (void *data, const char *msg, int errnum)
+{
+ struct print_data *pdata = (struct print_data *) data;
+
+ if (pdata->state->filename != NULL)
+ fprintf (stderr, "%s: ", pdata->state->filename);
+ fprintf (stderr, "libbacktrace: %s", msg);
+ if (errnum > 0)
+ fprintf (stderr, ": %s", strerror (errnum));
+ fputc ('\n', stderr);
+}
+
+/* Print a backtrace. */
+
+void __attribute__((noinline))
+backtrace_print (struct backtrace_state *state, int skip, FILE *f)
+{
+ struct print_data data;
+
+ data.state = state;
+ data.f = f;
+ backtrace_full (state, skip + 1, print_callback, error_callback,
+ (void *) &data);
+}
diff --git a/thirdparty/libbacktrace/read.c b/thirdparty/libbacktrace/read.c
new file mode 100644
index 0000000000..1811c8d2e0
--- /dev/null
+++ b/thirdparty/libbacktrace/read.c
@@ -0,0 +1,110 @@
+/* read.c -- File views without mmap.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* This file implements file views when mmap is not available. */
+
+/* Create a view of SIZE bytes from DESCRIPTOR at OFFSET. */
+
+int
+backtrace_get_view (struct backtrace_state *state, int descriptor,
+ off_t offset, uint64_t size,
+ backtrace_error_callback error_callback,
+ void *data, struct backtrace_view *view)
+{
+ uint64_t got;
+ ssize_t r;
+
+ if ((uint64_t) (size_t) size != size)
+ {
+ error_callback (data, "file size too large", 0);
+ return 0;
+ }
+
+ if (lseek (descriptor, offset, SEEK_SET) < 0)
+ {
+ error_callback (data, "lseek", errno);
+ return 0;
+ }
+
+ view->base = backtrace_alloc (state, size, error_callback, data);
+ if (view->base == NULL)
+ return 0;
+ view->data = view->base;
+ view->len = size;
+
+ got = 0;
+ while (got < size)
+ {
+ r = read (descriptor, view->base, size - got);
+ if (r < 0)
+ {
+ error_callback (data, "read", errno);
+ free (view->base);
+ return 0;
+ }
+ if (r == 0)
+ break;
+ got += (uint64_t) r;
+ }
+
+ if (got < size)
+ {
+ error_callback (data, "file too short", 0);
+ free (view->base);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Release a view read by backtrace_get_view. */
+
+void
+backtrace_release_view (struct backtrace_state *state,
+ struct backtrace_view *view,
+ backtrace_error_callback error_callback,
+ void *data)
+{
+ backtrace_free (state, view->base, view->len, error_callback, data);
+ view->data = NULL;
+ view->base = NULL;
+}
diff --git a/thirdparty/libbacktrace/simple.c b/thirdparty/libbacktrace/simple.c
new file mode 100644
index 0000000000..785e726e6b
--- /dev/null
+++ b/thirdparty/libbacktrace/simple.c
@@ -0,0 +1,108 @@
+/* simple.c -- The backtrace_simple function.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include "unwind.h"
+#include "backtrace.h"
+
+/* The simple_backtrace routine. */
+
+/* Data passed through _Unwind_Backtrace. */
+
+struct backtrace_simple_data
+{
+ /* Number of frames to skip. */
+ int skip;
+ /* Library state. */
+ struct backtrace_state *state;
+ /* Callback routine. */
+ backtrace_simple_callback callback;
+ /* Error callback routine. */
+ backtrace_error_callback error_callback;
+ /* Data to pass to callback routine. */
+ void *data;
+ /* Value to return from backtrace. */
+ int ret;
+};
+
+/* Unwind library callback routine. This is passed to
+ _Unwind_Backtrace. */
+
+static _Unwind_Reason_Code
+simple_unwind (struct _Unwind_Context *context, void *vdata)
+{
+ struct backtrace_simple_data *bdata = (struct backtrace_simple_data *) vdata;
+ uintptr_t pc;
+ int ip_before_insn = 0;
+
+#ifdef HAVE_GETIPINFO
+ pc = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
+ pc = _Unwind_GetIP (context);
+#endif
+
+ if (bdata->skip > 0)
+ {
+ --bdata->skip;
+ return _URC_NO_REASON;
+ }
+
+ if (!ip_before_insn)
+ --pc;
+
+ bdata->ret = bdata->callback (bdata->data, pc);
+
+ if (bdata->ret != 0)
+ return _URC_END_OF_STACK;
+
+ return _URC_NO_REASON;
+}
+
+/* Get a simple stack backtrace. */
+
+int __attribute__((noinline))
+backtrace_simple (struct backtrace_state *state, int skip,
+ backtrace_simple_callback callback,
+ backtrace_error_callback error_callback, void *data)
+{
+ struct backtrace_simple_data bdata;
+
+ bdata.skip = skip + 1;
+ bdata.state = state;
+ bdata.callback = callback;
+ bdata.error_callback = error_callback;
+ bdata.data = data;
+ bdata.ret = 0;
+ _Unwind_Backtrace (simple_unwind, &bdata);
+ return bdata.ret;
+}
diff --git a/thirdparty/libbacktrace/sort.c b/thirdparty/libbacktrace/sort.c
new file mode 100644
index 0000000000..a60a980e65
--- /dev/null
+++ b/thirdparty/libbacktrace/sort.c
@@ -0,0 +1,108 @@
+/* sort.c -- Sort without allocating memory
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <stddef.h>
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* The GNU glibc version of qsort allocates memory, which we must not
+ do if we are invoked by a signal handler. So provide our own
+ sort. */
+
+static void
+swap (char *a, char *b, size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++, a++, b++)
+ {
+ char t;
+
+ t = *a;
+ *a = *b;
+ *b = t;
+ }
+}
+
+void
+backtrace_qsort (void *basearg, size_t count, size_t size,
+ int (*compar) (const void *, const void *))
+{
+ char *base = (char *) basearg;
+ size_t i;
+ size_t mid;
+
+ tail_recurse:
+ if (count < 2)
+ return;
+
+ /* The symbol table and DWARF tables, which is all we use this
+ routine for, tend to be roughly sorted. Pick the middle element
+ in the array as our pivot point, so that we are more likely to
+ cut the array in half for each recursion step. */
+ swap (base, base + (count / 2) * size, size);
+
+ mid = 0;
+ for (i = 1; i < count; i++)
+ {
+ if ((*compar) (base, base + i * size) > 0)
+ {
+ ++mid;
+ if (i != mid)
+ swap (base + mid * size, base + i * size, size);
+ }
+ }
+
+ if (mid > 0)
+ swap (base, base + mid * size, size);
+
+ /* Recurse with the smaller array, loop with the larger one. That
+ ensures that our maximum stack depth is log count. */
+ if (2 * mid < count)
+ {
+ backtrace_qsort (base, mid, size, compar);
+ base += (mid + 1) * size;
+ count -= mid + 1;
+ goto tail_recurse;
+ }
+ else
+ {
+ backtrace_qsort (base + (mid + 1) * size, count - (mid + 1),
+ size, compar);
+ count = mid;
+ goto tail_recurse;
+ }
+}
diff --git a/thirdparty/libbacktrace/state.c b/thirdparty/libbacktrace/state.c
new file mode 100644
index 0000000000..0f368a2390
--- /dev/null
+++ b/thirdparty/libbacktrace/state.c
@@ -0,0 +1,72 @@
+/* state.c -- Create the backtrace state.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <string.h>
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "backtrace-supported.h"
+#include "internal.h"
+
+/* Create the backtrace state. This will then be passed to all the
+ other routines. */
+
+struct backtrace_state *
+backtrace_create_state (const char *filename, int threaded,
+ backtrace_error_callback error_callback,
+ void *data)
+{
+ struct backtrace_state init_state;
+ struct backtrace_state *state;
+
+#ifndef HAVE_SYNC_FUNCTIONS
+ if (threaded)
+ {
+ error_callback (data, "backtrace library does not support threads", 0);
+ return NULL;
+ }
+#endif
+
+ memset (&init_state, 0, sizeof init_state);
+ init_state.filename = filename;
+ init_state.threaded = threaded;
+
+ state = ((struct backtrace_state *)
+ backtrace_alloc (&init_state, sizeof *state, error_callback, data));
+ if (state == NULL)
+ return NULL;
+ *state = init_state;
+
+ return state;
+}
diff --git a/thirdparty/mbedtls/include/godot_module_mbedtls_config.h b/thirdparty/mbedtls/include/godot_module_mbedtls_config.h
index aed276766f..2011827b7a 100644
--- a/thirdparty/mbedtls/include/godot_module_mbedtls_config.h
+++ b/thirdparty/mbedtls/include/godot_module_mbedtls_config.h
@@ -49,8 +49,10 @@
#undef MBEDTLS_DES_C
#undef MBEDTLS_DHM_C
-#ifndef __linux__
+#if !(defined(__linux__) && defined(__aarch64__))
// ARMv8 hardware AES operations. Detection only possible on linux.
+// May technically be supported on some ARM32 arches but doesn't seem
+// to be in our current Linux SDK's neon-fp-armv8.
#undef MBEDTLS_AESCE_C
#endif