From 6d1ddf7eb13a2128d98b09380d1df36473914e84 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Mon, 18 May 2020 02:19:35 +0200 Subject: [PATCH 01/23] AudioDriverJavaScript now compute buffer size. Based on mix rate and expected latency. (cherry picked from commit 245c179bd31d65c6b31d156c4f6b08647df0e2fe) --- .../javascript/audio_driver_javascript.cpp | 43 +++++++++++++------ platform/javascript/audio_driver_javascript.h | 1 + 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp index f1bc7c4382d..5a6a66c42d1 100644 --- a/platform/javascript/audio_driver_javascript.cpp +++ b/platform/javascript/audio_driver_javascript.cpp @@ -30,6 +30,8 @@ #include "audio_driver_javascript.h" +#include "core/project_settings.h" + #include AudioDriverJavaScript *AudioDriverJavaScript::singleton = NULL; @@ -68,32 +70,32 @@ void AudioDriverJavaScript::process_capture(float sample) { Error AudioDriverJavaScript::init() { + int mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); + int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + /* clang-format off */ EM_ASM({ - _audioDriver_audioContext = new (window.AudioContext || window.webkitAudioContext); + const MIX_RATE = $0; + const LATENCY = $1; + _audioDriver_audioContext = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: MIX_RATE, latencyHint: LATENCY}); _audioDriver_audioInput = null; _audioDriver_inputStream = null; _audioDriver_scriptNode = null; - }); + }, mix_rate, latency); /* clang-format on */ int channel_count = get_total_channels_by_speaker_mode(get_speaker_mode()); + buffer_length = closest_power_of_2((latency * mix_rate / 1000) * channel_count); /* clang-format off */ buffer_length = EM_ASM_INT({ - var CHANNEL_COUNT = $0; + const BUFFER_LENGTH = $0; + const CHANNEL_COUNT = $1; - var channelCount = _audioDriver_audioContext.destination.channelCount; - try { - // Try letting the browser recommend a buffer length. - _audioDriver_scriptNode = _audioDriver_audioContext.createScriptProcessor(0, 2, channelCount); - } catch (e) { - // ...otherwise, default to 4096. - _audioDriver_scriptNode = _audioDriver_audioContext.createScriptProcessor(4096, 2, channelCount); - } + _audioDriver_scriptNode = _audioDriver_audioContext.createScriptProcessor(BUFFER_LENGTH, 2, CHANNEL_COUNT); _audioDriver_scriptNode.connect(_audioDriver_audioContext.destination); return _audioDriver_scriptNode.bufferSize; - }, channel_count); + }, buffer_length, channel_count); /* clang-format on */ if (!buffer_length) { return FAILED; @@ -155,6 +157,23 @@ void AudioDriverJavaScript::resume() { /* clang-format on */ } +float AudioDriverJavaScript::get_latency() { + /* clang-format off */ + return EM_ASM_DOUBLE({ + var latency = 0; + if (_audioDriver_audioContext) { + if (_audioDriver_audioContext.baseLatency) { + latency += _audioDriver_audioContext.baseLatency; + } + if (_audioDriver_audioContext.outputLatency) { + latency += _audioDriver_audioContext.outputLatency; + } + } + return latency; + }); + /* clang-format on */ +} + int AudioDriverJavaScript::get_mix_rate() const { /* clang-format off */ diff --git a/platform/javascript/audio_driver_javascript.h b/platform/javascript/audio_driver_javascript.h index 2bb97ba1929..229d04ea602 100644 --- a/platform/javascript/audio_driver_javascript.h +++ b/platform/javascript/audio_driver_javascript.h @@ -50,6 +50,7 @@ public: virtual Error init(); virtual void start(); void resume(); + virtual float get_latency(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; virtual void lock(); From 92031098bf75de47cc9e3bd1735304c0a09dcbef Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Mon, 18 May 2020 02:31:38 +0200 Subject: [PATCH 02/23] Move mixrate and latency definition to AudioServer Each driver used to define the (same) project settings value, but the setting names are not driver specific. Ovverriding is still possible via platform tags. (cherry picked from commit 90c7102b51e720d409e1e5597c7942b62e6a6e72) --- drivers/alsa/audio_driver_alsa.cpp | 4 ++-- drivers/coreaudio/audio_driver_coreaudio.cpp | 6 +++--- drivers/pulseaudio/audio_driver_pulseaudio.cpp | 4 ++-- drivers/wasapi/audio_driver_wasapi.cpp | 2 +- drivers/xaudio2/audio_driver_xaudio2.cpp | 4 ++-- platform/android/audio_driver_jandroid.cpp | 4 ++-- platform/haiku/audio_driver_media_kit.cpp | 4 ++-- platform/javascript/audio_driver_javascript.cpp | 4 ++-- servers/audio/audio_driver_dummy.cpp | 4 ++-- servers/audio_server.cpp | 3 +++ servers/audio_server.h | 6 +++--- 11 files changed, 24 insertions(+), 21 deletions(-) diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp index 425f12ae919..07b6cc441f2 100644 --- a/drivers/alsa/audio_driver_alsa.cpp +++ b/drivers/alsa/audio_driver_alsa.cpp @@ -38,7 +38,7 @@ #include Error AudioDriverALSA::init_device() { - mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); + mix_rate = GLOBAL_GET("audio/mix_rate"); speaker_mode = SPEAKER_MODE_STEREO; channels = 2; @@ -104,7 +104,7 @@ Error AudioDriverALSA::init_device() { // In ALSA the period size seems to be the one that will determine the actual latency // Ref: https://www.alsa-project.org/main/index.php/FramesPeriods unsigned int periods = 2; - int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + int latency = GLOBAL_GET("audio/output_latency"); buffer_frames = closest_power_of_2(latency * mix_rate / 1000); buffer_size = buffer_frames * periods; period_size = buffer_frames; diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp index c67e90c1dfc..a8a2433bdfe 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.cpp +++ b/drivers/coreaudio/audio_driver_coreaudio.cpp @@ -118,7 +118,7 @@ Error AudioDriverCoreAudio::init() { break; } - mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); + mix_rate = GLOBAL_GET("audio/mix_rate"); zeromem(&strdesc, sizeof(strdesc)); strdesc.mFormatID = kAudioFormatLinearPCM; @@ -133,7 +133,7 @@ Error AudioDriverCoreAudio::init() { result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &strdesc, sizeof(strdesc)); ERR_FAIL_COND_V(result != noErr, FAILED); - int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + int latency = GLOBAL_GET("audio/output_latency"); // Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels) buffer_frames = closest_power_of_2(latency * mix_rate / 1000); @@ -419,7 +419,7 @@ Error AudioDriverCoreAudio::capture_init() { break; } - mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); + mix_rate = GLOBAL_GET("audio/mix_rate"); zeromem(&strdesc, sizeof(strdesc)); strdesc.mFormatID = kAudioFormatLinearPCM; diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index 524f0363a1f..dfb3ddec9cd 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -182,7 +182,7 @@ Error AudioDriverPulseAudio::init_device() { break; } - int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + int latency = GLOBAL_GET("audio/output_latency"); buffer_frames = closest_power_of_2(latency * mix_rate / 1000); pa_buffer_size = buffer_frames * pa_map.channels; @@ -241,7 +241,7 @@ Error AudioDriverPulseAudio::init() { thread_exited = false; exit_thread = false; - mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); + mix_rate = GLOBAL_GET("audio/mix_rate"); pa_ml = pa_mainloop_new(); ERR_FAIL_COND_V(pa_ml == NULL, ERR_CANT_OPEN); diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index 9a05284aea3..6f056098182 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -396,7 +396,7 @@ Error AudioDriverWASAPI::finish_capture_device() { Error AudioDriverWASAPI::init() { - mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); + mix_rate = GLOBAL_GET("audio/mix_rate"); Error err = init_render_device(); if (err != OK) { diff --git a/drivers/xaudio2/audio_driver_xaudio2.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp index 85e505009b6..2367db44294 100644 --- a/drivers/xaudio2/audio_driver_xaudio2.cpp +++ b/drivers/xaudio2/audio_driver_xaudio2.cpp @@ -45,12 +45,12 @@ Error AudioDriverXAudio2::init() { pcm_open = false; samples_in = NULL; - mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); + mix_rate = GLOBAL_GET("audio/mix_rate"); // FIXME: speaker_mode seems unused in the Xaudio2 driver so far speaker_mode = SPEAKER_MODE_STEREO; channels = 2; - int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + int latency = GLOBAL_GET("audio/output_latency"); buffer_size = closest_power_of_2(latency * mix_rate / 1000); samples_in = memnew_arr(int32_t, buffer_size * channels); diff --git a/platform/android/audio_driver_jandroid.cpp b/platform/android/audio_driver_jandroid.cpp index 5a8e3b94da9..05b1399ce03 100644 --- a/platform/android/audio_driver_jandroid.cpp +++ b/platform/android/audio_driver_jandroid.cpp @@ -76,9 +76,9 @@ Error AudioDriverAndroid::init() { // __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device"); JNIEnv *env = ThreadAndroid::get_env(); - int mix_rate = GLOBAL_DEF_RST("audio/mix_rate", 44100); + int mix_rate = GLOBAL_GET("audio/mix_rate"); - int latency = GLOBAL_DEF_RST("audio/output_latency", 25); + int latency = GLOBAL_GET("audio/output_latency"); unsigned int buffer_size = next_power_of_2(latency * mix_rate / 1000); print_verbose("Audio buffer size: " + itos(buffer_size)); diff --git a/platform/haiku/audio_driver_media_kit.cpp b/platform/haiku/audio_driver_media_kit.cpp index b7f6b572447..b7c3e3198f2 100644 --- a/platform/haiku/audio_driver_media_kit.cpp +++ b/platform/haiku/audio_driver_media_kit.cpp @@ -39,11 +39,11 @@ int32_t *AudioDriverMediaKit::samples_in = NULL; Error AudioDriverMediaKit::init() { active = false; - mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); + mix_rate = GLOBAL_GET("audio/mix_rate"); speaker_mode = SPEAKER_MODE_STEREO; channels = 2; - int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + int latency = GLOBAL_GET("audio/output_latency"); buffer_size = next_power_of_2(latency * mix_rate / 1000); samples_in = memnew_arr(int32_t, buffer_size * channels); diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp index 5a6a66c42d1..0d4bbb359b9 100644 --- a/platform/javascript/audio_driver_javascript.cpp +++ b/platform/javascript/audio_driver_javascript.cpp @@ -70,8 +70,8 @@ void AudioDriverJavaScript::process_capture(float sample) { Error AudioDriverJavaScript::init() { - int mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); - int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + int mix_rate = GLOBAL_GET("audio/mix_rate"); + int latency = GLOBAL_GET("audio/output_latency"); /* clang-format off */ EM_ASM({ diff --git a/servers/audio/audio_driver_dummy.cpp b/servers/audio/audio_driver_dummy.cpp index 5389c640998..708f8fdd784 100644 --- a/servers/audio/audio_driver_dummy.cpp +++ b/servers/audio/audio_driver_dummy.cpp @@ -40,11 +40,11 @@ Error AudioDriverDummy::init() { exit_thread = false; samples_in = NULL; - mix_rate = DEFAULT_MIX_RATE; + mix_rate = GLOBAL_GET("audio/mix_rate"); speaker_mode = SPEAKER_MODE_STEREO; channels = 2; - int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + int latency = GLOBAL_GET("audio/output_latency"); buffer_frames = closest_power_of_2(latency * mix_rate / 1000); samples_in = memnew_arr(int32_t, buffer_frames * channels); diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 232a2f72d6f..c0aa0424787 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -182,6 +182,9 @@ int AudioDriverManager::get_driver_count() { void AudioDriverManager::initialize(int p_driver) { GLOBAL_DEF_RST("audio/enable_audio_input", false); + GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); + GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); + int failed_driver = -1; // Check if there is a selected driver diff --git a/servers/audio_server.h b/servers/audio_server.h index eff66d4008f..c9769788710 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -81,9 +81,6 @@ public: SPEAKER_SURROUND_71, }; - static const int DEFAULT_MIX_RATE = 44100; - static const int DEFAULT_OUTPUT_LATENCY = 15; - static AudioDriver *get_singleton(); void set_singleton(); @@ -131,6 +128,9 @@ class AudioDriverManager { MAX_DRIVERS = 10 }; + static const int DEFAULT_MIX_RATE = 44100; + static const int DEFAULT_OUTPUT_LATENCY = 15; + static AudioDriver *drivers[MAX_DRIVERS]; static int driver_count; From 05cd3ff8ba4b1b85f88a38022cc899c32d74ac70 Mon Sep 17 00:00:00 2001 From: Eduardo Nunes Pereira Date: Mon, 4 May 2020 07:30:57 -0300 Subject: [PATCH 03/23] Using command + comma on macOS as default shortcut for editor settings (cherry picked from commit 75f77f751ef1a2f4a1596225d0900ca9bde1527d) --- editor/editor_node.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 729c1046c91..546443f49a5 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -6249,7 +6249,11 @@ EditorNode::EditorNode() { p = settings_menu->get_popup(); p->set_hide_on_window_lose_focus(true); +#ifdef OSX_ENABLED + p->add_shortcut(ED_SHORTCUT("editor/editor_settings", TTR("Editor Settings..."), KEY_MASK_CMD + KEY_COMMA), SETTINGS_PREFERENCES); +#else p->add_shortcut(ED_SHORTCUT("editor/editor_settings", TTR("Editor Settings...")), SETTINGS_PREFERENCES); +#endif p->add_separator(); editor_layouts = memnew(PopupMenu); From 8cab02ec06f974ab928077644900f4fd8e8e229e Mon Sep 17 00:00:00 2001 From: smartin015 Date: Mon, 4 May 2020 20:36:52 +0200 Subject: [PATCH 04/23] Remove get_local_mouse_position() hack in GraphEdit (cherry picked from commit 00457c68bc7e8708e85d6fbd63cba55653f10d46) --- scene/gui/graph_edit.cpp | 14 ++++++-------- scene/gui/graph_edit.h | 1 - 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index db16dddc09f..91f5af89631 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -812,9 +812,7 @@ void GraphEdit::_gui_input(const Ref &p_ev) { if (mm.is_valid() && dragging) { just_selected = true; - // TODO: Remove local mouse pos hack if/when InputEventMouseMotion is fixed to support floats - //drag_accum+=Vector2(mm->get_relative().x,mm->get_relative().y); - drag_accum = get_local_mouse_position() - drag_origin; + drag_accum += mm->get_relative(); for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to(get_child(i)); if (gn && gn->is_selected()) { @@ -834,7 +832,7 @@ void GraphEdit::_gui_input(const Ref &p_ev) { } if (mm.is_valid() && box_selecting) { - box_selecting_to = get_local_mouse_position(); + box_selecting_to = mm->get_position(); box_selecting_rect = Rect2(MIN(box_selecting_from.x, box_selecting_to.x), MIN(box_selecting_from.y, box_selecting_to.y), @@ -894,8 +892,9 @@ void GraphEdit::_gui_input(const Ref &p_ev) { if (gn) { Rect2 r = gn->get_rect(); r.size *= zoom; - if (r.has_point(get_local_mouse_position())) + if (r.has_point(b->get_position())) { gn->set_selected(false); + } } } } @@ -932,7 +931,7 @@ void GraphEdit::_gui_input(const Ref &p_ev) { if (gn_selected->is_resizing()) continue; - if (gn_selected->has_point(gn_selected->get_local_mouse_position())) { + if (gn_selected->has_point(b->get_position() - gn_selected->get_position())) { gn = gn_selected; break; } @@ -946,7 +945,6 @@ void GraphEdit::_gui_input(const Ref &p_ev) { dragging = true; drag_accum = Vector2(); - drag_origin = get_local_mouse_position(); just_selected = !gn->is_selected(); if (!gn->is_selected() && !Input::get_singleton()->is_key_pressed(KEY_CONTROL)) { for (int i = 0; i < get_child_count(); i++) { @@ -980,7 +978,7 @@ void GraphEdit::_gui_input(const Ref &p_ev) { return; box_selecting = true; - box_selecting_from = get_local_mouse_position(); + box_selecting_from = b->get_position(); if (b->get_control()) { box_selection_mode_additive = true; previus_selected.clear(); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index f675f8c7f30..08f75e90ea6 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -99,7 +99,6 @@ private: bool dragging; bool just_selected; Vector2 drag_accum; - Point2 drag_origin; // Workaround for GH-5907 float zoom; From 4779d289d59055ce8418d326e932ef3b43cb4663 Mon Sep 17 00:00:00 2001 From: Marcus Brummer Date: Sat, 16 May 2020 16:31:43 +0200 Subject: [PATCH 05/23] Fixed text editor drawing if smooth scrolling is disabled. Fixes #38778 (cherry picked from commit b048eb05ad978ba5e0ed93f3d0488d7fe355df5c) --- scene/gui/text_edit.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 97ea9b6fd8f..07718df471c 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1135,8 +1135,7 @@ void TextEdit::_notification(int p_what) { int ofs_y = (i * get_row_height() + cache.line_spacing / 2) + ofs_readonly; ofs_y -= cursor.wrap_ofs * get_row_height(); - if (smooth_scroll_enabled) - ofs_y += (-get_v_scroll_offset()) * get_row_height(); + ofs_y -= get_v_scroll_offset() * get_row_height(); // Check if line contains highlighted word. int highlighted_text_col = -1; From 8ebcdeb3d7ad4d45b1be3e3b2754e83300cba234 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Sat, 16 May 2020 23:52:40 +0200 Subject: [PATCH 06/23] Document that Dictionary is always passed as reference See #38792. (cherry picked from commit 675fea16489048919a059d10c4ae846cc7fe4ba7) --- doc/classes/Array.xml | 2 +- doc/classes/Dictionary.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index 3b3239e9d8b..ed445896dc2 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -20,7 +20,7 @@ var array2 = [3, "Four"] print(array1 + array2) # ["One", 2, 3, "Four"] [/codeblock] - Arrays are always passed by reference. + [b]Note:[/b] Arrays are always passed by reference. To get a copy of an array which can be modified independently of the original array, use [method duplicate]. diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index 2e139435a76..d6497be7931 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -7,6 +7,7 @@ Dictionary type. Associative container which contains values referenced by unique keys. Dictionaries are composed of pairs of keys (which must be unique) and values. Dictionaries will preserve the insertion order when adding elements, even though this may not be reflected when printing the dictionary. In other programming languages, this data structure is sometimes referred to as an hash map or associative array. You can define a dictionary by placing a comma-separated list of [code]key: value[/code] pairs in curly braces [code]{}[/code]. Erasing elements while iterating over them [b]is not supported[/b] and will result in undefined behavior. + [b]Note:[/b] Dictionaries are always passed by reference. To get a copy of a dictionary which can be modified independently of the original dictionary, use [method duplicate]. Creating a dictionary: [codeblock] var my_dir = {} # Creates an empty dictionary. From 35d95918aeee4f6b4f4145e277ab9fbb2a8ffbea Mon Sep 17 00:00:00 2001 From: "Andrii Doroshenko (Xrayez)" Date: Mon, 18 May 2020 08:24:44 +0300 Subject: [PATCH 07/23] Add `custom.py` to .gitignore The default `custom.py` can be created at the root of the Godot repository to initialize any SCons build options via file which are specific to user. (cherry picked from commit a712e14038736500c780c3090558ee4185b80219) --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 0a4c849007e..55d14e51bdc 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,9 @@ logs/ *.sln *.vcxproj* +# Custom SCons configuration override +/custom.py + # Build results [Dd]ebug/ [Dd]ebugPublic/ From 9aa464857982398ab587c2a30632bfec8fb893a1 Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Sat, 2 Nov 2019 14:21:21 +0000 Subject: [PATCH 08/23] Added compilation database support for clang and gcc This tool is originally from mongodb. - Updated CPPSUFFIXES to use scons suffixes - objective-c files will also be loaded into the compilation database where the compiler / tooling is available to compile the files. Known limitations: - This will not work with msvc as your compiler. (cherry picked from commit 5a6f275b7498e85dc74ecbc14cf264260a2218fa) --- .appveyor.yml | 4 +- SConstruct | 9 ++ misc/scons/site_tools/compilation_db.py | 177 ++++++++++++++++++++++++ 3 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 misc/scons/site_tools/compilation_db.py diff --git a/.appveyor.yml b/.appveyor.yml index ca33dfa3018..9850cdfcb19 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -20,7 +20,9 @@ cache: install: - SET "PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - - pip install scons==3.1.2 + - pip install -U wheel # needed for pip install scons to work, otherwise a flag is missing + - pip install scons # use stable scons + - if defined VS call "%VS%" %ARCH% # if defined - so we can also use mingw before_build: - echo %GD_PLATFORM% diff --git a/SConstruct b/SConstruct index 4bd79957fbc..d61e28d3378 100644 --- a/SConstruct +++ b/SConstruct @@ -258,6 +258,15 @@ if selected_platform in platform_list: else: env = env_base.Clone() + # Custom tools are loaded automatically by SCons from site_scons/site_tools, + # but we want to use a different folder, so we register it manually. + from SCons.Script.Main import _load_site_scons_dir + + _load_site_scons_dir(".", "misc/scons") + + env.Tool("compilation_db") + env.Alias("compiledb", env.CompilationDatabase("compile_commands.json")) + if env['dev']: env['verbose'] = True env['warnings'] = "extra" diff --git a/misc/scons/site_tools/compilation_db.py b/misc/scons/site_tools/compilation_db.py new file mode 100644 index 00000000000..87db32adc96 --- /dev/null +++ b/misc/scons/site_tools/compilation_db.py @@ -0,0 +1,177 @@ +# Copyright 2015 MongoDB Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import SCons +import itertools + +# Implements the ability for SCons to emit a compilation database for the MongoDB project. See +# http://clang.llvm.org/docs/JSONCompilationDatabase.html for details on what a compilation +# database is, and why you might want one. The only user visible entry point here is +# 'env.CompilationDatabase'. This method takes an optional 'target' to name the file that +# should hold the compilation database, otherwise, the file defaults to compile_commands.json, +# which is the name that most clang tools search for by default. + +# TODO: Is there a better way to do this than this global? Right now this exists so that the +# emitter we add can record all of the things it emits, so that the scanner for the top level +# compilation database can access the complete list, and also so that the writer has easy +# access to write all of the files. But it seems clunky. How can the emitter and the scanner +# communicate more gracefully? +__COMPILATION_DB_ENTRIES = [] + +# We make no effort to avoid rebuilding the entries. Someday, perhaps we could and even +# integrate with the cache, but there doesn't seem to be much call for it. +class __CompilationDbNode(SCons.Node.Python.Value): + def __init__(self, value): + SCons.Node.Python.Value.__init__(self, value) + self.Decider(changed_since_last_build_node) + + +def changed_since_last_build_node(child, target, prev_ni, node): + """ Dummy decider to force always building""" + return True + + +def makeEmitCompilationDbEntry(comstr): + """ + Effectively this creates a lambda function to capture: + * command line + * source + * target + :param comstr: unevaluated command line + :return: an emitter which has captured the above + """ + user_action = SCons.Action.Action(comstr) + + def EmitCompilationDbEntry(target, source, env): + """ + This emitter will be added to each c/c++ object build to capture the info needed + for clang tools + :param target: target node(s) + :param source: source node(s) + :param env: Environment for use building this node + :return: target(s), source(s) + """ + + dbtarget = __CompilationDbNode(source) + + entry = env.__COMPILATIONDB_Entry( + target=dbtarget, + source=[], + __COMPILATIONDB_UTARGET=target, + __COMPILATIONDB_USOURCE=source, + __COMPILATIONDB_UACTION=user_action, + __COMPILATIONDB_ENV=env, + ) + + # TODO: Technically, these next two lines should not be required: it should be fine to + # cache the entries. However, they don't seem to update properly. Since they are quick + # to re-generate disable caching and sidestep this problem. + env.AlwaysBuild(entry) + env.NoCache(entry) + + __COMPILATION_DB_ENTRIES.append(dbtarget) + + return target, source + + return EmitCompilationDbEntry + + +def CompilationDbEntryAction(target, source, env, **kw): + """ + Create a dictionary with evaluated command line, target, source + and store that info as an attribute on the target + (Which has been stored in __COMPILATION_DB_ENTRIES array + :param target: target node(s) + :param source: source node(s) + :param env: Environment for use building this node + :param kw: + :return: None + """ + + command = env["__COMPILATIONDB_UACTION"].strfunction( + target=env["__COMPILATIONDB_UTARGET"], source=env["__COMPILATIONDB_USOURCE"], env=env["__COMPILATIONDB_ENV"], + ) + + entry = { + "directory": env.Dir("#").abspath, + "command": command, + "file": str(env["__COMPILATIONDB_USOURCE"][0]), + } + + target[0].write(entry) + + +def WriteCompilationDb(target, source, env): + entries = [] + + for s in __COMPILATION_DB_ENTRIES: + entries.append(s.read()) + + with open(str(target[0]), "w") as target_file: + json.dump(entries, target_file, sort_keys=True, indent=4, separators=(",", ": ")) + + +def ScanCompilationDb(node, env, path): + return __COMPILATION_DB_ENTRIES + + +def generate(env, **kwargs): + + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + env["COMPILATIONDB_COMSTR"] = kwargs.get("COMPILATIONDB_COMSTR", "Building compilation database $TARGET") + + components_by_suffix = itertools.chain( + itertools.product( + env["CPPSUFFIXES"], + [ + (static_obj, SCons.Defaults.StaticObjectEmitter, "$CXXCOM"), + (shared_obj, SCons.Defaults.SharedObjectEmitter, "$SHCXXCOM"), + ], + ), + ) + + for entry in components_by_suffix: + suffix = entry[0] + builder, base_emitter, command = entry[1] + + # Ensure we have a valid entry + # used to auto ignore header files + if suffix in builder.emitter: + emitter = builder.emitter[suffix] + builder.emitter[suffix] = SCons.Builder.ListEmitter([emitter, makeEmitCompilationDbEntry(command),]) + + env["BUILDERS"]["__COMPILATIONDB_Entry"] = SCons.Builder.Builder( + action=SCons.Action.Action(CompilationDbEntryAction, None), + ) + + env["BUILDERS"]["__COMPILATIONDB_Database"] = SCons.Builder.Builder( + action=SCons.Action.Action(WriteCompilationDb, "$COMPILATIONDB_COMSTR"), + target_scanner=SCons.Scanner.Scanner(function=ScanCompilationDb, node_class=None), + ) + + def CompilationDatabase(env, target): + result = env.__COMPILATIONDB_Database(target=target, source=[]) + + env.AlwaysBuild(result) + env.NoCache(result) + + return result + + env.AddMethod(CompilationDatabase, "CompilationDatabase") + + +def exists(env): + return True From dcb68e01068c731cef5565331ef8f2400c276f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Verschelde?= Date: Mon, 18 May 2020 13:58:27 +0200 Subject: [PATCH 09/23] SCons: Improve registration of compilation_db tool, check version There's a builtin `toolpath` option we can use for that, so no need to hack around a custom `scons_site` path. The script requires SCons 3.1.1 or later, so we enable it conditionally. Follow-up to #32848. (cherry picked from commit 22c718ab1777485ad5edf5254d45815e7ffa5000) --- SConstruct | 13 ++++++------- misc/scons/{site_tools => }/compilation_db.py | 0 2 files changed, 6 insertions(+), 7 deletions(-) rename misc/scons/{site_tools => }/compilation_db.py (100%) diff --git a/SConstruct b/SConstruct index d61e28d3378..8e83372c98c 100644 --- a/SConstruct +++ b/SConstruct @@ -258,14 +258,13 @@ if selected_platform in platform_list: else: env = env_base.Clone() - # Custom tools are loaded automatically by SCons from site_scons/site_tools, - # but we want to use a different folder, so we register it manually. - from SCons.Script.Main import _load_site_scons_dir + # Compilation DB requires SCons 3.1.1+. + from SCons import __version__ as scons_raw_version - _load_site_scons_dir(".", "misc/scons") - - env.Tool("compilation_db") - env.Alias("compiledb", env.CompilationDatabase("compile_commands.json")) + scons_ver = env._get_major_minor_revision(scons_raw_version) + if scons_ver >= (3, 1, 1): + env.Tool("compilation_db", toolpath=["misc/scons"]) + env.Alias("compiledb", env.CompilationDatabase("compile_commands.json")) if env['dev']: env['verbose'] = True diff --git a/misc/scons/site_tools/compilation_db.py b/misc/scons/compilation_db.py similarity index 100% rename from misc/scons/site_tools/compilation_db.py rename to misc/scons/compilation_db.py From 75f6d2ef32b47f893555aa4e415e60fcc25888c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Mon, 18 May 2020 14:22:37 +0200 Subject: [PATCH 10/23] Remove no longer needed patches to jpgd.cpp (cherry picked from commit abe03ff1f06fcea1e5384556b0e83168541c87ab) --- thirdparty/README.md | 4 +- .../patches/fix-msvc-sse2-detection.patch | 44 ------------------- .../patches/fix-msvc2017-build.patch | 31 ------------- 3 files changed, 1 insertion(+), 78 deletions(-) delete mode 100644 thirdparty/jpeg-compressor/patches/fix-msvc-sse2-detection.patch delete mode 100644 thirdparty/jpeg-compressor/patches/fix-msvc2017-build.patch diff --git a/thirdparty/README.md b/thirdparty/README.md index fbd51ec4a70..02ddeb7b5c5 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -144,15 +144,13 @@ the GLES version Godot targets. ## jpeg-compressor - Upstream: https://github.com/richgel999/jpeg-compressor -- Version: 2.00 (1eb17d558b9d3b7442d256642a5745974e9eeb1e, 2020) +- Version: 2.00 (aeb7d3b463aa8228b87a28013c15ee50a7e6fcf3, 2020) - License: Public domain Files extracted from upstream source: - `jpgd*.{c,h}` -Patches in the `patches` directory should be re-applied after updates. - ## libogg diff --git a/thirdparty/jpeg-compressor/patches/fix-msvc-sse2-detection.patch b/thirdparty/jpeg-compressor/patches/fix-msvc-sse2-detection.patch deleted file mode 100644 index 830b03b0c0d..00000000000 --- a/thirdparty/jpeg-compressor/patches/fix-msvc-sse2-detection.patch +++ /dev/null @@ -1,44 +0,0 @@ -From ae74fa2fcdef8ec44b925a649f66e8cbefce8315 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?R=C3=A9mi=20Verschelde?= -Date: Thu, 7 May 2020 12:14:09 +0200 -Subject: [PATCH] Fix detection of SSE2 with Visual Studio - -The previous code assumed that SSE2 is available when building with -Visual Studio, but that's not accurate on ARM with UWP. - -SSE2 could also be enabled on x86 if `_M_IX86_FP == 2`, but it requires -checking first that it's not actually set to 2 for AVX, AVX2 or AVX512 -(see https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019), -so I left it out for this quick fix. ---- - jpgd.cpp | 16 +++++++--------- - 1 file changed, 7 insertions(+), 9 deletions(-) - -diff --git a/jpgd.cpp b/jpgd.cpp -index 91e66ad..db1f3b4 100644 ---- a/jpgd.cpp -+++ b/jpgd.cpp -@@ -37,16 +37,14 @@ - - #ifndef JPGD_USE_SSE2 - -- #if defined(__GNUC__) -- -- #if (defined(__x86_64__) || defined(_M_X64)) -- #if defined(__SSE2__) -- #define JPGD_USE_SSE2 (1) -- #endif -+ #if defined(__GNUC__) -+ #if defined(__SSE2__) -+ #define JPGD_USE_SSE2 (1) -+ #endif -+ #elif defined(_MSC_VER) -+ #if defined(_M_X64) -+ #define JPGD_USE_SSE2 (1) - #endif -- -- #else -- #define JPGD_USE_SSE2 (1) - #endif - - #endif diff --git a/thirdparty/jpeg-compressor/patches/fix-msvc2017-build.patch b/thirdparty/jpeg-compressor/patches/fix-msvc2017-build.patch deleted file mode 100644 index 7b338de0846..00000000000 --- a/thirdparty/jpeg-compressor/patches/fix-msvc2017-build.patch +++ /dev/null @@ -1,31 +0,0 @@ -diff --git a/thirdparty/jpeg-compressor/jpgd.cpp b/thirdparty/jpeg-compressor/jpgd.cpp -index a0c494db61..257d0b7574 100644 ---- a/thirdparty/jpeg-compressor/jpgd.cpp -+++ b/thirdparty/jpeg-compressor/jpgd.cpp -@@ -2126,7 +2126,7 @@ namespace jpgd { - - int jpeg_decoder::decode_next_mcu_row() - { -- if (setjmp(m_jmp_state)) -+ if (::setjmp(m_jmp_state)) - return JPGD_FAILED; - - const bool chroma_y_filtering = ((m_flags & cFlagBoxChromaFiltering) == 0) && ((m_scan_type == JPGD_YH2V2) || (m_scan_type == JPGD_YH1V2)); -@@ -3042,7 +3042,7 @@ namespace jpgd { - - jpeg_decoder::jpeg_decoder(jpeg_decoder_stream* pStream, uint32_t flags) - { -- if (setjmp(m_jmp_state)) -+ if (::setjmp(m_jmp_state)) - return; - decode_init(pStream, flags); - } -@@ -3055,7 +3055,7 @@ namespace jpgd { - if (m_error_code) - return JPGD_FAILED; - -- if (setjmp(m_jmp_state)) -+ if (::setjmp(m_jmp_state)) - return JPGD_FAILED; - - decode_start(); From c2eea4bde05856d630e935f26d39b1d8ae862514 Mon Sep 17 00:00:00 2001 From: Eric Rybicki Date: Mon, 18 May 2020 14:47:54 +0200 Subject: [PATCH 11/23] Fix certain characters being recognized as special keys in Windows when using the us international layout (cherry picked from commit e460456e60b5129f89b4647d8bbb9c71a294c1ca) --- platform/windows/key_mapping_windows.cpp | 13 +++++++++++++ platform/windows/key_mapping_windows.h | 1 + platform/windows/os_windows.cpp | 3 ++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/platform/windows/key_mapping_windows.cpp b/platform/windows/key_mapping_windows.cpp index 6bf8a70d0f3..58daa220aea 100644 --- a/platform/windows/key_mapping_windows.cpp +++ b/platform/windows/key_mapping_windows.cpp @@ -251,3 +251,16 @@ unsigned int KeyMappingWindows::get_keysym(unsigned int p_code) { return KEY_UNKNOWN; } + +bool KeyMappingWindows::is_extended_key(unsigned int p_code) { + return p_code == VK_INSERT || + p_code == VK_DELETE || + p_code == VK_HOME || + p_code == VK_END || + p_code == VK_PRIOR || + p_code == VK_NEXT || + p_code == VK_LEFT || + p_code == VK_UP || + p_code == VK_RIGHT || + p_code == VK_DOWN; +} diff --git a/platform/windows/key_mapping_windows.h b/platform/windows/key_mapping_windows.h index 0f9bdecde1e..5f2ea68ef25 100644 --- a/platform/windows/key_mapping_windows.h +++ b/platform/windows/key_mapping_windows.h @@ -43,6 +43,7 @@ class KeyMappingWindows { public: static unsigned int get_keysym(unsigned int p_code); + static bool is_extended_key(unsigned int p_code); }; #endif // KEY_MAPPING_WINDOWS_H diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 3eb953ecd04..eca5be4e498 100755 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1216,7 +1216,8 @@ void OS_Windows::process_key_events() { switch (ke.uMsg) { case WM_CHAR: { - if ((i == 0 && ke.uMsg == WM_CHAR) || (i > 0 && key_event_buffer[i - 1].uMsg == WM_CHAR)) { + // extended keys should only be processed as WM_KEYDOWN message. + if (!KeyMappingWindows::is_extended_key(ke.wParam) && ((i == 0 && ke.uMsg == WM_CHAR) || (i > 0 && key_event_buffer[i - 1].uMsg == WM_CHAR))) { Ref k; k.instance(); From e6a3499bef0eb6b1cb9b30d2d47dead75976a40a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Verschelde?= Date: Mon, 18 May 2020 15:21:26 +0200 Subject: [PATCH 12/23] Fix Visual Studio C4334 warning --- drivers/gles2/rasterizer_canvas_gles2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index 494f688454f..02c32d91ff0 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -2333,7 +2333,7 @@ bool RasterizerCanvasGLES2::try_join_item(Item *p_ci, RenderItemState &r_ris, bo int light_count = -1; while (light) { light_count++; - uint64_t light_bit = 1 << light_count; + uint64_t light_bit = 1ULL << light_count; // note that as a cost of batching, the light culling will be less effective if (p_ci->light_mask & light->item_mask && r_ris.item_group_z >= light->z_min && r_ris.item_group_z <= light->z_max) { From f390749439f6317b950b4f14528ef4b38811cd66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Verschelde?= Date: Tue, 19 May 2020 10:19:08 +0200 Subject: [PATCH 13/23] Fix Visual Studio C4724 warning (potential mod by 0) --- scene/3d/navigation.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scene/3d/navigation.cpp b/scene/3d/navigation.cpp index 4a82fe00807..62576b1b829 100644 --- a/scene/3d/navigation.cpp +++ b/scene/3d/navigation.cpp @@ -251,6 +251,7 @@ void Navigation::_clip_path(Vector &path, Polygon *from_poly, const Vec while (from_poly != p_to_poly) { int pe = from_poly->prev_edge; + ERR_FAIL_COND(from_poly->edges.size() == 0); Vector3 a = _get_vertex(from_poly->edges[pe].point); Vector3 b = _get_vertex(from_poly->edges[(pe + 1) % from_poly->edges.size()].point); @@ -261,7 +262,7 @@ void Navigation::_clip_path(Vector &path, Polygon *from_poly, const Vec Vector3 inters; if (cut_plane.intersects_segment(a, b, &inters)) { - if (inters.distance_to(p_to_point) > CMP_EPSILON && inters.distance_to(path[path.size() - 1]) > CMP_EPSILON) { + if (inters.distance_to(p_to_point) > CMP_EPSILON && inters.distance_to(from) > CMP_EPSILON) { path.push_back(inters); } } @@ -457,6 +458,7 @@ Vector Navigation::get_simple_path(const Vector3 &p_start, const Vector right = begin_point; } else { int prev = p->prev_edge; + ERR_FAIL_COND_V(p->edges.size() == 0, Vector()); int prev_n = (p->prev_edge + 1) % p->edges.size(); left = _get_vertex(p->edges[prev].point); right = _get_vertex(p->edges[prev_n].point); @@ -529,6 +531,7 @@ Vector Navigation::get_simple_path(const Vector3 &p_start, const Vector #ifdef USE_ENTRY_POINT Vector3 point = p->entry; #else + ERR_FAIL_COND_V(p->edges.size() == 0, Vector()); int prev_n = (p->prev_edge + 1) % p->edges.size(); Vector3 point = (_get_vertex(p->edges[prev].point) + _get_vertex(p->edges[prev_n].point)) * 0.5; #endif @@ -586,6 +589,7 @@ Vector3 Navigation::get_closest_point_to_segment(const Vector3 &p_from, const Ve Vector3 a, b; + ERR_FAIL_COND_V(p.edges.size() == 0, Vector3()); Geometry::get_closest_points_between_segments(p_from, p_to, _get_vertex(p.edges[i].point), _get_vertex(p.edges[(i + 1) % p.edges.size()].point), a, b); float d = a.distance_to(b); From a520e03730f08263464943e8f3cfe016b5409d26 Mon Sep 17 00:00:00 2001 From: Marcel Admiraal Date: Thu, 14 Nov 2019 18:06:57 +0100 Subject: [PATCH 14/23] Update .appveyor.yml. - Set options to be the same as travis builds, except use warnings=all instead of warnings=extra. - Fail on warnings as per travis builds. (cherry picked from commit 106221a73332f51dbd40e0e62a56ab67917ab654) Edited to set `werror=no` until we've fixed all warnings. --- .appveyor.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 9850cdfcb19..9df4ab27abc 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -7,10 +7,15 @@ environment: PYTHON: C:\Python38 SCONS_CACHE_ROOT: "%HOME%\\scons_cache" SCONS_CACHE_LIMIT: 1024 + OPTIONS: "debug_symbols=no verbose=yes progress=no" + EXTRA_ARGS: "warnings=all werror=no" matrix: - - GD_PLATFORM: windows - TOOLS: yes - TARGET: release_debug + - GD_PLATFORM: windows + TARGET: release_debug + TOOLS: yes + +matrix: + fast_finish: true init: - ps: if ($env:APPVEYOR_REPO_BRANCH -ne "3.2") { $env:APPVEYOR_CACHE_SKIP_SAVE = "true" } @@ -31,7 +36,7 @@ before_build: - set "SCONS_CACHE=%SCONS_CACHE_ROOT%\%APPVEYOR_REPO_BRANCH%" build_script: - - scons platform=%GD_PLATFORM% target=%TARGET% tools=%TOOLS% debug_symbols=no verbose=yes progress=no gdnative_wrapper=yes + - scons platform=%GD_PLATFORM% target=%TARGET% tools=%TOOLS% %OPTIONS% %EXTRA_ARGS% after_build: - git rev-parse --short=9 HEAD > VERSION_HASH.txt From 9669c437ef0e6f90d31c89f595d5cc729cc97a99 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Fri, 8 Nov 2019 14:38:23 +0200 Subject: [PATCH 15/23] macOS signing improvements: allow signed app exporting as ZIP, sign DMG after exporting. (cherry picked from commit 4bec713b8c1f714ca8d822057a76d86dc0c1ad13) --- platform/osx/export/export.cpp | 345 +++++++++++++++------------------ 1 file changed, 156 insertions(+), 189 deletions(-) diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index cf38664022f..a57b21ad757 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -58,6 +58,7 @@ class EditorExportPlatformOSX : public EditorExportPlatform { Error _code_sign(const Ref &p_preset, const String &p_path); Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name); + void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name); #ifdef OSX_ENABLED bool use_codesign() const { return true; } @@ -370,6 +371,7 @@ void EditorExportPlatformOSX::_fix_plist(const Ref &p_preset **/ Error EditorExportPlatformOSX::_code_sign(const Ref &p_preset, const String &p_path) { +#ifdef OSX_ENABLED List args; if (p_preset->get("codesign/timestamp")) { @@ -380,8 +382,7 @@ Error EditorExportPlatformOSX::_code_sign(const Ref &p_prese args.push_back("runtime"); } - if (p_preset->get("codesign/entitlements") != "") { - /* this should point to our entitlements.plist file that sandboxes our application, I don't know if this should also be placed in our app bundle */ + if ((p_preset->get("codesign/entitlements") != "") && (p_path.get_extension() != "dmg")) { args.push_back("--entitlements"); args.push_back(p_preset->get("codesign/entitlements")); } @@ -414,6 +415,7 @@ Error EditorExportPlatformOSX::_code_sign(const Ref &p_prese EditorNode::add_io_error("codesign: invalid entitlements file"); return FAILED; } +#endif return OK; } @@ -506,53 +508,42 @@ Error EditorExportPlatformOSX::export_project(const Ref &p_p Error err = OK; String tmp_app_path_name = ""; - zlib_filefunc_def io2 = io; - FileAccess *dst_f = NULL; - io2.opaque = &dst_f; - zipFile dst_pkg_zip = NULL; DirAccess *tmp_app_path = NULL; String export_format = use_dmg() && p_path.ends_with("dmg") ? "dmg" : "zip"; - if (export_format == "dmg") { - // We're on OSX so we can export to DMG, but first we create our application bundle - tmp_app_path_name = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".app"); - print_line("Exporting to " + tmp_app_path_name); - tmp_app_path = DirAccess::create_for_path(tmp_app_path_name); - if (!tmp_app_path) { - err = ERR_CANT_CREATE; - } - // Create our folder structure or rely on unzip? - if (err == OK) { - print_line("Creating " + tmp_app_path_name + "/Contents/MacOS"); - err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS"); - } - - if (err == OK) { - print_line("Creating " + tmp_app_path_name + "/Contents/Frameworks"); - err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks"); - } - - if (err == OK) { - print_line("Creating " + tmp_app_path_name + "/Contents/Resources"); - err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/Resources"); - } - } else { - // Open our destination zip file - dst_pkg_zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2); - if (!dst_pkg_zip) { - err = ERR_CANT_CREATE; - } + // Create our application bundle. + tmp_app_path_name = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".app"); + print_line("Exporting to " + tmp_app_path_name); + tmp_app_path = DirAccess::create_for_path(tmp_app_path_name); + if (!tmp_app_path) { + err = ERR_CANT_CREATE; } - // Now process our template + // Create our folder structure. + if (err == OK) { + print_line("Creating " + tmp_app_path_name + "/Contents/MacOS"); + err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS"); + } + + if (err == OK) { + print_line("Creating " + tmp_app_path_name + "/Contents/Frameworks"); + err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks"); + } + + if (err == OK) { + print_line("Creating " + tmp_app_path_name + "/Contents/Resources"); + err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/Resources"); + } + + // Now process our template. bool found_binary = false; int total_size = 0; while (ret == UNZ_OK && err == OK) { bool is_execute = false; - //get filename + // Get filename. unz_file_info info; char fname[16384]; ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, NULL, 0, NULL, 0); @@ -562,13 +553,12 @@ Error EditorExportPlatformOSX::export_project(const Ref &p_p Vector data; data.resize(info.uncompressed_size); - //read + // Read. unzOpenCurrentFile(src_pkg_zip); unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size()); unzCloseCurrentFile(src_pkg_zip); - //write - + // Write. file = file.replace_first("osx_template.app/", ""); if (file == "Contents/Info.plist") { @@ -578,7 +568,7 @@ Error EditorExportPlatformOSX::export_project(const Ref &p_p if (file.begins_with("Contents/MacOS/godot_")) { if (file != "Contents/MacOS/" + binary_to_use) { ret = unzGoToNextFile(src_pkg_zip); - continue; //ignore! + continue; // skip } found_binary = true; is_execute = true; @@ -586,7 +576,7 @@ Error EditorExportPlatformOSX::export_project(const Ref &p_p } if (file == "Contents/Resources/icon.icns") { - //see if there is an icon + // See if there is an icon. String iconpath; if (p_preset->get("application/icon") != "") iconpath = p_preset->get("application/icon"); @@ -618,14 +608,14 @@ Error EditorExportPlatformOSX::export_project(const Ref &p_p if (file.find("/data.mono.osx.64.release_debug/") != -1) { if (!p_debug) { ret = unzGoToNextFile(src_pkg_zip); - continue; //skip + continue; // skip } file = file.replace("/data.mono.osx.64.release_debug/", "/data_" + pkg_name_safe + "/"); } if (file.find("/data.mono.osx.64.release/") != -1) { if (p_debug) { ret = unzGoToNextFile(src_pkg_zip); - continue; //skip + continue; // skip } file = file.replace("/data.mono.osx.64.release/", "/data_" + pkg_name_safe + "/"); } @@ -633,62 +623,31 @@ Error EditorExportPlatformOSX::export_project(const Ref &p_p print_line("ADDING: " + file + " size: " + itos(data.size())); total_size += data.size(); - if (export_format == "dmg") { - // write it into our application bundle - file = tmp_app_path_name.plus_file(file); - if (err == OK) { - err = tmp_app_path->make_dir_recursive(file.get_base_dir()); - } - if (err == OK) { - // write the file, need to add chmod - FileAccess *f = FileAccess::open(file, FileAccess::WRITE); - if (f) { - f->store_buffer(data.ptr(), data.size()); - f->close(); - if (is_execute) { - // Chmod with 0755 if the file is executable - FileAccess::set_unix_permissions(file, 0755); - } - memdelete(f); - } else { - err = ERR_CANT_CREATE; + // Write it into our application bundle. + file = tmp_app_path_name.plus_file(file); + if (err == OK) { + err = tmp_app_path->make_dir_recursive(file.get_base_dir()); + } + if (err == OK) { + FileAccess *f = FileAccess::open(file, FileAccess::WRITE); + if (f) { + f->store_buffer(data.ptr(), data.size()); + f->close(); + if (is_execute) { + // chmod with 0755 if the file is executable. + FileAccess::set_unix_permissions(file, 0755); } + memdelete(f); + } else { + err = ERR_CANT_CREATE; } - } else { - // add it to our zip file - file = pkg_name + ".app/" + file; - - zip_fileinfo fi; - fi.tmz_date.tm_hour = info.tmu_date.tm_hour; - fi.tmz_date.tm_min = info.tmu_date.tm_min; - fi.tmz_date.tm_sec = info.tmu_date.tm_sec; - fi.tmz_date.tm_mon = info.tmu_date.tm_mon; - fi.tmz_date.tm_mday = info.tmu_date.tm_mday; - fi.tmz_date.tm_year = info.tmu_date.tm_year; - fi.dosDate = info.dosDate; - fi.internal_fa = info.internal_fa; - fi.external_fa = info.external_fa; - - zipOpenNewFileInZip(dst_pkg_zip, - file.utf8().get_data(), - &fi, - NULL, - 0, - NULL, - 0, - NULL, - Z_DEFLATED, - Z_DEFAULT_COMPRESSION); - - zipWriteInFileInZip(dst_pkg_zip, data.ptr(), data.size()); - zipCloseFileInZip(dst_pkg_zip); } } ret = unzGoToNextFile(src_pkg_zip); } - // we're done with our source zip + // We're done with our source zip. unzClose(src_pkg_zip); if (!found_binary) { @@ -701,122 +660,130 @@ Error EditorExportPlatformOSX::export_project(const Ref &p_p return ERR_SKIP; } + String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; + Vector shared_objects; + err = save_pack(p_preset, pack_path, &shared_objects); + + // See if we can code sign our new package. + bool sign_enabled = p_preset->get("codesign/enable"); + + if (err == OK) { + DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + for (int i = 0; i < shared_objects.size(); i++) { + err = da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file()); + if (err == OK && sign_enabled) { + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file()); + } + } + memdelete(da); + } + + if (err == OK && sign_enabled) { + if (ep.step("Code signing bundle", 2)) { + return ERR_SKIP; + } + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name); + } + if (export_format == "dmg") { - String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; - Vector shared_objects; - err = save_pack(p_preset, pack_path, &shared_objects); - - // see if we can code sign our new package - bool sign_enabled = p_preset->get("codesign/enable"); - - if (err == OK) { - DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - for (int i = 0; i < shared_objects.size(); i++) { - err = da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file()); - if (err == OK && sign_enabled) { - err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file()); - } - } - memdelete(da); - } - - if (err == OK && sign_enabled) { - if (ep.step("Code signing bundle", 2)) { - return ERR_SKIP; - } - - // the order in which we code sign is important, this is a bit of a shame or we could do this in our loop that extracts the files from our ZIP - - // start with our application - err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name); - - ///@TODO we should check the contents of /Contents/Frameworks for frameworks to sign - } - - // and finally create a DMG + // Create a DMG. if (err == OK) { if (ep.step("Making DMG", 3)) { return ERR_SKIP; } err = _create_dmg(p_path, pkg_name, tmp_app_path_name); } - - // Clean up temporary .app dir - OS::get_singleton()->move_to_trash(tmp_app_path_name); - - } else { // pck - - String pack_path = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".pck"); - - Vector shared_objects; - err = save_pack(p_preset, pack_path, &shared_objects); - - if (err == OK) { - zipOpenNewFileInZip(dst_pkg_zip, - (pkg_name + ".app/Contents/Resources/" + pkg_name + ".pck").utf8().get_data(), - NULL, - NULL, - 0, - NULL, - 0, - NULL, - Z_DEFLATED, - Z_DEFAULT_COMPRESSION); - - FileAccess *pf = FileAccess::open(pack_path, FileAccess::READ); - if (pf) { - const int BSIZE = 16384; - uint8_t buf[BSIZE]; - - while (true) { - - int r = pf->get_buffer(buf, BSIZE); - if (r <= 0) - break; - zipWriteInFileInZip(dst_pkg_zip, buf, r); - } - - zipCloseFileInZip(dst_pkg_zip); - memdelete(pf); - } else { - err = ERR_CANT_OPEN; + // Sign DMG. + if (err == OK && sign_enabled) { + if (ep.step("Code signing DMG", 3)) { + return ERR_SKIP; } + err = _code_sign(p_preset, p_path); } - + } else { + // Create ZIP. if (err == OK) { - //add shared objects - for (int i = 0; i < shared_objects.size(); i++) { - Vector file = FileAccess::get_file_as_array(shared_objects[i].path); - ERR_CONTINUE(file.empty()); - - zipOpenNewFileInZip(dst_pkg_zip, - (pkg_name + ".app/Contents/Frameworks/").plus_file(shared_objects[i].path.get_file()).utf8().get_data(), - NULL, - NULL, - 0, - NULL, - 0, - NULL, - Z_DEFLATED, - Z_DEFAULT_COMPRESSION); - - zipWriteInFileInZip(dst_pkg_zip, file.ptr(), file.size()); - zipCloseFileInZip(dst_pkg_zip); + if (ep.step("Making ZIP", 3)) { + return ERR_SKIP; + } + if (FileAccess::exists(p_path)) { + OS::get_singleton()->move_to_trash(p_path); } - } - // Clean up generated file. - DirAccess::remove_file_or_error(pack_path); + FileAccess *dst_f = nullptr; + zlib_filefunc_def io_dst = zipio_create_io_from_file(&dst_f); + zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst); + + _zip_folder_recursive(zip, EditorSettings::get_singleton()->get_cache_dir(), pkg_name + ".app", pkg_name); + + zipClose(zip, nullptr); + } } - } - if (dst_pkg_zip) { - zipClose(dst_pkg_zip, NULL); + // Clean up temporary .app dir. + OS::get_singleton()->move_to_trash(tmp_app_path_name); } return err; } +void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) { + String dir = p_root_path.plus_file(p_folder); + + DirAccess *da = DirAccess::open(dir); + da->list_dir_begin(); + String f; + while ((f = da->get_next()) != "") { + if (f == "." || f == "..") { + continue; + } + if (da->current_is_dir()) { + _zip_folder_recursive(p_zip, p_root_path, p_folder.plus_file(f), p_pkg_name); + } else { + bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name)); + + OS::Time time = OS::get_singleton()->get_time(); + OS::Date date = OS::get_singleton()->get_date(); + + zip_fileinfo zipfi; + zipfi.tmz_date.tm_hour = time.hour; + zipfi.tmz_date.tm_mday = date.day; + zipfi.tmz_date.tm_min = time.min; + zipfi.tmz_date.tm_mon = date.month; + zipfi.tmz_date.tm_sec = time.sec; + zipfi.tmz_date.tm_year = date.year; + zipfi.dosDate = 0; + zipfi.external_fa = (is_executable ? 0755 : 0644) << 16L; + zipfi.internal_fa = 0; + + zipOpenNewFileInZip4(p_zip, + p_folder.plus_file(f).utf8().get_data(), + &zipfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION, + 0, + -MAX_WBITS, + DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, + nullptr, + 0, + 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions + 0); + + Vector array = FileAccess::get_file_as_array(dir.plus_file(f)); + zipWriteInFileInZip(p_zip, array.ptr(), array.size()); + zipCloseFileInZip(p_zip); + } + } + da->list_dir_end(); + memdelete(da); +} + bool EditorExportPlatformOSX::can_export(const Ref &p_preset, String &r_error, bool &r_missing_templates) const { String err; From b0ba12244323a2c9ad7189a02181c1fa4c7df0b7 Mon Sep 17 00:00:00 2001 From: Andrea Catania Date: Fri, 8 May 2020 14:21:46 +0200 Subject: [PATCH 16/23] Removed const from OAHashMap iterator value to allows to mutate the value while iterating over it. (cherry picked from commit 97f9bbcfa3ba3f09d4486c02cc2230dc21256a3b) --- core/oa_hash_map.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h index 4770e0ec454..9a2bd87ccd3 100644 --- a/core/oa_hash_map.h +++ b/core/oa_hash_map.h @@ -288,7 +288,7 @@ public: bool valid; const TKey *key; - const TValue *value; + TValue *value; private: uint32_t pos; From 2c81a82d533515a7dc09fad58c81d85b03c37e0b Mon Sep 17 00:00:00 2001 From: Fredia Huya-Kouadio Date: Mon, 18 May 2020 15:28:18 -0700 Subject: [PATCH 17/23] Update the permission string for the Oculus hand tracking to match the latest api update (cherry picked from commit 6e2988f26a789cfce5d3835c98560238728b5800) --- platform/android/export/export.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index a3bf5a563f1..27d20c6f7cf 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -991,8 +991,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { feature_required_list.push_back(hand_tracking_index == 2); feature_versions.push_back(-1); // no version attribute should be added. - if (perms.find("oculus.permission.handtracking") == -1) { - perms.push_back("oculus.permission.handtracking"); + if (perms.find("com.oculus.permission.HAND_TRACKING") == -1) { + perms.push_back("com.oculus.permission.HAND_TRACKING"); } } } From e3be0520a164f54edb11b533613b278b171f43ac Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Tue, 19 May 2020 16:34:15 +0300 Subject: [PATCH 18/23] [Windows] Add quotes only to the command line arguments with special characters. (cherry picked from commit cac399a829dcbf4baf27000c24cf2a8e73b797df) --- platform/windows/os_windows.cpp | 47 +++++++++++++++++++-------------- platform/windows/os_windows.h | 2 ++ 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index eca5be4e498..b7c3aff4094 100755 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -2770,26 +2770,31 @@ void OS_Windows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, DeleteDC(hMainDC); } +String OS_Windows::_quote_command_line_argument(const String &p_text) const { + for (int i = 0; i < p_text.size(); i++) { + CharType c = p_text[i]; + if (c == ' ' || c == '&' || c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '^' || c == '=' || c == ';' || c == '!' || c == '\'' || c == '+' || c == ',' || c == '`' || c == '~') { + return "\"" + p_text + "\""; + } + } + return p_text; +} + Error OS_Windows::execute(const String &p_path, const List &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) { if (p_blocking && r_pipe) { - - String argss; - argss = "\"\"" + p_path + "\""; - + String argss = _quote_command_line_argument(p_path); for (const List::Element *E = p_arguments.front(); E; E = E->next()) { - - argss += " \"" + E->get() + "\""; + argss += " " + _quote_command_line_argument(E->get()); } - argss += "\""; - if (read_stderr) { argss += " 2>&1"; // Read stderr too } + // Note: _wpopen is calling command as "cmd.exe /c argss", instead of executing it directly, add extra quotes around full command, to prevent it from stripping quotes in the command. + argss = _quote_command_line_argument(argss); FILE *f = _wpopen(argss.c_str(), L"r"); - ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); char buf[65535]; @@ -2805,20 +2810,19 @@ Error OS_Windows::execute(const String &p_path, const List &p_arguments, } int rv = _pclose(f); - if (r_exitcode) + if (r_exitcode) { *r_exitcode = rv; + } return OK; } - String cmdline = "\"" + p_path + "\""; + String cmdline = _quote_command_line_argument(p_path); const List::Element *I = p_arguments.front(); while (I) { - - cmdline += " \"" + I->get() + "\""; - + cmdline += " " + _quote_command_line_argument(I->get()); I = I->next(); - }; + } ProcessInfo pi; ZeroMemory(&pi.si, sizeof(pi.si)); @@ -2826,18 +2830,21 @@ Error OS_Windows::execute(const String &p_path, const List &p_arguments, ZeroMemory(&pi.pi, sizeof(pi.pi)); LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si; - Vector modstr; //windows wants to change this no idea why + Vector modstr; // Windows wants to change this no idea why. modstr.resize(cmdline.size()); - for (int i = 0; i < cmdline.size(); i++) + for (int i = 0; i < cmdline.size(); i++) { modstr.write[i] = cmdline[i]; + } + int ret = CreateProcessW(NULL, modstr.ptrw(), NULL, NULL, 0, NORMAL_PRIORITY_CLASS & CREATE_NO_WINDOW, NULL, NULL, si_w, &pi.pi); ERR_FAIL_COND_V(ret == 0, ERR_CANT_FORK); if (p_blocking) { DWORD ret2 = WaitForSingleObject(pi.pi.hProcess, INFINITE); - if (r_exitcode) + if (r_exitcode) { *r_exitcode = ret2; + } CloseHandle(pi.pi.hProcess); CloseHandle(pi.pi.hThread); @@ -2846,9 +2853,9 @@ Error OS_Windows::execute(const String &p_path, const List &p_arguments, ProcessID pid = pi.pi.dwProcessId; if (r_child_id) { *r_child_id = pid; - }; + } process_map->insert(pid, pi); - }; + } return OK; }; diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 98fe78f15f3..5ee50c66f92 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -377,6 +377,8 @@ protected: void process_events(); void process_key_events(); + String _quote_command_line_argument(const String &p_text) const; + struct ProcessInfo { STARTUPINFO si; From 8f90d423b99ae2b194c85d665341518d1fc3c3af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Tue, 19 May 2020 16:41:14 +0200 Subject: [PATCH 19/23] Fix too eager GDScriptFunctionState stack cleanup (cherry picked from commit 1f0548efd4c73178364ba99a54ae00f5483d41ac) --- modules/gdscript/gdscript_function.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index a3603479cf5..027350a665d 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -1906,8 +1906,6 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { state.result = Variant(); if (completed) { - _clear_stack(); - if (first_state.is_valid()) { first_state->emit_signal("completed", ret); } else { From 6dfd80673a0db5754e8726a524a256bc8a071277 Mon Sep 17 00:00:00 2001 From: Ignacio Etcheverry Date: Tue, 19 May 2020 17:51:53 +0200 Subject: [PATCH 20/23] Mono/C#: Remove script load error about not a Godot.Object Any C# file can be loaded as script and at load time we don't yet know if it's actually meant to be used as a script. As such, such an check can result in a lot of false errors. If the file is really meant to be used as a script, an error would be printed later when attempting to instantiate it any way. (cherry picked from commit 38cd2152e6aec4f633b573034256d75c603398a6) --- modules/mono/csharp_script.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 6deab0cf566..9906bf9dd1d 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -3194,9 +3194,7 @@ Error CSharpScript::reload(bool p_keep_state) { ERR_FAIL_NULL_V(namespace_, ERR_BUG); ERR_FAIL_NULL_V(class_name, ERR_BUG); GDMonoClass *klass = project_assembly->get_class(namespace_->operator String(), class_name->operator String()); - if (klass) { - bool obj_type = CACHED_CLASS(GodotObject)->is_assignable_from(klass); - ERR_FAIL_COND_V(!obj_type, ERR_BUG); + if (klass && CACHED_CLASS(GodotObject)->is_assignable_from(klass)) { script_class = klass; } } else { From 3d8cc49ec529581c11e01e0d3f3a47755ebea898 Mon Sep 17 00:00:00 2001 From: Michael Alexsander Date: Mon, 18 May 2020 11:38:48 -0300 Subject: [PATCH 21/23] Keep "lock" metadata when changing a Node's type, if applicable (cherry picked from commit d015e4d6fcc85ba892a7fa2a511f1f5d72880d0d) --- editor/scene_tree_dock.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index ebe7f16db68..e5cd55e70a7 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -2090,8 +2090,21 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_prop for (List::Element *E = pinfo.front(); E; E = E->next()) { if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) continue; - if (E->get().name == "__meta__") + + if (E->get().name == "__meta__") { + if (Object::cast_to(newnode)) { + Dictionary metadata = n->get(E->get().name); + if (metadata.has("_edit_group_") && metadata["_edit_group_"]) { + newnode->set_meta("_edit_group_", true); + } + if (metadata.has("_edit_lock_") && metadata["_edit_lock_"]) { + newnode->set_meta("_edit_lock_", true); + } + } + continue; + } + if (default_oldnode->get(E->get().name) != n->get(E->get().name)) { newnode->set(E->get().name, n->get(E->get().name)); } From 2197ef05662913d81df5846516930c3c47739001 Mon Sep 17 00:00:00 2001 From: SkyJJ Date: Tue, 28 Apr 2020 19:57:45 +0200 Subject: [PATCH 22/23] Fix Android LineEdit editing bugs (cherry picked from commit cc473b948f814a52b383590791b5226d15a8b010) --- core/os/os.cpp | 2 +- core/os/os.h | 2 +- .../src/org/godotengine/godot/GodotIO.java | 4 +- .../godot/input/GodotEditText.java | 39 +++++++++++++------ .../godot/input/GodotTextInputWrapper.java | 10 +++++ platform/android/java_godot_io_wrapper.cpp | 6 +-- platform/android/java_godot_io_wrapper.h | 2 +- platform/android/os_android.cpp | 4 +- platform/android/os_android.h | 2 +- platform/iphone/os_iphone.cpp | 2 +- platform/iphone/os_iphone.h | 2 +- platform/uwp/os_uwp.cpp | 3 +- platform/uwp/os_uwp.h | 2 +- scene/gui/line_edit.cpp | 27 +++++++++---- 14 files changed, 71 insertions(+), 36 deletions(-) diff --git a/core/os/os.cpp b/core/os/os.cpp index 1ed94842081..be1c93ea007 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -221,7 +221,7 @@ bool OS::has_virtual_keyboard() const { return false; } -void OS::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length) { +void OS::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length, int p_cursor_start, int p_cursor_end) { } void OS::hide_virtual_keyboard() { diff --git a/core/os/os.h b/core/os/os.h index f355cc09e51..56180216cbf 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -377,7 +377,7 @@ public: }; virtual bool has_virtual_keyboard() const; - virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1); + virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1); virtual void hide_virtual_keyboard(); // returns height of the currently shown virtual keyboard (0 if keyboard is hidden) diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java index 9efef2bcbf4..cf0766161ed 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java @@ -493,9 +493,9 @@ public class GodotIO { return (int)(metrics.density * 160f); } - public void showKeyboard(String p_existing_text, int p_max_input_length) { + public void showKeyboard(String p_existing_text, int p_max_input_length, int p_cursor_start, int p_cursor_end) { if (edit != null) - edit.showKeyboard(p_existing_text, p_max_input_length); + edit.showKeyboard(p_existing_text, p_max_input_length, p_cursor_start, p_cursor_end); //InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); //inputMgr.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java index c35ecb82a29..fe2847f04f3 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java @@ -58,6 +58,7 @@ public class GodotEditText extends EditText { private GodotTextInputWrapper mInputWrapper; private EditHandler sHandler = new EditHandler(this); private String mOriginText; + private int mMaxInputLength; private static class EditHandler extends Handler { private final WeakReference mEdit; @@ -104,11 +105,18 @@ public class GodotEditText extends EditText { String text = edit.mOriginText; if (edit.requestFocus()) { edit.removeTextChangedListener(edit.mInputWrapper); + setMaxInputLength(edit); edit.setText(""); edit.append(text); + if (msg.arg2 != -1) { + edit.setSelection(msg.arg1, msg.arg2); + edit.mInputWrapper.setSelection(true); + } else { + edit.mInputWrapper.setSelection(false); + } + edit.mInputWrapper.setOriginText(text); edit.addTextChangedListener(edit.mInputWrapper); - setMaxInputLength(edit, msg.arg1); final InputMethodManager imm = (InputMethodManager)mView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(edit, 0); } @@ -125,14 +133,10 @@ public class GodotEditText extends EditText { } } - private void setMaxInputLength(EditText p_edit_text, int p_max_input_length) { - if (p_max_input_length > 0) { - InputFilter[] filters = new InputFilter[1]; - filters[0] = new InputFilter.LengthFilter(p_max_input_length); - p_edit_text.setFilters(filters); - } else { - p_edit_text.setFilters(new InputFilter[] {}); - } + private void setMaxInputLength(EditText p_edit_text) { + InputFilter[] filters = new InputFilter[1]; + filters[0] = new InputFilter.LengthFilter(this.mMaxInputLength); + p_edit_text.setFilters(filters); } // =========================================================== @@ -164,13 +168,24 @@ public class GodotEditText extends EditText { // =========================================================== // Methods // =========================================================== - public void showKeyboard(String p_existing_text, int p_max_input_length) { - this.mOriginText = p_existing_text; + public void showKeyboard(String p_existing_text, int p_max_input_length, int p_cursor_start, int p_cursor_end) { + int maxInputLength = (p_max_input_length <= 0) ? Integer.MAX_VALUE : p_max_input_length; + if (p_cursor_start == -1) { // cursor position not given + this.mOriginText = p_existing_text; + this.mMaxInputLength = maxInputLength; + } else if (p_cursor_end == -1) { // not text selection + this.mOriginText = p_existing_text.substring(0, p_cursor_start); + this.mMaxInputLength = maxInputLength - (p_existing_text.length() - p_cursor_start); + } else { + this.mOriginText = p_existing_text.substring(0, p_cursor_end); + this.mMaxInputLength = maxInputLength - (p_existing_text.length() - p_cursor_end); + } final Message msg = new Message(); msg.what = HANDLER_OPEN_IME_KEYBOARD; msg.obj = this; - msg.arg1 = p_max_input_length; + msg.arg1 = p_cursor_start; + msg.arg2 = p_cursor_end; sHandler.sendMessage(msg); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java index 944b2dbc188..ce7eb1ff37f 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java @@ -53,6 +53,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene private final GodotView mView; private final GodotEditText mEdit; private String mOriginText; + private boolean mHasSelection; // =========================================================== // Constructors @@ -77,6 +78,10 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene this.mOriginText = originText; } + public void setSelection(boolean selection) { + mHasSelection = selection; + } + // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== @@ -95,6 +100,11 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene for (int i = 0; i < count; ++i) { GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true); GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false); + + if (mHasSelection) { + mHasSelection = false; + break; + } } } }); diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp index 8d075f8e972..cb2c9d144bf 100644 --- a/platform/android/java_godot_io_wrapper.cpp +++ b/platform/android/java_godot_io_wrapper.cpp @@ -53,7 +53,7 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc _get_model = p_env->GetMethodID(cls, "getModel", "()Ljava/lang/String;"); _get_screen_DPI = p_env->GetMethodID(cls, "getScreenDPI", "()I"); _get_unique_id = p_env->GetMethodID(cls, "getUniqueID", "()Ljava/lang/String;"); - _show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;I)V"); + _show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;III)V"); _hide_keyboard = p_env->GetMethodID(cls, "hideKeyboard", "()V"); _set_screen_orientation = p_env->GetMethodID(cls, "setScreenOrientation", "(I)V"); _get_system_dir = p_env->GetMethodID(cls, "getSystemDir", "(I)Ljava/lang/String;"); @@ -135,11 +135,11 @@ bool GodotIOJavaWrapper::has_vk() { return (_show_keyboard != 0) && (_hide_keyboard != 0); } -void GodotIOJavaWrapper::show_vk(const String &p_existing, int p_max_input_length) { +void GodotIOJavaWrapper::show_vk(const String &p_existing, int p_max_input_length, int p_cursor_start, int p_cursor_end) { if (_show_keyboard) { JNIEnv *env = ThreadAndroid::get_env(); jstring jStr = env->NewStringUTF(p_existing.utf8().get_data()); - env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_max_input_length); + env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_max_input_length, p_cursor_start, p_cursor_end); } } diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h index 7dfed521876..1a9eea736a1 100644 --- a/platform/android/java_godot_io_wrapper.h +++ b/platform/android/java_godot_io_wrapper.h @@ -73,7 +73,7 @@ public: int get_screen_dpi(); String get_unique_id(); bool has_vk(); - void show_vk(const String &p_existing, int p_max_input_length); + void show_vk(const String &p_existing, int p_max_input_length, int p_cursor_start, int p_cursor_end); void hide_vk(); int get_vk_height(); void set_vk_height(int p_height); diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index e5d9bdc60c1..99dd216bc2f 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -558,10 +558,10 @@ int OS_Android::get_virtual_keyboard_height() const { // return 0; } -void OS_Android::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length) { +void OS_Android::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length, int p_cursor_start, int p_cursor_end) { if (godot_io_java->has_vk()) { - godot_io_java->show_vk(p_existing_text, p_max_input_length); + godot_io_java->show_vk(p_existing_text, p_max_input_length, p_cursor_start, p_cursor_end); } else { ERR_PRINT("Virtual keyboard not available"); diff --git a/platform/android/os_android.h b/platform/android/os_android.h index c2f9a0992f3..0c957ebdb0f 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -158,7 +158,7 @@ public: virtual bool has_touchscreen_ui_hint() const; virtual bool has_virtual_keyboard() const; - virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1); + virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1); virtual void hide_virtual_keyboard(); virtual int get_virtual_keyboard_height() const; diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp index 7a699f9b50e..1b0f71a2254 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -482,7 +482,7 @@ extern Error _shell_open(String p_uri); extern void _set_keep_screen_on(bool p_enabled); extern void _vibrate(); -void OSIPhone::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length) { +void OSIPhone::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length, int p_cursor_start, int p_cursor_end) { _show_keyboard(p_existing_text); }; diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index d2d96181f55..179085402d5 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -165,7 +165,7 @@ public: virtual bool can_draw() const; virtual bool has_virtual_keyboard() const; - virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1); + virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1); virtual void hide_virtual_keyboard(); virtual int get_virtual_keyboard_height() const; diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index d5047b53ab1..90c1697e1c5 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -801,8 +801,7 @@ bool OS_UWP::has_virtual_keyboard() const { return UIViewSettings::GetForCurrentView()->UserInteractionMode == UserInteractionMode::Touch; } -void OS_UWP::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length) { - +void OS_UWP::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length, int p_cursor_start, int p_cursor_end) { InputPane ^ pane = InputPane::GetForCurrentView(); pane->TryShow(); } diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index fb43ab382e8..1ee1439ab23 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -239,7 +239,7 @@ public: virtual bool has_touchscreen_ui_hint() const; virtual bool has_virtual_keyboard() const; - virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1); + virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1); virtual void hide_virtual_keyboard(); virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index e659641ebda..f255b643f0b 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -127,8 +127,13 @@ void LineEdit::_gui_input(Ref p_event) { selection.creating = false; selection.doubleclick = false; - if (OS::get_singleton()->has_virtual_keyboard()) - OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), max_length); + if (OS::get_singleton()->has_virtual_keyboard()) { + if (selection.enabled) { + OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), max_length, selection.begin, selection.end); + } else { + OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), max_length, cursor_pos); + } + } } update(); @@ -903,13 +908,19 @@ void LineEdit::_notification(int p_what) { draw_caret = true; } - OS::get_singleton()->set_ime_active(true); - Point2 cursor_pos = Point2(get_cursor_position(), 1) * get_minimum_size().height; - OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos); - - if (OS::get_singleton()->has_virtual_keyboard()) - OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), max_length); + { + OS::get_singleton()->set_ime_active(true); + Point2 cursor_pos2 = Point2(get_cursor_position(), 1) * get_minimum_size().height; + OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos2); + } + if (OS::get_singleton()->has_virtual_keyboard()) { + if (selection.enabled) { + OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), max_length, selection.begin, selection.end); + } else { + OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), max_length, cursor_pos); + } + } } break; case NOTIFICATION_FOCUS_EXIT: { From a78564119648427770de0ce41e98e71cbc3af19a Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Wed, 6 May 2020 19:59:29 +0200 Subject: [PATCH 23/23] Allow `.res` and `.tres` extensions in the scene CLI positional argument This closes #35709. (cherry picked from commit 653334cc8d4f232034c6086bd7672bb9e04932b7) --- main/main.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/main/main.cpp b/main/main.cpp index 362a7c7c9e1..28113c00b15 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1455,7 +1455,11 @@ bool Main::start() { } else if (args[i].length() && args[i][0] != '-' && positional_arg == "") { positional_arg = args[i]; - if (args[i].ends_with(".scn") || args[i].ends_with(".tscn") || args[i].ends_with(".escn")) { + if (args[i].ends_with(".scn") || + args[i].ends_with(".tscn") || + args[i].ends_with(".escn") || + args[i].ends_with(".res") || + args[i].ends_with(".tres")) { // Only consider the positional argument to be a scene path if it ends with // a file extension associated with Godot scenes. This makes it possible // for projects to parse command-line arguments for custom CLI arguments