diff options
206 files changed, 4377 insertions, 1586 deletions
diff --git a/AUTHORS.md b/AUTHORS.md index 3d7a6adf60..a5ef24e825 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -117,7 +117,8 @@ name is available. m4nu3lf marynate mrezai - rraallvv + robfram romulox-x + rraallvv sersoong yg2f (SuperUserNameMan) @@ -30,9 +30,11 @@ generous deed immortalized in the next stable release of Godot Engine. codetrotter E Hewert Hein-Pieter van Braam + Igors Vaitkus Jamal Alyafei Jay Sistar Matthieu Huvé + Mike King Nathan Warden Neal Gompa (Conan Kudo) Pascal Julien @@ -50,6 +52,7 @@ generous deed immortalized in the next stable release of Godot Engine. Asdf cheese65536 Jake Bo + Kris Michael Manuele Finocchiaro Officine Pixel S.n.c. Rémi Verschelde @@ -60,11 +63,15 @@ generous deed immortalized in the next stable release of Godot Engine. Austen McRae Benjamin Botwin Bernhard Liebl + Catalin Moldovan + DeepSquid + Florian Breisch + Gary Oberbrunner Johannes Wuensch Josep G. Camarasa - Kris Michael + Joshua Lesperance Libre-Dépanne - Mike King + Matthew Bennett Ranoller Rob Messick Svenne Krap @@ -81,12 +88,14 @@ generous deed immortalized in the next stable release of Godot Engine. Garrett Dockins Guilherme Felipe de C. G. da Silva Harman Bains - Henrique Alves + John + Justo Delgado Baudà Karsten Bock Laurence Bannister Rami Robert Willes Robin Arys + Rufus Sasparilla ScottMakesGames Testus Maximus Thomas Bjarnelöf @@ -94,9 +103,9 @@ generous deed immortalized in the next stable release of Godot Engine. Xavier Tan Zaq Poi + Alessandra Pereyra Alexey Dyadchenko Amanda Haldy - Arnaud Verstuyf Chris Brown Chris Wilson Cody Parker @@ -111,23 +120,26 @@ generous deed immortalized in the next stable release of Godot Engine. Jeppe Zapp Jeremi Biernacki joe513 + John O'Mahoney Jordan M Lucas Juraj Móza Justin Arnold - Justo Delgado Baudà Leandro Voltolino Lisandro Lorea + Marco Andrew Cafolla Markus Wiesner - Marty Plumbo Marvin Nick Nikitin Pablo Cholaky Patrick Schnorbus Pete Goodwin Phyronnaz + Simon De Greve + Sofox Ted Travis Womack Trent McPheron + Vladimir ## Silver donors @@ -142,13 +154,13 @@ generous deed immortalized in the next stable release of Godot Engine. Anthony Bongiovanni Arda Erol Arthur S. Muszynski + Aubrey Falconer Avencherus Bastian Böhm Benedikt Benjamin Beshara Ben Vercammen Blair Allen - Bryan Crow Bryanna M Bryan Stevenson Carwyn Edwards @@ -161,6 +173,7 @@ generous deed immortalized in the next stable release of Godot Engine. Daniel Kaplan Daniel Langegger Daniel Mircea + David David Cravens David May Dominik Wetzel @@ -168,21 +181,23 @@ generous deed immortalized in the next stable release of Godot Engine. Fabian Becker fengjiongmax Francesco Lisi + Frédéric Alix G3Dev sà rl Geequlim Gerrit Großkopf Gerrit Procee Gilberto K. Otubo Guldoman - HeartBeast Heribert Hirth + hubert jenkins Hunter Jones ialex32x + Ivan Vodopiviz Jaime Ruiz-Borau Vizárraga - Jed Rose + Jed Jeff Hungerford Joel Fivat - Johannes du Randt + Johan Lindberg Jonas Yamazaki Jonathan Martin Jonathan Nieto @@ -199,9 +214,9 @@ generous deed immortalized in the next stable release of Godot Engine. Klavdij Voncina Lars pfeffer Linus Lind Lundgren + Macil magodev Martin Eigel - Martin Novák Matthew Fitzpatrick Matthias Hölzl Max R.R. Collada @@ -229,15 +244,14 @@ generous deed immortalized in the next stable release of Godot Engine. Patric Vormstein Paul Mason Paweł Kowal - Pedro Luz Pierre-Igor Berthet Pietro Vertechi Piotr Kaczmarski Richman Stewart - Rodolfo Baeza Roger Burgess Roger Smith Roman Tinkov + Ryan Whited Sasori Olkof Scott D. Yelich Sootstone @@ -245,10 +259,14 @@ generous deed immortalized in the next stable release of Godot Engine. Thibault Barbaroux Thomas Bell Thomas Herzog & Xananax + Thomas Kurz Tom Larrow Tyler Stafos UltyX + Victor Gonzalez Fernandez Victor Holt + Viktor Ferenczi + werner mendizabal Wout Standaert Yu He @@ -26,7 +26,7 @@ Before being open sourced in February 2014, Godot had been developed by Juan Linietsky and Ariel Manzur (both still maintaining the project) for several years as an in-house engine, used to publish several work-for-hire titles. -![Screenshot of a 3D scene in Godot Engine](http://download.tuxfamily.org/godotengine/media/screenshots/editor_3d_fracteed.jpg) +![Screenshot of a 3D scene in Godot Engine](https://download.tuxfamily.org/godotengine/media/screenshots/editor_3d_fracteed.jpg) ### Getting the engine diff --git a/core/class_db.cpp b/core/class_db.cpp index 291dc87e1c..92aa131e2d 100644 --- a/core/class_db.cpp +++ b/core/class_db.cpp @@ -651,7 +651,6 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName } type->constant_map[p_name] = p_constant; -#ifdef DEBUG_METHODS_ENABLED String enum_name = p_enum; if (enum_name != String()) { @@ -670,6 +669,7 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName } } +#ifdef DEBUG_METHODS_ENABLED type->constant_order.push_back(p_name); #endif } @@ -725,7 +725,6 @@ int ClassDB::get_integer_constant(const StringName &p_class, const StringName &p return 0; } -#ifdef DEBUG_METHODS_ENABLED StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) { OBJTYPE_RLOCK; @@ -794,7 +793,6 @@ void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_ type = type->inherits_ptr; } } -#endif void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) { diff --git a/core/class_db.h b/core/class_db.h index d74317239b..2c77ffe65f 100644 --- a/core/class_db.h +++ b/core/class_db.h @@ -116,10 +116,10 @@ public: ClassInfo *inherits_ptr; HashMap<StringName, MethodBind *, StringNameHasher> method_map; HashMap<StringName, int, StringNameHasher> constant_map; + HashMap<StringName, List<StringName> > enum_map; HashMap<StringName, MethodInfo, StringNameHasher> signal_map; List<PropertyInfo> property_list; #ifdef DEBUG_METHODS_ENABLED - HashMap<StringName, List<StringName> > enum_map; List<StringName> constant_order; List<StringName> method_order; Set<StringName> methods_in_properties; @@ -344,11 +344,9 @@ public: static void get_integer_constant_list(const StringName &p_class, List<String> *p_constants, bool p_no_inheritance = false); static int get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success = NULL); -#ifdef DEBUG_METHODS_ENABLED static StringName get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false); static void get_enum_list(const StringName &p_class, List<StringName> *p_enums, bool p_no_inheritance = false); static void get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance = false); -#endif static StringName get_category(const StringName &p_node); diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 5dfe067902..0c626c197b 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -1162,9 +1162,11 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons ERR_FAIL_V(ERR_FILE_UNRECOGNIZED); } - fw->store_32(VERSION_MAJOR); //current version - fw->store_32(VERSION_MINOR); - fw->store_32(FORMAT_VERSION); + // Since we're not actually converting the file contents, leave the version + // numbers in the file untouched. + fw->store_32(ver_major); + fw->store_32(ver_minor); + fw->store_32(ver_format); save_ustring(fw, get_ustring(f)); //type diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index 07a01ff99f..012ba78c6d 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -29,6 +29,8 @@ /*************************************************************************/ #include "stream_peer_ssl.h" +#include "os/file_access.h" +#include "project_settings.h" StreamPeerSSL *(*StreamPeerSSL::_create)() = NULL; @@ -50,6 +52,35 @@ bool StreamPeerSSL::is_available() { return available; } +PoolByteArray StreamPeerSSL::get_project_cert_array() { + + PoolByteArray out; + String certs_path = GLOBAL_DEF("network/ssl/certificates", ""); + ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt")); + + if (certs_path != "") { + + FileAccess *f = FileAccess::open(certs_path, FileAccess::READ); + if (f) { + int flen = f->get_len(); + out.resize(flen + 1); + { + PoolByteArray::Write w = out.write(); + f->get_buffer(w.ptr(), flen); + w[flen] = 0; //end f string + } + + memdelete(f); + +#ifdef DEBUG_ENABLED + print_line("Loaded certs from '" + certs_path); +#endif + } + } + + return out; +} + void StreamPeerSSL::_bind_methods() { ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll); diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h index f903438c28..77301a7c87 100644 --- a/core/io/stream_peer_ssl.h +++ b/core/io/stream_peer_ssl.h @@ -66,6 +66,7 @@ public: static StreamPeerSSL *create(); + static PoolByteArray get_project_cert_array(); static void load_certs_from_memory(const PoolByteArray &p_memory); static bool is_available(); diff --git a/core/os/input.cpp b/core/os/input.cpp index 3089ab2ce3..1d7cd7c791 100644 --- a/core/os/input.cpp +++ b/core/os/input.cpp @@ -111,7 +111,7 @@ void Input::_bind_methods() { BIND_ENUM_CONSTANT(CURSOR_HSPLIT); BIND_ENUM_CONSTANT(CURSOR_HELP); - ADD_SIGNAL(MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "connected"))); + ADD_SIGNAL(MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "device"), PropertyInfo(Variant::BOOL, "connected"))); } void Input::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp index fa53cc85c8..9dfc91e308 100644 --- a/core/os/keyboard.cpp +++ b/core/os/keyboard.cpp @@ -461,99 +461,6 @@ const char *find_keycode_name(int p_keycode) { return ""; } -struct _KeyCodeReplace { - int from; - int to; -}; - -static const _KeyCodeReplace _keycode_replace_qwertz[] = { - { KEY_Y, KEY_Z }, - { KEY_Z, KEY_Y }, - { 0, 0 } -}; - -static const _KeyCodeReplace _keycode_replace_azerty[] = { - { KEY_W, KEY_Z }, - { KEY_Z, KEY_W }, - { KEY_A, KEY_Q }, - { KEY_Q, KEY_A }, - { KEY_SEMICOLON, KEY_M }, - { KEY_M, KEY_SEMICOLON }, - { 0, 0 } -}; - -static const _KeyCodeReplace _keycode_replace_qzerty[] = { - { KEY_W, KEY_Z }, - { KEY_Z, KEY_W }, - { KEY_SEMICOLON, KEY_M }, - { KEY_M, KEY_SEMICOLON }, - { 0, 0 } -}; - -static const _KeyCodeReplace _keycode_replace_dvorak[] = { - { KEY_UNDERSCORE, KEY_BRACELEFT }, - { KEY_EQUAL, KEY_BRACERIGHT }, - { KEY_Q, KEY_APOSTROPHE }, - { KEY_W, KEY_COMMA }, - { KEY_E, KEY_PERIOD }, - { KEY_R, KEY_P }, - { KEY_T, KEY_Y }, - { KEY_Y, KEY_F }, - { KEY_U, KEY_G }, - { KEY_I, KEY_C }, - { KEY_O, KEY_R }, - { KEY_P, KEY_L }, - { KEY_BRACELEFT, KEY_SLASH }, - { KEY_BRACERIGHT, KEY_EQUAL }, - { KEY_A, KEY_A }, - { KEY_S, KEY_O }, - { KEY_D, KEY_E }, - { KEY_F, KEY_U }, - { KEY_G, KEY_I }, - { KEY_H, KEY_D }, - { KEY_J, KEY_H }, - { KEY_K, KEY_T }, - { KEY_L, KEY_N }, - { KEY_SEMICOLON, KEY_S }, - { KEY_APOSTROPHE, KEY_UNDERSCORE }, - { KEY_Z, KEY_SEMICOLON }, - { KEY_X, KEY_Q }, - { KEY_C, KEY_J }, - { KEY_V, KEY_K }, - { KEY_B, KEY_X }, - { KEY_N, KEY_B }, - { KEY_M, KEY_M }, - { KEY_COMMA, KEY_W }, - { KEY_PERIOD, KEY_V }, - { KEY_SLASH, KEY_Z }, - { 0, 0 } -}; - -static const _KeyCodeReplace _keycode_replace_neo[] = { - { 0, 0 } -}; - -static const _KeyCodeReplace _keycode_replace_colemak[] = { - { KEY_E, KEY_F }, - { KEY_R, KEY_P }, - { KEY_T, KEY_G }, - { KEY_Y, KEY_J }, - { KEY_U, KEY_L }, - { KEY_I, KEY_U }, - { KEY_O, KEY_Y }, - { KEY_P, KEY_SEMICOLON }, - { KEY_S, KEY_R }, - { KEY_D, KEY_S }, - { KEY_F, KEY_T }, - { KEY_G, KEY_D }, - { KEY_J, KEY_N }, - { KEY_K, KEY_E }, - { KEY_L, KEY_I }, - { KEY_SEMICOLON, KEY_O }, - { KEY_N, KEY_K }, - { 0, 0 } -}; - int keycode_get_count() { const _KeyCodeText *kct = &_keycodes[0]; @@ -574,31 +481,3 @@ int keycode_get_value_by_index(int p_index) { const char *keycode_get_name_by_index(int p_index) { return _keycodes[p_index].text; } - -int latin_keyboard_keycode_convert(int p_keycode) { - - const _KeyCodeReplace *kcr = NULL; - switch (OS::get_singleton()->get_latin_keyboard_variant()) { - - case OS::LATIN_KEYBOARD_QWERTY: return p_keycode; break; - case OS::LATIN_KEYBOARD_QWERTZ: kcr = _keycode_replace_qwertz; break; - case OS::LATIN_KEYBOARD_AZERTY: kcr = _keycode_replace_azerty; break; - case OS::LATIN_KEYBOARD_QZERTY: kcr = _keycode_replace_qzerty; break; - case OS::LATIN_KEYBOARD_DVORAK: kcr = _keycode_replace_dvorak; break; - case OS::LATIN_KEYBOARD_NEO: kcr = _keycode_replace_neo; break; - case OS::LATIN_KEYBOARD_COLEMAK: kcr = _keycode_replace_colemak; break; - default: return p_keycode; - } - - if (!kcr) { - return p_keycode; - } - - while (kcr->from) { - if (kcr->from == p_keycode) - return kcr->to; - kcr++; - } - - return p_keycode; -} diff --git a/core/os/keyboard.h b/core/os/keyboard.h index 4c253fa4ce..a0e6f8b2ef 100644 --- a/core/os/keyboard.h +++ b/core/os/keyboard.h @@ -331,6 +331,5 @@ const char *find_keycode_name(int p_keycode); int keycode_get_count(); int keycode_get_value_by_index(int p_index); const char *keycode_get_name_by_index(int p_index); -int latin_keyboard_keycode_convert(int p_keycode); #endif diff --git a/core/script_language.h b/core/script_language.h index 6d32fc054c..0c1f99cea6 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -221,7 +221,9 @@ public: RESULT_CLASS, RESULT_CLASS_CONSTANT, RESULT_CLASS_PROPERTY, - RESULT_CLASS_METHOD + RESULT_CLASS_METHOD, + RESULT_CLASS_ENUM, + RESULT_CLASS_TBD_GLOBALSCOPE }; Type type; Ref<Script> script; diff --git a/doc/classes/ARVRAnchor.xml b/doc/classes/ARVRAnchor.xml index a8366e3405..fa93d9668c 100644 --- a/doc/classes/ARVRAnchor.xml +++ b/doc/classes/ARVRAnchor.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="ARVRAnchor" inherits="Spatial" category="Core" version="3.1"> <brief_description> - Anchor point in AR Space + Anchor point in AR Space. </brief_description> <description> The ARVR Anchor point is a spatial node that maps a real world location identified by the AR platform to a position within the game world. For example, as long as plane detection in ARKit is on, ARKit will identify and update the position of planes (tables, floors, etc) and create anchors for them. diff --git a/doc/classes/AudioStreamPlayer3D.xml b/doc/classes/AudioStreamPlayer3D.xml index 1ab5184684..3418ef7a26 100644 --- a/doc/classes/AudioStreamPlayer3D.xml +++ b/doc/classes/AudioStreamPlayer3D.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="AudioStreamPlayer3D" inherits="Spatial" category="Core" version="3.1"> <brief_description> - Plays 3D sound in 3D space + Plays 3D sound in 3D space. </brief_description> <description> Plays a sound effect with directed sound effects, dampens with distance if needed, generates effect of hearable position in space. diff --git a/doc/classes/Camera.xml b/doc/classes/Camera.xml index 0b7cd4ef7a..9451ee959f 100644 --- a/doc/classes/Camera.xml +++ b/doc/classes/Camera.xml @@ -22,7 +22,7 @@ <return type="Transform"> </return> <description> - Get the camera transform. Subclassed cameras (such as CharacterCamera) may provide different transforms than the [Node] transform. + Gets the camera transform. Subclassed cameras (such as CharacterCamera) may provide different transforms than the [Node] transform. </description> </method> <method name="is_position_behind" qualifiers="const"> @@ -31,14 +31,14 @@ <argument index="0" name="world_point" type="Vector3"> </argument> <description> - Returns [code]true[/code] if the given position is behind the Camera. + Returns [code]true[/code] if the given position is behind the Camera. Note that a position which returns [code]false[/code] may still be outside the Camera's field of view. </description> </method> <method name="make_current"> <return type="void"> </return> <description> - Make this camera the current Camera for the [Viewport] (see class description). If the Camera Node is outside the scene tree, it will attempt to become current once it's added. + Makes this camera the current Camera for the [Viewport] (see class description). If the Camera Node is outside the scene tree, it will attempt to become current once it's added. </description> </method> <method name="project_local_ray_normal" qualifiers="const"> @@ -56,7 +56,7 @@ <argument index="0" name="screen_point" type="Vector2"> </argument> <description> - Returns how a 2D coordinate in the Viewport rectangle maps to a 3D point in worldspace. + Returns the 3D point in worldspace that maps to the given 2D coordinate in the [Viewport] rectangle. </description> </method> <method name="project_ray_normal" qualifiers="const"> @@ -87,7 +87,7 @@ <argument index="2" name="z_far" type="float"> </argument> <description> - Set the camera projection to orthogonal mode, by specifying a width and the [i]near[/i] and [i]far[/i] clip planes in worldspace units. (As a hint, 2D games often use this projection, with values specified in pixels) + Sets the camera projection to orthogonal mode, by specifying a width and the [i]near[/i] and [i]far[/i] clip planes in worldspace units. (As a hint, 2D games often use this projection, with values specified in pixels) </description> </method> <method name="set_perspective"> @@ -100,7 +100,7 @@ <argument index="2" name="z_far" type="float"> </argument> <description> - Set the camera projection to perspective mode, by specifying a [i]FOV[/i] Y angle in degrees (FOV means Field of View), and the [i]near[/i] and [i]far[/i] clip planes in worldspace units. + Sets the camera projection to perspective mode, by specifying a [i]FOV[/i] Y angle in degrees (FOV means Field of View), and the [i]near[/i] and [i]far[/i] clip planes in worldspace units. </description> </method> <method name="unproject_position" qualifiers="const"> @@ -109,7 +109,7 @@ <argument index="0" name="world_point" type="Vector3"> </argument> <description> - Returns how a 3D point in worldspace maps to a 2D coordinate in the [Viewport] rectangle. + Returns the 2D coordinate in the [Viewport] rectangle that maps to the given 3D point in worldspace. </description> </method> </methods> @@ -124,7 +124,7 @@ If not [code]DOPPLER_TRACKING_DISABLED[/code] this Camera will simulate the Doppler effect for objects changed in particular [code]_process[/code] methods. Default value: [code]DOPPLER_TRACKING_DISABLED[/code]. </member> <member name="environment" type="Environment" setter="set_environment" getter="get_environment"> - Set the [Environment] to use for this Camera. + The [Environment] to use for this Camera. </member> <member name="far" type="float" setter="set_zfar" getter="get_zfar"> The distance to the far culling boundary for this Camera relative to its local z-axis. @@ -136,7 +136,7 @@ The horizontal (X) offset of the Camera viewport. </member> <member name="keep_aspect" type="int" setter="set_keep_aspect_mode" getter="get_keep_aspect_mode" enum="Camera.KeepAspect"> - The axis to lock during [member fov]/[member size] adjustments. + The axis to lock during [member fov]/[member size] adjustments. Can be either [code]KEEP_WIDTH[/code] or [code]KEEP_HEIGHT[/code]. </member> <member name="near" type="float" setter="set_znear" getter="get_znear"> The distance to the near culling boundary for this Camera relative to its local z-axis. @@ -148,7 +148,7 @@ The camera's size measured as 1/2 the width or height. Only applicable in orthogonal mode. Since [member keep_aspect] locks on axis, [code]size[/code] sets the other axis' size length. </member> <member name="v_offset" type="float" setter="set_v_offset" getter="get_v_offset"> - The horizontal (Y) offset of the Camera viewport. + The vertical (Y) offset of the Camera viewport. </member> </members> <constants> diff --git a/doc/classes/CheckBox.xml b/doc/classes/CheckBox.xml index d37f0422bc..fb2cf64d98 100644 --- a/doc/classes/CheckBox.xml +++ b/doc/classes/CheckBox.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="CheckBox" inherits="Button" category="Core" version="3.1"> <brief_description> - Binary choice user interface widget + Binary choice user interface widget. </brief_description> <description> A checkbox allows the user to make a binary choice (choosing only one of two possible options), for example Answer 'yes' or 'no'. diff --git a/doc/classes/CollisionShape.xml b/doc/classes/CollisionShape.xml index 9910de494e..95fa1175c3 100644 --- a/doc/classes/CollisionShape.xml +++ b/doc/classes/CollisionShape.xml @@ -4,7 +4,7 @@ Node that represents collision shape data in 3D space. </brief_description> <description> - Editor facility for creating and editing collision shapes in 3D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area] to give it a detection shape, or add it to a [PhysicsBody] to give create solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape. + Editor facility for creating and editing collision shapes in 3D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area] to give it a detection shape, or add it to a [PhysicsBody] to create a solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape. </description> <tutorials> http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html diff --git a/doc/classes/CollisionShape2D.xml b/doc/classes/CollisionShape2D.xml index 6d62a0ba62..3136f132bf 100644 --- a/doc/classes/CollisionShape2D.xml +++ b/doc/classes/CollisionShape2D.xml @@ -4,7 +4,7 @@ Node that represents collision shape data in 2D space. </brief_description> <description> - Editor facility for creating and editing collision shapes in 2D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area2D] to give it a detection shape, or add it to a [PhysicsBody2D] to give create solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape. + Editor facility for creating and editing collision shapes in 2D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area2D] to give it a detection shape, or add it to a [PhysicsBody2D] to create a solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape. </description> <tutorials> http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml index 717f7e4efc..eb86dc8af8 100644 --- a/doc/classes/ColorPickerButton.xml +++ b/doc/classes/ColorPickerButton.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="ColorPickerButton" inherits="Button" category="Core" version="3.1"> <brief_description> - Button that pops out a [ColorPicker] + Button that pops out a [ColorPicker]. </brief_description> <description> Encapsulates a [ColorPicker] making it accesible by pressing a button, pressing the button will toggle the [ColorPicker] visibility diff --git a/doc/classes/ConeTwistJoint.xml b/doc/classes/ConeTwistJoint.xml index feb3262be0..d6b2f191a4 100644 --- a/doc/classes/ConeTwistJoint.xml +++ b/doc/classes/ConeTwistJoint.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="ConeTwistJoint" inherits="Joint" category="Core" version="3.1"> <brief_description> - A twist joint between two 3D bodies + A twist joint between two 3D bodies. </brief_description> <description> The joint can rotate the bodies across an axis defined by the local x-axes of the [Joint]. diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 722bb5d258..95e8622cf4 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -158,7 +158,7 @@ </argument> <description> Forces drag and bypasses [method get_drag_data] and [method set_drag_preview] by passing [code]data[/code] and [code]preview[/code]. Drag will start even if the mouse is neither over nor pressed on this control. - The methods [method can_drop_data] and [method drop_data] must be implemented on controls that want to recieve drop data. + The methods [method can_drop_data] and [method drop_data] must be implemented on controls that want to receive drop data. </description> </method> <method name="get_begin" qualifiers="const"> @@ -208,7 +208,7 @@ <argument index="0" name="position" type="Vector2"> </argument> <description> - Godot calls this method to get data that can be dragged and dropped onto controls that expect drop data. Return null if there is no data to drag. Controls that want to recieve drop data should implement [method can_drop_data] and [method drop_data]. [code]position[/code] is local to this control. Drag may be forced with [method force_drag]. + Godot calls this method to get data that can be dragged and dropped onto controls that expect drop data. Return null if there is no data to drag. Controls that want to receive drop data should implement [method can_drop_data] and [method drop_data]. [code]position[/code] is local to this control. Drag may be forced with [method force_drag]. A preview that will follow the mouse that should represent the data can be set with [method set_drag_preview]. A good time to set the preview is in this method. [codeblock] extends Control diff --git a/doc/classes/Generic6DOFJoint.xml b/doc/classes/Generic6DOFJoint.xml index f49f4231ef..2aec6d7f4e 100644 --- a/doc/classes/Generic6DOFJoint.xml +++ b/doc/classes/Generic6DOFJoint.xml @@ -4,7 +4,7 @@ The generic 6 degrees of freedom joint can implement a variety of joint-types by locking certain axes' rotation or translation. </brief_description> <description> - The first 3 dof axes are linear axes, which represent translation of Bodies, and the latter 3 dof axes represent the angular motion. Each axis can be either locked, or limited. + The first 3 DOF axes are linear axes, which represent translation of Bodies, and the latter 3 DOF axes represent the angular motion. Each axis can be either locked, or limited. </description> <tutorials> </tutorials> @@ -18,7 +18,7 @@ The lower, the longer an impulse from one side takes to travel to the other side. </member> <member name="angular_limit_x/enabled" type="bool" setter="set_flag_x" getter="get_flag_x"> - If [code]true[/code] rotation across the x-axis is enabled. + If [code]true[/code] rotation across the x-axis is limited. </member> <member name="angular_limit_x/erp" type="float" setter="set_param_x" getter="get_param_x"> When rotating across x-axis, this error tolerance factor defines how much the correction gets slowed down. The lower, the slower. @@ -42,7 +42,7 @@ The amount of rotational damping across the y-axis. The lower, the more dampening occurs. </member> <member name="angular_limit_y/enabled" type="bool" setter="set_flag_y" getter="get_flag_y"> - If [code]true[/code] rotation across the y-axis is enabled. + If [code]true[/code] rotation across the y-axis is limited. </member> <member name="angular_limit_y/erp" type="float" setter="set_param_y" getter="get_param_y"> When rotating across y-axis, this error tolerance factor defines how much the correction gets slowed down. The lower, the slower. @@ -66,7 +66,7 @@ The amount of rotational damping across the z-axis. The lower, the more dampening occurs. </member> <member name="angular_limit_z/enabled" type="bool" setter="set_flag_z" getter="get_flag_z"> - If [code]true[/code] rotation across the z-axis is enabled. + If [code]true[/code] rotation across the z-axis is limited. </member> <member name="angular_limit_z/erp" type="float" setter="set_param_z" getter="get_param_z"> When rotating across z-axis, this error tolerance factor defines how much the correction gets slowed down. The lower, the slower. @@ -117,7 +117,7 @@ The amount of damping that happens at the x-motion. </member> <member name="linear_limit_x/enabled" type="bool" setter="set_flag_x" getter="get_flag_x"> - If [code]true[/code] the linear motion across the x-axis is enabled. + If [code]true[/code] the linear motion across the x-axis is limited. </member> <member name="linear_limit_x/lower_distance" type="float" setter="set_param_x" getter="get_param_x"> The minimum difference between the pivot points' x-axis. @@ -135,7 +135,7 @@ The amount of damping that happens at the y-motion. </member> <member name="linear_limit_y/enabled" type="bool" setter="set_flag_y" getter="get_flag_y"> - If [code]true[/code] the linear motion across the y-axis is enabled. + If [code]true[/code] the linear motion across the y-axis is limited. </member> <member name="linear_limit_y/lower_distance" type="float" setter="set_param_y" getter="get_param_y"> The minimum difference between the pivot points' y-axis. @@ -153,7 +153,7 @@ The amount of damping that happens at the z-motion. </member> <member name="linear_limit_z/enabled" type="bool" setter="set_flag_z" getter="get_flag_z"> - If [code]true[/code] the linear motion across the z-axis is enabled. + If [code]true[/code] the linear motion across the z-axis is limited. </member> <member name="linear_limit_z/lower_distance" type="float" setter="set_param_z" getter="get_param_z"> The minimum difference between the pivot points' z-axis. diff --git a/doc/classes/GridContainer.xml b/doc/classes/GridContainer.xml index 7f70a8b7b4..346ab9d357 100644 --- a/doc/classes/GridContainer.xml +++ b/doc/classes/GridContainer.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GridContainer" inherits="Container" category="Core" version="3.1"> <brief_description> - Grid container used to arrange elements in a grid like layout + Grid container used to arrange elements in a grid like layout. </brief_description> <description> Grid container will arrange its children in a grid like structure, the grid columns are specified using the [method set_columns] method and the number of rows will be equal to the number of children in the container divided by the number of columns, for example: if the container has 5 children, and 2 columns, there will be 3 rows in the container. Notice that grid layout will preserve the columns and rows for every size of the container. diff --git a/doc/classes/InterpolatedCamera.xml b/doc/classes/InterpolatedCamera.xml index 9ee06b9d4f..1ac7b5107e 100644 --- a/doc/classes/InterpolatedCamera.xml +++ b/doc/classes/InterpolatedCamera.xml @@ -1,8 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="InterpolatedCamera" inherits="Camera" category="Core" version="3.1"> <brief_description> + Camera which moves toward another node. </brief_description> <description> + InterpolatedCamera is a [Camera] which smoothly moves to match a target node's position and rotation. + If it is not [member enabled] or does not have a valid target set, InterpolatedCamera acts like a normal Camera. </description> <tutorials> </tutorials> @@ -15,15 +18,19 @@ <argument index="0" name="target" type="Object"> </argument> <description> + Sets the node to move toward and orient with. </description> </method> </methods> <members> <member name="enabled" type="bool" setter="set_interpolation_enabled" getter="is_interpolation_enabled"> + If [code]true[/code] and a target is set, the camera will move automatically. </member> <member name="speed" type="float" setter="set_speed" getter="get_speed"> + How quickly the camera moves toward its target. Higher values will result in tighter camera motion. </member> <member name="target" type="NodePath" setter="set_target_path" getter="get_target_path"> + The target's [NodePath]. </member> </members> <constants> diff --git a/doc/classes/LinkButton.xml b/doc/classes/LinkButton.xml index f419802958..a09edfad89 100644 --- a/doc/classes/LinkButton.xml +++ b/doc/classes/LinkButton.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="LinkButton" inherits="BaseButton" category="Core" version="3.1"> <brief_description> - Simple button used to represent a link to some resource + Simple button used to represent a link to some resource. </brief_description> <description> This kind of buttons are primarily used when the interaction with the button causes a context change (like linking to a web page). diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 97d09a66c4..9129f64340 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -746,7 +746,7 @@ The name of the node. This name is unique among the siblings (other child nodes from the same parent). When set to an existing name, the node will be automatically renamed </member> <member name="owner" type="Node" setter="set_owner" getter="get_owner"> - The node owner. A node can have any other node as owner (as long as it is a valid parent, grandparent, etc. ascending in the tree). When saving a node (using SceneSaver) all the nodes it owns will be saved with it. This allows for the creation of complex [SceneTree]s, with instancing and subinstancing. + The node owner. A node can have any other node as owner (as long as it is a valid parent, grandparent, etc. ascending in the tree). When saving a node (using [PackedScene]) all the nodes it owns will be saved with it. This allows for the creation of complex [SceneTree]s, with instancing and subinstancing. </member> <member name="pause_mode" type="int" setter="set_pause_mode" getter="get_pause_mode" enum="Node.PauseMode"> Pause mode. How the node will behave if the [SceneTree] is paused. diff --git a/doc/classes/PackedScene.xml b/doc/classes/PackedScene.xml index cf89e6a2df..8d810bc9c4 100644 --- a/doc/classes/PackedScene.xml +++ b/doc/classes/PackedScene.xml @@ -5,7 +5,14 @@ </brief_description> <description> A simplified interface to a scene file. Provides access to operations and checks that can be performed on the scene resource itself. - TODO: explain ownership, and that node does not need to own itself + Can be used to save a node to a file. When saving, the node as well as all the node it owns get saved (see [code]owner[/code] property on [Node]). Note that the node doesn't need to own itself. + Example of saving a node: + [codeblock] + var scene = PackedScene.new() + var result = scene.pack(child) + if result == OK: + ResourceSaver.save("res://path/name.scn", scene) // or user://... + [/codeblock] </description> <tutorials> </tutorials> diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index 70e6fab5b6..6ead220236 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -24,6 +24,19 @@ Add a new checkable item with text "label". An id can optionally be provided, as well as an accelerator. If no id is provided, one will be created from the index. Note that checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually. </description> </method> + <method name="add_radio_check_item"> + <return type="void"> + </return> + <argument index="0" name="label" type="String"> + </argument> + <argument index="1" name="id" type="int" default="-1"> + </argument> + <argument index="2" name="accel" type="int" default="0"> + </argument> + <description> + The same as [method add_check_item] but the inserted item will look as a radio button. Remember this is just cosmetic and you have to add the logic for checking/unchecking items in radio groups. + </description> + </method> <method name="add_check_shortcut"> <return type="void"> </return> @@ -36,6 +49,18 @@ <description> </description> </method> + <method name="add_radio_check_shortcut"> + <return type="void"> + </return> + <argument index="0" name="shortcut" type="ShortCut"> + </argument> + <argument index="1" name="id" type="int" default="-1"> + </argument> + <argument index="2" name="global" type="bool" default="false"> + </argument> + <description> + </description> + </method> <method name="add_icon_check_item"> <return type="void"> </return> @@ -239,7 +264,16 @@ <argument index="0" name="idx" type="int"> </argument> <description> - Return whether the item at index "idx" has a checkbox. Note that checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually. + Return whether the item at index "idx" is checkable in some way, i.e., whether has a checkbox or radio button. Note that checkable items just display a checkmark or radio button, but don't have any built-in checking behavior and must be checked/unchecked manually. + </description> + </method> + <method name="is_item_radio_checkable" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="idx" type="int"> + </argument> + <description> + Return whether the item at index "idx" has radio-button-style checkability. Remember this is just cosmetic and you have to add the logic for checking/unchecking items in radio groups. </description> </method> <method name="is_item_checked" qualifiers="const"> @@ -248,7 +282,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> - Return the checkstate status of the item at index "idx". + Return whether the item at index "idx" is checked. </description> </method> <method name="is_item_disabled" qualifiers="const"> @@ -300,6 +334,18 @@ Set whether the item at index "idx" has a checkbox. Note that checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually. </description> </method> + <method name="set_item_as_radio_checkable"> + <return type="void"> + </return> + <argument index="0" name="idx" type="int"> + </argument> + <argument index="1" name="enable" type="bool"> + </argument> + <description> + The same as [method set_item_as_checkable] but placing a radio button in case of enabling. If used for disabling, it's the same. + Remember this is just cosmetic and you have to add the logic for checking/unchecking items in radio groups. + </description> + </method> <method name="set_item_as_separator"> <return type="void"> </return> diff --git a/doc/classes/Position3D.xml b/doc/classes/Position3D.xml index de29ac2154..7c0875cc0c 100644 --- a/doc/classes/Position3D.xml +++ b/doc/classes/Position3D.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="Position3D" inherits="Spatial" category="Core" version="3.1"> <brief_description> - Generic 3D Position hint for editing + Generic 3D Position hint for editing. </brief_description> <description> Generic 3D Position hint for editing. It's just like a plain [Spatial] but displays as a cross in the 3D-Editor at all times. diff --git a/doc/classes/VehicleWheel.xml b/doc/classes/VehicleWheel.xml index 6f9f5ea2c5..f2c6f5f287 100644 --- a/doc/classes/VehicleWheel.xml +++ b/doc/classes/VehicleWheel.xml @@ -31,16 +31,16 @@ The damping applied to the spring when the spring is being compressed. This value should be between 0.0 (no damping) and 1.0. A value of 0.0 means the car will keep bouncing as the spring keeps its energy. A good value for this is around 0.3 for a normal car, 0.5 for a race car. </member> <member name="damping_relaxation" type="float" setter="set_damping_relaxation" getter="get_damping_relaxation"> - The damping applied to the spring when relaxing. This value should be between 0.0 (no damping) and 1.0. This value should always be slighly higher then the [member damping_compression] property. For a [member damping_compression] value of 0.3, try a relaxation value of 0.5 + The damping applied to the spring when relaxing. This value should be between 0.0 (no damping) and 1.0. This value should always be slightly higher than the [member damping_compression] property. For a [member damping_compression] value of 0.3, try a relaxation value of 0.5 </member> <member name="suspension_max_force" type="float" setter="set_suspension_max_force" getter="get_suspension_max_force"> - The maximum force the spring can resist. This value should be higher then a quarter of the [member RigidBody.mass] of the [VehicleBody] or the spring will not carry the weight of the vehicle. Good results are often obtained by a value that is about 3x to 4x this number. + The maximum force the spring can resist. This value should be higher than a quarter of the [member RigidBody.mass] of the [VehicleBody] or the spring will not carry the weight of the vehicle. Good results are often obtained by a value that is about 3x to 4x this number. </member> <member name="suspension_stiffness" type="float" setter="set_suspension_stiffness" getter="get_suspension_stiffness"> - This value defines the stiffness of the suspension. Use a value lower then 50 for an offroad car, a value between 50 and 100 for a race car and try something around 200 for something like a Formula 1 car. + This value defines the stiffness of the suspension. Use a value lower than 50 for an off-road car, a value between 50 and 100 for a race car and try something around 200 for something like a Formula 1 car. </member> <member name="suspension_travel" type="float" setter="set_suspension_travel" getter="get_suspension_travel"> - This is the distance the suspension can travel. As Godots measures are in meters keep this setting relatively low. Try a value between 0.1 and 0.3 depending on the type of car . + This is the distance the suspension can travel. As Godot measures are in meters keep this setting relatively low. Try a value between 0.1 and 0.3 depending on the type of car . </member> <member name="use_as_steering" type="bool" setter="set_use_as_steering" getter="is_used_as_steering"> If true this wheel will be turned when the car steers. @@ -49,14 +49,14 @@ If true this wheel transfers engine force to the ground to propel the vehicle forward. </member> <member name="wheel_friction_slip" type="float" setter="set_friction_slip" getter="get_friction_slip"> - This determines how much grip this wheel has. It is combined with the friction setting of the surface the wheel is in contact with. 0.0 means no grip, 1.0 is normal grip. For a drift car setup, try setting the grip of the rear wheels slightly lower then the front wheels, or use a lower value to simulate tire wear. + This determines how much grip this wheel has. It is combined with the friction setting of the surface the wheel is in contact with. 0.0 means no grip, 1.0 is normal grip. For a drift car setup, try setting the grip of the rear wheels slightly lower than the front wheels, or use a lower value to simulate tire wear. It's best to set this to 1.0 when starting out. </member> <member name="wheel_radius" type="float" setter="set_radius" getter="get_radius"> The radius of the wheel in meters. </member> <member name="wheel_rest_length" type="float" setter="set_suspension_rest_length" getter="get_suspension_rest_length"> - This is the distance in meters the wheel is lowered from its origin point. Don't set this to 0.0 and move the wheel into position, instead move the origin point of your wheel (the gizmo in Godot) to the position the wheel will take when bottoming out, then use the rest lenght to move the wheel down to the position it should be in when the car is in rest. + This is the distance in meters the wheel is lowered from its origin point. Don't set this to 0.0 and move the wheel into position, instead move the origin point of your wheel (the gizmo in Godot) to the position the wheel will take when bottoming out, then use the rest length to move the wheel down to the position it should be in when the car is in rest. </member> <member name="wheel_roll_influence" type="float" setter="set_roll_influence" getter="get_roll_influence"> This value effects the roll of your vehicle. If set to 0.0 for all wheels your vehicle will be prone to rolling over while a value of 1.0 will resist body roll. diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp index 0bb03d23ea..1e17e72532 100644 --- a/drivers/alsa/audio_driver_alsa.cpp +++ b/drivers/alsa/audio_driver_alsa.cpp @@ -37,19 +37,20 @@ #include <errno.h> -Error AudioDriverALSA::init() { - - active = false; - thread_exited = false; - exit_thread = false; - pcm_open = false; - samples_in = NULL; - samples_out = NULL; - +Error AudioDriverALSA::init_device() { mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); speaker_mode = SPEAKER_MODE_STEREO; channels = 2; + // If there is a specified device check that it is really present + if (device_name != "Default") { + Array list = get_device_list(); + if (list.find(device_name) == -1) { + device_name = "Default"; + new_device = "Default"; + } + } + int status; snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; @@ -65,7 +66,16 @@ Error AudioDriverALSA::init() { //6 chans - "plug:surround51" //4 chans - "plug:surround40"; - status = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + if (device_name == "Default") { + status = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + } else { + String device = device_name; + int pos = device.find(";"); + if (pos != -1) { + device = device.substr(0, pos); + } + status = snd_pcm_open(&pcm_handle, device.utf8().get_data(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + } ERR_FAIL_COND_V(status < 0, ERR_CANT_OPEN); @@ -129,15 +139,28 @@ Error AudioDriverALSA::init() { status = snd_pcm_sw_params(pcm_handle, swparams); CHECK_FAIL(status < 0); - samples_in = memnew_arr(int32_t, period_size * channels); - samples_out = memnew_arr(int16_t, period_size * channels); + samples_in.resize(period_size * channels); + samples_out.resize(period_size * channels); snd_pcm_nonblock(pcm_handle, 0); - mutex = Mutex::create(); - thread = Thread::create(AudioDriverALSA::thread_func, this); - return OK; +} + +Error AudioDriverALSA::init() { + + active = false; + thread_exited = false; + exit_thread = false; + pcm_open = false; + + Error err = init_device(); + if (err == OK) { + mutex = Mutex::create(); + thread = Thread::create(AudioDriverALSA::thread_func, this); + } + + return err; }; void AudioDriverALSA::thread_func(void *p_udata) { @@ -152,7 +175,7 @@ void AudioDriverALSA::thread_func(void *p_udata) { } else { ad->lock(); - ad->audio_server_process(ad->period_size, ad->samples_in); + ad->audio_server_process(ad->period_size, ad->samples_in.ptrw()); ad->unlock(); @@ -167,7 +190,7 @@ void AudioDriverALSA::thread_func(void *p_udata) { while (todo) { if (ad->exit_thread) break; - uint8_t *src = (uint8_t *)ad->samples_out; + uint8_t *src = (uint8_t *)ad->samples_out.ptr(); int wrote = snd_pcm_writei(ad->pcm_handle, (void *)(src + (total * ad->channels)), todo); if (wrote < 0) { @@ -193,6 +216,26 @@ void AudioDriverALSA::thread_func(void *p_udata) { total += wrote; todo -= wrote; }; + + // User selected a new device, finish the current one so we'll init the new device + if (ad->device_name != ad->new_device) { + ad->device_name = ad->new_device; + ad->finish_device(); + + Error err = ad->init_device(); + if (err != OK) { + ERR_PRINT("ALSA: init_device error"); + ad->device_name = "Default"; + ad->new_device = "Default"; + + err = ad->init_device(); + if (err != OK) { + ad->active = false; + ad->exit_thread = true; + break; + } + } + } }; ad->thread_exited = true; @@ -213,6 +256,49 @@ AudioDriver::SpeakerMode AudioDriverALSA::get_speaker_mode() const { return speaker_mode; }; +Array AudioDriverALSA::get_device_list() { + + Array list; + + list.push_back("Default"); + + void **hints; + + if (snd_device_name_hint(-1, "pcm", &hints) < 0) + return list; + + for (void **n = hints; *n != NULL; n++) { + char *name = snd_device_name_get_hint(*n, "NAME"); + char *desc = snd_device_name_get_hint(*n, "DESC"); + + if (name != NULL && !strncmp(name, "plughw", 6)) { + if (desc) { + list.push_back(String(name) + ";" + String(desc)); + } else { + list.push_back(String(name)); + } + } + + if (desc != NULL) + free(desc); + if (name != NULL) + free(name); + } + snd_device_name_free_hint(hints); + + return list; +} + +String AudioDriverALSA::get_device() { + + return device_name; +} + +void AudioDriverALSA::set_device(String device) { + + new_device = device; +} + void AudioDriverALSA::lock() { if (!thread || !mutex) @@ -227,6 +313,14 @@ void AudioDriverALSA::unlock() { mutex->unlock(); }; +void AudioDriverALSA::finish_device() { + + if (pcm_open) { + snd_pcm_close(pcm_handle); + pcm_open = NULL; + } +} + void AudioDriverALSA::finish() { if (!thread) @@ -235,17 +329,13 @@ void AudioDriverALSA::finish() { exit_thread = true; Thread::wait_to_finish(thread); - if (pcm_open) - snd_pcm_close(pcm_handle); - - if (samples_in) { - memdelete_arr(samples_in); - memdelete_arr(samples_out); - }; + finish_device(); memdelete(thread); - if (mutex) + if (mutex) { memdelete(mutex); + mutex = NULL; + } thread = NULL; }; @@ -254,6 +344,9 @@ AudioDriverALSA::AudioDriverALSA() { mutex = NULL; thread = NULL; pcm_handle = NULL; + + device_name = "Default"; + new_device = "Default"; }; AudioDriverALSA::~AudioDriverALSA(){ diff --git a/drivers/alsa/audio_driver_alsa.h b/drivers/alsa/audio_driver_alsa.h index 8ed60dfdc7..2878e100a2 100644 --- a/drivers/alsa/audio_driver_alsa.h +++ b/drivers/alsa/audio_driver_alsa.h @@ -44,8 +44,14 @@ class AudioDriverALSA : public AudioDriver { snd_pcm_t *pcm_handle; - int32_t *samples_in; - int16_t *samples_out; + String device_name; + String new_device; + + Vector<int32_t> samples_in; + Vector<int16_t> samples_out; + + Error init_device(); + void finish_device(); static void thread_func(void *p_udata); @@ -71,6 +77,9 @@ public: virtual void start(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; + virtual Array get_device_list(); + virtual String get_device(); + virtual void set_device(String device); virtual void lock(); virtual void unlock(); virtual void finish(); diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp index 0b39f9ebc3..c84469f26f 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.cpp +++ b/drivers/coreaudio/audio_driver_coreaudio.cpp @@ -37,16 +37,22 @@ #define kOutputBus 0 #ifdef OSX_ENABLED -static OSStatus outputDeviceAddressCB(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData) { +OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID, + UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, + void *inClientData) { AudioDriverCoreAudio *driver = (AudioDriverCoreAudio *)inClientData; - driver->reopen(); + // If our selected device is the Default call set_device to update the + // kAudioOutputUnitProperty_CurrentDevice property + if (driver->device_name == "Default") { + driver->set_device("Default"); + } return noErr; } #endif -Error AudioDriverCoreAudio::initDevice() { +Error AudioDriverCoreAudio::init_device() { AudioComponentDescription desc; zeromem(&desc, sizeof(desc)); desc.componentType = kAudioUnitType_Output; @@ -129,7 +135,7 @@ Error AudioDriverCoreAudio::initDevice() { return OK; } -Error AudioDriverCoreAudio::finishDevice() { +Error AudioDriverCoreAudio::finish_device() { OSStatus result; if (active) { @@ -153,49 +159,18 @@ Error AudioDriverCoreAudio::init() { channels = 2; #ifdef OSX_ENABLED - outputDeviceAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - outputDeviceAddress.mScope = kAudioObjectPropertyScopeGlobal; - outputDeviceAddress.mElement = kAudioObjectPropertyElementMaster; + AudioObjectPropertyAddress prop; + prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + prop.mScope = kAudioObjectPropertyScopeGlobal; + prop.mElement = kAudioObjectPropertyElementMaster; - result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &outputDeviceAddress, &outputDeviceAddressCB, this); + result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); ERR_FAIL_COND_V(result != noErr, FAILED); #endif - return initDevice(); + return init_device(); }; -Error AudioDriverCoreAudio::reopen() { - bool restart = false; - - lock(); - - if (active) { - restart = true; - } - - Error err = finishDevice(); - if (err != OK) { - ERR_PRINT("finishDevice failed"); - unlock(); - return err; - } - - err = initDevice(); - if (err != OK) { - ERR_PRINT("initDevice failed"); - unlock(); - return err; - } - - if (restart) { - start(); - } - - unlock(); - - return OK; -} - OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, @@ -257,6 +232,150 @@ AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); }; +#ifdef OSX_ENABLED + +Array AudioDriverCoreAudio::get_device_list() { + + Array list; + + list.push_back("Default"); + + AudioObjectPropertyAddress prop; + + prop.mSelector = kAudioHardwarePropertyDevices; + prop.mScope = kAudioObjectPropertyScopeGlobal; + prop.mElement = kAudioObjectPropertyElementMaster; + + UInt32 size = 0; + AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, NULL, &size); + AudioDeviceID *audioDevices = (AudioDeviceID *)malloc(size); + AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &size, audioDevices); + + UInt32 deviceCount = size / sizeof(AudioDeviceID); + for (UInt32 i = 0; i < deviceCount; i++) { + prop.mScope = kAudioDevicePropertyScopeOutput; + prop.mSelector = kAudioDevicePropertyStreamConfiguration; + + AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size); + AudioBufferList *bufferList = (AudioBufferList *)malloc(size); + AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList); + + UInt32 outputChannelCount = 0; + for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) + outputChannelCount += bufferList->mBuffers[j].mNumberChannels; + + free(bufferList); + + if (outputChannelCount >= 1) { + CFStringRef cfname; + + size = sizeof(CFStringRef); + prop.mSelector = kAudioObjectPropertyName; + + AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, &cfname); + + CFIndex length = CFStringGetLength(cfname); + CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; + char *buffer = (char *)malloc(maxSize); + if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) { + // Append the ID to the name in case we have devices with duplicate name + list.push_back(String(buffer) + " (" + itos(audioDevices[i]) + ")"); + } + + free(buffer); + } + } + + free(audioDevices); + + return list; +} + +String AudioDriverCoreAudio::get_device() { + + return device_name; +} + +void AudioDriverCoreAudio::set_device(String device) { + + device_name = device; + if (!active) { + return; + } + + AudioDeviceID deviceId; + bool found = false; + if (device_name != "Default") { + AudioObjectPropertyAddress prop; + + prop.mSelector = kAudioHardwarePropertyDevices; + prop.mScope = kAudioObjectPropertyScopeGlobal; + prop.mElement = kAudioObjectPropertyElementMaster; + + UInt32 size = 0; + AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, NULL, &size); + AudioDeviceID *audioDevices = (AudioDeviceID *)malloc(size); + AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &size, audioDevices); + + UInt32 deviceCount = size / sizeof(AudioDeviceID); + for (UInt32 i = 0; i < deviceCount && !found; i++) { + prop.mScope = kAudioDevicePropertyScopeOutput; + prop.mSelector = kAudioDevicePropertyStreamConfiguration; + + AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size); + AudioBufferList *bufferList = (AudioBufferList *)malloc(size); + AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList); + + UInt32 outputChannelCount = 0; + for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) + outputChannelCount += bufferList->mBuffers[j].mNumberChannels; + + free(bufferList); + + if (outputChannelCount >= 1) { + CFStringRef cfname; + + size = sizeof(CFStringRef); + prop.mSelector = kAudioObjectPropertyName; + + AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, &cfname); + + CFIndex length = CFStringGetLength(cfname); + CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; + char *buffer = (char *)malloc(maxSize); + if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) { + String name = String(buffer) + " (" + itos(audioDevices[i]) + ")"; + if (name == device_name) { + deviceId = audioDevices[i]; + found = true; + } + } + + free(buffer); + } + } + + free(audioDevices); + } + + if (!found) { + UInt32 size = sizeof(AudioDeviceID); + AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + + OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, &size, &deviceId); + ERR_FAIL_COND(result != noErr); + + found = true; + } + + if (found) { + OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID)); + ERR_FAIL_COND(result != noErr); + } +} + +#endif + void AudioDriverCoreAudio::lock() { if (mutex) mutex->lock(); @@ -276,10 +395,15 @@ bool AudioDriverCoreAudio::try_lock() { void AudioDriverCoreAudio::finish() { OSStatus result; - finishDevice(); + finish_device(); #ifdef OSX_ENABLED - result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &outputDeviceAddress, &outputDeviceAddressCB, this); + AudioObjectPropertyAddress prop; + prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + prop.mScope = kAudioObjectPropertyScopeGlobal; + prop.mElement = kAudioObjectPropertyElementMaster; + + result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); if (result != noErr) { ERR_PRINT("AudioObjectRemovePropertyListener failed"); } @@ -309,6 +433,8 @@ AudioDriverCoreAudio::AudioDriverCoreAudio() { buffer_frames = 0; samples_in.clear(); + + device_name = "Default"; }; AudioDriverCoreAudio::~AudioDriverCoreAudio(){}; diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h index 51256085d8..1ef474ac19 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.h +++ b/drivers/coreaudio/audio_driver_coreaudio.h @@ -43,12 +43,12 @@ class AudioDriverCoreAudio : public AudioDriver { AudioComponentInstance audio_unit; -#ifdef OSX_ENABLED - AudioObjectPropertyAddress outputDeviceAddress; -#endif + bool active; Mutex *mutex; + String device_name; + int mix_rate; unsigned int channels; unsigned int buffer_frames; @@ -56,14 +56,18 @@ class AudioDriverCoreAudio : public AudioDriver { Vector<int32_t> samples_in; + static OSStatus output_device_address_cb(AudioObjectID inObjectID, + UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, + void *inClientData); + static OSStatus output_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); - Error initDevice(); - Error finishDevice(); + Error init_device(); + Error finish_device(); public: const char *get_name() const { @@ -74,12 +78,16 @@ public: virtual void start(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; +#ifdef OSX_ENABLED + virtual Array get_device_list(); + virtual String get_device(); + virtual void set_device(String device); +#endif virtual void lock(); virtual void unlock(); virtual void finish(); bool try_lock(); - Error reopen(); AudioDriverCoreAudio(); ~AudioDriverCoreAudio(); diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 02d851288a..5b6b3d44f2 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -2085,9 +2085,9 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_ case RasterizerStorageGLES3::Shader::Spatial::BLEND_MODE_MUL: { glBlendEquation(GL_FUNC_ADD); if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) { - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_DST_ALPHA, GL_ZERO); } else { - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE); } } break; diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index 9639f06345..5dddb1a900 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -37,131 +37,102 @@ #include "os/os.h" #include "project_settings.h" -void pa_state_cb(pa_context *c, void *userdata) { - pa_context_state_t state; - int *pa_ready = (int *)userdata; +void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) { + AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; - state = pa_context_get_state(c); - switch (state) { - case PA_CONTEXT_FAILED: + switch (pa_context_get_state(c)) { case PA_CONTEXT_TERMINATED: - *pa_ready = 2; + case PA_CONTEXT_FAILED: + ad->pa_ready = -1; break; case PA_CONTEXT_READY: - *pa_ready = 1; + ad->pa_ready = 1; break; } } -void sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { - unsigned int *channels = (unsigned int *)userdata; +void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { + AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; // If eol is set to a positive number, you're at the end of the list if (eol > 0) { return; } - *channels = l->channel_map.channels; + ad->pa_channels = l->channel_map.channels; + ad->pa_status++; } -void server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) { - char *default_output = (char *)userdata; +void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) { + AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; - strncpy(default_output, i->default_sink_name, 1024); + ad->default_device = i->default_sink_name; + ad->pa_status++; } -static unsigned int detect_channels() { - - pa_mainloop *pa_ml; - pa_mainloop_api *pa_mlapi; - pa_operation *pa_op; - pa_context *pa_ctx; - - int state = 0; - int pa_ready = 0; - - char default_output[1024]; - unsigned int channels = 2; - - pa_ml = pa_mainloop_new(); - pa_mlapi = pa_mainloop_get_api(pa_ml); - pa_ctx = pa_context_new(pa_mlapi, "Godot"); - - int ret = pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFLAGS, NULL); - if (ret < 0) { - pa_context_unref(pa_ctx); - pa_mainloop_free(pa_ml); +void AudioDriverPulseAudio::detect_channels() { - return 2; - } + pa_channels = 2; - pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready); + if (device_name == "Default") { + // Get the default output device name + pa_status = 0; + pa_operation *pa_op = pa_context_get_server_info(pa_ctx, &AudioDriverPulseAudio::pa_server_info_cb, (void *)this); + if (pa_op) { + while (pa_status == 0) { + int ret = pa_mainloop_iterate(pa_ml, 1, NULL); + if (ret < 0) { + ERR_PRINT("pa_mainloop_iterate error"); + } + } - // Wait until the pa server is ready - while (pa_ready == 0) { - pa_mainloop_iterate(pa_ml, 1, NULL); + pa_operation_unref(pa_op); + } else { + ERR_PRINT("pa_context_get_server_info error"); + } } - // Check if there was an error connecting to the pa server - if (pa_ready == 2) { - pa_context_disconnect(pa_ctx); - pa_context_unref(pa_ctx); - pa_mainloop_free(pa_ml); - - return 2; + char device[1024]; + if (device_name == "Default") { + strcpy(device, default_device.utf8().get_data()); + } else { + strcpy(device, device_name.utf8().get_data()); } - // Get the default output device name - pa_op = pa_context_get_server_info(pa_ctx, &server_info_cb, (void *)default_output); + // Now using the device name get the amount of channels + pa_status = 0; + pa_operation *pa_op = pa_context_get_sink_info_by_name(pa_ctx, device, &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this); if (pa_op) { - while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) { - ret = pa_mainloop_iterate(pa_ml, 1, NULL); + while (pa_status == 0) { + int ret = pa_mainloop_iterate(pa_ml, 1, NULL); if (ret < 0) { ERR_PRINT("pa_mainloop_iterate error"); } } pa_operation_unref(pa_op); - - // Now using the device name get the amount of channels - pa_op = pa_context_get_sink_info_by_name(pa_ctx, default_output, &sink_info_cb, (void *)&channels); - if (pa_op) { - while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) { - ret = pa_mainloop_iterate(pa_ml, 1, NULL); - if (ret < 0) { - ERR_PRINT("pa_mainloop_iterate error"); - } - } - - pa_operation_unref(pa_op); - } else { - ERR_PRINT("pa_context_get_sink_info_by_name error"); - } } else { - ERR_PRINT("pa_context_get_server_info error"); + ERR_PRINT("pa_context_get_sink_info_by_name error"); } - - pa_context_disconnect(pa_ctx); - pa_context_unref(pa_ctx); - pa_mainloop_free(pa_ml); - - return channels; } -Error AudioDriverPulseAudio::init() { - - active = false; - thread_exited = false; - exit_thread = false; +Error AudioDriverPulseAudio::init_device() { - mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); + // If there is a specified device check that it is really present + if (device_name != "Default") { + Array list = get_device_list(); + if (list.find(device_name) == -1) { + device_name = "Default"; + new_device = "Default"; + } + } // Detect the amount of channels PulseAudio is using // Note: If using an even amount of channels (2, 4, etc) channels and pa_channels will be equal, // if not then pa_channels will have the real amount of channels PulseAudio is using and channels // will have the amount of channels Godot is using (in this case it's pa_channels + 1) - pa_channels = detect_channels(); + detect_channels(); switch (pa_channels) { case 1: // Mono case 3: // Surround 2.1 @@ -205,27 +176,76 @@ Error AudioDriverPulseAudio::init() { attr.maxlength = (uint32_t)-1; attr.minreq = (uint32_t)-1; - int error_code; - pulse = pa_simple_new(NULL, // default server - "Godot", // application name - PA_STREAM_PLAYBACK, - NULL, // default device - "Sound", // stream description - &spec, - NULL, // use default channel map - &attr, // use buffering attributes from above - &error_code); - - if (pulse == NULL) { - fprintf(stderr, "PulseAudio ERR: %s\n", pa_strerror(error_code)); - ERR_FAIL_COND_V(pulse == NULL, ERR_CANT_OPEN); - } + pa_str = pa_stream_new(pa_ctx, "Sound", &spec, NULL); + ERR_FAIL_COND_V(pa_ctx == NULL, ERR_CANT_OPEN); + + const char *dev = device_name == "Default" ? NULL : device_name.utf8().get_data(); + pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE); + int error_code = pa_stream_connect_playback(pa_str, dev, &attr, flags, NULL, NULL); + ERR_FAIL_COND_V(error_code < 0, ERR_CANT_OPEN); samples_in.resize(buffer_frames * channels); samples_out.resize(pa_buffer_size); - mutex = Mutex::create(); - thread = Thread::create(AudioDriverPulseAudio::thread_func, this); + return OK; +} + +Error AudioDriverPulseAudio::init() { + + active = false; + thread_exited = false; + exit_thread = false; + + mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); + + pa_ml = pa_mainloop_new(); + ERR_FAIL_COND_V(pa_ml == NULL, ERR_CANT_OPEN); + + pa_ctx = pa_context_new(pa_mainloop_get_api(pa_ml), "Godot"); + ERR_FAIL_COND_V(pa_ctx == NULL, ERR_CANT_OPEN); + + pa_ready = 0; + pa_context_set_state_callback(pa_ctx, pa_state_cb, (void *)this); + + int ret = pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFLAGS, NULL); + if (ret < 0) { + if (pa_ctx) { + pa_context_unref(pa_ctx); + pa_ctx = NULL; + } + + if (pa_ml) { + pa_mainloop_free(pa_ml); + pa_ml = NULL; + } + + return ERR_CANT_OPEN; + } + + while (pa_ready == 0) { + pa_mainloop_iterate(pa_ml, 1, NULL); + } + + if (pa_ready < 0) { + if (pa_ctx) { + pa_context_disconnect(pa_ctx); + pa_context_unref(pa_ctx); + pa_ctx = NULL; + } + + if (pa_ml) { + pa_mainloop_free(pa_ml); + pa_ml = NULL; + } + + return ERR_CANT_OPEN; + } + + Error err = init_device(); + if (err == OK) { + mutex = Mutex::create(); + thread = Thread::create(AudioDriverPulseAudio::thread_func, this); + } return OK; } @@ -233,9 +253,24 @@ Error AudioDriverPulseAudio::init() { float AudioDriverPulseAudio::get_latency() { if (latency == 0) { //only do this once since it's approximate anyway - int error_code; - pa_usec_t palat = pa_simple_get_latency(pulse, &error_code); - latency = double(palat) / 1000000.0; + lock(); + + pa_usec_t palat = 0; + if (pa_stream_get_state(pa_str) == PA_STREAM_READY) { + int negative = 0; + + if (pa_stream_get_latency(pa_str, &palat, &negative) >= 0) { + if (negative) { + palat = 0; + } + } + } + + if (palat > 0) { + latency = double(palat) / 1000000.0; + } + + unlock(); } return latency; @@ -278,17 +313,57 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { } } - // pa_simple_write always consumes the entire buffer - int error_code; int byte_size = ad->pa_buffer_size * sizeof(int16_t); - if (pa_simple_write(ad->pulse, ad->samples_out.ptr(), byte_size, &error_code) < 0) { - // can't recover here - fprintf(stderr, "PulseAudio failed and can't recover: %s\n", pa_strerror(error_code)); - ad->active = false; - ad->exit_thread = true; - break; + + ad->lock(); + + int ret; + do { + ret = pa_mainloop_iterate(ad->pa_ml, 0, NULL); + } while (ret > 0); + + if (pa_stream_get_state(ad->pa_str) == PA_STREAM_READY) { + const void *ptr = ad->samples_out.ptr(); + while (byte_size > 0) { + size_t bytes = pa_stream_writable_size(ad->pa_str); + if (bytes > 0) { + if (bytes > byte_size) { + bytes = byte_size; + } + + int ret = pa_stream_write(ad->pa_str, ptr, bytes, NULL, 0LL, PA_SEEK_RELATIVE); + if (ret >= 0) { + byte_size -= bytes; + ptr = (const char *)ptr + bytes; + } + } else { + pa_mainloop_iterate(ad->pa_ml, 1, NULL); + } + } } + + // User selected a new device, finish the current one so we'll init the new device + if (ad->device_name != ad->new_device) { + ad->device_name = ad->new_device; + ad->finish_device(); + + Error err = ad->init_device(); + if (err != OK) { + ERR_PRINT("PulseAudio: init_device error"); + ad->device_name = "Default"; + ad->new_device = "Default"; + + err = ad->init_device(); + if (err != OK) { + ad->active = false; + ad->exit_thread = true; + break; + } + } + } + + ad->unlock(); } ad->thread_exited = true; @@ -309,6 +384,61 @@ AudioDriver::SpeakerMode AudioDriverPulseAudio::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); } +void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { + AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + int ctr = 0; + + // If eol is set to a positive number, you're at the end of the list + if (eol > 0) { + return; + } + + ad->pa_devices.push_back(l->name); + ad->pa_status++; +} + +Array AudioDriverPulseAudio::get_device_list() { + + pa_devices.clear(); + pa_devices.push_back("Default"); + + if (pa_ctx == NULL) { + return pa_devices; + } + + lock(); + + // Get the device list + pa_status = 0; + pa_operation *pa_op = pa_context_get_sink_info_list(pa_ctx, pa_sinklist_cb, (void *)this); + if (pa_op) { + while (pa_status == 0) { + int ret = pa_mainloop_iterate(pa_ml, 1, NULL); + if (ret < 0) { + ERR_PRINT("pa_mainloop_iterate error"); + } + } + + pa_operation_unref(pa_op); + } else { + ERR_PRINT("pa_context_get_server_info error"); + } + + unlock(); + + return pa_devices; +} + +String AudioDriverPulseAudio::get_device() { + + return device_name; +} + +void AudioDriverPulseAudio::set_device(String device) { + + new_device = device; +} + void AudioDriverPulseAudio::lock() { if (!thread || !mutex) @@ -323,6 +453,15 @@ void AudioDriverPulseAudio::unlock() { mutex->unlock(); } +void AudioDriverPulseAudio::finish_device() { + + if (pa_str) { + pa_stream_disconnect(pa_str); + pa_stream_unref(pa_str); + pa_str = NULL; + } +} + void AudioDriverPulseAudio::finish() { if (!thread) @@ -331,9 +470,17 @@ void AudioDriverPulseAudio::finish() { exit_thread = true; Thread::wait_to_finish(thread); - if (pulse) { - pa_simple_free(pulse); - pulse = NULL; + finish_device(); + + if (pa_ctx) { + pa_context_disconnect(pa_ctx); + pa_context_unref(pa_ctx); + pa_ctx = NULL; + } + + if (pa_ml) { + pa_mainloop_free(pa_ml); + pa_ml = NULL; } memdelete(thread); @@ -347,9 +494,16 @@ void AudioDriverPulseAudio::finish() { AudioDriverPulseAudio::AudioDriverPulseAudio() { + pa_ml = NULL; + pa_ctx = NULL; + pa_str = NULL; + mutex = NULL; thread = NULL; - pulse = NULL; + + device_name = "Default"; + new_device = "Default"; + default_device = ""; samples_in.clear(); samples_out.clear(); @@ -359,6 +513,8 @@ AudioDriverPulseAudio::AudioDriverPulseAudio() { pa_buffer_size = 0; channels = 0; pa_channels = 0; + pa_ready = 0; + pa_status = 0; active = false; thread_exited = false; diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h index 5737f24314..934031e2a5 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.h +++ b/drivers/pulseaudio/audio_driver_pulseaudio.h @@ -37,14 +37,20 @@ #include "core/os/thread.h" #include "servers/audio_server.h" -#include <pulse/simple.h> +#include <pulse/pulseaudio.h> class AudioDriverPulseAudio : public AudioDriver { Thread *thread; Mutex *mutex; - pa_simple *pulse; + pa_mainloop *pa_ml; + pa_context *pa_ctx; + pa_stream *pa_str; + + String device_name; + String new_device; + String default_device; Vector<int32_t> samples_in; Vector<int16_t> samples_out; @@ -54,6 +60,9 @@ class AudioDriverPulseAudio : public AudioDriver { unsigned int pa_buffer_size; int channels; int pa_channels; + int pa_ready; + int pa_status; + Array pa_devices; bool active; bool thread_exited; @@ -61,6 +70,16 @@ class AudioDriverPulseAudio : public AudioDriver { float latency; + static void pa_state_cb(pa_context *c, void *userdata); + static void pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata); + static void pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata); + static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata); + + Error init_device(); + void finish_device(); + + void detect_channels(); + static void thread_func(void *p_udata); public: @@ -72,6 +91,9 @@ public: virtual void start(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; + virtual Array get_device_list(); + virtual String get_device(); + virtual void set_device(String device); virtual void lock(); virtual void unlock(); virtual void finish(); diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index 36acfb10d1..966b69a67e 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -35,6 +35,8 @@ #include "os/os.h" #include "project_settings.h" +#include <functiondiscoverykeys.h> + const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); @@ -121,7 +123,61 @@ Error AudioDriverWASAPI::init_device(bool reinit) { HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); - hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); + if (device_name == "Default") { + hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); + } else { + IMMDeviceCollection *devices = NULL; + + hr = enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices); + ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); + + LPWSTR strId = NULL; + bool found = false; + + UINT count = 0; + hr = devices->GetCount(&count); + ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); + + for (ULONG i = 0; i < count && !found; i++) { + IMMDevice *device = NULL; + + hr = devices->Item(i, &device); + ERR_BREAK(hr != S_OK); + + IPropertyStore *props = NULL; + hr = device->OpenPropertyStore(STGM_READ, &props); + ERR_BREAK(hr != S_OK); + + PROPVARIANT propvar; + PropVariantInit(&propvar); + + hr = props->GetValue(PKEY_Device_FriendlyName, &propvar); + ERR_BREAK(hr != S_OK); + + if (device_name == String(propvar.pwszVal)) { + hr = device->GetId(&strId); + ERR_BREAK(hr != S_OK); + + found = true; + } + + PropVariantClear(&propvar); + props->Release(); + device->Release(); + } + + if (found) { + hr = enumerator->GetDevice(strId, &device); + } + + if (strId) { + CoTaskMemFree(strId); + } + + if (device == NULL) { + hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); + } + } if (reinit) { // In case we're trying to re-initialize the device prevent throwing this error on the console, // otherwise if there is currently no device available this will spam the console. @@ -151,7 +207,6 @@ Error AudioDriverWASAPI::init_device(bool reinit) { // Since we're using WASAPI Shared Mode we can't control any of these, we just tag along wasapi_channels = pwfex->nChannels; - mix_rate = pwfex->nSamplesPerSec; format_tag = pwfex->wFormatTag; bits_per_sample = pwfex->wBitsPerSample; @@ -187,7 +242,14 @@ Error AudioDriverWASAPI::init_device(bool reinit) { } } - hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, pwfex, NULL); + DWORD streamflags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; + if (mix_rate != pwfex->nSamplesPerSec) { + streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST; + pwfex->nSamplesPerSec = mix_rate; + pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8); + } + + hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, 0, 0, pwfex, NULL); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); event = CreateEvent(NULL, FALSE, FALSE, NULL); @@ -223,10 +285,11 @@ Error AudioDriverWASAPI::finish_device() { if (audio_client) { if (active) { audio_client->Stop(); - audio_client->Release(); - audio_client = NULL; active = false; } + + audio_client->Release(); + audio_client = NULL; } if (render_client) { @@ -244,6 +307,8 @@ Error AudioDriverWASAPI::finish_device() { Error AudioDriverWASAPI::init() { + mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); + Error err = init_device(); if (err != OK) { ERR_PRINT("WASAPI: init_device error"); @@ -285,6 +350,64 @@ AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); } +Array AudioDriverWASAPI::get_device_list() { + + Array list; + IMMDeviceCollection *devices = NULL; + IMMDeviceEnumerator *enumerator = NULL; + + list.push_back(String("Default")); + + CoInitialize(NULL); + + HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); + ERR_FAIL_COND_V(hr != S_OK, Array()); + + hr = enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices); + ERR_FAIL_COND_V(hr != S_OK, Array()); + + UINT count = 0; + hr = devices->GetCount(&count); + ERR_FAIL_COND_V(hr != S_OK, Array()); + + for (ULONG i = 0; i < count; i++) { + IMMDevice *device = NULL; + + hr = devices->Item(i, &device); + ERR_BREAK(hr != S_OK); + + IPropertyStore *props = NULL; + hr = device->OpenPropertyStore(STGM_READ, &props); + ERR_BREAK(hr != S_OK); + + PROPVARIANT propvar; + PropVariantInit(&propvar); + + hr = props->GetValue(PKEY_Device_FriendlyName, &propvar); + ERR_BREAK(hr != S_OK); + + list.push_back(String(propvar.pwszVal)); + + PropVariantClear(&propvar); + props->Release(); + device->Release(); + } + + devices->Release(); + enumerator->Release(); + return list; +} + +String AudioDriverWASAPI::get_device() { + + return device_name; +} + +void AudioDriverWASAPI::set_device(String device) { + + new_device = device; +} + void AudioDriverWASAPI::write_sample(AudioDriverWASAPI *ad, BYTE *buffer, int i, int32_t sample) { if (ad->format_tag == WAVE_FORMAT_PCM) { switch (ad->bits_per_sample) { @@ -400,7 +523,8 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { } } - if (default_device_changed) { + // If we're using the Default device and it changed finish it so we'll re-init the device + if (ad->device_name == "Default" && default_device_changed) { Error err = ad->finish_device(); if (err != OK) { ERR_PRINT("WASAPI: finish_device error"); @@ -409,6 +533,15 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { default_device_changed = false; } + // User selected a new device, finish the current one so we'll init the new device + if (ad->device_name != ad->new_device) { + ad->device_name = ad->new_device; + Error err = ad->finish_device(); + if (err != OK) { + ERR_PRINT("WASAPI: finish_device error"); + } + } + if (!ad->audio_client) { Error err = ad->init_device(true); if (err == OK) { @@ -483,6 +616,9 @@ AudioDriverWASAPI::AudioDriverWASAPI() { thread_exited = false; exit_thread = false; active = false; + + device_name = "Default"; + new_device = "Default"; } #endif diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h index 2b19f0cca1..c97f4c288c 100644 --- a/drivers/wasapi/audio_driver_wasapi.h +++ b/drivers/wasapi/audio_driver_wasapi.h @@ -49,6 +49,9 @@ class AudioDriverWASAPI : public AudioDriver { Mutex *mutex; Thread *thread; + String device_name; + String new_device; + WORD format_tag; WORD bits_per_sample; @@ -80,6 +83,9 @@ public: virtual void start(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; + virtual Array get_device_list(); + virtual String get_device(); + virtual void set_device(String device); virtual void lock(); virtual void unlock(); virtual void finish(); diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index e10f4d05e8..23c8ea2ec7 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -139,19 +139,22 @@ void FileAccessWindows::close() { //atomic replace for existing file rename_error = !ReplaceFileW(save_path.c_str(), (save_path + ".tmp").c_str(), NULL, 2 | 4, NULL, NULL); } - if (rename_error && close_fail_notify) { - close_fail_notify(save_path); - } if (rename_error) { attempts--; OS::get_singleton()->delay_usec(1000000); //wait 100msec and try again } } - save_path = ""; if (rename_error) { + if (close_fail_notify) { + close_fail_notify(save_path); + } + ERR_EXPLAIN("Safe save failed. This may be a permissions problem, but also may happen because you are running a paranoid antivirus. If this is the case, please switch to Windows Defender or disable the 'safe save' option in editor settings. This makes it work, but increases the risk of file corruption in a crash."); } + + save_path = ""; + ERR_FAIL_COND(rename_error); } } diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 9ae9ab1501..4c7f2f53cc 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -212,35 +212,39 @@ void FindReplaceBar::_replace_all() { text_edit->begin_complex_operation(); - while (search_next()) { - - // replace area - Point2i match_from(result_line, result_col); - Point2i match_to(result_line, result_col + search_text_len); - - if (match_from < prev_match) - break; // done + if (search_current()) { + do { + // replace area + Point2i match_from(result_line, result_col); + Point2i match_to(result_line, result_col + search_text_len); + + if (match_from < prev_match) { + break; // done + } - prev_match = Point2i(result_line, result_col + replace_text.length()); + prev_match = Point2i(result_line, result_col + replace_text.length()); - text_edit->unfold_line(result_line); - text_edit->select(result_line, result_col, result_line, match_to.y); + text_edit->unfold_line(result_line); + text_edit->select(result_line, result_col, result_line, match_to.y); - if (selection_enabled && is_selection_only()) { + if (selection_enabled && is_selection_only()) { + if (match_from < selection_begin || match_to > selection_end) { + continue; + } - if (match_from < selection_begin || match_to > selection_end) - continue; + // replace but adjust selection bounds + text_edit->insert_text_at_cursor(replace_text); + if (match_to.x == selection_end.x) { + selection_end.y += replace_text.length() - search_text_len; + } - // replace but adjust selection bounds - text_edit->insert_text_at_cursor(replace_text); - if (match_to.x == selection_end.x) - selection_end.y += replace_text.length() - search_text_len; - } else { - // just replace - text_edit->insert_text_at_cursor(replace_text); - } + } else { + // just replace + text_edit->insert_text_at_cursor(replace_text); + } - rc++; + rc++; + } while (search_next()); } text_edit->end_complex_operation(); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 7f76cf1af2..f3be02a8c7 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -1172,7 +1172,12 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) { class_desc->push_indent(1); Vector<DocData::ConstantDoc> enum_list = E->get(); + Map<String, int> enumValuesContainer; + int enumStartingLine = enum_line[E->key()]; + for (int i = 0; i < enum_list.size(); i++) { + if (cd.name == "@GlobalScope") + enumValuesContainer[enum_list[i].name] = enumStartingLine; class_desc->push_font(doc_code_font); class_desc->push_color(headline_color); @@ -1200,6 +1205,9 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) { class_desc->add_newline(); } + if (cd.name == "@GlobalScope") + enum_values_line[E->key()] = enumValuesContainer; + class_desc->pop(); class_desc->add_newline(); @@ -1485,21 +1493,32 @@ void EditorHelp::_help_callback(const String &p_topic) { if (method_line.has(name)) line = method_line[name]; } else if (what == "class_property") { - if (property_line.has(name)) line = property_line[name]; } else if (what == "class_enum") { - if (enum_line.has(name)) line = enum_line[name]; } else if (what == "class_theme_item") { - if (theme_property_line.has(name)) line = theme_property_line[name]; } else if (what == "class_constant") { - if (constant_line.has(name)) line = constant_line[name]; + } else if (what == "class_global") { + if (constant_line.has(name)) + line = constant_line[name]; + else { + Map<String, Map<String, int> >::Element *iter = enum_values_line.front(); + while (true) { + if (iter->value().has(name)) { + line = iter->value()[name]; + break; + } else if (iter == enum_values_line.back()) + break; + else + iter = iter->next(); + } + } } class_desc->call_deferred("scroll_to_line", line); diff --git a/editor/editor_help.h b/editor/editor_help.h index aa84aa611f..0f93e1b55b 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -152,6 +152,7 @@ class EditorHelp : public VBoxContainer { Map<String, int> theme_property_line; Map<String, int> constant_line; Map<String, int> enum_line; + Map<String, Map<String, int> > enum_values_line; int description_line; RichTextLabel *class_desc; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 7936cf446f..0bca78b493 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -470,24 +470,28 @@ void EditorNode::_fs_changed() { } if (preset.is_null()) { String err = "Unknown export preset: " + export_defer.preset; - ERR_PRINT(err.utf8().get_data()); + ERR_PRINTS(err); } else { Ref<EditorExportPlatform> platform = preset->get_platform(); if (platform.is_null()) { String err = "Preset \"" + export_defer.preset + "\" doesn't have a platform."; - ERR_PRINT(err.utf8().get_data()); + ERR_PRINTS(err); } else { // ensures export_project does not loop infinitely, because notifications may // come during the export export_defer.preset = ""; + Error err; if (!preset->is_runnable() && (export_defer.path.ends_with(".pck") || export_defer.path.ends_with(".zip"))) { if (export_defer.path.ends_with(".zip")) { - platform->save_zip(preset, export_defer.path); + err = platform->save_zip(preset, export_defer.path); } else if (export_defer.path.ends_with(".pck")) { - platform->save_pack(preset, export_defer.path); + err = platform->save_pack(preset, export_defer.path); } } else { - platform->export_project(preset, export_defer.debug, export_defer.path, /*p_flags*/ 0); + err = platform->export_project(preset, export_defer.debug, export_defer.path, /*p_flags*/ 0); + } + if (err != OK) { + ERR_PRINTS(vformat(TTR("Project export failed with error code %d."), (int)err)); } } } diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index b10f785a28..3582379e34 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -626,8 +626,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("font_color_disabled", "PopupMenu", font_color_disabled); theme->set_icon("checked", "PopupMenu", theme->get_icon("GuiChecked", "EditorIcons")); theme->set_icon("unchecked", "PopupMenu", theme->get_icon("GuiUnchecked", "EditorIcons")); - theme->set_icon("radio_checked", "PopupMenu", theme->get_icon("GuiChecked", "EditorIcons")); - theme->set_icon("radio_unchecked", "PopupMenu", theme->get_icon("GuiUnchecked", "EditorIcons")); + theme->set_icon("radio_checked", "PopupMenu", theme->get_icon("GuiRadioChecked", "EditorIcons")); + theme->set_icon("radio_unchecked", "PopupMenu", theme->get_icon("GuiRadioUnchecked", "EditorIcons")); theme->set_icon("submenu", "PopupMenu", theme->get_icon("ArrowRight", "EditorIcons")); theme->set_icon("visibility_hidden", "PopupMenu", theme->get_icon("GuiVisibilityHidden", "EditorIcons")); theme->set_icon("visibility_visible", "PopupMenu", theme->get_icon("GuiVisibilityVisible", "EditorIcons")); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 9bfa50148f..d5783c9b1a 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1034,8 +1034,19 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path) { void FileSystemDock::_file_option(int p_option) { switch (p_option) { case FILE_SHOW_IN_EXPLORER: { - String dir = ProjectSettings::get_singleton()->globalize_path(this->path); - OS::get_singleton()->shell_open(String("file://") + dir); + + String path = this->path; + + // first try to grab directory from selected file, so that it works for searched files + int idx = files->get_current(); + + if (idx >= 0 && idx < files->get_item_count()) { + path = files->get_item_metadata(idx); + path = path.get_base_dir(); + } + + path = ProjectSettings::get_singleton()->globalize_path(path); + OS::get_singleton()->shell_open(String("file://") + path); } break; case FILE_OPEN: { for (int i = 0; i < files->get_item_count(); i++) { @@ -1660,6 +1671,8 @@ void FileSystemDock::_files_gui_input(Ref<InputEvent> p_event) { _file_option(FILE_COPY_PATH); } else if (ED_IS_SHORTCUT("filesystem_dock/delete", p_event)) { _file_option(FILE_REMOVE); + } else if (ED_IS_SHORTCUT("filesystem_dock/rename", p_event)) { + _file_option(FILE_RENAME); } } } @@ -1770,6 +1783,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { ED_SHORTCUT("filesystem_dock/copy_path", TTR("Copy Path"), KEY_MASK_CMD | KEY_C); ED_SHORTCUT("filesystem_dock/duplicate", TTR("Duplicate..."), KEY_MASK_CMD | KEY_D); ED_SHORTCUT("filesystem_dock/delete", TTR("Delete"), KEY_DELETE); + ED_SHORTCUT("filesystem_dock/rename", TTR("Rename")); HBoxContainer *toolbar_hbc = memnew(HBoxContainer); add_child(toolbar_hbc); diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp index 863b13cbd7..c1e897a04c 100644 --- a/editor/import/editor_import_collada.cpp +++ b/editor/import/editor_import_collada.cpp @@ -480,7 +480,7 @@ Error ColladaImport::_create_material(const String &p_target) { } } - float roughness = Math::sqrt(1.0 - ((Math::log(effect.shininess) / Math::log(2.0)) / 8.0)); //not very right.. + float roughness = (effect.shininess - 1.0) / 510; material->set_roughness(roughness); if (effect.double_sided) { diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index 321d29f2f6..af79f9946a 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -1732,14 +1732,19 @@ void EditorSceneImporterGLTF::_generate_bone(GLTFState &state, int p_node, Vecto for (int i = 0; i < n->joints.size(); i++) { ERR_FAIL_COND(n->joints[i].skin < 0); - int bone_index = skeletons[n->joints[i].skin]->get_bone_count(); - skeletons[n->joints[i].skin]->add_bone(n->name); + int bone_index = n->joints[i].bone; + + Skeleton *s = skeletons[n->joints[i].skin]; + while (s->get_bone_count() <= bone_index) { + s->add_bone("Bone " + itos(s->get_bone_count())); + } + if (p_parent_bones.size()) { - skeletons[n->joints[i].skin]->set_bone_parent(bone_index, p_parent_bones[i]); + s->set_bone_parent(bone_index, p_parent_bones[i]); } - skeletons[n->joints[i].skin]->set_bone_rest(bone_index, state.skins[n->joints[i].skin].bones[n->joints[i].bone].inverse_bind.affine_inverse()); + s->set_bone_rest(bone_index, state.skins[n->joints[i].skin].bones[n->joints[i].bone].inverse_bind.affine_inverse()); - n->godot_nodes.push_back(skeletons[n->joints[i].skin]); + n->godot_nodes.push_back(s); n->joints[i].godot_bone_index = bone_index; parent_bones.push_back(bone_index); } diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 04c9246aed..7612f18aff 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -1679,10 +1679,10 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay onion_skinning->get_popup()->add_separator(); onion_skinning->get_popup()->add_item(TTR("Depth"), -1); onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true); - onion_skinning->get_popup()->add_check_item(TTR("1 step"), ONION_SKINNING_1_STEP); + onion_skinning->get_popup()->add_radio_check_item(TTR("1 step"), ONION_SKINNING_1_STEP); onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true); - onion_skinning->get_popup()->add_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS); - onion_skinning->get_popup()->add_check_item(TTR("3 steps"), ONION_SKINNING_3_STEPS); + onion_skinning->get_popup()->add_radio_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS); + onion_skinning->get_popup()->add_radio_check_item(TTR("3 steps"), ONION_SKINNING_3_STEPS); onion_skinning->get_popup()->add_separator(); onion_skinning->get_popup()->add_check_item(TTR("Differences Only"), ONION_SKINNING_DIFFERENCES_ONLY); onion_skinning->get_popup()->add_check_item(TTR("Force White Modulate"), ONION_SKINNING_FORCE_WHITE_MODULATE); diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index f0e186e4b0..37213c1866 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -756,6 +756,7 @@ void AnimationTreeEditor::_gui_input(Ref<InputEvent> p_event) { if (rclick_type == CLICK_INPUT_SLOT || rclick_type == CLICK_OUTPUT_SLOT) { node_popup->clear(); + node_popup->set_size(Size2(1, 1)); node_popup->add_item(TTR("Disconnect"), NODE_DISCONNECT); if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) { node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); @@ -774,6 +775,7 @@ void AnimationTreeEditor::_gui_input(Ref<InputEvent> p_event) { if (rclick_type == CLICK_NODE) { node_popup->clear(); + node_popup->set_size(Size2(1, 1)); node_popup->add_item(TTR("Rename"), NODE_RENAME); node_popup->add_item(TTR("Remove"), NODE_ERASE); if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 67323fa753..b8d0958c99 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -210,6 +210,7 @@ void CanvasItemEditor::_snap_other_nodes(Point2 p_value, Point2 &r_current_snap, Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const CanvasItem *p_canvas_item, unsigned int p_forced_modes) { bool snapped[2] = { false, false }; + bool is_snap_active = snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL); // Smart snap using the canvas position Vector2 output = p_target; @@ -219,7 +220,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const rotation = p_canvas_item->get_global_transform_with_canvas().get_rotation(); // Parent sides and center - if ((snap_active && snap_node_parent && (p_modes & SNAP_NODE_PARENT)) || (p_forced_modes & SNAP_NODE_PARENT)) { + if ((is_snap_active && snap_node_parent && (p_modes & SNAP_NODE_PARENT)) || (p_forced_modes & SNAP_NODE_PARENT)) { Point2 begin; Point2 end; bool can_snap = false; @@ -241,7 +242,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const } // Self anchors - if ((snap_active && snap_node_anchors && (p_modes & SNAP_NODE_ANCHORS)) || (p_forced_modes & SNAP_NODE_ANCHORS)) { + if ((is_snap_active && snap_node_anchors && (p_modes & SNAP_NODE_ANCHORS)) || (p_forced_modes & SNAP_NODE_ANCHORS)) { if (const Control *c = Object::cast_to<Control>(p_canvas_item)) { Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_LEFT), c->get_anchor(MARGIN_TOP)))); Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_RIGHT), c->get_anchor(MARGIN_BOTTOM)))); @@ -251,7 +252,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const } // Self sides - if ((snap_active && snap_node_sides && (p_modes & SNAP_NODE_SIDES)) || (p_forced_modes & SNAP_NODE_SIDES)) { + if ((is_snap_active && snap_node_sides && (p_modes & SNAP_NODE_SIDES)) || (p_forced_modes & SNAP_NODE_SIDES)) { Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position()); Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size()); _snap_if_closer_point(p_target, begin, output, snapped, rotation); @@ -259,18 +260,18 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const } // Self center - if ((snap_active && snap_node_center && (p_modes & SNAP_NODE_CENTER)) || (p_forced_modes & SNAP_NODE_CENTER)) { + if ((is_snap_active && snap_node_center && (p_modes & SNAP_NODE_CENTER)) || (p_forced_modes & SNAP_NODE_CENTER)) { Point2 center = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size() / 2.0); _snap_if_closer_point(p_target, center, output, snapped, rotation); } } // Other nodes sides - if ((snap_active && snap_other_nodes && (p_modes & SNAP_OTHER_NODES)) || (p_forced_modes & SNAP_OTHER_NODES)) { + if ((is_snap_active && snap_other_nodes && (p_modes & SNAP_OTHER_NODES)) || (p_forced_modes & SNAP_OTHER_NODES)) { _snap_other_nodes(p_target, output, snapped, get_tree()->get_edited_scene_root(), p_canvas_item); } - if (((snap_active && snap_guides && (p_modes & SNAP_GUIDES)) || (p_forced_modes & SNAP_GUIDES)) && fmod(rotation, (real_t)360.0) == 0.0) { + if (((is_snap_active && snap_guides && (p_modes & SNAP_GUIDES)) || (p_forced_modes & SNAP_GUIDES)) && fmod(rotation, (real_t)360.0) == 0.0) { // Guides if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_vertical_guides_")) { Array vguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_vertical_guides_"); @@ -287,7 +288,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const } } - if (((snap_active && snap_grid && (p_modes & SNAP_GRID)) || (p_forced_modes & SNAP_GRID)) && fmod(rotation, (real_t)360.0) == 0.0) { + if (((is_snap_active && snap_grid && (p_modes & SNAP_GRID)) || (p_forced_modes & SNAP_GRID)) && fmod(rotation, (real_t)360.0) == 0.0) { // Grid Point2 offset = grid_offset; if (snap_relative) { @@ -313,7 +314,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const } float CanvasItemEditor::snap_angle(float p_target, float p_start) const { - return (snap_rotation && snap_rotation_step != 0) ? Math::stepify(p_target - snap_rotation_offset, snap_rotation_step) + snap_rotation_offset : p_target; + return (((snap_active || snap_rotation) ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL)) && snap_rotation_step != 0) ? Math::stepify(p_target - snap_rotation_offset, snap_rotation_step) + snap_rotation_offset : p_target; } void CanvasItemEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) { @@ -1234,26 +1235,26 @@ bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) { switch (drag_type) { case DRAG_ANCHOR_TOP_LEFT: - if (!use_single_axis || (use_single_axis && !use_y)) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, false); - if (!use_single_axis || (use_single_axis && use_y)) control->set_anchor(MARGIN_TOP, new_anchor.y, false, false); + if (!use_single_axis || !use_y) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, false); + if (!use_single_axis || use_y) control->set_anchor(MARGIN_TOP, new_anchor.y, false, false); break; case DRAG_ANCHOR_TOP_RIGHT: - if (!use_single_axis || (use_single_axis && !use_y)) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, false); - if (!use_single_axis || (use_single_axis && use_y)) control->set_anchor(MARGIN_TOP, new_anchor.y, false, false); + if (!use_single_axis || !use_y) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, false); + if (!use_single_axis || use_y) control->set_anchor(MARGIN_TOP, new_anchor.y, false, false); break; case DRAG_ANCHOR_BOTTOM_RIGHT: - if (!use_single_axis || (use_single_axis && !use_y)) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, false); - if (!use_single_axis || (use_single_axis && use_y)) control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, false); + if (!use_single_axis || !use_y) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, false); + if (!use_single_axis || use_y) control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, false); break; case DRAG_ANCHOR_BOTTOM_LEFT: - if (!use_single_axis || (use_single_axis && !use_y)) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, false); - if (!use_single_axis || (use_single_axis && use_y)) control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, false); + if (!use_single_axis || !use_y) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, false); + if (!use_single_axis || use_y) control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, false); break; case DRAG_ANCHOR_ALL: - if (!use_single_axis || (use_single_axis && !use_y)) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, true); - if (!use_single_axis || (use_single_axis && !use_y)) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, true); - if (!use_single_axis || (use_single_axis && use_y)) control->set_anchor(MARGIN_TOP, new_anchor.y, false, true); - if (!use_single_axis || (use_single_axis && use_y)) control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, true); + if (!use_single_axis || !use_y) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, true); + if (!use_single_axis || !use_y) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, true); + if (!use_single_axis || use_y) control->set_anchor(MARGIN_TOP, new_anchor.y, false, true); + if (!use_single_axis || use_y) control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, true); break; default: break; @@ -1671,8 +1672,6 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { selection_menu_additive_selection = b->get_shift(); selection_menu->set_global_position(b->get_global_position()); selection_menu->popup(); - selection_menu->call_deferred("grab_click_focus"); - selection_menu->set_invalidate_click_until_motion(); return true; } } @@ -1974,10 +1973,11 @@ void CanvasItemEditor::_draw_rulers() { Color font_color = get_color("font_color", "Editor"); font_color.a = 0.8; Ref<Font> font = get_font("rulers", "EditorFonts"); + bool is_snap_active = snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL); // The rule transform Transform2D ruler_transform = Transform2D(); - if (show_grid || (snap_active && snap_grid)) { + if (show_grid || (is_snap_active && snap_grid)) { List<CanvasItem *> selection = _get_edited_canvas_items(); if (snap_relative && selection.size() > 0) { ruler_transform.translate(_get_encompassing_rect_from_list(selection).position); diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp index 5020f5b851..8b44f672b0 100644 --- a/editor/plugins/item_list_editor_plugin.cpp +++ b/editor/plugins/item_list_editor_plugin.cpp @@ -42,9 +42,18 @@ bool ItemListPlugin::_set(const StringName &p_name, const Variant &p_value) { set_item_text(idx, p_value); else if (what == "icon") set_item_icon(idx, p_value); - else if (what == "checkable") - set_item_checkable(idx, p_value); - else if (what == "checked") + else if (what == "checkable") { + // This keeps compatibility to/from versions where this property was a boolean, before radio buttons + switch ((int)p_value) { + case 0: + case 1: + set_item_checkable(idx, p_value); + break; + case 2: + set_item_radio_checkable(idx, true); + break; + } + } else if (what == "checked") set_item_checked(idx, p_value); else if (what == "id") set_item_id(idx, p_value); @@ -68,9 +77,14 @@ bool ItemListPlugin::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_item_text(idx); else if (what == "icon") r_ret = get_item_icon(idx); - else if (what == "checkable") - r_ret = is_item_checkable(idx); - else if (what == "checked") + else if (what == "checkable") { + // This keeps compatibility to/from versions where this property was a boolean, before radio buttons + if (!is_item_checkable(idx)) { + r_ret = 0; + } else { + r_ret = is_item_radio_checkable(idx) ? 2 : 1; + } + } else if (what == "checked") r_ret = is_item_checked(idx); else if (what == "id") r_ret = get_item_id(idx); @@ -95,7 +109,7 @@ void ItemListPlugin::_get_property_list(List<PropertyInfo> *p_list) const { int flags = get_flags(); if (flags & FLAG_CHECKABLE) { - p_list->push_back(PropertyInfo(Variant::BOOL, base + "checkable")); + p_list->push_back(PropertyInfo(Variant::BOOL, base + "checkable", PROPERTY_HINT_ENUM, "No,As checkbox,As radio button")); p_list->push_back(PropertyInfo(Variant::BOOL, base + "checked")); } diff --git a/editor/plugins/item_list_editor_plugin.h b/editor/plugins/item_list_editor_plugin.h index bd7d3db10d..d6a071b9b9 100644 --- a/editor/plugins/item_list_editor_plugin.h +++ b/editor/plugins/item_list_editor_plugin.h @@ -74,7 +74,9 @@ public: virtual Ref<Texture> get_item_icon(int p_idx) const { return Ref<Texture>(); }; virtual void set_item_checkable(int p_idx, bool p_check) {} + virtual void set_item_radio_checkable(int p_idx, bool p_check) {} virtual bool is_item_checkable(int p_idx) const { return false; }; + virtual bool is_item_radio_checkable(int p_idx) const { return false; }; virtual void set_item_checked(int p_idx, bool p_checked) {} virtual bool is_item_checked(int p_idx) const { return false; }; @@ -145,7 +147,9 @@ public: virtual Ref<Texture> get_item_icon(int p_idx) const { return pp->get_item_icon(p_idx); } virtual void set_item_checkable(int p_idx, bool p_check) { pp->set_item_as_checkable(p_idx, p_check); } + virtual void set_item_radio_checkable(int p_idx, bool p_check) { pp->set_item_as_radio_checkable(p_idx, p_check); } virtual bool is_item_checkable(int p_idx) const { return pp->is_item_checkable(p_idx); } + virtual bool is_item_radio_checkable(int p_idx) const { return pp->is_item_radio_checkable(p_idx); } virtual void set_item_checked(int p_idx, bool p_checked) { pp->set_item_checked(p_idx, p_checked); } virtual bool is_item_checked(int p_idx) const { return pp->is_item_checked(p_idx); } diff --git a/editor/plugins/mesh_instance_editor_plugin.cpp b/editor/plugins/mesh_instance_editor_plugin.cpp index cb5f7ba76c..7ea2b27744 100644 --- a/editor/plugins/mesh_instance_editor_plugin.cpp +++ b/editor/plugins/mesh_instance_editor_plugin.cpp @@ -344,6 +344,10 @@ void MeshInstanceEditor::_create_outline_mesh() { err_dialog->set_text(TTR("Mesh has not surface to create outlines from!")); err_dialog->popup_centered_minsize(); return; + } else if (mesh->get_surface_count() == 1 && mesh->surface_get_primitive_type(0) != Mesh::PRIMITIVE_TRIANGLES) { + err_dialog->set_text(TTR("Mesh primitive type is not PRIMITIVE_TRIANGLES!")); + err_dialog->popup_centered_minsize(); + return; } Ref<Mesh> mesho = mesh->create_outline(outline_size->get_value()); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 09388870f1..2ce36ee8d5 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -1778,6 +1778,20 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool } ERR_FAIL_COND_V(!se, false); + bool highlighter_set = false; + for (int i = 0; i < syntax_highlighters_func_count; i++) { + SyntaxHighlighter *highlighter = syntax_highlighters_funcs[i](); + se->add_syntax_highlighter(highlighter); + + if (!highlighter_set) { + List<String> languages = highlighter->get_supported_languages(); + if (languages.find(p_script->get_language()->get_name())) { + se->set_syntax_highlighter(highlighter); + highlighter_set = true; + } + } + } + tab_container->add_child(se); se->set_edited_script(p_script); se->set_tooltip_request_func("_get_debug_tooltip", this); @@ -2494,6 +2508,14 @@ void ScriptEditor::_open_script_request(const String &p_path) { } } +int ScriptEditor::syntax_highlighters_func_count = 0; +CreateSyntaxHighlighterFunc ScriptEditor::syntax_highlighters_funcs[ScriptEditor::SYNTAX_HIGHLIGHTER_FUNC_MAX]; + +void ScriptEditor::register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func) { + ERR_FAIL_COND(syntax_highlighters_func_count == SYNTAX_HIGHLIGHTER_FUNC_MAX); + syntax_highlighters_funcs[syntax_highlighters_func_count++] = p_func; +} + int ScriptEditor::script_editor_func_count = 0; CreateScriptEditorFunc ScriptEditor::script_editor_funcs[ScriptEditor::SCRIPT_EDITOR_FUNC_MAX]; diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index bcc604d990..f947351089 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -80,6 +80,9 @@ protected: static void _bind_methods(); public: + virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter) = 0; + virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter) = 0; + virtual void apply_code() = 0; virtual Ref<Script> get_edited_script() const = 0; virtual Vector<String> get_functions() = 0; @@ -112,6 +115,7 @@ public: ScriptEditorBase() {} }; +typedef SyntaxHighlighter *(*CreateSyntaxHighlighterFunc)(); typedef ScriptEditorBase *(*CreateScriptEditorFunc)(const Ref<Script> &p_script); class EditorScriptCodeCompletionCache; @@ -214,12 +218,16 @@ class ScriptEditor : public PanelContainer { ToolButton *script_forward; enum { - SCRIPT_EDITOR_FUNC_MAX = 32 + SCRIPT_EDITOR_FUNC_MAX = 32, + SYNTAX_HIGHLIGHTER_FUNC_MAX = 32 }; static int script_editor_func_count; static CreateScriptEditorFunc script_editor_funcs[SCRIPT_EDITOR_FUNC_MAX]; + static int syntax_highlighters_func_count; + static CreateSyntaxHighlighterFunc syntax_highlighters_funcs[SYNTAX_HIGHLIGHTER_FUNC_MAX]; + struct ScriptHistory { Control *control; @@ -399,7 +407,9 @@ public: ScriptEditorDebugger *get_debugger() { return debugger; } void set_live_auto_reload_running_scripts(bool p_enabled); + static void register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func); static void register_create_script_editor_function(CreateScriptEditorFunc p_func); + ScriptEditor(EditorNode *p_editor); ~ScriptEditor(); }; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index c8ea2f79fe..711a313902 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -573,6 +573,7 @@ void ScriptTextEditor::set_edited_script(const Ref<Script> &p_script) { ERR_FAIL_COND(!script.is_null()); script = p_script; + _set_theme_for_script(); code_editor->get_text_edit()->set_text(script->get_source_code()); code_editor->get_text_edit()->clear_undo_history(); @@ -580,8 +581,6 @@ void ScriptTextEditor::set_edited_script(const Ref<Script> &p_script) { emit_signal("name_changed"); code_editor->update_line_and_column(); - - _set_theme_for_script(); } void ScriptTextEditor::_validate_script() { @@ -789,6 +788,26 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c emit_signal("go_to_help", "class_method:" + result.class_name + ":" + result.class_member); } break; + case ScriptLanguage::LookupResult::RESULT_CLASS_ENUM: { + + StringName cname = result.class_name; + StringName success; + while (true) { + success = ClassDB::get_integer_constant_enum(cname, result.class_member, true); + if (success != StringName()) { + result.class_name = cname; + cname = ClassDB::get_parent_class(cname); + } else { + break; + } + } + + emit_signal("go_to_help", "class_enum:" + result.class_name + ":" + result.class_member); + + } break; + case ScriptLanguage::LookupResult::RESULT_CLASS_TBD_GLOBALSCOPE: { + emit_signal("go_to_help", "class_global:" + result.class_name + ":" + result.class_member); + } break; } } } @@ -1245,11 +1264,26 @@ void ScriptTextEditor::_edit_option(int p_op) { } } +void ScriptTextEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) { + highlighters[p_highlighter->get_name()] = p_highlighter; + highlighter_menu->get_popup()->add_item(p_highlighter->get_name()); +} + +void ScriptTextEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) { + TextEdit *te = code_editor->get_text_edit(); + te->_set_syntax_highlighting(p_highlighter); +} + +void ScriptTextEditor::_change_syntax_highlighter(int p_idx) { + set_syntax_highlighter(highlighters[highlighter_menu->get_popup()->get_item_text(p_idx)]); +} + void ScriptTextEditor::_bind_methods() { ClassDB::bind_method("_validate_script", &ScriptTextEditor::_validate_script); ClassDB::bind_method("_load_theme_settings", &ScriptTextEditor::_load_theme_settings); ClassDB::bind_method("_breakpoint_toggled", &ScriptTextEditor::_breakpoint_toggled); + ClassDB::bind_method("_change_syntax_highlighter", &ScriptTextEditor::_change_syntax_highlighter); ClassDB::bind_method("_edit_option", &ScriptTextEditor::_edit_option); ClassDB::bind_method("_goto_line", &ScriptTextEditor::_goto_line); ClassDB::bind_method("_lookup_symbol", &ScriptTextEditor::_lookup_symbol); @@ -1635,6 +1669,14 @@ ScriptTextEditor::ScriptTextEditor() { edit_hb->add_child(edit_menu); + highlighters["Standard"] = NULL; + + highlighter_menu = memnew(MenuButton); + highlighter_menu->set_text(TTR("Syntax Highlighter")); + highlighter_menu->get_popup()->add_item("Standard"); + highlighter_menu->get_popup()->connect("id_pressed", this, "_change_syntax_highlighter"); + edit_hb->add_child(highlighter_menu); + quick_open = memnew(ScriptEditorQuickOpen); add_child(quick_open); quick_open->connect("goto_line", this, "_goto_line"); diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index 22e8fbce25..eb52d2593a 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -49,6 +49,7 @@ class ScriptTextEditor : public ScriptEditorBase { HBoxContainer *edit_hb; MenuButton *edit_menu; + MenuButton *highlighter_menu; MenuButton *search_menu; PopupMenu *context_menu; @@ -125,6 +126,9 @@ protected: void _notification(int p_what); static void _bind_methods(); + Map<String, SyntaxHighlighter *> highlighters; + void _change_syntax_highlighter(int p_idx); + void _edit_option(int p_op); void _make_context_menu(bool p_selection, bool p_color, bool p_can_fold, bool p_is_folded); void _text_edit_gui_input(const Ref<InputEvent> &ev); @@ -145,6 +149,9 @@ protected: void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); public: + virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter); + virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter); + virtual void apply_code(); virtual Ref<Script> get_edited_script() const; virtual Vector<String> get_functions(); diff --git a/editor/plugins/shader_graph_editor_plugin.cpp b/editor/plugins/shader_graph_editor_plugin.cpp index 59085c203f..e1d28cc215 100644 --- a/editor/plugins/shader_graph_editor_plugin.cpp +++ b/editor/plugins/shader_graph_editor_plugin.cpp @@ -2769,8 +2769,6 @@ void ShaderGraphEditor::_popup_requested(const Vector2 &p_position) popup->set_global_position(p_position); popup->set_size( Size2( 200, 0) ); popup->popup(); - popup->call_deferred("grab_click_focus"); - popup->set_invalidate_click_until_motion(); } void ShaderGraphEditor::_notification(int p_what) { diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index e484140527..9d7c582e0e 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -886,8 +886,6 @@ void SpatialEditorViewport::_list_select(Ref<InputEventMouseButton> b) { selection_menu->set_global_position(b->get_global_position()); selection_menu->popup(); - selection_menu->call_deferred("grab_click_focus"); - selection_menu->set_invalidate_click_until_motion(); } } @@ -3353,14 +3351,14 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/front_view"), VIEW_FRONT); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/rear_view"), VIEW_REAR); view_menu->get_popup()->add_separator(); - view_menu->get_popup()->add_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE); - view_menu->get_popup()->add_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL); + view_menu->get_popup()->add_radio_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE); + view_menu->get_popup()->add_radio_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true); view_menu->get_popup()->add_separator(); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_normal", TTR("Display Normal")), VIEW_DISPLAY_NORMAL); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS); + view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_normal", TTR("Display Normal")), VIEW_DISPLAY_NORMAL); + view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME); + view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW); + view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), true); view_menu->get_popup()->add_separator(); view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_environment", TTR("View Environment")), VIEW_ENVIRONMENT); @@ -5111,12 +5109,12 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { accept = memnew(AcceptDialog); editor->get_gui_base()->add_child(accept); - p->add_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT); - p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS); - p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT); - p->add_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"), KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS); - p->add_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT); - p->add_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"), KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS); p->add_separator(); p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index d8d0a6f013..71a3c90795 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -458,8 +458,6 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) { List<StringName> anim_names; - anim_names.sort_custom<StringName::AlphCompare>(); - frames->get_animation_list(&anim_names); anim_names.sort_custom<StringName::AlphCompare>(); diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 4367fe8976..5ba3931689 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -805,12 +805,10 @@ TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) { snap_mode_button->set_text(TTR("<None>")); PopupMenu *p = snap_mode_button->get_popup(); p->set_hide_on_checkable_item_selection(false); - p->add_item(TTR("<None>"), 0); - p->add_item(TTR("Pixel Snap"), 1); - p->add_item(TTR("Grid Snap"), 2); - p->add_item(TTR("Auto Slice"), 3); - for (int i = 0; i < 4; i++) - p->set_item_as_checkable(i, true); + p->add_radio_check_item(TTR("<None>"), 0); + p->add_radio_check_item(TTR("Pixel Snap"), 1); + p->add_radio_check_item(TTR("Grid Snap"), 2); + p->add_radio_check_item(TTR("Auto Slice"), 3); p->set_item_checked(0, true); p->connect("id_pressed", this, "_set_snap_mode"); hb_grid = memnew(HBoxContainer); diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 295d9439ad..550dfb3ae1 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -692,6 +692,10 @@ ThemeEditor::ThemeEditor() { test_menu_button->get_popup()->add_check_item(TTR("Check Item")); test_menu_button->get_popup()->add_check_item(TTR("Checked Item")); test_menu_button->get_popup()->set_item_checked(2, true); + test_menu_button->get_popup()->add_separator(); + test_menu_button->get_popup()->add_check_item(TTR("Radio Item")); + test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item")); + test_menu_button->get_popup()->set_item_checked(5, true); first_vb->add_child(test_menu_button); OptionButton *test_option_button = memnew(OptionButton); diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index c5c7272ed2..14c584fa35 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -78,6 +78,7 @@ void TileMapEditor::_notification(int p_what) { p->set_item_icon(p->get_item_index(OPTION_PAINTING), get_icon("Edit", "EditorIcons")); p->set_item_icon(p->get_item_index(OPTION_PICK_TILE), get_icon("ColorPick", "EditorIcons")); p->set_item_icon(p->get_item_index(OPTION_SELECT), get_icon("ToolSelect", "EditorIcons")); + p->set_item_icon(p->get_item_index(OPTION_MOVE), get_icon("ToolMove", "EditorIcons")); p->set_item_icon(p->get_item_index(OPTION_DUPLICATE), get_icon("Duplicate", "EditorIcons")); p->set_item_icon(p->get_item_index(OPTION_ERASE_SELECTION), get_icon("Remove", "EditorIcons")); @@ -156,6 +157,14 @@ void TileMapEditor::_menu_option(int p_option) { undo_redo->commit_action(); } break; + case OPTION_MOVE: { + + if (selection_active) { + _update_copydata(); + tool = TOOL_MOVING; + canvas_item_editor->update(); + } + } break; } } @@ -829,6 +838,29 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { copydata.clear(); canvas_item_editor->update(); + } else if (tool == TOOL_MOVING) { + + Point2 ofs = over_tile - rectangle.position; + + undo_redo->create_action(TTR("Move")); + undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data")); + for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) { + for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) { + + _set_cell(Point2i(j, i), TileMap::INVALID_CELL, false, false, false); + } + } + for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) { + + _set_cell(E->get().pos + ofs, E->get().cell, E->get().flip_h, E->get().flip_v, E->get().transpose); + } + undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data")); + undo_redo->commit_action(); + + copydata.clear(); + selection_active = false; + + canvas_item_editor->update(); } else if (tool == TOOL_SELECTING) { @@ -888,6 +920,16 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return true; } + if (tool == TOOL_MOVING) { + + tool = TOOL_NONE; + copydata.clear(); + + canvas_item_editor->update(); + + return true; + } + if (tool == TOOL_NONE) { paint_undo.clear(); @@ -1095,7 +1137,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (k->get_scancode() == KEY_ESCAPE) { - if (tool == TOOL_DUPLICATING) + if (tool == TOOL_DUPLICATING || tool == TOOL_MOVING) copydata.clear(); else if (tool == TOOL_SELECTING || selection_active) selection_active = false; @@ -1150,6 +1192,14 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return true; } } + if (ED_IS_SHORTCUT("tile_map_editor/move_selection", p_event)) { + if (selection_active) { + _update_copydata(); + tool = TOOL_MOVING; + canvas_item_editor->update(); + return true; + } + } if (ED_IS_SHORTCUT("tile_map_editor/find_tile", p_event)) { search_box->select_all(); search_box->grab_focus(); @@ -1159,18 +1209,21 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (ED_IS_SHORTCUT("tile_map_editor/mirror_x", p_event)) { flip_h = !flip_h; mirror_x->set_pressed(flip_h); + _update_transform_buttons(); canvas_item_editor->update(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/mirror_y", p_event)) { flip_v = !flip_v; mirror_y->set_pressed(flip_v); + _update_transform_buttons(); canvas_item_editor->update(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/transpose", p_event)) { transpose = !transpose; transp->set_pressed(transpose); + _update_transform_buttons(); canvas_item_editor->update(); return true; } @@ -1343,7 +1396,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { _draw_cell(id, Point2i(j, i), flip_h, flip_v, transpose, xform); } } - } else if (tool == TOOL_DUPLICATING) { + } else if (tool == TOOL_DUPLICATING || tool == TOOL_MOVING) { if (copydata.empty()) return; @@ -1590,6 +1643,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { p->add_item(TTR("Pick Tile"), OPTION_PICK_TILE, KEY_CONTROL); p->add_separator(); p->add_shortcut(ED_SHORTCUT("tile_map_editor/select", TTR("Select"), KEY_MASK_CMD + KEY_B), OPTION_SELECT); + p->add_shortcut(ED_SHORTCUT("tile_map_editor/move_selection", TTR("Move Selection"), KEY_MASK_CMD + KEY_M), OPTION_MOVE); p->add_shortcut(ED_SHORTCUT("tile_map_editor/duplicate_selection", TTR("Duplicate Selection"), KEY_MASK_CMD + KEY_D), OPTION_DUPLICATE); p->add_shortcut(ED_GET_SHORTCUT("tile_map_editor/erase_selection"), OPTION_ERASE_SELECTION); p->add_separator(); diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h index 2d582d030b..3257901c88 100644 --- a/editor/plugins/tile_map_editor_plugin.h +++ b/editor/plugins/tile_map_editor_plugin.h @@ -61,6 +61,7 @@ class TileMapEditor : public VBoxContainer { TOOL_BUCKET, TOOL_PICKING, TOOL_DUPLICATING, + TOOL_MOVING }; enum Options { @@ -72,6 +73,7 @@ class TileMapEditor : public VBoxContainer { OPTION_ERASE_SELECTION, OPTION_PAINTING, OPTION_FIX_INVALID, + OPTION_MOVE }; TileMap *node; diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index d5ec858c37..b13b238fd7 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -916,9 +916,6 @@ void SceneTreeDock::perform_node_renames(Node *p_base, List<Pair<NodePath, NodeP if (!r_rem_anims) r_rem_anims = &rem_anims; - if (!bool(EDITOR_DEF("editors/animation/autorename_animation_tracks", true))) - return; - if (!p_base) { p_base = edited_scene; @@ -927,7 +924,54 @@ void SceneTreeDock::perform_node_renames(Node *p_base, List<Pair<NodePath, NodeP if (!p_base) return; - if (Object::cast_to<AnimationPlayer>(p_base)) { + // Renaming node paths used in script instances + if (p_base->get_script_instance()) { + + ScriptInstance *si = p_base->get_script_instance(); + + if (si) { + + List<PropertyInfo> properties; + si->get_property_list(&properties); + + for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { + + String propertyname = E->get().name; + Variant p = p_base->get(propertyname); + if (p.get_type() == Variant::NODE_PATH) { + + // Goes through all paths to check if its matching + for (List<Pair<NodePath, NodePath> >::Element *E = p_renames->front(); E; E = E->next()) { + + NodePath root_path = p_base->get_path(); + + NodePath rel_path_old = root_path.rel_path_to(E->get().first); + + NodePath rel_path_new = E->get().second; + + // if not empty, get new relative path + if (E->get().second != NodePath()) { + rel_path_new = root_path.rel_path_to(E->get().second); + } + + // if old path detected, then it needs to be replaced with the new one + if (p == rel_path_old) { + + editor_data->get_undo_redo().add_do_property(p_base, propertyname, rel_path_new); + editor_data->get_undo_redo().add_undo_property(p_base, propertyname, rel_path_old); + + p_base->set(propertyname, rel_path_new); + break; + } + } + } + } + } + } + + bool autorename_animation_tracks = bool(EDITOR_DEF("editors/animation/autorename_animation_tracks", true)); + + if (autorename_animation_tracks && Object::cast_to<AnimationPlayer>(p_base)) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_base); List<StringName> anims; @@ -1158,7 +1202,30 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V editor_data->get_undo_redo().add_do_method(new_parent, "move_child", node, p_position_in_parent + inc); ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); + String old_name = former_names[ni]; String new_name = new_parent->validate_child_name(node); + + // name was modified, fix the path renames + if (old_name.casecmp_to(new_name) != 0) { + + // Fix the to name to have the new name + NodePath old_new_name = path_renames[ni].second; + NodePath new_path; + + Vector<StringName> unfixed_new_names = old_new_name.get_names(); + Vector<StringName> fixed_new_names; + + // Get last name and replace with fixed new name + for (int a = 0; a < (unfixed_new_names.size() - 1); a++) { + fixed_new_names.push_back(unfixed_new_names[a]); + } + fixed_new_names.push_back(new_name); + + NodePath fixed_node_path = NodePath(fixed_new_names, true); + + path_renames[ni].second = fixed_node_path; + } + editor_data->get_undo_redo().add_do_method(sed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, -1); editor_data->get_undo_redo().add_undo_method(sed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)) + "/" + new_name), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index()); @@ -1794,6 +1861,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { subresources.clear(); menu_subresources->clear(); + menu_subresources->set_size(Size2(1, 1)); _add_children_to_popup(selection.front()->get(), 0); if (menu->get_item_count() > 0) menu->add_separator(); diff --git a/main/SCsub b/main/SCsub index 9eddc4f5b6..e2bf03234f 100644 --- a/main/SCsub +++ b/main/SCsub @@ -121,7 +121,7 @@ def make_default_controller_mappings(target, source, env): g.write("\t\"{}\",\n".format(mapping)) g.write("#endif\n") - g.write("};\n") + g.write("\tNULL\n};\n") g.close() env.main_sources = [] diff --git a/main/tests/test_gui.cpp b/main/tests/test_gui.cpp index b7b6c21692..305b749717 100644 --- a/main/tests/test_gui.cpp +++ b/main/tests/test_gui.cpp @@ -185,6 +185,10 @@ public: popup->add_item("Popup"); popup->add_check_item("Check Popup"); popup->set_item_checked(4, true); + popup->add_separator(); + popup->add_radio_check_item("Option A"); + popup->set_item_checked(6, true); + popup->add_radio_check_item("Option B"); OptionButton *options = memnew(OptionButton); diff --git a/misc/dist/linux/godot.6 b/misc/dist/linux/godot.6 index 86abdf6ef2..26ebe01af7 100644 --- a/misc/dist/linux/godot.6 +++ b/misc/dist/linux/godot.6 @@ -59,7 +59,7 @@ Password for remote filesystem. Audio driver ('PulseAudio', 'ALSA'). .TP \fB\-\-video\-driver\fR <driver> -Video driver ('GLES3'). +Video driver ('GLES3', 'GLES2'). .SS "Display options:" .TP \fB\-f\fR, \fB\-\-fullscreen\fR diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp index 648919e612..ec78cddb6a 100644 --- a/modules/bullet/area_bullet.cpp +++ b/modules/bullet/area_bullet.cpp @@ -68,7 +68,8 @@ AreaBullet::AreaBullet() : } AreaBullet::~AreaBullet() { - remove_all_overlapping_instantly(); + // Call "remove_all_overlapping_instantly();" is not necessary because the exit + // signal are handled by godot, so just clear the array } void AreaBullet::dispatch_callbacks() { @@ -131,12 +132,13 @@ void AreaBullet::remove_all_overlapping_instantly() { overlappingObjects.clear(); } -void AreaBullet::remove_overlapping_instantly(CollisionObjectBullet *p_object) { +void AreaBullet::remove_overlapping_instantly(CollisionObjectBullet *p_object, bool p_notify) { CollisionObjectBullet *supportObject; for (int i = overlappingObjects.size() - 1; 0 <= i; --i) { supportObject = overlappingObjects[i].object; if (supportObject == p_object) { - call_event(supportObject, PhysicsServer::AREA_BODY_REMOVED); + if (p_notify) + call_event(supportObject, PhysicsServer::AREA_BODY_REMOVED); supportObject->on_exit_area(this); overlappingObjects.remove(i); break; diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h index 78136d574b..4104780de9 100644 --- a/modules/bullet/area_bullet.h +++ b/modules/bullet/area_bullet.h @@ -152,7 +152,7 @@ public: void remove_all_overlapping_instantly(); // Dispatch the callbacks and removes from overlapping list - void remove_overlapping_instantly(CollisionObjectBullet *p_object); + void remove_overlapping_instantly(CollisionObjectBullet *p_object, bool p_notify); virtual void on_collision_filters_change(); virtual void on_collision_checker_start() {} diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp index 34aff68a4a..77f8df34cb 100644 --- a/modules/bullet/collision_object_bullet.cpp +++ b/modules/bullet/collision_object_bullet.cpp @@ -68,12 +68,10 @@ CollisionObjectBullet::CollisionObjectBullet(Type p_type) : force_shape_reset(false) {} CollisionObjectBullet::~CollisionObjectBullet() { - // Remove all overlapping + // Remove all overlapping, notify is not required since godot take care of it for (int i = areasOverlapped.size() - 1; 0 <= i; --i) { - areasOverlapped[i]->remove_overlapping_instantly(this); + areasOverlapped[i]->remove_overlapping_instantly(this, /*Notify*/ false); } - // not required - // areasOverlapped.clear(); destroyBulletCollisionObject(); } diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp index 72c982bb0b..caa3d677dd 100644 --- a/modules/bullet/godot_result_callbacks.cpp +++ b/modules/bullet/godot_result_callbacks.cpp @@ -63,6 +63,9 @@ bool GodotClosestRayResultCallback::needsCollision(btBroadphaseProxy *proxy0) co } bool GodotAllConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const { + if (count >= m_resultMax) + return false; + const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask); if (needs) { btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject); @@ -70,6 +73,7 @@ bool GodotAllConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) con if (m_exclude->has(gObj->get_self())) { return false; } + return true; } else { return false; @@ -87,7 +91,7 @@ btScalar GodotAllConvexResultCallback::addSingleResult(btCollisionWorld::LocalCo result.collider = 0 == result.collider_id ? NULL : ObjectDB::get_instance(result.collider_id); ++count; - return count < m_resultMax; + return 1; // not used by bullet } bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const { @@ -181,6 +185,9 @@ btScalar GodotAllContactResultCallback::addSingleResult(btManifoldPoint &cp, con } bool GodotContactPairContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const { + if (m_count >= m_resultMax) + return false; + const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask); if (needs) { btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject); @@ -206,7 +213,7 @@ btScalar GodotContactPairContactResultCallback::addSingleResult(btManifoldPoint ++m_count; - return m_count < m_resultMax; + return 1; // Not used by bullet } bool GodotRestInfoContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const { @@ -252,20 +259,17 @@ btScalar GodotRestInfoContactResultCallback::addSingleResult(btManifoldPoint &cp m_collided = true; } - return cp.getDistance(); + return 1; // Not used by bullet } void GodotDeepPenetrationContactResultCallback::addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorldOnB, btScalar depth) { - // Has penetration - if (m_penetration_distance < ABS(depth)) { + if (m_penetration_distance > depth) { // Has penetration? bool isSwapped = m_manifoldPtr->getBody0() != m_body0Wrap->getCollisionObject(); - m_penetration_distance = depth; - m_pointCollisionObject = (isSwapped ? m_body0Wrap : m_body1Wrap)->getCollisionObject(); - m_other_compound_shape_index = isSwapped ? m_index1 : m_index0; + m_other_compound_shape_index = isSwapped ? m_index0 : m_index1; m_pointNormalWorld = isSwapped ? normalOnBInWorld * -1 : normalOnBInWorld; - m_pointWorld = isSwapped ? (pointInWorldOnB + normalOnBInWorld * depth) : pointInWorldOnB; + m_pointWorld = isSwapped ? (pointInWorldOnB + (normalOnBInWorld * depth)) : pointInWorldOnB; } } diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h index 60493d4788..363051f24c 100644 --- a/modules/bullet/godot_result_callbacks.h +++ b/modules/bullet/godot_result_callbacks.h @@ -185,21 +185,18 @@ struct GodotDeepPenetrationContactResultCallback : public btManifoldResult { btVector3 m_pointWorld; btScalar m_penetration_distance; int m_other_compound_shape_index; - const btCollisionObject *m_pointCollisionObject; GodotDeepPenetrationContactResultCallback(const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap) : btManifoldResult(body0Wrap, body1Wrap), - m_pointCollisionObject(NULL), m_penetration_distance(0), m_other_compound_shape_index(0) {} void reset() { - m_pointCollisionObject = NULL; m_penetration_distance = 0; } bool hasHit() { - return m_pointCollisionObject; + return m_penetration_distance < 0; } virtual void addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorldOnB, btScalar depth); diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp index 5d8d391bd9..76d9614465 100644 --- a/modules/bullet/shape_bullet.cpp +++ b/modules/bullet/shape_bullet.cpp @@ -125,14 +125,13 @@ btScaledBvhTriangleMeshShape *ShapeBullet::create_shape_concave(btBvhTriangleMes } } -btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size) { +btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) { const btScalar ignoredHeightScale(1); - const btScalar fieldHeight(500); // Meters const int YAxis = 1; // 0=X, 1=Y, 2=Z const bool flipQuadEdges = false; const void *heightsPtr = p_heights.read().ptr(); - return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, -fieldHeight, fieldHeight, YAxis, PHY_FLOAT, flipQuadEdges)); + return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, p_min_height, p_max_height, YAxis, PHY_FLOAT, flipQuadEdges)); } btRayShape *ShapeBullet::create_shape_ray(real_t p_length, bool p_slips_on_slope) { @@ -337,10 +336,10 @@ void ConcavePolygonShapeBullet::setup(PoolVector<Vector3> p_faces) { int src_face_count = faces.size(); if (0 < src_face_count) { - btTriangleMesh *shapeInterface = bulletnew(btTriangleMesh); - // It counts the faces and assert the array contains the correct number of vertices. ERR_FAIL_COND(src_face_count % 3); + + btTriangleMesh *shapeInterface = bulletnew(btTriangleMesh); src_face_count /= 3; PoolVector<Vector3>::Read r = p_faces.read(); const Vector3 *facesr = r.ptr(); @@ -387,19 +386,44 @@ void HeightMapShapeBullet::set_data(const Variant &p_data) { Dictionary d = p_data; ERR_FAIL_COND(!d.has("width")); ERR_FAIL_COND(!d.has("depth")); - ERR_FAIL_COND(!d.has("cell_size")); ERR_FAIL_COND(!d.has("heights")); + real_t l_min_height = 0.0; + real_t l_max_height = 0.0; + + // If specified, min and max height will be used as precomputed values + if (d.has("min_height")) + l_min_height = d["min_height"]; + if (d.has("max_height")) + l_max_height = d["max_height"]; + + ERR_FAIL_COND(l_min_height > l_max_height); + int l_width = d["width"]; int l_depth = d["depth"]; - real_t l_cell_size = d["cell_size"]; PoolVector<real_t> l_heights = d["heights"]; ERR_FAIL_COND(l_width <= 0); ERR_FAIL_COND(l_depth <= 0); - ERR_FAIL_COND(l_cell_size <= CMP_EPSILON); - ERR_FAIL_COND(l_heights.size() != (width * depth)); - setup(heights, width, depth, cell_size); + ERR_FAIL_COND(l_heights.size() != (l_width * l_depth)); + + // Compute min and max heights if not specified. + if (!d.has("min_height") && !d.has("max_height")) { + + PoolVector<real_t>::Read r = heights.read(); + int heights_size = heights.size(); + + for (int i = 0; i < heights_size; ++i) { + real_t h = r[i]; + + if (h < l_min_height) + l_min_height = h; + else if (h > l_max_height) + l_max_height = h; + } + } + + setup(l_heights, l_width, l_depth, l_min_height, l_max_height); } Variant HeightMapShapeBullet::get_data() const { @@ -410,8 +434,14 @@ PhysicsServer::ShapeType HeightMapShapeBullet::get_type() const { return PhysicsServer::SHAPE_HEIGHTMAP; } -void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size) { +void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) { + // TODO cell size must be tweaked using localScaling, which is a shared property for all Bullet shapes + { // Copy + + // TODO If Godot supported 16-bit integer image format, we could share the same memory block for heightfields + // without having to copy anything, optimizing memory and loading performance (Bullet only reads and doesn't take ownership of the data). + const int heights_size = p_heights.size(); heights.resize(heights_size); PoolVector<real_t>::Read p_heights_r = p_heights.read(); @@ -420,14 +450,16 @@ void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int heights_w[i] = p_heights_r[i]; } } + width = p_width; depth = p_depth; - cell_size = p_cell_size; + min_height = p_min_height; + max_height = p_max_height; notifyShapeChanged(); } btCollisionShape *HeightMapShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) { - btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, cell_size)); + btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, min_height, max_height)); cs->setLocalScaling(p_implicit_scale); prepare(cs); cs->setMargin(p_margin); diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h index 2acba90e36..abeea0f9ce 100644 --- a/modules/bullet/shape_bullet.h +++ b/modules/bullet/shape_bullet.h @@ -85,7 +85,7 @@ public: /// IMPORTANT: Remember to delete the shape interface by calling: delete my_shape->getMeshInterface(); static class btConvexPointCloudShape *create_shape_convex(btAlignedObjectArray<btVector3> &p_vertices, const btVector3 &p_local_scaling = btVector3(1, 1, 1)); static class btScaledBvhTriangleMeshShape *create_shape_concave(btBvhTriangleMeshShape *p_mesh_shape, const btVector3 &p_local_scaling = btVector3(1, 1, 1)); - static class btHeightfieldTerrainShape *create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size); + static class btHeightfieldTerrainShape *create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height); static class btRayShape *create_shape_ray(real_t p_length, bool p_slips_on_slope); }; @@ -199,7 +199,8 @@ public: PoolVector<real_t> heights; int width; int depth; - real_t cell_size; + real_t min_height; + real_t max_height; HeightMapShapeBullet(); @@ -209,7 +210,7 @@ public: virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: - void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size); + void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height); }; class RayShapeBullet : public ShapeBullet { diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index 8c15758e0f..51a76ff8c5 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -660,7 +660,10 @@ void SpaceBullet::check_ghost_overlaps() { // For each overlapping for (i = ghostOverlaps.size() - 1; 0 <= i; --i) { - if (!(ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_RIGID_BODY || ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_AREA)) + if (ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_AREA) { + if (!static_cast<AreaBullet *>(ghostOverlaps[i]->getUserPointer())->is_monitorable()) + continue; + } else if (ghostOverlaps[i]->getUserIndex() != CollisionObjectBullet::TYPE_RIGID_BODY) continue; otherObject = static_cast<RigidCollisionObjectBullet *>(ghostOverlaps[i]->getUserPointer()); @@ -1117,7 +1120,7 @@ bool SpaceBullet::RFP_convex_world_test(const btConvexShape *p_shapeA, const btC dispatcher->freeCollisionAlgorithm(algorithm); if (contactPointResult.hasHit()) { - r_delta_recover_movement += contactPointResult.m_pointNormalWorld * (contactPointResult.m_penetration_distance * p_recover_movement_scale); + r_delta_recover_movement += contactPointResult.m_pointNormalWorld * (contactPointResult.m_penetration_distance * -1 * p_recover_movement_scale); if (r_recover_result) { if (contactPointResult.m_penetration_distance < r_recover_result->penetration_distance) { diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index 42c3028f2c..897588385a 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -66,8 +66,169 @@ GDNativeLibrary::GDNativeLibrary() { GDNativeLibrary::~GDNativeLibrary() { } +bool GDNativeLibrary::_set(const StringName &p_name, const Variant &p_property) { + + String name = p_name; + + if (name.begins_with("entry/")) { + String key = name.substr(6, name.length() - 6); + + config_file->set_value("entry", key, p_property); + + set_config_file(config_file); + + return true; + } + + if (name.begins_with("dependency/")) { + String key = name.substr(11, name.length() - 11); + + config_file->set_value("dependencies", key, p_property); + + set_config_file(config_file); + + return true; + } + + return false; +} + +bool GDNativeLibrary::_get(const StringName &p_name, Variant &r_property) const { + String name = p_name; + + if (name.begins_with("entry/")) { + String key = name.substr(6, name.length() - 6); + + r_property = config_file->get_value("entry", key); + + return true; + } + + if (name.begins_with("dependency/")) { + String key = name.substr(11, name.length() - 11); + + r_property = config_file->get_value("dependencies", key); + + return true; + } + + return false; +} + +void GDNativeLibrary::_get_property_list(List<PropertyInfo> *p_list) const { + // set entries + List<String> entry_key_list; + + if (config_file->has_section("entry")) + config_file->get_section_keys("entry", &entry_key_list); + + for (List<String>::Element *E = entry_key_list.front(); E; E = E->next()) { + String key = E->get(); + + PropertyInfo prop; + + prop.type = Variant::STRING; + prop.name = "entry/" + key; + + p_list->push_back(prop); + } + + // set dependencies + List<String> dependency_key_list; + + if (config_file->has_section("dependencies")) + config_file->get_section_keys("dependencies", &dependency_key_list); + + for (List<String>::Element *E = dependency_key_list.front(); E; E = E->next()) { + String key = E->get(); + + PropertyInfo prop; + + prop.type = Variant::STRING; + prop.name = "dependency/" + key; + + p_list->push_back(prop); + } +} + +void GDNativeLibrary::set_config_file(Ref<ConfigFile> p_config_file) { + + set_singleton(p_config_file->get_value("general", "singleton", default_singleton)); + set_load_once(p_config_file->get_value("general", "load_once", default_load_once)); + set_symbol_prefix(p_config_file->get_value("general", "symbol_prefix", default_symbol_prefix)); + set_reloadable(p_config_file->get_value("general", "reloadable", default_reloadable)); + + String entry_lib_path; + { + + List<String> entry_keys; + + if (p_config_file->has_section("entry")) + p_config_file->get_section_keys("entry", &entry_keys); + + for (List<String>::Element *E = entry_keys.front(); E; E = E->next()) { + String key = E->get(); + + Vector<String> tags = key.split("."); + + bool skip = false; + for (int i = 0; i < tags.size(); i++) { + bool has_feature = OS::get_singleton()->has_feature(tags[i]); + + if (!has_feature) { + skip = true; + break; + } + } + + if (skip) { + continue; + } + + entry_lib_path = p_config_file->get_value("entry", key); + break; + } + } + + Vector<String> dependency_paths; + { + + List<String> dependency_keys; + + if (p_config_file->has_section("dependencies")) + p_config_file->get_section_keys("dependencies", &dependency_keys); + + for (List<String>::Element *E = dependency_keys.front(); E; E = E->next()) { + String key = E->get(); + + Vector<String> tags = key.split("."); + + bool skip = false; + for (int i = 0; i < tags.size(); i++) { + bool has_feature = OS::get_singleton()->has_feature(tags[i]); + + if (!has_feature) { + skip = true; + break; + } + } + + if (skip) { + continue; + } + + dependency_paths = p_config_file->get_value("dependencies", key); + break; + } + } + + current_library_path = entry_lib_path; + current_dependencies = dependency_paths; +} + void GDNativeLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("get_config_file"), &GDNativeLibrary::get_config_file); + ClassDB::bind_method(D_METHOD("set_config_file", "config_file"), &GDNativeLibrary::set_config_file); ClassDB::bind_method(D_METHOD("get_current_library_path"), &GDNativeLibrary::get_current_library_path); ClassDB::bind_method(D_METHOD("get_current_dependencies"), &GDNativeLibrary::get_current_dependencies); @@ -82,6 +243,8 @@ void GDNativeLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("set_symbol_prefix", "symbol_prefix"), &GDNativeLibrary::set_symbol_prefix); ClassDB::bind_method(D_METHOD("set_reloadable", "reloadable"), &GDNativeLibrary::set_reloadable); + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"), "set_config_file", "get_config_file"); + ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton"); ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "symbol_prefix"), "set_symbol_prefix", "get_symbol_prefix"); @@ -337,73 +500,7 @@ RES GDNativeLibraryResourceLoader::load(const String &p_path, const String &p_or *r_error = err; } - lib->set_singleton(config->get_value("general", "singleton", default_singleton)); - lib->set_load_once(config->get_value("general", "load_once", default_load_once)); - lib->set_symbol_prefix(config->get_value("general", "symbol_prefix", default_symbol_prefix)); - lib->set_reloadable(config->get_value("general", "reloadable", default_reloadable)); - - String entry_lib_path; - { - - List<String> entry_keys; - config->get_section_keys("entry", &entry_keys); - - for (List<String>::Element *E = entry_keys.front(); E; E = E->next()) { - String key = E->get(); - - Vector<String> tags = key.split("."); - - bool skip = false; - for (int i = 0; i < tags.size(); i++) { - bool has_feature = OS::get_singleton()->has_feature(tags[i]); - - if (!has_feature) { - skip = true; - break; - } - } - - if (skip) { - continue; - } - - entry_lib_path = config->get_value("entry", key); - break; - } - } - - Vector<String> dependency_paths; - { - - List<String> dependency_keys; - config->get_section_keys("dependencies", &dependency_keys); - - for (List<String>::Element *E = dependency_keys.front(); E; E = E->next()) { - String key = E->get(); - - Vector<String> tags = key.split("."); - - bool skip = false; - for (int i = 0; i < tags.size(); i++) { - bool has_feature = OS::get_singleton()->has_feature(tags[i]); - - if (!has_feature) { - skip = true; - break; - } - } - - if (skip) { - continue; - } - - dependency_paths = config->get_value("dependencies", key); - break; - } - } - - lib->current_library_path = entry_lib_path; - lib->current_dependencies = dependency_paths; + lib->set_config_file(config); return lib; } diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h index 3298ea950f..b17bb94f1c 100644 --- a/modules/gdnative/gdnative.h +++ b/modules/gdnative/gdnative.h @@ -66,8 +66,14 @@ public: GDNativeLibrary(); ~GDNativeLibrary(); + virtual bool _set(const StringName &p_name, const Variant &p_property); + virtual bool _get(const StringName &p_name, Variant &r_property) const; + virtual void _get_property_list(List<PropertyInfo> *p_list) const; + _FORCE_INLINE_ Ref<ConfigFile> get_config_file() { return config_file; } + void set_config_file(Ref<ConfigFile> p_config_file); + // things that change per-platform // so there are no setters for this _FORCE_INLINE_ String get_current_library_path() const { diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 434dacffd5..9da2a69360 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -5808,6 +5808,23 @@ ] }, { + "name": "godot_nativescript_set_global_type_tag", + "return_type": "void", + "arguments": [ + ["int", "p_idx"], + ["const char *", "p_name"], + ["const void *", "p_type_tag"] + ] + }, + { + "name": "godot_nativescript_get_global_type_tag", + "return_type": "const void *", + "arguments": [ + ["int", "p_idx"], + ["const char *", "p_name"] + ] + }, + { "name": "godot_nativescript_set_type_tag", "return_type": "void", "arguments": [ diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h index 747328bc41..cfbe16fa7d 100644 --- a/modules/gdnative/include/nativescript/godot_nativescript.h +++ b/modules/gdnative/include/nativescript/godot_nativescript.h @@ -214,16 +214,19 @@ void GDAPI godot_nativescript_set_signal_documentation(void *p_gdnative_handle, // type tag API +void GDAPI godot_nativescript_set_global_type_tag(int p_idx, const char *p_name, const void *p_type_tag); +const void GDAPI *godot_nativescript_get_global_type_tag(int p_idx, const char *p_name); + void GDAPI godot_nativescript_set_type_tag(void *p_gdnative_handle, const char *p_name, const void *p_type_tag); const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object); // instance binding API typedef struct { - void *(*alloc_instance_binding_data)(void *, godot_object *); - void (*free_instance_binding_data)(void *, void *); + GDCALLINGCONV void *(*alloc_instance_binding_data)(void *, const void *, godot_object *); + GDCALLINGCONV void (*free_instance_binding_data)(void *, void *); void *data; - void (*free_func)(void *); + GDCALLINGCONV void (*free_func)(void *); } godot_instance_binding_functions; int GDAPI godot_nativescript_register_instance_binding_data_functions(godot_instance_binding_functions p_binding_functions); diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp index aea595d0f0..ace2ecac5c 100644 --- a/modules/gdnative/nativescript/godot_nativescript.cpp +++ b/modules/gdnative/nativescript/godot_nativescript.cpp @@ -313,6 +313,14 @@ void GDAPI godot_nativescript_set_signal_documentation(void *p_gdnative_handle, signal->get().documentation = *(String *)&p_documentation; } +void GDAPI godot_nativescript_set_global_type_tag(int p_idx, const char *p_name, const void *p_type_tag) { + NativeScriptLanguage::get_singleton()->set_global_type_tag(p_idx, StringName(p_name), p_type_tag); +} + +const void GDAPI *godot_nativescript_get_global_type_tag(int p_idx, const char *p_name) { + return NativeScriptLanguage::get_singleton()->get_global_type_tag(p_idx, StringName(p_name)); +} + void GDAPI godot_nativescript_set_type_tag(void *p_gdnative_handle, const char *p_name, const void *p_type_tag) { String *s = (String *)p_gdnative_handle; @@ -331,13 +339,11 @@ const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object) const Object *o = (Object *)p_object; if (!o->get_script_instance()) { - ERR_EXPLAIN("Attempted to get type tag on an object without a script!"); - ERR_FAIL_V(NULL); + return NULL; } else { NativeScript *script = Object::cast_to<NativeScript>(o->get_script_instance()->get_script().ptr()); if (!script) { - ERR_EXPLAIN("Attempted to get type tag on an object without a nativescript attached"); - ERR_FAIL_V(NULL); + return NULL; } if (script->get_script_desc()) @@ -347,10 +353,6 @@ const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object) return NULL; } -#ifdef __cplusplus -} -#endif - int GDAPI godot_nativescript_register_instance_binding_data_functions(godot_instance_binding_functions p_binding_functions) { return NativeScriptLanguage::get_singleton()->register_binding_functions(p_binding_functions); } @@ -362,3 +364,7 @@ void GDAPI godot_nativescript_unregister_instance_binding_data_functions(int p_i void GDAPI *godot_nativescript_get_instance_binding_data(int p_idx, godot_object *p_object) { return NativeScriptLanguage::get_singleton()->get_instance_binding_data(p_idx, (Object *)p_object); } + +#ifdef __cplusplus +} +#endif diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index 5806ee3f3f..d255148e0f 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -55,12 +55,6 @@ #include "editor/editor_node.h" #endif -// -// -// Script stuff -// -// - void NativeScript::_bind_methods() { ClassDB::bind_method(D_METHOD("set_class_name", "class_name"), &NativeScript::set_class_name); ClassDB::bind_method(D_METHOD("get_class_name"), &NativeScript::get_class_name); @@ -528,12 +522,6 @@ NativeScript::~NativeScript() { #endif } - // - // - // ScriptInstance stuff - // - // - #define GET_SCRIPT_DESC() script->get_script_desc() void NativeScriptInstance::_ml_call_reversed(NativeScriptDesc *script_data, const StringName &p_method, const Variant **p_args, int p_argcount) { @@ -872,15 +860,12 @@ NativeScriptInstance::~NativeScriptInstance() { } } -// -// -// ScriptingLanguage stuff -// -// - NativeScriptLanguage *NativeScriptLanguage::singleton; void NativeScriptLanguage::_unload_stuff(bool p_reload) { + + Map<String, Ref<GDNative> > erase_and_unload; + for (Map<String, Map<StringName, NativeScriptDesc> >::Element *L = library_classes.front(); L; L = L->next()) { String lib_path = L->key(); @@ -916,18 +901,6 @@ void NativeScriptLanguage::_unload_stuff(bool p_reload) { gdn = E->get(); } - if (gdn.is_valid() && gdn->get_library().is_valid()) { - Ref<GDNativeLibrary> lib = gdn->get_library(); - void *terminate_fn; - Error err = gdn->get_symbol(lib->get_symbol_prefix() + _terminate_call_name, terminate_fn, true); - - if (err == OK) { - void (*terminate)(void *) = (void (*)(void *))terminate_fn; - - terminate((void *)&lib_path); - } - } - for (Map<StringName, NativeScriptDesc>::Element *C = classes.front(); C; C = C->next()) { // free property stuff first @@ -952,6 +925,27 @@ void NativeScriptLanguage::_unload_stuff(bool p_reload) { if (C->get().destroy_func.free_func) C->get().destroy_func.free_func(C->get().destroy_func.method_data); } + + erase_and_unload.insert(lib_path, gdn); + } + + for (Map<String, Ref<GDNative> >::Element *E = erase_and_unload.front(); E; E = E->next()) { + String lib_path = E->key(); + Ref<GDNative> gdn = E->get(); + + library_classes.erase(lib_path); + + if (gdn.is_valid() && gdn->get_library().is_valid()) { + Ref<GDNativeLibrary> lib = gdn->get_library(); + void *terminate_fn; + Error err = gdn->get_symbol(lib->get_symbol_prefix() + _terminate_call_name, terminate_fn, true); + + if (err == OK) { + void (*terminate)(void *) = (void (*)(void *))terminate_fn; + + terminate((void *)&lib_path); + } + } } } @@ -1183,8 +1177,11 @@ void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_objec } if (!(*binding_data)[p_idx]) { + + const void *global_type_tag = global_type_tags[p_idx].get(p_object->get_class_name()); + // no binding data yet, soooooo alloc new one \o/ - (*binding_data)[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, (godot_object *)p_object); + (*binding_data)[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, global_type_tag, (godot_object *)p_object); } return (*binding_data)[p_idx]; @@ -1226,6 +1223,27 @@ void NativeScriptLanguage::free_instance_binding_data(void *p_data) { delete &binding_data; } +void NativeScriptLanguage::set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag) { + if (!global_type_tags.has(p_idx)) { + global_type_tags.insert(p_idx, HashMap<StringName, const void *>()); + } + + HashMap<StringName, const void *> &tags = global_type_tags[p_idx]; + + tags.set(p_class_name, p_type_tag); +} + +const void *NativeScriptLanguage::get_global_type_tag(int p_idx, StringName p_class_name) const { + if (!global_type_tags.has(p_idx)) + return NULL; + + const HashMap<StringName, const void *> &tags = global_type_tags[p_idx]; + + const void *tag = tags.get(p_class_name); + + return tag; +} + #ifndef NO_THREADS void NativeScriptLanguage::defer_init_library(Ref<GDNativeLibrary> lib, NativeScript *script) { MutexLock lock(mutex); @@ -1363,6 +1381,7 @@ void NativeReloadNode::_notification(int p_what) { MutexLock lock(NSL->mutex); #endif NSL->_unload_stuff(true); + for (Map<String, Ref<GDNative> >::Element *L = NSL->library_gdnatives.front(); L; L = L->next()) { Ref<GDNative> gdn = L->get(); @@ -1376,7 +1395,6 @@ void NativeReloadNode::_notification(int p_what) { } gdn->terminate(); - NSL->library_classes.erase(L->key()); } unloaded = true; diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h index 17b6ddc747..68a8126a32 100644 --- a/modules/gdnative/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -36,6 +36,7 @@ #include "core/self_list.h" #include "io/resource_loader.h" #include "io/resource_saver.h" +#include "oa_hash_map.h" #include "ordered_hash_map.h" #include "os/thread_safe.h" #include "scene/main/node.h" @@ -240,6 +241,8 @@ private: Vector<Pair<bool, godot_instance_binding_functions> > binding_functions; Set<Vector<void *> *> binding_instances; + Map<int, HashMap<StringName, const void *> > global_type_tags; + public: // These two maps must only be touched on the main thread Map<String, Map<StringName, NativeScriptDesc> > library_classes; @@ -323,6 +326,9 @@ public: virtual void *alloc_instance_binding_data(Object *p_object); virtual void free_instance_binding_data(void *p_data); + + void set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag); + const void *get_global_type_tag(int p_idx, StringName p_class_name) const; }; inline NativeScriptDesc *NativeScript::get_script_desc() const { diff --git a/modules/gdnative/pluginscript/register_types.cpp b/modules/gdnative/pluginscript/register_types.cpp index 8888f9e157..924abf29df 100644 --- a/modules/gdnative/pluginscript/register_types.cpp +++ b/modules/gdnative/pluginscript/register_types.cpp @@ -64,7 +64,7 @@ static Error _check_language_desc(const godot_pluginscript_language_desc *desc) // desc->make_function is not mandatory // desc->complete_code is not mandatory // desc->auto_indent_code is not mandatory - // desc->add_global_constant is not mandatory + ERR_FAIL_COND_V(!desc->add_global_constant, ERR_BUG); // desc->debug_get_error is not mandatory // desc->debug_get_stack_level_count is not mandatory // desc->debug_get_stack_level_line is not mandatory @@ -78,7 +78,7 @@ static Error _check_language_desc(const godot_pluginscript_language_desc *desc) // desc->profiling_stop is not mandatory // desc->profiling_get_accumulated_data is not mandatory // desc->profiling_get_frame_data is not mandatory - // desc->frame is not mandatory + // desc->profiling_frame is not mandatory ERR_FAIL_COND_V(!desc->script_desc.init, ERR_BUG); ERR_FAIL_COND_V(!desc->script_desc.finish, ERR_BUG); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 5f72dca866..87d8fe1bf5 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -2850,7 +2850,24 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol return OK; } } else { - r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + /* + // Because get_integer_constant_enum and get_integer_constant dont work on @GlobalScope + // We cannot determine the exact nature of the identifier here + // Otherwise these codes would work + StringName enumName = ClassDB::get_integer_constant_enum("@GlobalScope", p_symbol, true); + if (enumName != NULL) { + r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM; + r_result.class_name = "@GlobalScope"; + r_result.class_member = enumName; + return OK; + } + else { + r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + r_result.class_name = "@GlobalScope"; + r_result.class_member = p_symbol; + return OK; + }*/ + r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_TBD_GLOBALSCOPE; r_result.class_name = "@GlobalScope"; r_result.class_member = p_symbol; return OK; @@ -2913,6 +2930,14 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol return OK; } + StringName enumName = ClassDB::get_integer_constant_enum(t.obj_type, p_symbol, true); + if (enumName != StringName()) { + r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM; + r_result.class_name = t.obj_type; + r_result.class_member = enumName; + return OK; + } + bool success; ClassDB::get_integer_constant(t.obj_type, p_symbol, &success); if (success) { diff --git a/modules/gdscript/gdscript_highlighter.cpp b/modules/gdscript/gdscript_highlighter.cpp new file mode 100644 index 0000000000..5b8b652c29 --- /dev/null +++ b/modules/gdscript/gdscript_highlighter.cpp @@ -0,0 +1,278 @@ +/*************************************************************************/ +/* gdscript_highlighter.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "gdscript_highlighter.h" +#include "scene/gui/text_edit.h" + +inline bool _is_symbol(CharType c) { + + return is_symbol(c); +} + +static bool _is_text_char(CharType c) { + + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; +} + +static bool _is_whitespace(CharType c) { + return c == '\t' || c == ' '; +} + +static bool _is_char(CharType c) { + + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; +} + +static bool _is_number(CharType c) { + return (c >= '0' && c <= '9'); +} + +static bool _is_hex_symbol(CharType c) { + return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + +Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) { + Map<int, TextEdit::HighlighterInfo> color_map; + + bool prev_is_char = false; + bool prev_is_number = false; + bool in_keyword = false; + bool in_word = false; + bool in_function_name = false; + bool in_member_variable = false; + bool is_hex_notation = false; + Color keyword_color; + Color color; + + int in_region = -1; + int deregion = 0; + for (int i = 0; i < p_line; i++) { + int ending_color_region = text_editor->_get_line_ending_color_region(i); + if (in_region == -1) { + in_region = ending_color_region; + } else if (in_region == ending_color_region) { + in_region = -1; + } else { + const Map<int, TextEdit::Text::ColorRegionInfo> &cri_map = text_editor->_get_line_color_region_info(i); + for (const Map<int, TextEdit::Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) { + const TextEdit::Text::ColorRegionInfo &cri = E->get(); + if (cri.region == in_region) { + in_region = -1; + } + } + } + } + + const Map<int, TextEdit::Text::ColorRegionInfo> cri_map = text_editor->_get_line_color_region_info(p_line); + const String &str = text_editor->get_line(p_line); + Color prev_color; + for (int j = 0; j < str.length(); j++) { + TextEdit::HighlighterInfo highlighter_info; + + if (deregion > 0) { + deregion--; + if (deregion == 0) { + in_region = -1; + } + } + + if (deregion != 0) { + if (color != prev_color) { + prev_color = color; + highlighter_info.color = color; + color_map[j] = highlighter_info; + } + continue; + } + + color = font_color; + + bool is_char = _is_text_char(str[j]); + bool is_symbol = _is_symbol(str[j]); + bool is_number = _is_number(str[j]); + + // allow ABCDEF in hex notation + if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) { + is_number = true; + } else { + is_hex_notation = false; + } + + // check for dot or underscore or 'x' for hex notation in floating point number + if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) { + is_number = true; + is_symbol = false; + is_char = false; + + if (str[j] == 'x' && str[j - 1] == '0') { + is_hex_notation = true; + } + } + + if (!in_word && _is_char(str[j]) && !is_number) { + in_word = true; + } + + if ((in_keyword || in_word) && !is_hex_notation) { + is_number = false; + } + + if (is_symbol && str[j] != '.' && in_word) { + in_word = false; + } + + if (is_symbol && cri_map.has(j)) { + const TextEdit::Text::ColorRegionInfo &cri = cri_map[j]; + + if (in_region == -1) { + if (!cri.end) { + in_region = cri.region; + } + } else { + TextEdit::ColorRegion cr = text_editor->_get_color_region(cri.region); + if (in_region == cri.region && !cr.line_only) { //ignore otherwise + if (cri.end || cr.eq) { + deregion = cr.eq ? cr.begin_key.length() : cr.end_key.length(); + } + } + } + } + + if (!is_char) { + in_keyword = false; + } + + if (in_region == -1 && !in_keyword && is_char && !prev_is_char) { + + int to = j; + while (to < str.length() && _is_text_char(str[to])) + to++; + + String word = str.substr(j, to - j); + Color col = Color(); + if (text_editor->has_keyword_color(word)) { + col = text_editor->get_keyword_color(word); + } else if (text_editor->has_member_color(word)) { + col = text_editor->get_member_color(word); + for (int k = j - 1; k >= 0; k--) { + if (str[k] == '.') { + col = Color(); //member indexing not allowed + break; + } else if (str[k] > 32) { + break; + } + } + } + + if (col != Color()) { + in_keyword = true; + keyword_color = col; + } + } + + if (!in_function_name && in_word && !in_keyword) { + + int k = j; + while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') { + k++; + } + + // check for space between name and bracket + while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) { + k++; + } + + if (str[k] == '(') { + in_function_name = true; + } + } + + if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) { + int k = j; + while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') { + k--; + } + + if (str[k] == '.') { + in_member_variable = true; + } + } + + if (is_symbol) { + in_function_name = false; + in_member_variable = false; + } + + if (in_region >= 0) + color = text_editor->_get_color_region(in_region).color; + else if (in_keyword) + color = keyword_color; + else if (in_member_variable) + color = member_color; + else if (in_function_name) + color = function_color; + else if (is_symbol) + color = symbol_color; + else if (is_number) + color = number_color; + + prev_is_char = is_char; + prev_is_number = is_number; + + if (color != prev_color) { + prev_color = color; + highlighter_info.color = color; + color_map[j] = highlighter_info; + } + } + return color_map; +} + +String GDScriptSyntaxHighlighter::get_name() { + return "GDScript"; +} + +List<String> GDScriptSyntaxHighlighter::get_supported_languages() { + List<String> languages; + languages.push_back("GDScript"); + return languages; +} + +void GDScriptSyntaxHighlighter::_update_cache() { + font_color = text_editor->get_color("font_color"); + symbol_color = text_editor->get_color("symbol_color"); + function_color = text_editor->get_color("function_color"); + number_color = text_editor->get_color("number_color"); + member_color = text_editor->get_color("member_variable_color"); +} + +SyntaxHighlighter *GDScriptSyntaxHighlighter::create() { + return memnew(GDScriptSyntaxHighlighter); +} diff --git a/modules/gdscript/gdscript_highlighter.h b/modules/gdscript/gdscript_highlighter.h new file mode 100644 index 0000000000..ef1bdd4103 --- /dev/null +++ b/modules/gdscript/gdscript_highlighter.h @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* gdscript_highlighter.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GDSCRIPT_HIGHLIGHTER_H +#define GDSCRIPT_HIGHLIGHTER_H + +#include "scene/gui/text_edit.h" + +class GDScriptSyntaxHighlighter : public SyntaxHighlighter { +private: + // colours + Color font_color; + Color symbol_color; + Color function_color; + Color built_in_type_color; + Color number_color; + Color member_color; + +public: + static SyntaxHighlighter *create(); + + virtual void _update_cache(); + virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line); + + virtual String get_name(); + virtual List<String> get_supported_languages(); +}; + +#endif // GDSCRIPT_HIGHLIGHTER_H diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 95efcda80f..85c94c3596 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -31,6 +31,7 @@ #include "register_types.h" #include "gdscript.h" +#include "gdscript_highlighter.h" #include "gdscript_tokenizer.h" #include "io/file_access_encrypted.h" #include "io/resource_loader.h" @@ -92,6 +93,7 @@ void register_gdscript_types() { ResourceSaver::add_resource_format_saver(resource_saver_gd); #ifdef TOOLS_ENABLED + ScriptEditor::register_create_syntax_highlighter_function(GDScriptSyntaxHighlighter::create); EditorNode::add_init_callback(_editor_init); #endif } diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 869492232c..f523eef895 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -1046,14 +1046,14 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { options->get_popup()->add_item(TTR("Previous Floor"), MENU_OPTION_PREV_LEVEL, KEY_Q); options->get_popup()->add_item(TTR("Next Floor"), MENU_OPTION_NEXT_LEVEL, KEY_E); options->get_popup()->add_separator(); - options->get_popup()->add_check_item(TTR("Clip Disabled"), MENU_OPTION_CLIP_DISABLED); + options->get_popup()->add_radio_check_item(TTR("Clip Disabled"), MENU_OPTION_CLIP_DISABLED); options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_CLIP_DISABLED), true); - options->get_popup()->add_check_item(TTR("Clip Above"), MENU_OPTION_CLIP_ABOVE); - options->get_popup()->add_check_item(TTR("Clip Below"), MENU_OPTION_CLIP_BELOW); + options->get_popup()->add_radio_check_item(TTR("Clip Above"), MENU_OPTION_CLIP_ABOVE); + options->get_popup()->add_radio_check_item(TTR("Clip Below"), MENU_OPTION_CLIP_BELOW); options->get_popup()->add_separator(); - options->get_popup()->add_check_item(TTR("Edit X Axis"), MENU_OPTION_X_AXIS, KEY_Z); - options->get_popup()->add_check_item(TTR("Edit Y Axis"), MENU_OPTION_Y_AXIS, KEY_X); - options->get_popup()->add_check_item(TTR("Edit Z Axis"), MENU_OPTION_Z_AXIS, KEY_C); + options->get_popup()->add_radio_check_item(TTR("Edit X Axis"), MENU_OPTION_X_AXIS, KEY_Z); + options->get_popup()->add_radio_check_item(TTR("Edit Y Axis"), MENU_OPTION_Y_AXIS, KEY_X); + options->get_popup()->add_radio_check_item(TTR("Edit Z Axis"), MENU_OPTION_Z_AXIS, KEY_C); options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_Y_AXIS), true); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Cursor Rotate X"), MENU_OPTION_CURSOR_ROTATE_X, KEY_A); diff --git a/modules/mbedtls/stream_peer_mbed_tls.cpp b/modules/mbedtls/stream_peer_mbed_tls.cpp index 4135eb40ff..a63e53ec1f 100755 --- a/modules/mbedtls/stream_peer_mbed_tls.cpp +++ b/modules/mbedtls/stream_peer_mbed_tls.cpp @@ -293,28 +293,10 @@ void StreamPeerMbedTLS::initialize_ssl() { mbedtls_debug_set_threshold(1); #endif - String certs_path = GLOBAL_DEF("network/ssl/certificates", ""); - ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt")); - - if (certs_path != "") { - - FileAccess *f = FileAccess::open(certs_path, FileAccess::READ); - if (f) { - PoolByteArray arr; - int flen = f->get_len(); - arr.resize(flen + 1); - { - PoolByteArray::Write w = arr.write(); - f->get_buffer(w.ptr(), flen); - w[flen] = 0; //end f string - } - - memdelete(f); - - _load_certs(arr); - print_line("Loaded certs from '" + certs_path); - } - } + PoolByteArray cert_array = StreamPeerSSL::get_project_cert_array(); + + if (cert_array.size() > 0) + _load_certs(cert_array); available = true; } diff --git a/modules/mbedtls/stream_peer_mbed_tls.h b/modules/mbedtls/stream_peer_mbed_tls.h index ce17614d85..2b96a194a1 100755 --- a/modules/mbedtls/stream_peer_mbed_tls.h +++ b/modules/mbedtls/stream_peer_mbed_tls.h @@ -32,8 +32,6 @@ #define STREAM_PEER_OPEN_SSL_H #include "io/stream_peer_ssl.h" -#include "os/file_access.h" -#include "project_settings.h" #include "mbedtls/config.h" #include "mbedtls/ctr_drbg.h" diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index ad07f043b2..2f2b5768db 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -525,11 +525,10 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { } } -GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) { - - build_info = p_build_info; - build_tab = NULL; - exit_callback = p_callback; - exited = true; - exit_code = -1; +GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) : + build_info(p_build_info), + build_tab(NULL), + exit_callback(p_callback), + exited(true), + exit_code(-1) { } diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp index 32aec2a3b5..f1cf0bcdf5 100644 --- a/modules/mono/editor/mono_bottom_panel.cpp +++ b/modules/mono/editor/mono_bottom_panel.cpp @@ -437,21 +437,16 @@ void MonoBuildTab::_bind_methods() { ClassDB::bind_method("_issue_activated", &MonoBuildTab::_issue_activated); } -MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) { - - build_info = p_build_info; - logs_dir = p_logs_dir; - - build_exited = false; - - issues_list = memnew(ItemList); +MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) : + build_info(p_build_info), + logs_dir(p_logs_dir), + build_exited(false), + issues_list(memnew(ItemList)), + error_count(0), + warning_count(0), + errors_visible(true), + warnings_visible(true) { issues_list->set_v_size_flags(SIZE_EXPAND_FILL); issues_list->connect("item_activated", this, "_issue_activated"); add_child(issues_list); - - error_count = 0; - warning_count = 0; - - errors_visible = true; - warnings_visible = true; } diff --git a/modules/mono/glue/cs_files/Basis.cs b/modules/mono/glue/cs_files/Basis.cs index 89b3e94c35..2e7e5404c4 100644 --- a/modules/mono/glue/cs_files/Basis.cs +++ b/modules/mono/glue/cs_files/Basis.cs @@ -49,20 +49,20 @@ namespace Godot public Vector3 x { - get => GetAxis(0); - set => SetAxis(0, value); + get { return GetAxis(0); } + set { SetAxis(0, value); } } public Vector3 y { - get => GetAxis(1); - set => SetAxis(1, value); + get { return GetAxis(1); } + set { SetAxis(1, value); } } public Vector3 z { - get => GetAxis(2); - set => SetAxis(2, value); + get { return GetAxis(2); } + set { SetAxis(2, value); } } private Vector3 _x; diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/cs_files/Mathf.cs index 8b9c264d0d..adbcc855ef 100644 --- a/modules/mono/glue/cs_files/Mathf.cs +++ b/modules/mono/glue/cs_files/Mathf.cs @@ -257,5 +257,17 @@ namespace Godot { return (real_t)Math.Tanh(s); } + + public static int Wrap(int val, int min, int max) + { + int rng = max - min; + return min + ((((val - min) % rng) + rng) % rng); + } + + public static real_t Wrap(real_t val, real_t min, real_t max) + { + real_t rng = max - min; + return min + (val - min) - (rng * Floor((val - min) / rng)); + } } } diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 58c6d73ab2..9e6307c0bf 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -261,14 +261,12 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) { /* look for further theora headers */ while (theora_p && (theora_p < 3) && (ret = ogg_stream_packetout(&to, &op))) { if (ret < 0) { - fprintf(stderr, "Error parsing Theora stream headers; " - "corrupt stream?\n"); + fprintf(stderr, "Error parsing Theora stream headers; corrupt stream?\n"); clear(); return; } if (!th_decode_headerin(&ti, &tc, &ts, &op)) { - fprintf(stderr, "Error parsing Theora stream headers; " - "corrupt stream?\n"); + fprintf(stderr, "Error parsing Theora stream headers; corrupt stream?\n"); clear(); return; } @@ -312,9 +310,15 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) { td = th_decode_alloc(&ti, ts); px_fmt = ti.pixel_fmt; switch (ti.pixel_fmt) { - case TH_PF_420: printf(" 4:2:0 video\n"); break; - case TH_PF_422: printf(" 4:2:2 video\n"); break; - case TH_PF_444: printf(" 4:4:4 video\n"); break; + case TH_PF_420: + //printf(" 4:2:0 video\n"); + break; + case TH_PF_422: + //printf(" 4:2:2 video\n"); + break; + case TH_PF_444: + //printf(" 4:4:4 video\n"); + break; case TH_PF_RSVD: default: printf(" video\n (UNKNOWN Chroma sampling!)\n"); @@ -519,7 +523,7 @@ void VideoStreamPlaybackTheora::update(float p_delta) { #else if (file && /*!videobuf_ready && */ no_theora && theora_eos) { #endif - printf("video done, stopping\n"); + //printf("video done, stopping\n"); stop(); return; }; diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 69503e631c..eb10c5e99f 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -3236,6 +3236,12 @@ void VisualScriptEditor::_member_option(int p_option) { } } +void VisualScriptEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) { +} + +void VisualScriptEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) { +} + void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_member_button", &VisualScriptEditor::_member_button); diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 80bbf142d9..72b5e09222 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -246,6 +246,9 @@ protected: static void _bind_methods(); public: + virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter); + virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter); + virtual void apply_code(); virtual Ref<Script> get_edited_script() const; virtual Vector<String> get_functions(); diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub index 0ebafa022d..b09c232b3c 100644 --- a/modules/webm/libvpx/SCsub +++ b/modules/webm/libvpx/SCsub @@ -340,7 +340,7 @@ if webm_simd_optimizations == False: env_libvpx.add_source_files(env.modules_sources, libvpx_sources) if webm_cpu_x86: - is_clang_or_gcc = ('gcc' in env["CC"]) or ('clang' in env["CC"]) + is_clang_or_gcc = ('gcc' in env["CC"]) or ('clang' in env["CC"]) or ("OSXCROSS_ROOT" in os.environ) env_libvpx_mmx = env_libvpx.Clone() if cpu_bits == '32' and is_clang_or_gcc: diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp index bebf342f8c..2220c9adf2 100644 --- a/modules/websocket/lws_client.cpp +++ b/modules/websocket/lws_client.cpp @@ -31,6 +31,7 @@ #include "lws_client.h" #include "core/io/ip.h" +#include "core/io/stream_peer_ssl.h" Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { @@ -64,6 +65,9 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, info.uid = -1; //info.ws_ping_pong_interval = 5; info.user = _lws_ref; +#if defined(LWS_OPENSSL_SUPPORT) + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; +#endif context = lws_create_context(&info); if (context == NULL) { @@ -87,7 +91,14 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, i.host = hbuf; i.path = pbuf; i.port = p_port; - i.ssl_connection = p_ssl; + + if (p_ssl) { + i.ssl_connection = LCCSCF_USE_SSL; + if (!verify_ssl) + i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; + } else { + i.ssl_connection = 0; + } lws_client_connect_via_info(&i); return OK; @@ -104,6 +115,13 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user; switch (reason) { + case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: { + PoolByteArray arr = StreamPeerSSL::get_project_cert_array(); + if (arr.size() > 0) + SSL_CTX_add_client_CA((SSL_CTX *)user, d2i_X509(NULL, &arr.read()[0], arr.size())); + else if (verify_ssl) + WARN_PRINTS("No CA cert specified in project settings, SSL will not work"); + } break; case LWS_CALLBACK_CLIENT_ESTABLISHED: peer->set_wsi(wsi); diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp index 591d9510ce..7701163085 100644 --- a/modules/websocket/websocket_client.cpp +++ b/modules/websocket/websocket_client.cpp @@ -32,6 +32,8 @@ GDCINULL(WebSocketClient); WebSocketClient::WebSocketClient() { + + verify_ssl = true; } WebSocketClient::~WebSocketClient() { @@ -72,6 +74,16 @@ Error WebSocketClient::connect_to_url(String p_url, PoolVector<String> p_protoco return connect_to_host(host, path, port, ssl, p_protocols); } +void WebSocketClient::set_verify_ssl_enabled(bool p_verify_ssl) { + + verify_ssl = p_verify_ssl; +} + +bool WebSocketClient::is_verify_ssl_enabled() const { + + return verify_ssl; +} + bool WebSocketClient::is_server() const { return false; @@ -116,6 +128,10 @@ void WebSocketClient::_on_error() { void WebSocketClient::_bind_methods() { ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api"), &WebSocketClient::connect_to_url, DEFVAL(PoolVector<String>()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("disconnect_from_host"), &WebSocketClient::disconnect_from_host); + ClassDB::bind_method(D_METHOD("set_verify_ssl_enabled", "enabled"), &WebSocketClient::set_verify_ssl_enabled); + ClassDB::bind_method(D_METHOD("is_verify_ssl_enabled"), &WebSocketClient::is_verify_ssl_enabled); + + ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled"); ADD_SIGNAL(MethodInfo("data_received")); ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol"))); diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h index 5c863559bc..6165f37d40 100644 --- a/modules/websocket/websocket_client.h +++ b/modules/websocket/websocket_client.h @@ -41,12 +41,16 @@ class WebSocketClient : public WebSocketMultiplayerPeer { protected: Ref<WebSocketPeer> _peer; + bool verify_ssl; static void _bind_methods(); public: Error connect_to_url(String p_url, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); + void set_verify_ssl_enabled(bool p_verify_ssl); + bool is_verify_ssl_enabled() const; + virtual void poll() = 0; virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()) = 0; virtual void disconnect_from_host() = 0; diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp index 9baf58c3eb..579c06f76b 100644 --- a/platform/android/java_glue.cpp +++ b/platform/android/java_glue.cpp @@ -936,6 +936,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj) { + if (step == 0) + return; + os_android->main_loop_request_go_back(); } @@ -976,6 +979,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions) { + if (step == 0) + return; + Vector<OS_Android::TouchPos> points; for (int i = 0; i < count; i++) { @@ -1250,6 +1256,8 @@ static unsigned int android_get_keysym(unsigned int p_code) { } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jobject obj, jint p_device, jint p_button, jboolean p_pressed) { + if (step == 0) + return; OS_Android::JoypadEvent jevent; jevent.device = p_device; @@ -1261,6 +1269,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value) { + if (step == 0) + return; OS_Android::JoypadEvent jevent; jevent.device = p_device; @@ -1272,6 +1282,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jobject obj, jint p_device, jint p_hat_x, jint p_hat_y) { + if (step == 0) + return; + OS_Android::JoypadEvent jevent; jevent.device = p_device; jevent.type = OS_Android::JOY_EVENT_HAT; @@ -1301,6 +1314,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged( } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_scancode, jint p_unicode_char, jboolean p_pressed) { + if (step == 0) + return; Ref<InputEventKey> ievent; ievent.instance(); @@ -1344,14 +1359,18 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jobject obj) { - if (os_android && step > 0) - os_android->main_loop_focusin(); + if (step == 0) + return; + + os_android->main_loop_focusin(); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jobject obj) { - if (os_android && step > 0) - os_android->main_loop_focusout(); + if (step == 0) + return; + + os_android->main_loop_focusout(); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jobject obj) { diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub index 6b5f30dc41..b96bec16b4 100644 --- a/platform/iphone/SCsub +++ b/platform/iphone/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +import os Import('env') @@ -21,6 +22,10 @@ ios_lib = env_ios.add_library('iphone', iphone_lib) def combine_libs(target=None, source=None, env=None): lib_path = target[0].srcnode().abspath - env.Execute('$IPHONEPATH/usr/bin/libtool -static -o "' + lib_path + '" ' + ' '.join([('"' + lib.srcnode().abspath + '"') for lib in source])) + if ("OSXCROSS_IOS" in os.environ): + libtool = '$IPHONEPATH/usr/bin/${ios_triple}libtool' + else: + libtool = "$IPHONEPATH/usr/bin/libtool" + env.Execute(libtool + ' -static -o "' + lib_path + '" ' + ' '.join([('"' + lib.srcnode().abspath + '"') for lib in source])) combine_command = env_ios.Command('#bin/libgodot' + env_ios['LIBSUFFIX'], [ios_lib] + env_ios['LIBS'], combine_libs) diff --git a/platform/iphone/power_iphone.cpp b/platform/iphone/power_iphone.cpp index 95a9aa9705..7f9dadc363 100644 --- a/platform/iphone/power_iphone.cpp +++ b/platform/iphone/power_iphone.cpp @@ -30,7 +30,7 @@ #include "power_iphone.h" -bool OS::PowerState::UpdatePowerInfo() { +bool PowerIphone::UpdatePowerInfo() { return false; } diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index 851f4ecb49..a48cb872ee 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -12,7 +12,7 @@ def get_name(): def can_build(): - return 'EMSCRIPTEN_ROOT' in os.environ or 'EMSCRIPTEN' in os.environ + return 'EM_CONFIG' in os.environ or os.path.exists(os.path.expanduser('~/.emscripten')) def get_opts(): @@ -67,10 +67,20 @@ def configure(env): ## Compiler configuration env['ENV'] = os.environ - if 'EMSCRIPTEN_ROOT' in os.environ: - env.PrependENVPath('PATH', os.environ['EMSCRIPTEN_ROOT']) - elif 'EMSCRIPTEN' in os.environ: - env.PrependENVPath('PATH', os.environ['EMSCRIPTEN']) + + em_config_file = os.getenv('EM_CONFIG') or os.path.expanduser('~/.emscripten') + if not os.path.exists(em_config_file): + raise RuntimeError("Emscripten configuration file '%s' does not exist" % em_config_file) + with open(em_config_file) as f: + em_config = {} + try: + # Emscripten configuration file is a Python file with simple assignments. + exec(f.read(), em_config) + except StandardError as e: + raise RuntimeError("Emscripten configuration file '%s' is invalid:\n%s" % (em_config_file, e)) + if 'EMSCRIPTEN_ROOT' not in em_config: + raise RuntimeError("'EMSCRIPTEN_ROOT' missing in Emscripten configuration file '%s'" % em_config_file) + env.PrependENVPath('PATH', em_config['EMSCRIPTEN_ROOT']) env['CC'] = 'emcc' env['CXX'] = 'em++' @@ -129,13 +139,6 @@ def configure(env): # This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1. env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1']) - # engine.js uses FS but is not currently evaluated by Emscripten, so export FS. - # TODO: Getting rid of this export is desirable. - extra_exports = [ - 'FS', - ] - env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS="%s"' % repr(extra_exports)]) - env.Append(LINKFLAGS=['-s', 'INVOKE_RUN=0']) # TODO: Reevaluate usage of this setting now that engine.js manages engine runtime. diff --git a/platform/javascript/engine.js b/platform/javascript/engine.js index e6fb48d0d2..e4839af433 100644 --- a/platform/javascript/engine.js +++ b/platform/javascript/engine.js @@ -1,3 +1,5 @@ + exposedLibs['PATH'] = PATH; + exposedLibs['FS'] = FS; return Module; }, }; @@ -12,6 +14,13 @@ var loadingFiles = {}; + function getPathLeaf(path) { + + while (path.endsWith('/')) + path = path.slice(0, -1); + return path.slice(path.lastIndexOf('/') + 1); + } + function getBasePath(path) { if (path.endsWith('/')) @@ -23,14 +32,15 @@ function getBaseName(path) { - path = getBasePath(path); - return path.slice(path.lastIndexOf('/') + 1); + return getPathLeaf(getBasePath(path)); } Engine = function Engine() { this.rtenv = null; + var LIBS = {}; + var initPromise = null; var unloadAfterInit = true; @@ -80,11 +90,11 @@ return new Promise(function(resolve, reject) { rtenvProps.onRuntimeInitialized = resolve; rtenvProps.onAbort = reject; - rtenvProps.engine.rtenv = Engine.RuntimeEnvironment(rtenvProps); + rtenvProps.engine.rtenv = Engine.RuntimeEnvironment(rtenvProps, LIBS); }); } - this.preloadFile = function(pathOrBuffer, bufferFilename) { + this.preloadFile = function(pathOrBuffer, destPath) { if (pathOrBuffer instanceof ArrayBuffer) { pathOrBuffer = new Uint8Array(pathOrBuffer); @@ -93,14 +103,14 @@ } if (pathOrBuffer instanceof Uint8Array) { preloadedFiles.push({ - name: bufferFilename, + path: destPath, buffer: pathOrBuffer }); return Promise.resolve(); } else if (typeof pathOrBuffer === 'string') { return loadPromise(pathOrBuffer, preloadProgressTracker).then(function(xhr) { preloadedFiles.push({ - name: pathOrBuffer, + path: destPath || pathOrBuffer, buffer: xhr.response }); }); @@ -119,7 +129,12 @@ this.startGame = function(mainPack) { executableName = getBaseName(mainPack); - return Promise.all([this.init(getBasePath(mainPack)), this.preloadFile(mainPack)]).then( + return Promise.all([ + // Load from directory, + this.init(getBasePath(mainPack)), + // ...but write to root where the engine expects it. + this.preloadFile(mainPack, getPathLeaf(mainPack)) + ]).then( Function.prototype.apply.bind(synchronousStart, this, []) ); }; @@ -163,7 +178,16 @@ this.rtenv.thisProgram = executableName || getBaseName(basePath); preloadedFiles.forEach(function(file) { - this.rtenv.FS.createDataFile('/', file.name, new Uint8Array(file.buffer), true, true, true); + var dir = LIBS.PATH.dirname(file.path); + try { + LIBS.FS.stat(dir); + } catch (e) { + if (e.code !== 'ENOENT') { + throw e; + } + LIBS.FS.mkdirTree(dir); + } + LIBS.FS.createDataFile('/', file.path, new Uint8Array(file.buffer), true, true, true); }, this); preloadedFiles = null; diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp index 118a77835e..8d90e01ae1 100644 --- a/platform/javascript/http_client_javascript.cpp +++ b/platform/javascript/http_client_javascript.cpp @@ -88,7 +88,7 @@ Error HTTPClient::prepare_request(Method p_method, const String &p_url, const Ve ERR_FAIL_COND_V(port < 0, ERR_UNCONFIGURED); ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER); - String url = (use_tls ? "https://" : "http://") + host + ":" + itos(port) + "/" + p_url; + String url = (use_tls ? "https://" : "http://") + host + ":" + itos(port) + p_url; godot_xhr_reset(xhr_id); godot_xhr_open(xhr_id, _methods[p_method], url.utf8().get_data(), username.empty() ? NULL : username.utf8().get_data(), diff --git a/platform/javascript/pre.js b/platform/javascript/pre.js index 311aa44fda..02194bc75e 100644 --- a/platform/javascript/pre.js +++ b/platform/javascript/pre.js @@ -1,2 +1,2 @@ var Engine = { - RuntimeEnvironment: function(Module) { + RuntimeEnvironment: function(Module, exposedLibs) { diff --git a/platform/osx/detect.py b/platform/osx/detect.py index e51a8082f7..1e9631fae0 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -93,6 +93,7 @@ def configure(env): env['AR'] = basecmd + "ar" env['RANLIB'] = basecmd + "ranlib" env['AS'] = basecmd + "as" + env.Append(CCFLAGS=['-D__MACPORTS__']) #hack to fix libvpx MM256_BROADCASTSI128_SI256 define if (env["CXX"] == "clang++"): env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) diff --git a/platform/osx/godot_main_osx.mm b/platform/osx/godot_main_osx.mm index 6ccbaf896b..64116fa1e0 100644 --- a/platform/osx/godot_main_osx.mm +++ b/platform/osx/godot_main_osx.mm @@ -101,5 +101,5 @@ int main(int argc, char **argv) { Main::cleanup(); - return 0; + return os.get_exit_code(); }; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index ef23d61141..0c5524dd08 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -192,6 +192,7 @@ static Vector2 get_mouse_pos(NSEvent *event) { // Note: called before main loop init! char *utfs = strdup([filename UTF8String]); OS_OSX::singleton->open_with_filename.parse_utf8(utfs); + free(utfs); return YES; } @@ -838,6 +839,108 @@ static int translateKey(unsigned int key) { return table[key]; } +struct _KeyCodeMap { + UniChar kchar; + int kcode; +}; + +static const _KeyCodeMap _keycodes[55] = { + { '`', KEY_QUOTELEFT }, + { '~', KEY_ASCIITILDE }, + { '0', KEY_KP_0 }, + { '1', KEY_KP_1 }, + { '2', KEY_KP_2 }, + { '3', KEY_KP_3 }, + { '4', KEY_KP_4 }, + { '5', KEY_KP_5 }, + { '6', KEY_KP_6 }, + { '7', KEY_KP_7 }, + { '8', KEY_KP_8 }, + { '9', KEY_KP_9 }, + { '-', KEY_MINUS }, + { '_', KEY_UNDERSCORE }, + { '=', KEY_EQUAL }, + { '+', KEY_PLUS }, + { 'q', KEY_Q }, + { 'w', KEY_W }, + { 'e', KEY_E }, + { 'r', KEY_R }, + { 't', KEY_T }, + { 'y', KEY_Y }, + { 'u', KEY_U }, + { 'i', KEY_I }, + { 'o', KEY_O }, + { 'p', KEY_P }, + { '[', KEY_BRACERIGHT }, + { ']', KEY_BRACELEFT }, + { '{', KEY_BRACERIGHT }, + { '}', KEY_BRACELEFT }, + { 'a', KEY_A }, + { 's', KEY_S }, + { 'd', KEY_D }, + { 'f', KEY_F }, + { 'g', KEY_G }, + { 'h', KEY_H }, + { 'j', KEY_J }, + { 'k', KEY_K }, + { 'l', KEY_L }, + { ';', KEY_SEMICOLON }, + { ':', KEY_COLON }, + { '\'', KEY_APOSTROPHE }, + { '\"', KEY_QUOTEDBL }, + { '\\', KEY_BACKSLASH }, + { '#', KEY_NUMBERSIGN }, + { 'z', KEY_Z }, + { 'x', KEY_X }, + { 'c', KEY_C }, + { 'v', KEY_V }, + { 'b', KEY_B }, + { 'n', KEY_N }, + { 'm', KEY_M }, + { ',', KEY_COMMA }, + { '.', KEY_PERIOD }, + { '/', KEY_SLASH } +}; + +static int remapKey(unsigned int key) { + + TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); + if (!currentKeyboard) + return translateKey(key); + + CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); + if (!layoutData) + return nil; + + const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData); + + UInt32 keysDown = 0; + UniChar chars[4]; + UniCharCount realLength; + + OSStatus err = UCKeyTranslate(keyboardLayout, + key, + kUCKeyActionDisplay, + 0, + LMGetKbdType(), + kUCKeyTranslateNoDeadKeysBit, + &keysDown, + sizeof(chars) / sizeof(chars[0]), + &realLength, + chars); + + if (err != noErr) { + return translateKey(key); + } + + for (unsigned int i = 0; i < 55; i++) { + if (_keycodes[i].kchar == chars[0]) { + return _keycodes[i].kcode; + } + } + return translateKey(key); +} + - (void)keyDown:(NSEvent *)event { //disable raw input in IME mode @@ -847,7 +950,7 @@ static int translateKey(unsigned int key) { ke.osx_state = [event modifierFlags]; ke.pressed = true; ke.echo = [event isARepeat]; - ke.scancode = latin_keyboard_keycode_convert(translateKey([event keyCode])); + ke.scancode = remapKey([event keyCode]); ke.unicode = 0; push_to_key_event_buffer(ke); @@ -900,7 +1003,7 @@ static int translateKey(unsigned int key) { } ke.osx_state = mod; - ke.scancode = latin_keyboard_keycode_convert(translateKey(key)); + ke.scancode = remapKey(key); ke.unicode = 0; push_to_key_event_buffer(ke); @@ -916,7 +1019,7 @@ static int translateKey(unsigned int key) { ke.osx_state = [event modifierFlags]; ke.pressed = false; ke.echo = false; - ke.scancode = latin_keyboard_keycode_convert(translateKey([event keyCode])); + ke.scancode = remapKey([event keyCode]); ke.unicode = 0; push_to_key_event_buffer(ke); @@ -1404,9 +1507,7 @@ void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c Ref<Texture> texture = p_cursor; Ref<Image> image = texture->get_data(); - int image_size = 32 * 32; - - ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32); + ERR_FAIL_COND(texture->get_width() > 256 || texture->get_height() > 256); NSBitmapImageRep *imgrep = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py index 3ee195e4f9..0e7b125dc5 100644 --- a/platform/uwp/detect.py +++ b/platform/uwp/detect.py @@ -43,6 +43,8 @@ def get_flags(): def configure(env): + env.msvc = True + if (env["bits"] != "default"): print("Error: bits argument is disabled for MSVC") print(""" diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 13fe781ff3..f51d969c16 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1896,26 +1896,25 @@ void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shap Ref<Texture> texture = p_cursor; Ref<Image> image = texture->get_data(); - UINT image_size = 32 * 32; + UINT image_size = texture->get_width() * texture->get_height(); UINT size = sizeof(UINT) * image_size; - ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32); + ERR_FAIL_COND(texture->get_width() > 256 || texture->get_height() > 256); // Create the BITMAP with alpha channel COLORREF *buffer = (COLORREF *)malloc(sizeof(COLORREF) * image_size); image->lock(); for (UINT index = 0; index < image_size; index++) { - int column_index = floor(index / 32); - int row_index = index % 32; + int row_index = floor(index / texture->get_width()); + int column_index = index % texture->get_width(); - Color pcColor = image->get_pixel(row_index, column_index); - *(buffer + index) = image->get_pixel(row_index, column_index).to_argb32(); + *(buffer + index) = image->get_pixel(column_index, row_index).to_argb32(); } image->unlock(); // Using 4 channels, so 4 * 8 bits - HBITMAP bitmap = CreateBitmap(32, 32, 1, 4 * 8, buffer); + HBITMAP bitmap = CreateBitmap(texture->get_width(), texture->get_height(), 1, 4 * 8, buffer); COLORREF clrTransparent = -1; // Create the AND and XOR masks for the bitmap diff --git a/platform/x11/detect.py b/platform/x11/detect.py index da2b0701b6..5820a926e9 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -234,10 +234,10 @@ def configure(env): print("ALSA libraries not found, disabling driver") if env['pulseaudio']: - if (os.system("pkg-config --exists libpulse-simple") == 0): # 0 means found + if (os.system("pkg-config --exists libpulse") == 0): # 0 means found print("Enabling PulseAudio") env.Append(CPPFLAGS=["-DPULSEAUDIO_ENABLED"]) - env.ParseConfig('pkg-config --cflags --libs libpulse-simple') + env.ParseConfig('pkg-config --cflags --libs libpulse') else: print("PulseAudio development libraries not found, disabling driver") diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index c06c7516d0..338d13410f 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -2375,11 +2375,11 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c Ref<Texture> texture = p_cursor; Ref<Image> image = texture->get_data(); - ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32); + ERR_FAIL_COND(texture->get_width() > 256 || texture->get_height() > 256); // Create the cursor structure XcursorImage *cursor_image = XcursorImageCreate(texture->get_width(), texture->get_height()); - XcursorUInt image_size = 32 * 32; + XcursorUInt image_size = texture->get_width() * texture->get_height(); XcursorDim size = sizeof(XcursorPixel) * image_size; cursor_image->version = 1; @@ -2393,10 +2393,10 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c image->lock(); for (XcursorPixel index = 0; index < image_size; index++) { - int column_index = floor(index / 32); - int row_index = index % 32; + int row_index = floor(index / texture->get_width()); + int column_index = index % texture->get_width(); - *(cursor_image->pixels + index) = image->get_pixel(row_index, column_index).to_argb32(); + *(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32(); } image->unlock(); diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index 5cca5705a0..918fb1616f 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -821,6 +821,12 @@ float CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const void CanvasItem::_notify_transform(CanvasItem *p_node) { + /* This check exists to avoid re-propagating the transform + * notification down the tree on dirty nodes. It provides + * optimization by avoiding redundancy (nodes are dirty, will get the + * notification anyway). + */ + if (/*p_node->xform_change.in_list() &&*/ p_node->global_invalid) { return; //nothing to do } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index b602839b99..c126dd8f6b 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -265,12 +265,18 @@ void TileMap::_update_dirty_quadrants() { SceneTree *st = SceneTree::get_singleton(); Color debug_collision_color; + Color debug_navigation_color; bool debug_shapes = st && st->is_debugging_collisions_hint(); if (debug_shapes) { debug_collision_color = st->get_debug_collisions_color(); } + bool debug_navigation = st && st->is_debugging_navigation_hint(); + if (debug_navigation) { + debug_navigation_color = st->get_debug_navigation_color(); + } + while (dirty_quadrant_list.first()) { Quadrant &q = *dirty_quadrant_list.first()->self(); @@ -497,6 +503,55 @@ void TileMap::_update_dirty_quadrants() { np.id = pid; np.xform = xform; q.navpoly_ids[E->key()] = np; + + if (debug_navigation) { + RID debug_navigation_item = vs->canvas_item_create(); + vs->canvas_item_set_parent(debug_navigation_item, canvas_item); + vs->canvas_item_set_z_as_relative_to_parent(debug_navigation_item, false); + vs->canvas_item_set_z_index(debug_navigation_item, VS::CANVAS_ITEM_Z_MAX - 2); // Display one below collision debug + + if (debug_navigation_item.is_valid()) { + PoolVector<Vector2> navigation_polygon_vertices = navpoly->get_vertices(); + int vsize = navigation_polygon_vertices.size(); + + if (vsize > 2) { + Vector<Color> colors; + Vector<Vector2> vertices; + vertices.resize(vsize); + colors.resize(vsize); + { + PoolVector<Vector2>::Read vr = navigation_polygon_vertices.read(); + for (int i = 0; i < vsize; i++) { + vertices[i] = vr[i]; + colors[i] = debug_navigation_color; + } + } + + Vector<int> indices; + + for (int i = 0; i < navpoly->get_polygon_count(); i++) { + Vector<int> polygon = navpoly->get_polygon(i); + + for (int j = 2; j < polygon.size(); j++) { + + int kofs[3] = { 0, j - 1, j }; + for (int k = 0; k < 3; k++) { + + int idx = polygon[kofs[k]]; + ERR_FAIL_INDEX(idx, vsize); + indices.push_back(idx); + } + } + } + Transform2D navxform; + navxform.set_origin(offset.floor()); + _fix_cell_transform(navxform, c, npoly_ofs + center_ofs, s); + + vs->canvas_item_set_transform(debug_navigation_item, navxform); + vs->canvas_item_add_triangle_array(debug_navigation_item, indices, vertices, colors); + } + } + } } } diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp index 8617bbc2f6..693b416f6d 100644 --- a/scene/3d/particles.cpp +++ b/scene/3d/particles.cpp @@ -849,9 +849,9 @@ void ParticlesMaterial::_update_shader() { code += " vec4(1.250, -1.050, -0.203, 0.0),\n"; code += " vec4(0.000, 0.000, 0.000, 0.0)) * hue_rot_s;\n"; if (color_ramp.is_valid()) { - code += " COLOR = textureLod(color_ramp,vec2(CUSTOM.y,0.0),0.0) * hue_rot_mat;\n"; + code += " COLOR = hue_rot_mat * textureLod(color_ramp,vec2(CUSTOM.y,0.0),0.0);\n"; } else { - code += " COLOR = color_value * hue_rot_mat;\n"; + code += " COLOR = hue_rot_mat * color_value;\n"; } if (emission_color_texture.is_valid() && emission_shape >= EMISSION_SHAPE_POINTS) { code += " COLOR*= texelFetch(emission_texture_color,emission_tex_ofs,0);\n"; diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp index ed3bde9504..b72665aa2b 100644 --- a/scene/3d/vehicle_body.cpp +++ b/scene/3d/vehicle_body.cpp @@ -524,7 +524,7 @@ void VehicleBody::_update_suspension(PhysicsDirectBodyState *s) { //bilateral constraint between two dynamic objects void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, - PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse) { + PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, real_t p_rollInfluence) { real_t normalLenSqr = normal.length_squared(); //ERR_FAIL_COND( normalLenSqr < real_t(1.1)); @@ -582,8 +582,12 @@ void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vec rel_vel = normal.dot(vel); - //TODO: move this into proper structure - real_t contactDamping = real_t(0.4); + // !BAS! We had this set to 0.4, in bullet its 0.2 + // real_t contactDamping = real_t(0.2); + + // !BAS! But seeing we apply this frame by frame, makes more sense to me to make this time based + // keeping in mind our anti roll factor + real_t contactDamping = s->get_step() / p_rollInfluence; #define ONLY_USE_LINEAR_MASS #ifdef ONLY_USE_LINEAR_MASS real_t massTerm = real_t(1.) / ((1.0 / mass) + b2invmass); @@ -704,7 +708,7 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { _resolve_single_bilateral(s, wheelInfo.m_raycastInfo.m_contactPointWS, wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS, - m_axle[i], m_sideImpulse[i]); + m_axle[i], m_sideImpulse[i], wheelInfo.m_rollInfluence); m_sideImpulse[i] *= sideFrictionStiffness2; } diff --git a/scene/3d/vehicle_body.h b/scene/3d/vehicle_body.h index 7810a42e8a..1ac3693cc4 100644 --- a/scene/3d/vehicle_body.h +++ b/scene/3d/vehicle_body.h @@ -168,7 +168,7 @@ class VehicleBody : public RigidBody { btVehicleWheelContactPoint(PhysicsDirectBodyState *s, PhysicsBody *body1, const Vector3 &frictionPosWorld, const Vector3 &frictionDirectionWorld, real_t maxImpulse); }; - void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse); + void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, real_t p_rollInfluence); real_t _calc_rolling_friction(btVehicleWheelContactPoint &contactPoint); void _update_friction(PhysicsDirectBodyState *s); diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 04e7d5cc10..2cf488ade4 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -590,8 +590,8 @@ void AnimationPlayer::_animation_update_transforms() { Transform t; t.origin = nc->loc_accum; - t.basis = nc->rot_accum; t.basis.scale(nc->scale_accum); + t.basis.rotate(nc->rot_accum.get_euler()); if (nc->skeleton && nc->bone_idx >= 0) { diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index 89f0e43a86..e811b7a7b3 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -895,13 +895,13 @@ void AnimationTreePlayer::_process_animation(float p_delta) { } Transform xform; - xform.basis = t.rot; xform.origin = t.loc; t.scale.x += 1.0; t.scale.y += 1.0; t.scale.z += 1.0; xform.basis.scale(t.scale); + xform.basis.rotate(t.rot.get_euler()); if (t.bone_idx >= 0) { if (t.skeleton) diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 2e74faa61d..87cf4dc334 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -59,7 +59,6 @@ void MenuButton::pressed() { popup->set_size(Size2(size.width, 0)); popup->set_parent_rect(Rect2(Point2(gp - popup->get_global_position()), get_size())); popup->popup(); - popup->set_invalidate_click_until_motion(); } void MenuButton::_gui_input(Ref<InputEvent> p_event) { @@ -109,7 +108,6 @@ MenuButton::MenuButton() { add_child(popup); popup->set_as_toplevel(true); popup->set_pass_on_modal_close_click(false); - connect("button_up", popup, "call_deferred", make_binds("grab_click_focus")); set_process_unhandled_key_input(true); set_action_mode(ACTION_MODE_BUTTON_PRESS); } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 6e53f11b99..aaad10f579 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -118,7 +118,7 @@ void OptionButton::add_icon_item(const Ref<Texture> &p_icon, const String &p_lab } void OptionButton::add_item(const String &p_label, int p_ID) { - popup->add_check_item(p_label, p_ID); + popup->add_radio_check_item(p_label, p_ID); if (popup->get_item_count() == 1) select(0); } diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 747230e69f..9ff3bd6e81 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -55,7 +55,7 @@ Size2 PopupMenu::get_minimum_size() const { float max_w = 0; int font_h = font->get_height(); - int check_w = get_icon("checked")->get_width(); + int check_w = MAX(get_icon("checked")->get_width(), get_icon("radio_checked")->get_width()); int accel_max_w = 0; for (int i = 0; i < items.size(); i++) { @@ -74,7 +74,7 @@ Size2 PopupMenu::get_minimum_size() const { size.width += items[i].h_ofs; - if (items[i].checkable) { + if (items[i].checkable_type) { size.width += check_w + hseparation; } @@ -284,7 +284,8 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { if (b->is_pressed()) return; - switch (b->get_button_index()) { + int button_idx = b->get_button_index(); + switch (button_idx) { case BUTTON_WHEEL_DOWN: { @@ -298,30 +299,37 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { _scroll(b->get_factor(), b->get_position()); } } break; - case BUTTON_LEFT: { - - int over = _get_mouse_over(b->get_position()); - - if (invalidated_click) { - invalidated_click = false; - break; - } - if (over < 0) { - hide(); - break; //non-activable + default: { + // Allow activating item by releasing the LMB or any that was down when the popup appeared + if (button_idx == BUTTON_LEFT || (initial_button_mask & (1 << (button_idx - 1)))) { + + bool was_during_grabbed_click = during_grabbed_click; + during_grabbed_click = false; + + int over = _get_mouse_over(b->get_position()); + + if (invalidated_click) { + invalidated_click = false; + break; + } + if (over < 0) { + if (!was_during_grabbed_click) { + hide(); + } + break; //non-activable + } + + if (items[over].separator || items[over].disabled) + break; + + if (items[over].submenu != "") { + + _activate_submenu(over); + return; + } + activate_item(over); } - - if (items[over].separator || items[over].disabled) - break; - - if (items[over].submenu != "") { - - _activate_submenu(over); - return; - } - activate_item(over); - - } break; + } } //update(); @@ -408,8 +416,9 @@ void PopupMenu::_notification(int p_what) { Ref<StyleBox> style = get_stylebox("panel"); Ref<StyleBox> hover = get_stylebox("hover"); Ref<Font> font = get_font("font"); - Ref<Texture> check = get_icon("checked"); - Ref<Texture> uncheck = get_icon("unchecked"); + // In Item::checkable_type enum order (less the non-checkable member) + Ref<Texture> check[] = { get_icon("checked"), get_icon("radio_checked") }; + Ref<Texture> uncheck[] = { get_icon("unchecked"), get_icon("radio_unchecked") }; Ref<Texture> submenu = get_icon("submenu"); Ref<StyleBox> separator = get_stylebox("separator"); @@ -452,14 +461,10 @@ void PopupMenu::_notification(int p_what) { separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h))); } - if (items[i].checkable) { - - if (items[i].checked) - check->draw(ci, item_ofs + Point2(0, Math::floor((h - check->get_height()) / 2.0))); - else - uncheck->draw(ci, item_ofs + Point2(0, Math::floor((h - check->get_height()) / 2.0))); - - item_ofs.x += check->get_width() + hseparation; + if (items[i].checkable_type) { + Texture *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr(); + icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon->get_height()) / 2.0))); + item_ofs.x += icon->get_width() + hseparation; } if (!items[i].icon.is_null()) { @@ -503,6 +508,11 @@ void PopupMenu::_notification(int p_what) { update(); } } break; + case NOTIFICATION_POST_POPUP: { + + initial_button_mask = Input::get_singleton()->get_mouse_button_mask(); + during_grabbed_click = (bool)initial_button_mask; + } break; case NOTIFICATION_POPUP_HIDE: { if (mouse_over >= 0) { @@ -554,10 +564,11 @@ void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_ item.xl_text = tr(p_label); item.accel = p_accel; item.ID = p_ID; - item.checkable = true; + item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); update(); } + void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel) { Item item; @@ -565,11 +576,18 @@ void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel item.xl_text = tr(p_label); item.accel = p_accel; item.ID = p_ID; - item.checkable = true; + item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); update(); } +void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p_accel) { + + add_check_item(p_label, p_ID, p_accel); + items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + update(); +} + void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) { ERR_FAIL_COND(p_shortcut.is_null()); @@ -598,6 +616,7 @@ void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_g items.push_back(item); update(); } + void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) { ERR_FAIL_COND(p_shortcut.is_null()); @@ -607,7 +626,7 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<Sh Item item; item.ID = p_ID; item.shortcut = p_shortcut; - item.checkable = true; + item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; item.icon = p_icon; item.shortcut_is_global = p_global; items.push_back(item); @@ -624,11 +643,18 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bo item.ID = p_ID; item.shortcut = p_shortcut; item.shortcut_is_global = p_global; - item.checkable = true; + item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); update(); } +void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) { + + add_check_shortcut(p_shortcut, p_ID, p_global); + items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + update(); +} + void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID, uint32_t p_accel) { Item item; @@ -636,7 +662,6 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int item.xl_text = tr(p_label); item.accel = p_accel; item.ID = p_ID; - item.checkable = false; item.max_states = p_max_states; item.state = p_default_state; items.push_back(item); @@ -811,7 +836,14 @@ bool PopupMenu::is_item_separator(int p_idx) const { void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].checkable = p_checkable; + items[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE; + update(); +} + +void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) { + + ERR_FAIL_INDEX(p_idx, items.size()); + items[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE; update(); } @@ -867,7 +899,12 @@ void PopupMenu::toggle_item_multistate(int p_idx) { bool PopupMenu::is_item_checkable(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, items.size(), false); - return items[p_idx].checkable; + return items[p_idx].checkable_type; +} + +bool PopupMenu::is_item_radio_checkable(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, items.size(), false); + return items[p_idx].checkable_type == Item::CHECKABLE_TYPE_RADIO_BUTTON; } int PopupMenu::get_item_count() const { @@ -941,7 +978,7 @@ void PopupMenu::activate_item(int p_item) { // We close all parents that are chained together, // with hide_on_item_selection enabled - if (items[p_item].checkable) { + if (items[p_item].checkable_type) { if (!hide_on_checkable_item_selection || !pop->is_hide_on_checkable_item_selection()) break; } else if (0 < items[p_item].max_states) { @@ -958,7 +995,7 @@ void PopupMenu::activate_item(int p_item) { // Hides popup by default; unless otherwise specified // by using set_hide_on_item_selection and set_hide_on_checkable_item_selection - if (items[p_item].checkable) { + if (items[p_item].checkable_type) { if (!hide_on_checkable_item_selection) return; } else if (0 < items[p_item].max_states) { @@ -1010,7 +1047,9 @@ Array PopupMenu::_get_items() const { items.push_back(get_item_text(i)); items.push_back(get_item_icon(i)); - items.push_back(is_item_checkable(i)); + // For compatibility, use false/true for no/checkbox and integers for other values + int ct = this->items[i].checkable_type; + items.push_back(Variant(ct <= Item::CHECKABLE_TYPE_CHECK_BOX ? is_item_checkable(i) : ct)); items.push_back(is_item_checked(i)); items.push_back(is_item_disabled(i)); @@ -1053,7 +1092,9 @@ void PopupMenu::_set_items(const Array &p_items) { String text = p_items[i + 0]; Ref<Texture> icon = p_items[i + 1]; + // For compatibility, use false/true for no/checkbox and integers for other values bool checkable = p_items[i + 2]; + bool radio_checkable = (int)p_items[i + 2] == Item::CHECKABLE_TYPE_RADIO_BUTTON; bool checked = p_items[i + 3]; bool disabled = p_items[i + 4]; @@ -1066,7 +1107,13 @@ void PopupMenu::_set_items(const Array &p_items) { int idx = get_item_count(); add_item(text, id); set_item_icon(idx, icon); - set_item_as_checkable(idx, checkable); + if (checkable) { + if (radio_checkable) { + set_item_as_radio_checkable(idx, true); + } else { + set_item_as_checkable(idx, true); + } + } set_item_checked(idx, checked); set_item_disabled(idx, disabled); set_item_id(idx, id); @@ -1147,12 +1194,14 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("add_item", "label", "id", "accel"), &PopupMenu::add_item, DEFVAL(-1), DEFVAL(0)); ClassDB::bind_method(D_METHOD("add_icon_check_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_check_item, DEFVAL(-1), DEFVAL(0)); ClassDB::bind_method(D_METHOD("add_check_item", "label", "id", "accel"), &PopupMenu::add_check_item, DEFVAL(-1), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("add_radio_check_item", "label", "id", "accel"), &PopupMenu::add_radio_check_item, DEFVAL(-1), DEFVAL(0)); ClassDB::bind_method(D_METHOD("add_submenu_item", "label", "submenu", "id"), &PopupMenu::add_submenu_item, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_shortcut", "shortcut", "id", "global"), &PopupMenu::add_shortcut, DEFVAL(-1), DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_icon_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_check_shortcut, DEFVAL(-1), DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_check_shortcut, DEFVAL(-1), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_radio_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_radio_check_shortcut, DEFVAL(-1), DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_item_text", "idx", "text"), &PopupMenu::set_item_text); ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "icon"), &PopupMenu::set_item_icon); @@ -1164,6 +1213,7 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_submenu", "idx", "submenu"), &PopupMenu::set_item_submenu); ClassDB::bind_method(D_METHOD("set_item_as_separator", "idx", "enable"), &PopupMenu::set_item_as_separator); ClassDB::bind_method(D_METHOD("set_item_as_checkable", "idx", "enable"), &PopupMenu::set_item_as_checkable); + ClassDB::bind_method(D_METHOD("set_item_as_radio_checkable", "idx", "enable"), &PopupMenu::set_item_as_radio_checkable); ClassDB::bind_method(D_METHOD("set_item_tooltip", "idx", "tooltip"), &PopupMenu::set_item_tooltip); ClassDB::bind_method(D_METHOD("set_item_shortcut", "idx", "shortcut", "global"), &PopupMenu::set_item_shortcut, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_item_multistate", "idx", "state"), &PopupMenu::set_item_multistate); @@ -1182,6 +1232,7 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("get_item_submenu", "idx"), &PopupMenu::get_item_submenu); ClassDB::bind_method(D_METHOD("is_item_separator", "idx"), &PopupMenu::is_item_separator); ClassDB::bind_method(D_METHOD("is_item_checkable", "idx"), &PopupMenu::is_item_checkable); + ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "idx"), &PopupMenu::is_item_radio_checkable); ClassDB::bind_method(D_METHOD("get_item_tooltip", "idx"), &PopupMenu::get_item_tooltip); ClassDB::bind_method(D_METHOD("get_item_shortcut", "idx"), &PopupMenu::get_item_shortcut); @@ -1216,15 +1267,20 @@ void PopupMenu::_bind_methods() { ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index"))); } -void PopupMenu::set_invalidate_click_until_motion() { +void PopupMenu::popup(const Rect2 &p_bounds) { + + grab_click_focus(); moved = Vector2(); invalidated_click = true; + Popup::popup(p_bounds); } PopupMenu::PopupMenu() { mouse_over = -1; submenu_over = -1; + initial_button_mask = 0; + during_grabbed_click = false; set_focus_mode(FOCUS_ALL); set_as_toplevel(true); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 60f36e95ec..c7851969d0 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -46,7 +46,11 @@ class PopupMenu : public Popup { String text; String xl_text; bool checked; - bool checkable; + enum { + CHECKABLE_TYPE_NONE, + CHECKABLE_TYPE_CHECK_BOX, + CHECKABLE_TYPE_RADIO_BUTTON, + } checkable_type; int max_states; int state; bool separator; @@ -63,7 +67,7 @@ class PopupMenu : public Popup { Item() { checked = false; - checkable = false; + checkable_type = CHECKABLE_TYPE_NONE; separator = false; max_states = 0; state = 0; @@ -78,6 +82,8 @@ class PopupMenu : public Popup { Timer *submenu_timer; List<Rect2> autohide_areas; Vector<Item> items; + int initial_button_mask; + bool during_grabbed_click; int mouse_over; int submenu_over; Rect2 parent_rect; @@ -115,12 +121,14 @@ public: void add_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0); void add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0); void add_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0); + void add_radio_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0); void add_submenu_item(const String &p_label, const String &p_submenu, int p_ID = -1); void add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false); void add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false); void add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false); void add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false); + void add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false); void add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID = -1, uint32_t p_accel = 0); @@ -134,6 +142,7 @@ public: void set_item_submenu(int p_idx, const String &p_submenu); void set_item_as_separator(int p_idx, bool p_separator); void set_item_as_checkable(int p_idx, bool p_checkable); + void set_item_as_radio_checkable(int p_idx, bool p_radio_checkable); void set_item_tooltip(int p_idx, const String &p_tooltip); void set_item_shortcut(int p_idx, const Ref<ShortCut> &p_shortcut, bool p_global = false); void set_item_h_offset(int p_idx, int p_offset); @@ -154,6 +163,7 @@ public: String get_item_submenu(int p_idx) const; bool is_item_separator(int p_idx) const; bool is_item_checkable(int p_idx) const; + bool is_item_radio_checkable(int p_idx) const; String get_item_tooltip(int p_idx) const; Ref<ShortCut> get_item_shortcut(int p_idx) const; int get_item_state(int p_idx) const; @@ -178,7 +188,6 @@ public: void add_autohide_area(const Rect2 &p_area); void clear_autohide_areas(); - void set_invalidate_click_until_motion(); void set_hide_on_item_selection(bool p_enabled); bool is_hide_on_item_selection() const; @@ -188,6 +197,8 @@ public: void set_hide_on_multistate_item_selection(bool p_enabled); bool is_hide_on_multistate_item_selection() const; + virtual void popup(const Rect2 &p_bounds = Rect2()); + PopupMenu(); ~PopupMenu(); }; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index ae07d5e671..6bfc4d4dee 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -516,6 +516,39 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & table->total_width += table->columns[i].width + hseparation; } + //resize to max_width if needed and distribute the remaining space + bool table_need_fit = true; + while (table_need_fit) { + table_need_fit = false; + //fit slim + for (int i = 0; i < table->columns.size(); i++) { + if (!table->columns[i].expand) + continue; + int dif = table->columns[i].width - table->columns[i].max_width; + if (dif > 0) { + table_need_fit = true; + table->columns[i].width = table->columns[i].max_width; + table->total_width -= dif; + total_ratio -= table->columns[i].expand_ratio; + } + } + //grow + remaining_width = available_width - table->total_width; + if (remaining_width > 0 && total_ratio > 0) { + for (int i = 0; i < table->columns.size(); i++) { + if (table->columns[i].expand) { + int dif = table->columns[i].max_width - table->columns[i].width; + if (dif > 0) { + int slice = table->columns[i].expand_ratio * remaining_width / total_ratio; + int incr = MIN(dif, slice); + table->columns[i].width += incr; + table->total_width += incr; + } + } + } + } + } + //compute caches properly again with the right width idx = 0; for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) { diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 48bd733e80..e214a020d5 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -145,6 +145,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const { text[p_line].region_info.clear(); + int ending_color_region = -1; for (int i = 0; i < len; i++) { if (!_is_symbol(str[i])) @@ -184,6 +185,12 @@ void TextEdit::Text::_update_line_cache(int p_line) const { cri.region = j; text[p_line].region_info[i] = cri; i += lr - 1; + + if (ending_color_region == -1 && !cr.line_only) { + ending_color_region = j; + } else if (ending_color_region == j) { + ending_color_region = -1; + } break; } @@ -211,10 +218,16 @@ void TextEdit::Text::_update_line_cache(int p_line) const { cri.region = j; text[p_line].region_info[i] = cri; i += lr - 1; + + if (ending_color_region == j) { + ending_color_region = -1; + } + break; } } } + text[p_line].ending_color_region = ending_color_region; } const Map<int, TextEdit::Text::ColorRegionInfo> &TextEdit::Text::get_color_region_info(int p_line) const { @@ -619,44 +632,10 @@ void TextEdit::_notification(int p_what) { Color color = cache.font_color; color.a *= readonly_alpha; - int in_region = -1; - if (syntax_coloring) { - if (cache.background_color.a > 0.01) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), cache.background_color); } - //compute actual region to start (may be inside say, a comment). - //slow in very large documents :( but ok for source! - - for (int i = 0; i < cursor.line_ofs; i++) { - - const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(i); - - if (in_region >= 0 && color_regions[in_region].line_only) { - in_region = -1; //reset regions that end at end of line - } - - for (const Map<int, Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) { - - const Text::ColorRegionInfo &cri = E->get(); - - if (in_region == -1) { - - if (!cri.end) { - - in_region = cri.region; - } - } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise - - if (cri.end || color_regions[cri.region].eq) { - - in_region = -1; - } - } - } - } } int brace_open_match_line = -1; @@ -804,7 +783,6 @@ void TextEdit::_notification(int p_what) { } } - int deregion = 0; //force it to clear inrgion Point2 cursor_pos; // get the highlighted words @@ -848,19 +826,12 @@ void TextEdit::_notification(int p_what) { if (smooth_scroll_enabled) ofs_y -= ((v_scroll->get_value() - get_line_scroll_pos()) * get_row_height()); - bool prev_is_char = false; - bool prev_is_number = false; - bool in_keyword = false; bool underlined = false; - bool in_word = false; - bool in_function_name = false; - bool in_member_variable = false; - bool is_hex_notation = false; - Color keyword_color; // check if line contains highlighted word int highlighted_text_col = -1; int search_text_col = -1; + int highlighted_word_col = -1; if (!search_text.empty()) search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0); @@ -868,7 +839,11 @@ void TextEdit::_notification(int p_what) { if (highlighted_text.length() != 0 && highlighted_text != search_text) highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0); - const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(line); + if (select_identifiers_enabled && highlighted_word.length() != 0) { + if (_is_char(highlighted_word[0])) { + highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0); + } + } if (text.is_marked(line)) { @@ -936,170 +911,28 @@ void TextEdit::_notification(int p_what) { cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, ofs_y + cache.font->get_ascent()), fc, cache.line_number_color); } - //loop through characters in one line - for (int j = 0; j < str.length(); j++) { - - //look for keyword - - if (deregion > 0) { - deregion--; - if (deregion == 0) - in_region = -1; - } - if (syntax_coloring && deregion == 0) { - - color = cache.font_color; //reset - color.a *= readonly_alpha; - //find keyword - bool is_char = _is_text_char(str[j]); - bool is_symbol = _is_symbol(str[j]); - bool is_number = _is_number(str[j]); - - if (j == 0 && in_region >= 0 && color_regions[in_region].line_only) { - in_region = -1; //reset regions that end at end of line - } - - // allow ABCDEF in hex notation - if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) { - is_number = true; - } else { - is_hex_notation = false; - } - - // check for dot or underscore or 'x' for hex notation in floating point number - if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) { - is_number = true; - is_symbol = false; - is_char = false; - - if (str[j] == 'x' && str[j - 1] == '0') { - is_hex_notation = true; - } - } - - if (!in_word && _is_char(str[j]) && !is_number) { - in_word = true; - } - - if ((in_keyword || in_word) && !is_hex_notation) { - is_number = false; - } - - if (is_symbol && str[j] != '.' && in_word) { - in_word = false; - } - - if (is_symbol && cri_map.has(j)) { - - const Text::ColorRegionInfo &cri = cri_map[j]; - - if (in_region == -1) { - - if (!cri.end) { - in_region = cri.region; - } - } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise - - if (cri.end || color_regions[cri.region].eq) { - - deregion = color_regions[cri.region].eq ? color_regions[cri.region].begin_key.length() : color_regions[cri.region].end_key.length(); - } - } - } - - if (!is_char) { - in_keyword = false; - underlined = false; - } - - if (in_region == -1 && !in_keyword && is_char && !prev_is_char) { - - int to = j; - while (to < str.length() && _is_text_char(str[to])) - to++; - - uint32_t hash = String::hash(&str[j], to - j); - StrRange range(&str[j], to - j); - - const Color *col = keywords.custom_getptr(range, hash); - - if (!col) { - col = member_keywords.custom_getptr(range, hash); - - if (col) { - for (int k = j - 1; k >= 0; k--) { - if (str[k] == '.') { - col = NULL; //member indexing not allowed - break; - } else if (str[k] > 32) { - break; - } - } - } - } - - if (col) { - - in_keyword = true; - keyword_color = *col; - } - - if (select_identifiers_enabled && highlighted_word != String()) { - if (highlighted_word == range) { - underlined = true; - } - } - } - - if (!in_function_name && in_word && !in_keyword) { - - int k = j; - while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') { - k++; - } - - // check for space between name and bracket - while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) { - k++; - } - - if (str[k] == '(') { - in_function_name = true; - } - } + //loop through characters in one line + Map<int, HighlighterInfo> color_map; + if (syntax_coloring) { + color_map = _get_line_syntax_highlighting(line); + } - if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) { - int k = j; - while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') { - k--; - } + // ensure we at least use the font color + Color current_color = cache.font_color; + if (readonly) { + current_color.a *= readonly_alpha; + } + for (int j = 0; j < str.length(); j++) { - if (str[k] == '.') { - in_member_variable = true; + if (syntax_coloring) { + if (color_map.has(j)) { + current_color = color_map[j].color; + if (readonly) { + current_color.a *= readonly_alpha; } } - - if (is_symbol) { - in_function_name = false; - in_member_variable = false; - } - - if (in_region >= 0) - color = color_regions[in_region].color; - else if (in_keyword) - color = keyword_color; - else if (in_member_variable) - color = cache.member_variable_color; - else if (in_function_name) - color = cache.function_color; - else if (is_symbol) - color = cache.symbol_color; - else if (is_number) - color = cache.number_color; - - prev_is_char = is_char; - prev_is_number = is_number; + color = current_color; } int char_w; @@ -1206,6 +1039,13 @@ void TextEdit::_notification(int p_what) { } } + if (highlighted_word_col != -1) { + if (j > highlighted_word_col + highlighted_word.length()) { + highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j); + } + underlined = (j >= highlighted_word_col && j < highlighted_word_col + highlighted_word.length()); + } + if (brace_matching_enabled) { if ((brace_open_match_line == line && brace_open_match_column == j) || (cursor.column == j && cursor.line == line && (brace_open_matching || brace_open_mismatch))) { @@ -1512,6 +1352,7 @@ void TextEdit::_notification(int p_what) { OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos + Point2(0, get_row_height())); OS::get_singleton()->set_ime_intermediate_text_callback(_ime_text_callback, this); } + } break; case NOTIFICATION_FOCUS_ENTER: { @@ -1528,7 +1369,6 @@ void TextEdit::_notification(int p_what) { if (raised_from_completion) { VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1); } - } break; case NOTIFICATION_FOCUS_EXIT: { @@ -4150,6 +3990,44 @@ void TextEdit::_update_caches() { cache.can_fold_icon = get_icon("GuiTreeArrowDown", "EditorIcons"); cache.folded_eol_icon = get_icon("GuiEllipsis", "EditorIcons"); text.set_font(cache.font); + + if (syntax_highlighter) { + syntax_highlighter->_update_cache(); + } +} + +SyntaxHighlighter *TextEdit::_get_syntax_highlighting() { + return syntax_highlighter; +} + +void TextEdit::_set_syntax_highlighting(SyntaxHighlighter *p_syntax_highlighter) { + syntax_highlighter = p_syntax_highlighter; + if (syntax_highlighter) { + syntax_highlighter->set_text_editor(this); + syntax_highlighter->_update_cache(); + } + update(); +} + +int TextEdit::_get_line_ending_color_region(int p_line) const { + if (p_line < 0 || p_line > text.size() - 1) { + return -1; + } + return text.get_line_ending_color_region(p_line); +} + +TextEdit::ColorRegion TextEdit::_get_color_region(int p_region) const { + if (p_region < 0 || p_region > color_regions.size()) { + return ColorRegion(); + } + return color_regions[p_region]; +} + +Map<int, TextEdit::Text::ColorRegionInfo> TextEdit::_get_line_color_region_info(int p_line) const { + if (p_line < 0 || p_line > text.size() - 1) { + return Map<int, Text::ColorRegionInfo>(); + } + return text.get_color_region_info(p_line); } void TextEdit::clear_colors() { @@ -4165,6 +4043,14 @@ void TextEdit::add_keyword_color(const String &p_keyword, const Color &p_color) update(); } +bool TextEdit::has_keyword_color(String p_keyword) const { + return keywords.has(p_keyword); +} + +Color TextEdit::get_keyword_color(String p_keyword) const { + return keywords[p_keyword]; +} + void TextEdit::add_color_region(const String &p_begin_key, const String &p_end_key, const Color &p_color, bool p_line_only) { color_regions.push_back(ColorRegion(p_begin_key, p_end_key, p_color, p_line_only)); @@ -4177,6 +4063,14 @@ void TextEdit::add_member_keyword(const String &p_keyword, const Color &p_color) update(); } +bool TextEdit::has_member_color(String p_member) const { + return member_keywords.has(p_member); +} + +Color TextEdit::get_member_color(String p_member) const { + return member_keywords[p_member]; +} + void TextEdit::clear_member_keywords() { member_keywords.clear(); update(); @@ -5691,6 +5585,8 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_v_scroll_speed"), &TextEdit::get_v_scroll_speed); ClassDB::bind_method(D_METHOD("add_keyword_color", "keyword", "color"), &TextEdit::add_keyword_color); + ClassDB::bind_method(D_METHOD("has_keyword_color", "keyword"), &TextEdit::has_keyword_color); + ClassDB::bind_method(D_METHOD("get_keyword_color", "keyword"), &TextEdit::get_keyword_color); ClassDB::bind_method(D_METHOD("add_color_region", "begin_key", "end_key", "color", "line_only"), &TextEdit::add_color_region, DEFVAL(false)); ClassDB::bind_method(D_METHOD("clear_colors"), &TextEdit::clear_colors); ClassDB::bind_method(D_METHOD("menu_option", "option"), &TextEdit::menu_option); @@ -5744,6 +5640,7 @@ TextEdit::TextEdit() { clear(); wrap = false; set_focus_mode(FOCUS_ALL); + syntax_highlighter = NULL; _update_caches(); cache.size = Size2(1, 1); cache.row_height = 1; @@ -5860,3 +5757,216 @@ TextEdit::TextEdit() { TextEdit::~TextEdit() { } + +/////////////////////////////////////////////////////////////////////////////// + +Map<int, TextEdit::HighlighterInfo> TextEdit::_get_line_syntax_highlighting(int p_line) { + if (syntax_highlighter != NULL) { + return syntax_highlighter->_get_line_syntax_highlighting(p_line); + } + + Map<int, HighlighterInfo> color_map; + + bool prev_is_char = false; + bool prev_is_number = false; + bool in_keyword = false; + bool in_word = false; + bool in_function_name = false; + bool in_member_variable = false; + bool is_hex_notation = false; + Color keyword_color; + Color color; + + int in_region = -1; + int deregion = 0; + for (int i = 0; i < p_line; i++) { + int ending_color_region = text.get_line_ending_color_region(i); + if (in_region == -1) { + in_region = ending_color_region; + } else if (in_region == ending_color_region) { + in_region = -1; + } else { + const Map<int, TextEdit::Text::ColorRegionInfo> &cri_map = text.get_color_region_info(i); + for (const Map<int, TextEdit::Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) { + const TextEdit::Text::ColorRegionInfo &cri = E->get(); + if (cri.region == in_region) { + in_region = -1; + } + } + } + } + + const Map<int, TextEdit::Text::ColorRegionInfo> cri_map = text.get_color_region_info(p_line); + const String &str = text[p_line]; + Color prev_color; + for (int j = 0; j < str.length(); j++) { + HighlighterInfo highlighter_info; + + if (deregion > 0) { + deregion--; + if (deregion == 0) { + in_region = -1; + } + } + + if (deregion != 0) { + if (color != prev_color) { + prev_color = color; + highlighter_info.color = color; + color_map[j] = highlighter_info; + } + continue; + } + + color = cache.font_color; + + bool is_char = _is_text_char(str[j]); + bool is_symbol = _is_symbol(str[j]); + bool is_number = _is_number(str[j]); + + // allow ABCDEF in hex notation + if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) { + is_number = true; + } else { + is_hex_notation = false; + } + + // check for dot or underscore or 'x' for hex notation in floating point number + if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) { + is_number = true; + is_symbol = false; + is_char = false; + + if (str[j] == 'x' && str[j - 1] == '0') { + is_hex_notation = true; + } + } + + if (!in_word && _is_char(str[j]) && !is_number) { + in_word = true; + } + + if ((in_keyword || in_word) && !is_hex_notation) { + is_number = false; + } + + if (is_symbol && str[j] != '.' && in_word) { + in_word = false; + } + + if (is_symbol && cri_map.has(j)) { + const TextEdit::Text::ColorRegionInfo &cri = cri_map[j]; + + if (in_region == -1) { + if (!cri.end) { + in_region = cri.region; + } + } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise + if (cri.end || color_regions[cri.region].eq) { + deregion = color_regions[cri.region].eq ? color_regions[cri.region].begin_key.length() : color_regions[cri.region].end_key.length(); + } + } + } + + if (!is_char) { + in_keyword = false; + } + + if (in_region == -1 && !in_keyword && is_char && !prev_is_char) { + + int to = j; + while (to < str.length() && _is_text_char(str[to])) + to++; + + uint32_t hash = String::hash(&str[j], to - j); + StrRange range(&str[j], to - j); + + const Color *col = keywords.custom_getptr(range, hash); + + if (!col) { + col = member_keywords.custom_getptr(range, hash); + + if (col) { + for (int k = j - 1; k >= 0; k--) { + if (str[k] == '.') { + col = NULL; //member indexing not allowed + break; + } else if (str[k] > 32) { + break; + } + } + } + } + + if (col) { + in_keyword = true; + keyword_color = *col; + } + } + + if (!in_function_name && in_word && !in_keyword) { + + int k = j; + while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') { + k++; + } + + // check for space between name and bracket + while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) { + k++; + } + + if (str[k] == '(') { + in_function_name = true; + } + } + + if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) { + int k = j; + while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') { + k--; + } + + if (str[k] == '.') { + in_member_variable = true; + } + } + + if (is_symbol) { + in_function_name = false; + in_member_variable = false; + } + + if (in_region >= 0) + color = color_regions[in_region].color; + else if (in_keyword) + color = keyword_color; + else if (in_member_variable) + color = cache.member_variable_color; + else if (in_function_name) + color = cache.function_color; + else if (is_symbol) + color = cache.symbol_color; + else if (is_number) + color = cache.number_color; + + prev_is_char = is_char; + prev_is_number = is_number; + + if (color != prev_color) { + prev_color = color; + highlighter_info.color = color; + color_map[j] = highlighter_info; + } + } + + return color_map; +} + +void SyntaxHighlighter::set_text_editor(TextEdit *p_text_editor) { + text_editor = p_text_editor; +} + +TextEdit *SyntaxHighlighter::get_text_editor() { + return text_editor; +} diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 8ac3b9fce6..2360ce79db 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -36,10 +36,84 @@ #include "scene/gui/scroll_bar.h" #include "scene/main/timer.h" +class SyntaxHighlighter; + class TextEdit : public Control { - GDCLASS(TextEdit, Control); + GDCLASS(TextEdit, Control) + +public: + struct HighlighterInfo { + Color color; + }; + + struct ColorRegion { + + Color color; + String begin_key; + String end_key; + bool line_only; + bool eq; + ColorRegion(const String &p_begin_key = "", const String &p_end_key = "", const Color &p_color = Color(), bool p_line_only = false) { + begin_key = p_begin_key; + end_key = p_end_key; + color = p_color; + line_only = p_line_only || p_end_key == ""; + eq = begin_key == end_key; + } + }; + + class Text { + public: + struct ColorRegionInfo { + + int region; + bool end; + }; + + struct Line { + int width_cache : 24; + bool marked : 1; + bool breakpoint : 1; + bool hidden : 1; + int ending_color_region; + Map<int, ColorRegionInfo> region_info; + String data; + }; + + private: + const Vector<ColorRegion> *color_regions; + mutable Vector<Line> text; + Ref<Font> font; + int indent_size; + + void _update_line_cache(int p_line) const; + + public: + void set_indent_size(int p_indent_size); + void set_font(const Ref<Font> &p_font); + void set_color_regions(const Vector<ColorRegion> *p_regions) { color_regions = p_regions; } + int get_line_width(int p_line) const; + int get_max_width(bool p_exclude_hidden = false) const; + const Map<int, ColorRegionInfo> &get_color_region_info(int p_line) const; + void set(int p_line, const String &p_text); + void set_marked(int p_line, bool p_marked) { text[p_line].marked = p_marked; } + bool is_marked(int p_line) const { return text[p_line].marked; } + void set_breakpoint(int p_line, bool p_breakpoint) { text[p_line].breakpoint = p_breakpoint; } + bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; } + void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; } + bool is_hidden(int p_line) const { return text[p_line].hidden; } + int get_line_ending_color_region(int p_line) const { return text[p_line].ending_color_region; } + void insert(int p_at, const String &p_text); + void remove(int p_at); + int size() const { return text.size(); } + void clear(); + void clear_caches(); + _FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; } + Text() { indent_size = 4; } + }; +private: struct Cursor { int last_fit_x; int line, column; ///< cursor @@ -115,70 +189,6 @@ class TextEdit : public Control { Size2 size; } cache; - struct ColorRegion { - - Color color; - String begin_key; - String end_key; - bool line_only; - bool eq; - ColorRegion(const String &p_begin_key = "", const String &p_end_key = "", const Color &p_color = Color(), bool p_line_only = false) { - begin_key = p_begin_key; - end_key = p_end_key; - color = p_color; - line_only = p_line_only || p_end_key == ""; - eq = begin_key == end_key; - } - }; - - class Text { - public: - struct ColorRegionInfo { - - int region; - bool end; - }; - - struct Line { - int width_cache : 24; - bool marked : 1; - bool breakpoint : 1; - bool hidden : 1; - Map<int, ColorRegionInfo> region_info; - String data; - }; - - private: - const Vector<ColorRegion> *color_regions; - mutable Vector<Line> text; - Ref<Font> font; - int indent_size; - - void _update_line_cache(int p_line) const; - - public: - void set_indent_size(int p_indent_size); - void set_font(const Ref<Font> &p_font); - void set_color_regions(const Vector<ColorRegion> *p_regions) { color_regions = p_regions; } - int get_line_width(int p_line) const; - int get_max_width(bool p_exclude_hidden = false) const; - const Map<int, ColorRegionInfo> &get_color_region_info(int p_line) const; - void set(int p_line, const String &p_text); - void set_marked(int p_line, bool p_marked) { text[p_line].marked = p_marked; } - bool is_marked(int p_line) const { return text[p_line].marked; } - void set_breakpoint(int p_line, bool p_breakpoint) { text[p_line].breakpoint = p_breakpoint; } - bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; } - void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; } - bool is_hidden(int p_line) const { return text[p_line].hidden; } - void insert(int p_at, const String &p_text); - void remove(int p_at); - int size() const { return text.size(); } - void clear(); - void clear_caches(); - _FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; } - Text() { indent_size = 4; } - }; - struct TextOperation { enum Type { @@ -209,9 +219,12 @@ class TextEdit : public Control { void _do_text_op(const TextOperation &p_op, bool p_reverse); //syntax coloring + SyntaxHighlighter *syntax_highlighter; HashMap<String, Color> keywords; HashMap<String, Color> member_keywords; + Map<int, HighlighterInfo> _get_line_syntax_highlighting(int p_line); + Vector<ColorRegion> color_regions; Set<String> completion_prefixes; @@ -391,6 +404,13 @@ protected: static void _bind_methods(); public: + SyntaxHighlighter *_get_syntax_highlighting(); + void _set_syntax_highlighting(SyntaxHighlighter *p_syntax_highlighter); + + int _get_line_ending_color_region(int p_line) const; + ColorRegion _get_color_region(int p_region) const; + Map<int, Text::ColorRegionInfo> _get_line_color_region_info(int p_line) const; + enum MenuItems { MENU_CUT, MENU_COPY, @@ -545,10 +565,15 @@ public: bool is_insert_mode() const; void add_keyword_color(const String &p_keyword, const Color &p_color); + bool has_keyword_color(String p_keyword) const; + Color get_keyword_color(String p_keyword) const; + void add_color_region(const String &p_begin_key = String(), const String &p_end_key = String(), const Color &p_color = Color(), bool p_line_only = false); void clear_colors(); void add_member_keyword(const String &p_keyword, const Color &p_color); + bool has_member_color(String p_member) const; + Color get_member_color(String p_member) const; void clear_member_keywords(); int get_v_scroll() const; @@ -621,4 +646,19 @@ public: VARIANT_ENUM_CAST(TextEdit::MenuItems); VARIANT_ENUM_CAST(TextEdit::SearchFlags); +class SyntaxHighlighter { +protected: + TextEdit *text_editor; + +public: + virtual void _update_cache() = 0; + virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line) = 0; + + virtual String get_name() = 0; + virtual List<String> get_supported_languages() = 0; + + void set_text_editor(TextEdit *p_text_editor); + TextEdit *get_text_editor(); +}; + #endif // TEXT_EDIT_H diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp index 4b3ba6df3c..82d983184b 100644 --- a/scene/gui/texture_progress.cpp +++ b/scene/gui/texture_progress.cpp @@ -106,6 +106,33 @@ Ref<Texture> TextureProgress::get_progress_texture() const { return progress; } +void TextureProgress::set_tint_under(const Color &p_tint) { + tint_under = p_tint; + update(); +} + +Color TextureProgress::get_tint_under() const { + return tint_under; +} + +void TextureProgress::set_tint_progress(const Color &p_tint) { + tint_progress = p_tint; + update(); +} + +Color TextureProgress::get_tint_progress() const { + return tint_progress; +} + +void TextureProgress::set_tint_over(const Color &p_tint) { + tint_over = p_tint; + update(); +} + +Color TextureProgress::get_tint_over() const { + return tint_over; +} + Point2 TextureProgress::unit_val_to_uv(float val) { if (progress.is_null()) return Point2(); @@ -170,7 +197,7 @@ Point2 TextureProgress::get_relative_center() { return p; } -void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio) { +void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate) { Vector2 texture_size = p_texture->get_size(); Vector2 topleft = Vector2(stretch_margin[MARGIN_LEFT], stretch_margin[MARGIN_TOP]); Vector2 bottomright = Vector2(stretch_margin[MARGIN_RIGHT], stretch_margin[MARGIN_BOTTOM]); @@ -240,7 +267,7 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F } RID ci = get_canvas_item(); - VS::get_singleton()->canvas_item_add_nine_patch(ci, dst_rect, src_rect, p_texture->get_rid(), topleft, bottomright); + VS::get_singleton()->canvas_item_add_nine_patch(ci, dst_rect, src_rect, p_texture->get_rid(), topleft, bottomright, VS::NINE_PATCH_STRETCH, VS::NINE_PATCH_STRETCH, true, p_modulate); } void TextureProgress::_notification(int p_what) { @@ -251,42 +278,42 @@ void TextureProgress::_notification(int p_what) { if (nine_patch_stretch && (mode == FILL_LEFT_TO_RIGHT || mode == FILL_RIGHT_TO_LEFT || mode == FILL_TOP_TO_BOTTOM || mode == FILL_BOTTOM_TO_TOP)) { if (under.is_valid()) { - draw_nine_patch_stretched(under, FILL_LEFT_TO_RIGHT, 1.0); + draw_nine_patch_stretched(under, FILL_LEFT_TO_RIGHT, 1.0, tint_under); } if (progress.is_valid()) { - draw_nine_patch_stretched(progress, mode, get_as_ratio()); + draw_nine_patch_stretched(progress, mode, get_as_ratio(), tint_progress); } if (over.is_valid()) { - draw_nine_patch_stretched(over, FILL_LEFT_TO_RIGHT, 1.0); + draw_nine_patch_stretched(over, FILL_LEFT_TO_RIGHT, 1.0, tint_over); } } else { if (under.is_valid()) - draw_texture(under, Point2()); + draw_texture(under, Point2(), tint_under); if (progress.is_valid()) { Size2 s = progress->get_size(); switch (mode) { case FILL_LEFT_TO_RIGHT: { Rect2 region = Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)); - draw_texture_rect_region(progress, region, region); + draw_texture_rect_region(progress, region, region, tint_progress); } break; case FILL_RIGHT_TO_LEFT: { Rect2 region = Rect2(Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y)); - draw_texture_rect_region(progress, region, region); + draw_texture_rect_region(progress, region, region, tint_progress); } break; case FILL_TOP_TO_BOTTOM: { Rect2 region = Rect2(Point2(), Size2(s.x, s.y * get_as_ratio())); - draw_texture_rect_region(progress, region, region); + draw_texture_rect_region(progress, region, region, tint_progress); } break; case FILL_BOTTOM_TO_TOP: { Rect2 region = Rect2(Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio())); - draw_texture_rect_region(progress, region, region); + draw_texture_rect_region(progress, region, region, tint_progress); } break; case FILL_CLOCKWISE: case FILL_COUNTER_CLOCKWISE: { float val = get_as_ratio() * rad_max_degrees / 360; if (val == 1) { Rect2 region = Rect2(Point2(), s); - draw_texture_rect_region(progress, region, region); + draw_texture_rect_region(progress, region, region, tint_progress); } else if (val != 0) { Array pts; float direction = mode == FILL_CLOCKWISE ? 1 : -1; @@ -311,7 +338,9 @@ void TextureProgress::_notification(int p_what) { uvs.push_back(uv); points.push_back(Point2(uv.x * s.x, uv.y * s.y)); } - draw_polygon(points, Vector<Color>(), uvs, progress); + Vector<Color> colors; + colors.push_back(tint_progress); + draw_polygon(points, colors, uvs, progress); } if (Engine::get_singleton()->is_editor_hint()) { Point2 p = progress->get_size(); @@ -323,11 +352,11 @@ void TextureProgress::_notification(int p_what) { } } break; default: - draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y))); + draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress); } } if (over.is_valid()) - draw_texture(over, Point2()); + draw_texture(over, Point2(), tint_over); } } break; @@ -389,6 +418,15 @@ void TextureProgress::_bind_methods() { ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &TextureProgress::set_fill_mode); ClassDB::bind_method(D_METHOD("get_fill_mode"), &TextureProgress::get_fill_mode); + ClassDB::bind_method(D_METHOD("set_tint_under", "tint"), &TextureProgress::set_tint_under); + ClassDB::bind_method(D_METHOD("get_tint_under"), &TextureProgress::get_tint_under); + + ClassDB::bind_method(D_METHOD("set_tint_progress", "tint"), &TextureProgress::set_tint_progress); + ClassDB::bind_method(D_METHOD("get_tint_progress"), &TextureProgress::get_tint_progress); + + ClassDB::bind_method(D_METHOD("set_tint_over", "tint"), &TextureProgress::set_tint_over); + ClassDB::bind_method(D_METHOD("get_tint_over"), &TextureProgress::get_tint_over); + ClassDB::bind_method(D_METHOD("set_radial_initial_angle", "mode"), &TextureProgress::set_radial_initial_angle); ClassDB::bind_method(D_METHOD("get_radial_initial_angle"), &TextureProgress::get_radial_initial_angle); @@ -409,6 +447,10 @@ void TextureProgress::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_over_texture", "get_over_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_progress_texture", "get_progress_texture"); ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise"), "set_fill_mode", "get_fill_mode"); + ADD_GROUP("Tint", "tint_"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_under", "get_tint_under"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_over", "get_tint_over"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_progress", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_progress", "get_tint_progress"); ADD_GROUP("Radial Fill", "radial_"); ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "radial_initial_angle", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_radial_initial_angle", "get_radial_initial_angle"); ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "radial_fill_degrees", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_fill_degrees", "get_fill_degrees"); @@ -440,4 +482,6 @@ TextureProgress::TextureProgress() { stretch_margin[MARGIN_RIGHT] = 0; stretch_margin[MARGIN_BOTTOM] = 0; stretch_margin[MARGIN_TOP] = 0; + + tint_under = tint_progress = tint_over = Color(1, 1, 1); } diff --git a/scene/gui/texture_progress.h b/scene/gui/texture_progress.h index 77c3980e29..34158b5db5 100644 --- a/scene/gui/texture_progress.h +++ b/scene/gui/texture_progress.h @@ -82,6 +82,15 @@ public: void set_nine_patch_stretch(bool p_stretch); bool get_nine_patch_stretch() const; + void set_tint_under(const Color &p_tint); + Color get_tint_under() const; + + void set_tint_progress(const Color &p_tint); + Color get_tint_progress() const; + + void set_tint_over(const Color &p_tint); + Color get_tint_over() const; + Size2 get_minimum_size() const; TextureProgress(); @@ -93,10 +102,11 @@ private: Point2 rad_center_off; bool nine_patch_stretch; int stretch_margin[4]; + Color tint_under, tint_progress, tint_over; Point2 unit_val_to_uv(float val); Point2 get_relative_center(); - void draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio); + void draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate); }; VARIANT_ENUM_CAST(TextureProgress::FillMode); diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp index 4eee0126d8..88e1847533 100644 --- a/scene/gui/video_player.cpp +++ b/scene/gui/video_player.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "video_player.h" +#include "scene/scene_string_names.h" #include "os/os.h" #include "servers/audio_server.h" @@ -159,11 +160,7 @@ void VideoPlayer::_notification(int p_notification) { bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus); - if (stream.is_null()) - return; - if (paused) - return; - if (!playback->is_playing()) + if (stream.is_null() || paused || !playback->is_playing()) return; double audio_time = USEC_TO_SEC(OS::get_singleton()->get_ticks_usec()); @@ -174,7 +171,11 @@ void VideoPlayer::_notification(int p_notification) { if (delta == 0) return; - playback->update(delta); + playback->update(delta); // playback->is_playing() returns false in the last video frame + + if (!playback->is_playing()) { + emit_signal(SceneStringNames::get_singleton()->finished); + } } break; @@ -467,6 +468,8 @@ void VideoPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_video_texture"), &VideoPlayer::get_video_texture); + ADD_SIGNAL(MethodInfo("finished")); + ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_track", PROPERTY_HINT_RANGE, "0,128,1"), "set_audio_track", "get_audio_track"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "VideoStream"), "set_stream", "get_stream"); //ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), "set_loop", "has_loop") ; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 08fbf44469..45a969eeda 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -181,6 +181,7 @@ public: Viewport::GUI::GUI() { mouse_focus = NULL; + mouse_click_grabber = NULL; mouse_focus_button = -1; key_focus = NULL; mouse_over = NULL; @@ -2278,7 +2279,7 @@ List<Control *>::Element *Viewport::_gui_show_modal(Control *p_control) { else p_control->_modal_set_prev_focus_owner(0); - if (gui.mouse_focus && !p_control->is_a_parent_of(gui.mouse_focus)) { + if (gui.mouse_focus && !p_control->is_a_parent_of(gui.mouse_focus) && !gui.mouse_click_grabber) { Ref<InputEventMouseButton> mb; mb.instance(); mb->set_position(gui.mouse_focus->get_local_mouse_position()); @@ -2300,9 +2301,22 @@ Control *Viewport::_gui_get_focus_owner() { void Viewport::_gui_grab_click_focus(Control *p_control) { + gui.mouse_click_grabber = p_control; + call_deferred("_post_gui_grab_click_focus"); +} + +void Viewport::_post_gui_grab_click_focus() { + + Control *focus_grabber = gui.mouse_click_grabber; + if (!focus_grabber) { + // Redundant grab requests were made + return; + } + gui.mouse_click_grabber = NULL; + if (gui.mouse_focus) { - if (gui.mouse_focus == p_control) + if (gui.mouse_focus == focus_grabber) return; Ref<InputEventMouseButton> mb; mb.instance(); @@ -2313,9 +2327,9 @@ void Viewport::_gui_grab_click_focus(Control *p_control) { mb->set_position(click); mb->set_button_index(gui.mouse_focus_button); mb->set_pressed(false); - gui.mouse_focus->call_deferred(SceneStringNames::get_singleton()->_gui_input, mb); + gui.mouse_focus->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb); - gui.mouse_focus = p_control; + gui.mouse_focus = focus_grabber; gui.focus_inv_xform = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse(); click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos); mb->set_position(click); @@ -2648,6 +2662,7 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_show_tooltip"), &Viewport::_gui_show_tooltip); ClassDB::bind_method(D_METHOD("_gui_remove_focus"), &Viewport::_gui_remove_focus); + ClassDB::bind_method(D_METHOD("_post_gui_grab_click_focus"), &Viewport::_post_gui_grab_click_focus); ClassDB::bind_method(D_METHOD("set_shadow_atlas_size", "size"), &Viewport::set_shadow_atlas_size); ClassDB::bind_method(D_METHOD("get_shadow_atlas_size"), &Viewport::get_shadow_atlas_size); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 07bbd3f1fa..94e49033e0 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -248,6 +248,7 @@ private: bool key_event_accepted; Control *mouse_focus; + Control *mouse_click_grabber; int mouse_focus_button; Control *key_focus; Control *mouse_over; @@ -323,6 +324,7 @@ private: bool _gui_control_has_focus(const Control *p_control); void _gui_control_grab_focus(Control *p_control); void _gui_grab_click_focus(Control *p_control); + void _post_gui_grab_click_focus(); void _gui_accept_event(); Control *_gui_get_focus_owner(); diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 3e244aa8f8..ea70797530 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -582,6 +582,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("checked", "PopupMenu", make_icon(checked_png)); theme->set_icon("unchecked", "PopupMenu", make_icon(unchecked_png)); + theme->set_icon("radio_checked", "PopupMenu", make_icon(radio_checked_png)); + theme->set_icon("radio_unchecked", "PopupMenu", make_icon(radio_unchecked_png)); theme->set_icon("submenu", "PopupMenu", make_icon(submenu_png)); theme->set_font("font", "PopupMenu", default_font); diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 949ba12a4c..b832ea1239 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -315,6 +315,8 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const { } } + ERR_FAIL_COND_V(arrays.size() != ARRAY_MAX, Ref<ArrayMesh>()); + { PoolVector<int>::Write ir; PoolVector<int> indices = arrays[ARRAY_INDEX]; diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp index 030b822f3b..597866eb74 100644 --- a/scene/resources/scene_format_text.cpp +++ b/scene/resources/scene_format_text.cpp @@ -1672,7 +1672,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r f->store_string(vars); } - f->store_line("]\n"); + f->store_line("]"); for (int j = 0; j < state->get_node_property_count(i); j++) { @@ -1682,10 +1682,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r f->store_string(_valprop(String(state->get_node_property_name(i, j))) + " = " + vars + "\n"); } - if (state->get_node_property_count(i)) { - //add space - f->store_line(String()); - } + f->store_line(String()); } for (int i = 0; i < state->get_connection_count(); i++) { @@ -1708,14 +1705,12 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r f->store_string(" binds= " + vars); } - f->store_line("]\n"); + f->store_line("]"); } - f->store_line(String()); - Vector<NodePath> editable_instances = state->get_editable_instances(); for (int i = 0; i < editable_instances.size(); i++) { - f->store_line("[editable path=\"" + editable_instances[i].operator String() + "\"]"); + f->store_line("\n[editable path=\"" + editable_instances[i].operator String() + "\"]"); } } diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index c3b0de6d9a..b08e41301a 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -101,6 +101,18 @@ int AudioDriver::get_total_channels_by_speaker_mode(AudioDriver::SpeakerMode p_m ERR_FAIL_V(2); } +Array AudioDriver::get_device_list() { + Array list; + + list.push_back("Default"); + + return list; +} + +String AudioDriver::get_device() { + return "Default"; +} + AudioDriver::AudioDriver() { _last_mix_time = 0; @@ -1108,6 +1120,21 @@ Ref<AudioBusLayout> AudioServer::generate_bus_layout() const { return state; } +Array AudioServer::get_device_list() { + + return AudioDriver::get_singleton()->get_device_list(); +} + +String AudioServer::get_device() { + + return AudioDriver::get_singleton()->get_device(); +} + +void AudioServer::set_device(String device) { + + AudioDriver::get_singleton()->set_device(device); +} + void AudioServer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bus_count", "amount"), &AudioServer::set_bus_count); @@ -1154,6 +1181,9 @@ void AudioServer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_speaker_mode"), &AudioServer::get_speaker_mode); ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioServer::get_mix_rate); + ClassDB::bind_method(D_METHOD("get_device_list"), &AudioServer::get_device_list); + ClassDB::bind_method(D_METHOD("get_device"), &AudioServer::get_device); + ClassDB::bind_method(D_METHOD("set_device"), &AudioServer::set_device); ClassDB::bind_method(D_METHOD("set_bus_layout", "bus_layout"), &AudioServer::set_bus_layout); ClassDB::bind_method(D_METHOD("generate_bus_layout"), &AudioServer::generate_bus_layout); diff --git a/servers/audio_server.h b/servers/audio_server.h index a8be48b4c3..af2668b69e 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -70,6 +70,9 @@ public: virtual void start() = 0; virtual int get_mix_rate() const = 0; virtual SpeakerMode get_speaker_mode() const = 0; + virtual Array get_device_list(); + virtual String get_device(); + virtual void set_device(String device) {} virtual void lock() = 0; virtual void unlock() = 0; virtual void finish() = 0; @@ -300,6 +303,10 @@ public: void set_bus_layout(const Ref<AudioBusLayout> &p_bus_layout); Ref<AudioBusLayout> generate_bus_layout() const; + Array get_device_list(); + String get_device(); + void set_device(String device); + AudioServer(); virtual ~AudioServer(); }; diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index d5a9bfb606..37aeef8999 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -1492,11 +1492,6 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "abs", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID } }, { "abs", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID } }, - { "abs", TYPE_UINT, { TYPE_UINT, TYPE_VOID } }, - { "abs", TYPE_UVEC2, { TYPE_UVEC2, TYPE_VOID } }, - { "abs", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID } }, - { "abs", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID } }, - { "sign", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } }, { "sign", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } }, { "sign", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } }, diff --git a/thirdparty/README.md b/thirdparty/README.md index d3fa0e4664..394d6098b0 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -234,7 +234,7 @@ changes are marked with `// -- GODOT --` comments. ## libwebsockets - Upstream: https://github.com/warmcat/libwebsockets -- Version: 2.4.1 +- Version: 2.4.2 - License: LGPLv2.1 + static linking exception File extracted from upstream source: @@ -246,6 +246,7 @@ File extracted from upstream source: - Also copy `win32helpers/` from `win32port/` - `mbedtls_wrapper/include/platform/ssl_port.h` has a small change to check for OSX and FreeBSD (missing `malloc.h`). The bug is fixed in upstream master via `LWS_HAVE_MALLOC_H`, but not in the 2.4.1 branch (as the file structure has changed). +- You might need to apply the patch in `thirdparty/lws/mbedtls_verify.diff` (port of PR 1215) to future `2.4.x` releases if it does not get cherry picked. Important: `lws_config.h` and `lws_config_private.h` contains custom Godot build configurations, check them out when updating. @@ -253,12 +254,15 @@ Godot build configurations, check them out when updating. ## mbedTLS - Upstream: https://tls.mbed.org/ -- Version: 2.7.0 +- Version: 2.8.0 - License: Apache 2.0 -File extracted from upstream release tarball `mbedtls-2.7.0-apache.tgz`: -- All `*.h` from `include/mbedtls/` to `thirdparty/include/mbedtls/` -- All `*.c` from `library/` to `thirdparty/library/` +File extracted from upstream release tarball `mbedtls-2.8.0-apache.tgz`: +- All `*.h` from `include/mbedtls/` to `thirdparty/mbedtls/include/mbedtls/` +- All `*.c` from `library/` to `thirdparty/mbedtls/library/` +- In file `thirdparty/mbedtls/library/net_sockets.c` mbedTLS overrides the `_WIN32_WINNT` define. + Be sure to check the Godot addition to only redfine it when undefined or `< 0x0501` (PRed upstream). +- Applied the patch in `thirdparty/mbedtls/1453.diff` (PR 1453). Soon to be merged upstream. Check it out at next update. ## minizip diff --git a/thirdparty/lws/client/client.c b/thirdparty/lws/client/client.c index 20450aa923..ded4e4bf0b 100644 --- a/thirdparty/lws/client/client.c +++ b/thirdparty/lws/client/client.c @@ -258,9 +258,10 @@ start_ws_handshake: #ifdef LWS_OPENSSL_SUPPORT /* we can retry this... just cook the SSL BIO the first time */ - if (wsi->use_ssl && !wsi->ssl) { - if (lws_ssl_client_bio_create(wsi)) - return -1; + if (wsi->use_ssl && !wsi->ssl && + lws_ssl_client_bio_create(wsi) < 0) { + cce = "bio_create failed"; + goto bail3; } if (wsi->use_ssl) { @@ -727,9 +728,10 @@ lws_client_interpret_server_handshake(struct lws *wsi) return 0; } - if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) { - lwsl_info("no ACCEPT\n"); - cce = "HS: ACCEPT missing"; + if (p && !strncmp(p, "401", 3)) { + lwsl_warn( + "lws_client_handshake: got bad HTTP response '%s'\n", p); + cce = "HS: ws upgrade unauthorized"; goto bail3; } @@ -740,6 +742,12 @@ lws_client_interpret_server_handshake(struct lws *wsi) goto bail3; } + if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) { + lwsl_info("no ACCEPT\n"); + cce = "HS: ACCEPT missing"; + goto bail3; + } + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE); if (!p) { lwsl_info("no UPGRADE\n"); diff --git a/thirdparty/lws/client/ssl-client.c b/thirdparty/lws/client/ssl-client.c index b69fd2da30..962c6e3cb5 100644 --- a/thirdparty/lws/client/ssl-client.c +++ b/thirdparty/lws/client/ssl-client.c @@ -176,11 +176,7 @@ lws_ssl_client_bio_create(struct lws *wsi) #endif #else #if defined(LWS_WITH_MBEDTLS) - if (wsi->vhost->x509_client_CA) - SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback); - else - SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, OpenSSL_client_verify_callback); - + SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback); #else #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME SSL_set_tlsext_host_name(wsi->ssl, hostname); @@ -284,9 +280,13 @@ some_wait: char *p = (char *)&pt->serv_buf[0]; char *sb = p; - lwsl_err("ssl hs1 error, X509_V_ERR = %d: %s\n", - n, ERR_error_string(n, sb)); + lwsl_err("ssl hs1 error, X509_V_ERR = %d: errno %d: %s\n", + n, errno, ERR_error_string(n, sb)); lws_ssl_elaborate_error(); +#if defined(LWS_WITH_MBEDTLS) + if (n == SSL_ERROR_SYSCALL) + return -1; +#endif } n = -1; diff --git a/thirdparty/lws/context.c b/thirdparty/lws/context.c index f67476b1e3..9f221f50f1 100644 --- a/thirdparty/lws/context.c +++ b/thirdparty/lws/context.c @@ -1621,7 +1621,7 @@ lws_context_destroy2(struct lws_context *context) lws_check_deferred_free(context, 1); #if LWS_MAX_SMP > 1 - pthread_mutex_destroy(&context->lock, NULL); + pthread_mutex_destroy(&context->lock); #endif lws_free(context); diff --git a/thirdparty/lws/libwebsockets.c b/thirdparty/lws/libwebsockets.c index 50f975d21e..8fe0854041 100644 --- a/thirdparty/lws/libwebsockets.c +++ b/thirdparty/lws/libwebsockets.c @@ -482,8 +482,9 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason) wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE) goto just_kill_connection; - if (wsi->mode == LWSCM_HTTP_SERVING || - wsi->mode == LWSCM_HTTP2_SERVING) { + if (!wsi->told_user_closed && + (wsi->mode == LWSCM_HTTP_SERVING || + wsi->mode == LWSCM_HTTP2_SERVING)) { if (wsi->user_space) wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_HTTP_DROP_PROTOCOL, @@ -583,7 +584,7 @@ just_kill_connection: lws_remove_child_from_any_parent(wsi); n = 0; - if (wsi->user_space) { + if (!wsi->told_user_closed && wsi->user_space) { lwsl_debug("%s: %p: DROP_PROTOCOL %s\n", __func__, wsi, wsi->protocol->name); wsi->protocol->callback(wsi, @@ -656,8 +657,10 @@ just_kill_connection: __func__, wsi, (int)(long)wsi->desc.sockfd, wsi->state); if (!wsi->socket_is_permanently_unusable && - lws_sockfd_valid(wsi->desc.sockfd)) + lws_sockfd_valid(wsi->desc.sockfd)) { + wsi->socket_is_permanently_unusable = 1; n = shutdown(wsi->desc.sockfd, SHUT_WR); + } } if (n) lwsl_debug("closing: shutdown (state %d) ret %d\n", diff --git a/thirdparty/lws/libwebsockets.h b/thirdparty/lws/libwebsockets.h index ef996c5d78..460c732602 100644 --- a/thirdparty/lws/libwebsockets.h +++ b/thirdparty/lws/libwebsockets.h @@ -1073,7 +1073,7 @@ enum lws_callback_reasons { LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS = 22, /**< if configured for * including OpenSSL support, this callback allows your user code - * to load extra certifcates into the server which allow it to + * to load extra certificates into the server which allow it to * verify the validity of certificates returned by clients. user * is the server's OpenSSL SSL_CTX* */ LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION = 23, @@ -4013,9 +4013,6 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs); #if !defined(LWS_SIZEOFPTR) #define LWS_SIZEOFPTR (sizeof (void *)) #endif -#if !defined(u_int64_t) -#define u_int64_t unsigned long long -#endif #if defined(__x86_64__) #define _LWS_PAD_SIZE 16 /* Intel recommended for best performance */ @@ -4808,7 +4805,7 @@ LWS_VISIBLE LWS_EXTERN unsigned long lws_now_secs(void); /** - * lws_get_context - Allow geting lws_context from a Websocket connection + * lws_get_context - Allow getting lws_context from a Websocket connection * instance * * With this function, users can access context in the callback function. diff --git a/thirdparty/lws/lws_config.h b/thirdparty/lws/lws_config.h index 3a918747b1..6005d94ec6 100644 --- a/thirdparty/lws/lws_config.h +++ b/thirdparty/lws/lws_config.h @@ -1,5 +1,10 @@ /* lws_config.h Generated from lws_config.h.in */ -#include "lws_config_private.h" + +/* GODOT ADDITION */ +#ifndef DEBUG_ENABLED +#define LWS_WITH_NO_LOGS +#endif +/* END GODOT ADDITION */ #ifndef NDEBUG #ifndef _DEBUG @@ -25,54 +30,45 @@ /* #undef LWS_WITH_PLUGINS */ /* #undef LWS_WITH_NO_LOGS */ -#ifndef DEBUG_ENABLED -#define LWS_WITH_NO_LOGS -#endif /* The Libwebsocket version */ -#define LWS_LIBRARY_VERSION "2.4.1" +#define LWS_LIBRARY_VERSION "2.4.2" #define LWS_LIBRARY_VERSION_MAJOR 2 #define LWS_LIBRARY_VERSION_MINOR 4 -#define LWS_LIBRARY_VERSION_PATCH 1 +#define LWS_LIBRARY_VERSION_PATCH 2 /* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */ #define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR*1000000)+(LWS_LIBRARY_VERSION_MINOR*1000)+LWS_LIBRARY_VERSION_PATCH /* The current git commit hash that we're building from */ -#define LWS_BUILD_HASH "55f97b7806e07db2d4c8a158172cd309d0faf450" +#define LWS_BUILD_HASH "8964ce9db75a98e463dfafd2e89f2bc8a95ec6ed" /* Build with OpenSSL support */ #define LWS_OPENSSL_SUPPORT /* The client should load and trust CA root certs it finds in the OS */ -#define LWS_SSL_CLIENT_USE_OS_CA_CERTS +/* #undef LWS_SSL_CLIENT_USE_OS_CA_CERTS */ /* Sets the path where the client certs should be installed. */ -#define LWS_OPENSSL_CLIENT_CERTS "../share" +/* #undef LWS_OPENSSL_CLIENT_CERTS "../share" */ /* Turn off websocket extensions */ /* #undef LWS_NO_EXTENSIONS */ /* Enable libev io loop */ /* #undef LWS_WITH_LIBEV */ -#undef LWS_WITH_LIBEV /* Enable libuv io loop */ /* #undef LWS_WITH_LIBUV */ -#undef LWS_WITH_LIBUV /* Enable libevent io loop */ /* #undef LWS_WITH_LIBEVENT */ -#undef LWS_WITH_LIBEVENT /* Build with support for ipv6 */ /* #undef LWS_WITH_IPV6 */ /* Build with support for UNIX domain socket */ /* #undef LWS_WITH_UNIX_SOCK */ -#ifdef WINDOWS_ENABLED -#undef LWS_USE_UNIX_SOCK -#endif /* Build with support for HTTP2 */ /* #undef LWS_WITH_HTTP2 */ @@ -100,7 +96,7 @@ /* SSL server using ECDH certificate */ /* #undef LWS_SSL_SERVER_WITH_ECDH_CERT */ -#define LWS_HAVE_SSL_CTX_set1_param +/* #undef LWS_HAVE_SSL_CTX_set1_param */ #define LWS_HAVE_X509_VERIFY_PARAM_set1_host /* #undef LWS_HAVE_RSA_SET0_KEY */ @@ -110,7 +106,7 @@ /* #undef LWS_WITH_CGI */ /* whether the Openssl is recent enough, and / or built with, ecdh */ -#define LWS_HAVE_OPENSSL_ECDH_H +/* #undef LWS_HAVE_OPENSSL_ECDH_H */ /* HTTP Proxy support */ /* #undef LWS_WITH_HTTP_PROXY */ @@ -157,9 +153,9 @@ /* OpenSSL various APIs */ -/* #undef LWS_HAVE_TLS_CLIENT_METHOD */ -#define LWS_HAVE_TLSV1_2_CLIENT_METHOD -#define LWS_HAVE_SSL_SET_INFO_CALLBACK +#define LWS_HAVE_TLS_CLIENT_METHOD +/* #undef LWS_HAVE_TLSV1_2_CLIENT_METHOD */ +/* #undef LWS_HAVE_SSL_SET_INFO_CALLBACK */ #define LWS_HAS_INTPTR_T diff --git a/thirdparty/lws/mbedtls_verify.diff b/thirdparty/lws/mbedtls_verify.diff new file mode 100644 index 0000000000..d320645d67 --- /dev/null +++ b/thirdparty/lws/mbedtls_verify.diff @@ -0,0 +1,74 @@ +diff --git a/thirdparty/lws/client/ssl-client.c b/thirdparty/lws/client/ssl-client.c +index 6626e0844..962c6e3cb 100644 +--- a/thirdparty/lws/client/ssl-client.c ++++ b/thirdparty/lws/client/ssl-client.c +@@ -176,11 +176,7 @@ lws_ssl_client_bio_create(struct lws *wsi) + #endif + #else + #if defined(LWS_WITH_MBEDTLS) +- if (wsi->vhost->x509_client_CA) +- SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback); +- else +- SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, OpenSSL_client_verify_callback); +- ++ SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback); + #else + #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + SSL_set_tlsext_host_name(wsi->ssl, hostname); +diff --git a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c b/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c +index 63504919c..4e3d61109 100644 +--- a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c ++++ b/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c +@@ -218,7 +218,7 @@ static int ssl_pm_reload_crt(SSL *ssl) + struct x509_pm *crt_pm = (struct x509_pm *)ssl->cert->x509->x509_pm; + + if (ssl->verify_mode == SSL_VERIFY_PEER) +- mode = MBEDTLS_SSL_VERIFY_REQUIRED; ++ mode = MBEDTLS_SSL_VERIFY_OPTIONAL; + else if (ssl->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT) + mode = MBEDTLS_SSL_VERIFY_OPTIONAL; + else if (ssl->verify_mode == SSL_VERIFY_CLIENT_ONCE) +@@ -712,11 +712,39 @@ long ssl_pm_get_verify_result(const SSL *ssl) + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl); +- if (ret) { +- SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_get_verify_result() return 0x%x", ret); ++ ++ if (!ret) ++ return X509_V_OK; ++ ++ if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED || ++ (ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED)) ++ // Allows us to use LCCSCF_ALLOW_SELFSIGNED to skip verification ++ verify_result = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; ++ ++ else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH) ++ verify_result = X509_V_ERR_HOSTNAME_MISMATCH; ++ ++ else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) || ++ (ret & MBEDTLS_X509_BADCRL_BAD_KEY)) ++ verify_result = X509_V_ERR_CA_KEY_TOO_SMALL; ++ ++ else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) || ++ (ret & MBEDTLS_X509_BADCRL_BAD_MD)) ++ verify_result = X509_V_ERR_CA_MD_TOO_WEAK; ++ ++ else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) || ++ (ret & MBEDTLS_X509_BADCRL_FUTURE)) ++ verify_result = X509_V_ERR_CERT_NOT_YET_VALID; ++ ++ else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) || ++ (ret & MBEDTLS_X509_BADCRL_EXPIRED)) ++ verify_result = X509_V_ERR_CERT_HAS_EXPIRED; ++ ++ else + verify_result = X509_V_ERR_UNSPECIFIED; +- } else +- verify_result = X509_V_OK; ++ ++ SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, ++ "mbedtls_ssl_get_verify_result() return 0x%x", ret); + + return verify_result; + } diff --git a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h b/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h index 45198bc978..2ca438c422 100644 --- a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h +++ b/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h @@ -215,6 +215,7 @@ struct ssl_st int (*verify_callback) (int ok, X509_STORE_CTX *ctx); int rwstate; + int interrupted_remaining_write; long verify_result; diff --git a/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h b/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h index 2ffd7e7544..eca68f20d1 100644 --- a/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h +++ b/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h @@ -25,11 +25,13 @@ */ #include "string.h" -#if defined(__APPLE__) || defined(__FreeBSD__) +/* GODOT ADDITION */ +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) #include <stdlib.h> #else #include "malloc.h" #endif +/* END GODOT ADDITION */ void *ssl_mem_zalloc(size_t size); diff --git a/thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c b/thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c index 187fc9f005..d8fdd06fad 100644 --- a/thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c +++ b/thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c @@ -142,9 +142,9 @@ int SSL_get_error(const SSL *ssl, int ret_code) ret = SSL_ERROR_NONE; else if (ret_code < 0) { - if (SSL_want_read(ssl)) + if (ssl->err == SSL_ERROR_WANT_READ || SSL_want_read(ssl)) ret = SSL_ERROR_WANT_READ; - else if (SSL_want_write(ssl)) + else if (ssl->err == SSL_ERROR_WANT_WRITE || SSL_want_write(ssl)) ret = SSL_ERROR_WANT_WRITE; else ret = SSL_ERROR_SYSCALL; //unknown @@ -457,7 +457,7 @@ int SSL_read(SSL *ssl, void *buffer, int len) int SSL_write(SSL *ssl, const void *buffer, int len) { int ret; - int send_bytes; + int send_bytes, bytes; const unsigned char *pbuf; SSL_ASSERT1(ssl); @@ -470,25 +470,36 @@ int SSL_write(SSL *ssl, const void *buffer, int len) pbuf = (const unsigned char *)buffer; do { - int bytes; - if (send_bytes > SSL_SEND_DATA_MAX_LENGTH) bytes = SSL_SEND_DATA_MAX_LENGTH; else bytes = send_bytes; + if (ssl->interrupted_remaining_write) { + bytes = ssl->interrupted_remaining_write; + ssl->interrupted_remaining_write = 0; + } + ret = SSL_METHOD_CALL(send, ssl, pbuf, bytes); + //printf("%s: ssl_pm said %d for %d requested (cum %d)\n", __func__, ret, bytes, len -send_bytes); + /* the return is a NEGATIVE OpenSSL error code, or the length sent */ if (ret > 0) { pbuf += ret; send_bytes -= ret; - } - } while (ret > 0 && send_bytes); + } else + ssl->interrupted_remaining_write = bytes; + } while (ret > 0 && send_bytes && ret == bytes); if (ret >= 0) { ret = len - send_bytes; - ssl->rwstate = SSL_NOTHING; - } else - ret = -1; + if (!ret) + ssl->rwstate = SSL_NOTHING; + } else { + if (send_bytes == len) + ret = -1; + else + ret = len - send_bytes; + } return ret; } diff --git a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c b/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c index 536733fbab..4e3d611095 100644 --- a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c +++ b/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c @@ -218,7 +218,7 @@ static int ssl_pm_reload_crt(SSL *ssl) struct x509_pm *crt_pm = (struct x509_pm *)ssl->cert->x509->x509_pm; if (ssl->verify_mode == SSL_VERIFY_PEER) - mode = MBEDTLS_SSL_VERIFY_REQUIRED; + mode = MBEDTLS_SSL_VERIFY_OPTIONAL; else if (ssl->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT) mode = MBEDTLS_SSL_VERIFY_OPTIONAL; else if (ssl->verify_mode == SSL_VERIFY_CLIENT_ONCE) @@ -360,17 +360,52 @@ int ssl_pm_read(SSL *ssl, void *buffer, int len) return ret; } +/* + * This returns -1, or the length sent. + * If -1, then you need to find out if the error was + * fatal or recoverable using SSL_get_error() + */ int ssl_pm_send(SSL *ssl, const void *buffer, int len) { int ret; struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; ret = mbedtls_ssl_write(&ssl_pm->ssl, buffer, len); + /* + * We can get a positive number, which may be less than len... that + * much was sent successfully and you can call again to send more. + * + * We can get a negative mbedtls error code... if WANT_WRITE or WANT_READ, + * it's nonfatal and means it should be retried as-is. If something else, + * it's fatal actually. + * + * If this function returns something other than a positive value or + * MBEDTLS_ERR_SSL_WANT_READ/WRITE, the ssl context becomes unusable, and + * you should either free it or call mbedtls_ssl_session_reset() on it + * before re-using it for a new connection; the current connection must + * be closed. + * + * When this function returns MBEDTLS_ERR_SSL_WANT_WRITE/READ, it must be + * called later with the same arguments, until it returns a positive value. + */ + if (ret < 0) { - if (ret == MBEDTLS_ERR_NET_CONN_RESET) + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_write() return -0x%x", -ret); + switch (ret) { + case MBEDTLS_ERR_NET_CONN_RESET: ssl->err = SSL_ERROR_SYSCALL; - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_write() return -0x%x", -ret); - ret = -1; + break; + case MBEDTLS_ERR_SSL_WANT_WRITE: + ssl->err = SSL_ERROR_WANT_WRITE; + break; + case MBEDTLS_ERR_SSL_WANT_READ: + ssl->err = SSL_ERROR_WANT_READ; + break; + default: + break; + } + + ret = -1; } return ret; @@ -677,11 +712,39 @@ long ssl_pm_get_verify_result(const SSL *ssl) struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl); - if (ret) { - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_get_verify_result() return 0x%x", ret); + + if (!ret) + return X509_V_OK; + + if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED || + (ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED)) + // Allows us to use LCCSCF_ALLOW_SELFSIGNED to skip verification + verify_result = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; + + else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH) + verify_result = X509_V_ERR_HOSTNAME_MISMATCH; + + else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) || + (ret & MBEDTLS_X509_BADCRL_BAD_KEY)) + verify_result = X509_V_ERR_CA_KEY_TOO_SMALL; + + else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) || + (ret & MBEDTLS_X509_BADCRL_BAD_MD)) + verify_result = X509_V_ERR_CA_MD_TOO_WEAK; + + else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) || + (ret & MBEDTLS_X509_BADCRL_FUTURE)) + verify_result = X509_V_ERR_CERT_NOT_YET_VALID; + + else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) || + (ret & MBEDTLS_X509_BADCRL_EXPIRED)) + verify_result = X509_V_ERR_CERT_HAS_EXPIRED; + + else verify_result = X509_V_ERR_UNSPECIFIED; - } else - verify_result = X509_V_OK; + + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, + "mbedtls_ssl_get_verify_result() return 0x%x", ret); return verify_result; } diff --git a/thirdparty/lws/misc/lejp.c b/thirdparty/lws/misc/lejp.c index 5407c90f97..38efa8b122 100644 --- a/thirdparty/lws/misc/lejp.c +++ b/thirdparty/lws/misc/lejp.c @@ -444,7 +444,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) goto append_npos; } if (c == '.') { - if (ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) { + if (!ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) { ret = LEJP_REJECT_MP_VAL_NUM_FORMAT; goto reject; } diff --git a/thirdparty/lws/misc/sha-1.c b/thirdparty/lws/misc/sha-1.c index 9353fbefe4..50205a0100 100644 --- a/thirdparty/lws/misc/sha-1.c +++ b/thirdparty/lws/misc/sha-1.c @@ -45,7 +45,7 @@ struct sha1_ctxt { } h; union { unsigned char b8[8]; - u_int64_t b64[1]; + uint64_t b64[1]; } c; union { unsigned char b8[64]; diff --git a/thirdparty/lws/output.c b/thirdparty/lws/output.c index ed4752490e..375ff3ef99 100644 --- a/thirdparty/lws/output.c +++ b/thirdparty/lws/output.c @@ -270,9 +270,12 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len, if (wsi->state != LWSS_ESTABLISHED && ((wsi->state != LWSS_RETURNED_CLOSE_ALREADY && + wsi->state != LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION && wsi->state != LWSS_AWAITING_CLOSE_ACK) || - wp != LWS_WRITE_CLOSE)) + wp != LWS_WRITE_CLOSE)) { + lwsl_debug("binning\n"); return 0; + } /* if we are continuing a frame that already had its header done */ @@ -507,7 +510,7 @@ send_raw: (wp & 0x1f) == LWS_WRITE_HTTP_FINAL) && wsi->u.http.tx_content_length) { wsi->u.http.tx_content_remain -= len; - lwsl_info("%s: content_remain = %llu\n", __func__, + lwsl_info("%s: wsi %p: tx_content_remain = %llu\n", __func__, wsi, (unsigned long long)wsi->u.http.tx_content_remain); if (!wsi->u.http.tx_content_remain) { lwsl_info("%s: selecting final write mode\n", __func__); @@ -639,6 +642,9 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi) poss = context->pt_serv_buf_size - n - LWS_H2_FRAME_HEADER_LENGTH; + if (poss > wsi->u.http.tx_content_remain) + poss = wsi->u.http.tx_content_remain; + /* * if there is a hint about how much we will do well to send at one time, * restrict ourselves to only trying to send that. diff --git a/thirdparty/lws/pollfd.c b/thirdparty/lws/pollfd.c index 4d6704d41c..54a4a86057 100644 --- a/thirdparty/lws/pollfd.c +++ b/thirdparty/lws/pollfd.c @@ -537,9 +537,14 @@ LWS_VISIBLE int lws_callback_on_writable_all_protocol(const struct lws_context *context, const struct lws_protocols *protocol) { - struct lws_vhost *vhost = context->vhost_list; + struct lws_vhost *vhost; int n; + if (!context) + return 0; + + vhost = context->vhost_list; + while (vhost) { for (n = 0; n < vhost->count_protocols; n++) if (protocol->callback == diff --git a/thirdparty/lws/private-libwebsockets.h b/thirdparty/lws/private-libwebsockets.h index 4f0b374332..535fa0be57 100644 --- a/thirdparty/lws/private-libwebsockets.h +++ b/thirdparty/lws/private-libwebsockets.h @@ -356,9 +356,6 @@ esp8266_tcp_stream_bind(lws_sockfd_type fd, int port, struct lws *wsi); #ifndef BYTE_ORDER #define BYTE_ORDER LITTLE_ENDIAN #endif -#ifndef u_int64_t -typedef unsigned __int64 u_int64_t; -#endif #undef __P #ifndef __P @@ -1633,7 +1630,6 @@ struct lws_h2_netconn { unsigned int pad_length:1; unsigned int collected_priority:1; unsigned int is_first_header_char:1; - unsigned int seen_nonpseudoheader:1; unsigned int zero_huff_padding:1; unsigned int last_action_dyntable_resize:1; @@ -1922,6 +1918,7 @@ struct lws { unsigned int hdr_parsing_completed:1; unsigned int http2_substream:1; unsigned int upgraded_to_http2:1; + unsigned int seen_nonpseudoheader:1; unsigned int listener:1; unsigned int user_space_externally_allocated:1; unsigned int socket_is_permanently_unusable:1; diff --git a/thirdparty/lws/server/ssl-server.c b/thirdparty/lws/server/ssl-server.c index a9516f2239..c4362824bf 100644 --- a/thirdparty/lws/server/ssl-server.c +++ b/thirdparty/lws/server/ssl-server.c @@ -155,7 +155,7 @@ lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) */ vh = context->vhost_list; while (vh) { - if (!vh->being_destroyed && vh->ssl_ctx == SSL_get_SSL_CTX(ssl)) + if (!vh->being_destroyed && ssl && vh->ssl_ctx == SSL_get_SSL_CTX(ssl)) break; vh = vh->vhost_next; } diff --git a/thirdparty/lws/service.c b/thirdparty/lws/service.c index 6748e30bd4..8cf455e2c9 100644 --- a/thirdparty/lws/service.c +++ b/thirdparty/lws/service.c @@ -1073,6 +1073,8 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t c = lws_token_to_string(m); if (!c) break; + if (!(*c)) + break; len = lws_hdr_total_length(wsi, m); if (!len || len > sizeof(buf) - 1) { @@ -1090,6 +1092,11 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t m++; } while (1); + /* explicitly detach the ah */ + + lws_header_table_force_to_detachable_state(wsi); + lws_header_table_detach(wsi, 0); + /* ... and then drop the connection */ if (wsi->desc.sockfd == our_fd) @@ -1098,7 +1105,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); - ah = ah->next; + ah = pt->ah_list; } #ifdef LWS_WITH_CGI @@ -1644,6 +1651,14 @@ drain: break; } #endif + /* + * something went wrong with parsing the handshake, and + * we ended up back in the event loop without completing it + */ + case LWSCM_PRE_WS_SERVING_ACCEPT: + wsi->socket_is_permanently_unusable = 1; + goto close_and_handled; + default: #ifdef LWS_NO_CLIENT break; diff --git a/thirdparty/lws/ssl.c b/thirdparty/lws/ssl.c index 0a647b469c..4ff3088ab3 100644 --- a/thirdparty/lws/ssl.c +++ b/thirdparty/lws/ssl.c @@ -20,11 +20,7 @@ */ #include "private-libwebsockets.h" - -/* workaround for mingw */ -#if !defined(ECONNABORTED) -#define ECONNABORTED 103 -#endif +#include <errno.h> int lws_alloc_vfs_file(struct lws_context *context, const char *filename, uint8_t **buf, lws_filepos_t *amount) @@ -463,7 +459,7 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len) lwsl_debug("%p: SSL_read says %d\n", wsi, n); /* manpage: returning 0 means connection shut down */ - if (!n) { + if (!n || (n == -1 && errno == ENOTCONN)) { wsi->socket_is_permanently_unusable = 1; return LWS_SSL_CAPABLE_ERROR; @@ -476,12 +472,12 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len) m == SSL_ERROR_SYSCALL) return LWS_SSL_CAPABLE_ERROR; - if (SSL_want_read(wsi->ssl)) { + if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) { lwsl_debug("%s: WANT_READ\n", __func__); lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi); return LWS_SSL_CAPABLE_MORE_SERVICE; } - if (SSL_want_write(wsi->ssl)) { + if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) { lwsl_debug("%s: WANT_WRITE\n", __func__); lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi); return LWS_SSL_CAPABLE_MORE_SERVICE; @@ -885,6 +881,7 @@ go_again: failed: lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1); + wsi->socket_is_permanently_unusable = 1; lwsl_info("SSL_accept failed socket %u: %s\n", wsi->desc.sockfd, lws_ssl_get_error_string(m, n, buf, sizeof(buf))); lws_ssl_elaborate_error(); @@ -903,7 +900,7 @@ accepted: /* adapt our vhost to match the SNI SSL_CTX that was chosen */ vh = context->vhost_list; while (vh) { - if (!vh->being_destroyed && + if (!vh->being_destroyed && wsi->ssl && vh->ssl_ctx == SSL_get_SSL_CTX(wsi->ssl)) { lwsl_info("setting wsi to vh %s\n", vh->name); wsi->vhost = vh; diff --git a/thirdparty/mbedtls/1453.diff b/thirdparty/mbedtls/1453.diff new file mode 100644 index 0000000000..acc3654cd4 --- /dev/null +++ b/thirdparty/mbedtls/1453.diff @@ -0,0 +1,120 @@ +diff --git a/thirdparty/mbedtls/library/entropy_poll.c b/thirdparty/mbedtls/library/entropy_poll.c +index 67900c46c8..cefe882d2a 100644 +--- a/thirdparty/mbedtls/library/entropy_poll.c ++++ b/thirdparty/mbedtls/library/entropy_poll.c +@@ -54,28 +54,43 @@ + #define _WIN32_WINNT 0x0400 + #endif + #include <windows.h> +-#include <wincrypt.h> ++#include <bcrypt.h> ++#if defined(_MSC_VER) && _MSC_VER <= 1600 ++/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and ++ * <intsafe.h> are included, as they redefine a number of <TYPE>_MAX constants. ++ * These constants are guaranteed to be the same, though, so we suppress the ++ * warning when including intsafe.h. ++ */ ++#pragma warning( push ) ++#pragma warning( disable : 4005 ) ++#endif ++#include <intsafe.h> ++#if defined(_MSC_VER) && _MSC_VER <= 1600 ++#pragma warning( pop ) ++#endif + + int mbedtls_platform_entropy_poll( void *data, unsigned char *output, size_t len, + size_t *olen ) + { +- HCRYPTPROV provider; ++ ULONG len_as_ulong = 0; + ((void) data); + *olen = 0; + +- if( CryptAcquireContext( &provider, NULL, NULL, +- PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) == FALSE ) ++ /* ++ * BCryptGenRandom takes ULONG for size, which is smaller than size_t on ++ * 64-bit Windows platforms. Ensure len's value can be safely converted into ++ * a ULONG. ++ */ ++ if ( FAILED( SizeTToULong( len, &len_as_ulong ) ) ) + { + return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED ); + } + +- if( CryptGenRandom( provider, (DWORD) len, output ) == FALSE ) ++ if ( !BCRYPT_SUCCESS( BCryptGenRandom( NULL, output, len_as_ulong, BCRYPT_USE_SYSTEM_PREFERRED_RNG ) ) ) + { +- CryptReleaseContext( provider, 0 ); + return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED ); + } + +- CryptReleaseContext( provider, 0 ); + *olen = len; + + return( 0 ); +diff --git a/thirdparty/mbedtls/library/x509_crt.c b/thirdparty/mbedtls/library/x509_crt.c +index afff4e18bf..7960fa1a1a 100644 +--- a/thirdparty/mbedtls/library/x509_crt.c ++++ b/thirdparty/mbedtls/library/x509_crt.c +@@ -64,6 +64,19 @@ + + #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + #include <windows.h> ++#if defined(_MSC_VER) && _MSC_VER <= 1600 ++/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and ++ * <intsafe.h> are included, as they redefine a number of <TYPE>_MAX constants. ++ * These constants are guaranteed to be the same, though, so we suppress the ++ * warning when including intsafe.h. ++ */ ++#pragma warning( push ) ++#pragma warning( disable : 4005 ) ++#endif ++#include <intsafe.h> ++#if defined(_MSC_VER) && _MSC_VER <= 1600 ++#pragma warning( pop ) ++#endif + #else + #include <time.h> + #endif +@@ -1130,6 +1143,7 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path ) + char filename[MAX_PATH]; + char *p; + size_t len = strlen( path ); ++ int lengthAsInt = 0; + + WIN32_FIND_DATAW file_data; + HANDLE hFind; +@@ -1144,7 +1158,18 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path ) + p = filename + len; + filename[len++] = '*'; + +- w_ret = MultiByteToWideChar( CP_ACP, 0, filename, (int)len, szDir, ++ if ( FAILED ( SizeTToInt( len, &lengthAsInt ) ) ) ++ return( MBEDTLS_ERR_X509_FILE_IO_ERROR ); ++ ++ /* ++ * Note this function uses the code page CP_ACP, and assumes the incoming ++ * string is encoded in ANSI, before translating it into Unicode. If the ++ * incoming string were changed to be UTF-8, then the length check needs to ++ * change to check the number of characters, not the number of bytes, in the ++ * incoming string are less than MAX_PATH to avoid a buffer overrun with ++ * MultiByteToWideChar(). ++ */ ++ w_ret = MultiByteToWideChar( CP_ACP, 0, filename, lengthAsInt, szDir, + MAX_PATH - 3 ); + if( w_ret == 0 ) + return( MBEDTLS_ERR_X509_BAD_INPUT_DATA ); +@@ -1161,8 +1186,11 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path ) + if( file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) + continue; + ++ if ( FAILED( SizeTToInt( wcslen( file_data.cFileName ), &lengthAsInt ) ) ) ++ return( MBEDTLS_ERR_X509_FILE_IO_ERROR ); ++ + w_ret = WideCharToMultiByte( CP_ACP, 0, file_data.cFileName, +- lstrlenW( file_data.cFileName ), ++ lengthAsInt, + p, (int) len - 1, + NULL, NULL ); + if( w_ret == 0 ) diff --git a/thirdparty/mbedtls/include/mbedtls/asn1.h b/thirdparty/mbedtls/include/mbedtls/asn1.h index fde328a128..96c1c9a8ab 100644 --- a/thirdparty/mbedtls/include/mbedtls/asn1.h +++ b/thirdparty/mbedtls/include/mbedtls/asn1.h @@ -88,6 +88,21 @@ #define MBEDTLS_ASN1_PRIMITIVE 0x00 #define MBEDTLS_ASN1_CONSTRUCTED 0x20 #define MBEDTLS_ASN1_CONTEXT_SPECIFIC 0x80 + +/* + * Bit masks for each of the components of an ASN.1 tag as specified in + * ITU X.690 (08/2015), section 8.1 "General rules for encoding", + * paragraph 8.1.2.2: + * + * Bit 8 7 6 5 1 + * +-------+-----+------------+ + * | Class | P/C | Tag number | + * +-------+-----+------------+ + */ +#define MBEDTLS_ASN1_TAG_CLASS_MASK 0xC0 +#define MBEDTLS_ASN1_TAG_PC_MASK 0x20 +#define MBEDTLS_ASN1_TAG_VALUE_MASK 0x1F + /* \} name */ /* \} addtogroup asn1_module */ diff --git a/thirdparty/mbedtls/include/mbedtls/ccm.h b/thirdparty/mbedtls/include/mbedtls/ccm.h index 5a9ee4a1cd..630b7fdf6c 100644 --- a/thirdparty/mbedtls/include/mbedtls/ccm.h +++ b/thirdparty/mbedtls/include/mbedtls/ccm.h @@ -105,7 +105,7 @@ void mbedtls_ccm_free( mbedtls_ccm_context *ctx ); * Must be at least \p length Bytes wide. * \param tag The buffer holding the tag. * \param tag_len The length of the tag to generate in Bytes: - * 4, 6, 8, 10, 14 or 16. + * 4, 6, 8, 10, 12, 14 or 16. * * \note The tag is written to a separate buffer. To concatenate * the \p tag with the \p output, as done in <em>RFC-3610: @@ -131,10 +131,13 @@ int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, * \param iv_len The length of the IV in Bytes: 7, 8, 9, 10, 11, 12, or 13. * \param add The additional data field. * \param add_len The length of additional data in Bytes. + * Must be less than 2^16 - 2^8. * \param input The buffer holding the input data. * \param output The buffer holding the output data. + * Must be at least \p length Bytes wide. * \param tag The buffer holding the tag. * \param tag_len The length of the tag in Bytes. + * 4, 6, 8, 10, 12, 14 or 16. * * \return 0 if successful and authenticated, or * #MBEDTLS_ERR_CCM_AUTH_FAILED if the tag does not match. diff --git a/thirdparty/mbedtls/include/mbedtls/check_config.h b/thirdparty/mbedtls/include/mbedtls/check_config.h index 1143aa2687..be80332963 100644 --- a/thirdparty/mbedtls/include/mbedtls/check_config.h +++ b/thirdparty/mbedtls/include/mbedtls/check_config.h @@ -78,6 +78,10 @@ #error "MBEDTLS_DHM_C defined, but not all prerequisites" #endif +#if defined(MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT) && !defined(MBEDTLS_SSL_TRUNCATED_HMAC) +#error "MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT defined, but not all prerequisites" +#endif + #if defined(MBEDTLS_CMAC_C) && \ !defined(MBEDTLS_AES_C) && !defined(MBEDTLS_DES_C) #error "MBEDTLS_CMAC_C defined, but not all prerequisites" diff --git a/thirdparty/mbedtls/include/mbedtls/config.h b/thirdparty/mbedtls/include/mbedtls/config.h index 79eedffddd..b5905ef9d0 100644 --- a/thirdparty/mbedtls/include/mbedtls/config.h +++ b/thirdparty/mbedtls/include/mbedtls/config.h @@ -1049,7 +1049,8 @@ /** * \def MBEDTLS_RSA_NO_CRT * - * Do not use the Chinese Remainder Theorem for the RSA private operation. + * Do not use the Chinese Remainder Theorem + * for the RSA private operation. * * Uncomment this macro to disable the use of CRT in RSA. * @@ -1412,6 +1413,30 @@ #define MBEDTLS_SSL_TRUNCATED_HMAC /** + * \def MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT + * + * Fallback to old (pre-2.7), non-conforming implementation of the truncated + * HMAC extension which also truncates the HMAC key. Note that this option is + * only meant for a transitory upgrade period and is likely to be removed in + * a future version of the library. + * + * \warning The old implementation is non-compliant and has a security weakness + * (2^80 brute force attack on the HMAC key used for a single, + * uninterrupted connection). This should only be enabled temporarily + * when (1) the use of truncated HMAC is essential in order to save + * bandwidth, and (2) the peer is an Mbed TLS stack that doesn't use + * the fixed implementation yet (pre-2.7). + * + * \deprecated This option is deprecated and will likely be removed in a + * future version of Mbed TLS. + * + * Uncomment to fallback to old, non-compliant truncated HMAC implementation. + * + * Requires: MBEDTLS_SSL_TRUNCATED_HMAC + */ +//#define MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT + +/** * \def MBEDTLS_THREADING_ALT * * Provide your own alternate threading implementation. @@ -1517,6 +1542,9 @@ * * \note Currently compression can't be used with DTLS. * + * \deprecated This feature is deprecated and will be removed + * in the next major revision of the library. + * * Used in: library/ssl_tls.c * library/ssl_cli.c * library/ssl_srv.c diff --git a/thirdparty/mbedtls/include/mbedtls/dhm.h b/thirdparty/mbedtls/include/mbedtls/dhm.h index da2e66b111..00fafd8d16 100644 --- a/thirdparty/mbedtls/include/mbedtls/dhm.h +++ b/thirdparty/mbedtls/include/mbedtls/dhm.h @@ -372,7 +372,7 @@ MBEDTLS_DEPRECATED typedef char const * mbedtls_deprecated_constant_t; * in <em>RFC-5114: Additional Diffie-Hellman Groups for Use with * IETF Standards</em>. */ -#define MBEDTLS_DHM_RFC5114_MODP_P \ +#define MBEDTLS_DHM_RFC5114_MODP_2048_P \ MBEDTLS_DEPRECATED_STRING_CONSTANT( \ "AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1" \ "B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15" \ diff --git a/thirdparty/mbedtls/include/mbedtls/md2.h b/thirdparty/mbedtls/include/mbedtls/md2.h index 2ff3f171a3..0fd8b5afcc 100644 --- a/thirdparty/mbedtls/include/mbedtls/md2.h +++ b/thirdparty/mbedtls/include/mbedtls/md2.h @@ -39,11 +39,6 @@ #define MBEDTLS_ERR_MD2_HW_ACCEL_FAILED -0x002B /**< MD2 hardware accelerator failed */ -#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ - !defined(inline) && !defined(__cplusplus) -#define inline __inline -#endif - #if !defined(MBEDTLS_MD2_ALT) // Regular implementation // @@ -187,11 +182,7 @@ int mbedtls_internal_md2_process( mbedtls_md2_context *ctx ); * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_md2_starts( - mbedtls_md2_context *ctx ) -{ - mbedtls_md2_starts_ret( ctx ); -} +MBEDTLS_DEPRECATED void mbedtls_md2_starts( mbedtls_md2_context *ctx ); /** * \brief MD2 process buffer @@ -207,13 +198,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md2_starts( * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_md2_update( - mbedtls_md2_context *ctx, - const unsigned char *input, - size_t ilen ) -{ - mbedtls_md2_update_ret( ctx, input, ilen ); -} +MBEDTLS_DEPRECATED void mbedtls_md2_update( mbedtls_md2_context *ctx, + const unsigned char *input, + size_t ilen ); /** * \brief MD2 final digest @@ -228,12 +215,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md2_update( * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_md2_finish( - mbedtls_md2_context *ctx, - unsigned char output[16] ) -{ - mbedtls_md2_finish_ret( ctx, output ); -} +MBEDTLS_DEPRECATED void mbedtls_md2_finish( mbedtls_md2_context *ctx, + unsigned char output[16] ); /** * \brief MD2 process data block (internal use only) @@ -247,11 +230,7 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md2_finish( * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_md2_process( - mbedtls_md2_context *ctx ) -{ - mbedtls_internal_md2_process( ctx ); -} +MBEDTLS_DEPRECATED void mbedtls_md2_process( mbedtls_md2_context *ctx ); #undef MBEDTLS_DEPRECATED #endif /* !MBEDTLS_DEPRECATED_REMOVED */ @@ -304,12 +283,9 @@ int mbedtls_md2_ret( const unsigned char *input, * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_md2( const unsigned char *input, - size_t ilen, - unsigned char output[16] ) -{ - mbedtls_md2_ret( input, ilen, output ); -} +MBEDTLS_DEPRECATED void mbedtls_md2( const unsigned char *input, + size_t ilen, + unsigned char output[16] ); #undef MBEDTLS_DEPRECATED #endif /* !MBEDTLS_DEPRECATED_REMOVED */ diff --git a/thirdparty/mbedtls/include/mbedtls/md4.h b/thirdparty/mbedtls/include/mbedtls/md4.h index a2ab57f078..23fa95e46a 100644 --- a/thirdparty/mbedtls/include/mbedtls/md4.h +++ b/thirdparty/mbedtls/include/mbedtls/md4.h @@ -40,11 +40,6 @@ #define MBEDTLS_ERR_MD4_HW_ACCEL_FAILED -0x002D /**< MD4 hardware accelerator failed */ -#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ - !defined(inline) && !defined(__cplusplus) -#define inline __inline -#endif - #if !defined(MBEDTLS_MD4_ALT) // Regular implementation // @@ -188,11 +183,7 @@ int mbedtls_internal_md4_process( mbedtls_md4_context *ctx, * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_md4_starts( - mbedtls_md4_context *ctx ) -{ - mbedtls_md4_starts_ret( ctx ); -} +MBEDTLS_DEPRECATED void mbedtls_md4_starts( mbedtls_md4_context *ctx ); /** * \brief MD4 process buffer @@ -208,13 +199,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md4_starts( * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_md4_update( - mbedtls_md4_context *ctx, - const unsigned char *input, - size_t ilen ) -{ - mbedtls_md4_update_ret( ctx, input, ilen ); -} +MBEDTLS_DEPRECATED void mbedtls_md4_update( mbedtls_md4_context *ctx, + const unsigned char *input, + size_t ilen ); /** * \brief MD4 final digest @@ -229,12 +216,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md4_update( * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_md4_finish( - mbedtls_md4_context *ctx, - unsigned char output[16] ) -{ - mbedtls_md4_finish_ret( ctx, output ); -} +MBEDTLS_DEPRECATED void mbedtls_md4_finish( mbedtls_md4_context *ctx, + unsigned char output[16] ); /** * \brief MD4 process data block (internal use only) @@ -249,12 +232,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md4_finish( * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_md4_process( - mbedtls_md4_context *ctx, - const unsigned char data[64] ) -{ - mbedtls_internal_md4_process( ctx, data ); -} +MBEDTLS_DEPRECATED void mbedtls_md4_process( mbedtls_md4_context *ctx, + const unsigned char data[64] ); #undef MBEDTLS_DEPRECATED #endif /* !MBEDTLS_DEPRECATED_REMOVED */ @@ -309,12 +288,9 @@ int mbedtls_md4_ret( const unsigned char *input, * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_md4( const unsigned char *input, - size_t ilen, - unsigned char output[16] ) -{ - mbedtls_md4_ret( input, ilen, output ); -} +MBEDTLS_DEPRECATED void mbedtls_md4( const unsigned char *input, + size_t ilen, + unsigned char output[16] ); #undef MBEDTLS_DEPRECATED #endif /* !MBEDTLS_DEPRECATED_REMOVED */ diff --git a/thirdparty/mbedtls/include/mbedtls/md5.h b/thirdparty/mbedtls/include/mbedtls/md5.h index d49391f811..06ea4c5d44 100644 --- a/thirdparty/mbedtls/include/mbedtls/md5.h +++ b/thirdparty/mbedtls/include/mbedtls/md5.h @@ -43,11 +43,6 @@ // Regular implementation // -#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ - !defined(inline) && !defined(__cplusplus) -#define inline __inline -#endif - #ifdef __cplusplus extern "C" { #endif @@ -188,11 +183,7 @@ int mbedtls_internal_md5_process( mbedtls_md5_context *ctx, * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_md5_starts( - mbedtls_md5_context *ctx ) -{ - mbedtls_md5_starts_ret( ctx ); -} +MBEDTLS_DEPRECATED void mbedtls_md5_starts( mbedtls_md5_context *ctx ); /** * \brief MD5 process buffer @@ -208,13 +199,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md5_starts( * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_md5_update( - mbedtls_md5_context *ctx, - const unsigned char *input, - size_t ilen ) -{ - mbedtls_md5_update_ret( ctx, input, ilen ); -} +MBEDTLS_DEPRECATED void mbedtls_md5_update( mbedtls_md5_context *ctx, + const unsigned char *input, + size_t ilen ); /** * \brief MD5 final digest @@ -229,12 +216,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md5_update( * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_md5_finish( - mbedtls_md5_context *ctx, - unsigned char output[16] ) -{ - mbedtls_md5_finish_ret( ctx, output ); -} +MBEDTLS_DEPRECATED void mbedtls_md5_finish( mbedtls_md5_context *ctx, + unsigned char output[16] ); /** * \brief MD5 process data block (internal use only) @@ -249,12 +232,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md5_finish( * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_md5_process( - mbedtls_md5_context *ctx, - const unsigned char data[64] ) -{ - mbedtls_internal_md5_process( ctx, data ); -} +MBEDTLS_DEPRECATED void mbedtls_md5_process( mbedtls_md5_context *ctx, + const unsigned char data[64] ); #undef MBEDTLS_DEPRECATED #endif /* !MBEDTLS_DEPRECATED_REMOVED */ @@ -309,12 +288,9 @@ int mbedtls_md5_ret( const unsigned char *input, * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_md5( const unsigned char *input, - size_t ilen, - unsigned char output[16] ) -{ - mbedtls_md5_ret( input, ilen, output ); -} +MBEDTLS_DEPRECATED void mbedtls_md5( const unsigned char *input, + size_t ilen, + unsigned char output[16] ); #undef MBEDTLS_DEPRECATED #endif /* !MBEDTLS_DEPRECATED_REMOVED */ diff --git a/thirdparty/mbedtls/include/mbedtls/oid.h b/thirdparty/mbedtls/include/mbedtls/oid.h index bf2ef5ece4..408645ece7 100644 --- a/thirdparty/mbedtls/include/mbedtls/oid.h +++ b/thirdparty/mbedtls/include/mbedtls/oid.h @@ -228,6 +228,14 @@ #define MBEDTLS_OID_HMAC_SHA1 MBEDTLS_OID_RSA_COMPANY "\x02\x07" /**< id-hmacWithSHA1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 7 } */ +#define MBEDTLS_OID_HMAC_SHA224 MBEDTLS_OID_RSA_COMPANY "\x02\x08" /**< id-hmacWithSHA224 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 8 } */ + +#define MBEDTLS_OID_HMAC_SHA256 MBEDTLS_OID_RSA_COMPANY "\x02\x09" /**< id-hmacWithSHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 9 } */ + +#define MBEDTLS_OID_HMAC_SHA384 MBEDTLS_OID_RSA_COMPANY "\x02\x0A" /**< id-hmacWithSHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 10 } */ + +#define MBEDTLS_OID_HMAC_SHA512 MBEDTLS_OID_RSA_COMPANY "\x02\x0B" /**< id-hmacWithSHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 11 } */ + /* * Encryption algorithms */ @@ -514,6 +522,16 @@ int mbedtls_oid_get_oid_by_sig_alg( mbedtls_pk_type_t pk_alg, mbedtls_md_type_t * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND */ int mbedtls_oid_get_md_alg( const mbedtls_asn1_buf *oid, mbedtls_md_type_t *md_alg ); + +/** + * \brief Translate hmac algorithm OID into md_type + * + * \param oid OID to use + * \param md_hmac place to store message hmac algorithm + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_md_hmac( const mbedtls_asn1_buf *oid, mbedtls_md_type_t *md_hmac ); #endif /* MBEDTLS_MD_C */ /** diff --git a/thirdparty/mbedtls/include/mbedtls/ripemd160.h b/thirdparty/mbedtls/include/mbedtls/ripemd160.h index c21868b185..3a8b50a621 100644 --- a/thirdparty/mbedtls/include/mbedtls/ripemd160.h +++ b/thirdparty/mbedtls/include/mbedtls/ripemd160.h @@ -35,11 +35,6 @@ #define MBEDTLS_ERR_RIPEMD160_HW_ACCEL_FAILED -0x0031 /**< RIPEMD160 hardware accelerator failed */ -#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ - !defined(inline) && !defined(__cplusplus) -#define inline __inline -#endif - #if !defined(MBEDTLS_RIPEMD160_ALT) // Regular implementation // @@ -139,11 +134,8 @@ int mbedtls_internal_ripemd160_process( mbedtls_ripemd160_context *ctx, * * \param ctx context to be initialized */ -MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_starts( - mbedtls_ripemd160_context *ctx ) -{ - mbedtls_ripemd160_starts_ret( ctx ); -} +MBEDTLS_DEPRECATED void mbedtls_ripemd160_starts( + mbedtls_ripemd160_context *ctx ); /** * \brief RIPEMD-160 process buffer @@ -154,13 +146,10 @@ MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_starts( * \param input buffer holding the data * \param ilen length of the input data */ -MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_update( +MBEDTLS_DEPRECATED void mbedtls_ripemd160_update( mbedtls_ripemd160_context *ctx, const unsigned char *input, - size_t ilen ) -{ - mbedtls_ripemd160_update_ret( ctx, input, ilen ); -} + size_t ilen ); /** * \brief RIPEMD-160 final digest @@ -170,12 +159,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_update( * \param ctx RIPEMD-160 context * \param output RIPEMD-160 checksum result */ -MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_finish( +MBEDTLS_DEPRECATED void mbedtls_ripemd160_finish( mbedtls_ripemd160_context *ctx, - unsigned char output[20] ) -{ - mbedtls_ripemd160_finish_ret( ctx, output ); -} + unsigned char output[20] ); /** * \brief RIPEMD-160 process data block (internal use only) @@ -185,12 +171,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_finish( * \param ctx RIPEMD-160 context * \param data buffer holding one block of data */ -MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_process( +MBEDTLS_DEPRECATED void mbedtls_ripemd160_process( mbedtls_ripemd160_context *ctx, - const unsigned char data[64] ) -{ - mbedtls_internal_ripemd160_process( ctx, data ); -} + const unsigned char data[64] ); #undef MBEDTLS_DEPRECATED #endif /* !MBEDTLS_DEPRECATED_REMOVED */ @@ -235,13 +218,9 @@ int mbedtls_ripemd160_ret( const unsigned char *input, * \param ilen length of the input data * \param output RIPEMD-160 checksum result */ -MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160( - const unsigned char *input, - size_t ilen, - unsigned char output[20] ) -{ - mbedtls_ripemd160_ret( input, ilen, output ); -} +MBEDTLS_DEPRECATED void mbedtls_ripemd160( const unsigned char *input, + size_t ilen, + unsigned char output[20] ); #undef MBEDTLS_DEPRECATED #endif /* !MBEDTLS_DEPRECATED_REMOVED */ diff --git a/thirdparty/mbedtls/include/mbedtls/rsa.h b/thirdparty/mbedtls/include/mbedtls/rsa.h index fb2f77f94f..5548f3c127 100644 --- a/thirdparty/mbedtls/include/mbedtls/rsa.h +++ b/thirdparty/mbedtls/include/mbedtls/rsa.h @@ -518,6 +518,18 @@ int mbedtls_rsa_public( mbedtls_rsa_context *ctx, * * \note The input and output buffers must be large * enough. For example, 128 Bytes if RSA-1024 is used. + * + * \note Blinding is used if and only if a PRNG is provided. + * + * \note If blinding is used, both the base of exponentation + * and the exponent are blinded, providing protection + * against some side-channel attacks. + * + * \warning It is deprecated and a security risk to not provide + * a PRNG here and thereby prevent the use of blinding. + * Future versions of the library may enforce the presence + * of a PRNG. + * */ int mbedtls_rsa_private( mbedtls_rsa_context *ctx, int (*f_rng)(void *, unsigned char *, size_t), diff --git a/thirdparty/mbedtls/include/mbedtls/sha1.h b/thirdparty/mbedtls/include/mbedtls/sha1.h index e4f8650216..05540cde12 100644 --- a/thirdparty/mbedtls/include/mbedtls/sha1.h +++ b/thirdparty/mbedtls/include/mbedtls/sha1.h @@ -39,11 +39,6 @@ #define MBEDTLS_ERR_SHA1_HW_ACCEL_FAILED -0x0035 /**< SHA-1 hardware accelerator failed */ -#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ - !defined(inline) && !defined(__cplusplus) -#define inline __inline -#endif - #if !defined(MBEDTLS_SHA1_ALT) // Regular implementation // @@ -190,11 +185,7 @@ int mbedtls_internal_sha1_process( mbedtls_sha1_context *ctx, * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_sha1_starts( - mbedtls_sha1_context *ctx ) -{ - mbedtls_sha1_starts_ret( ctx ); -} +MBEDTLS_DEPRECATED void mbedtls_sha1_starts( mbedtls_sha1_context *ctx ); /** * \brief SHA-1 process buffer @@ -210,13 +201,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha1_starts( * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_sha1_update( - mbedtls_sha1_context *ctx, - const unsigned char *input, - size_t ilen ) -{ - mbedtls_sha1_update_ret( ctx, input, ilen ); -} +MBEDTLS_DEPRECATED void mbedtls_sha1_update( mbedtls_sha1_context *ctx, + const unsigned char *input, + size_t ilen ); /** * \brief SHA-1 final digest @@ -231,12 +218,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha1_update( * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_sha1_finish( - mbedtls_sha1_context *ctx, - unsigned char output[20] ) -{ - mbedtls_sha1_finish_ret( ctx, output ); -} +MBEDTLS_DEPRECATED void mbedtls_sha1_finish( mbedtls_sha1_context *ctx, + unsigned char output[20] ); /** * \brief SHA-1 process data block (internal use only) @@ -251,12 +234,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha1_finish( * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_sha1_process( - mbedtls_sha1_context *ctx, - const unsigned char data[64] ) -{ - mbedtls_internal_sha1_process( ctx, data ); -} +MBEDTLS_DEPRECATED void mbedtls_sha1_process( mbedtls_sha1_context *ctx, + const unsigned char data[64] ); #undef MBEDTLS_DEPRECATED #endif /* !MBEDTLS_DEPRECATED_REMOVED */ @@ -317,12 +296,9 @@ int mbedtls_sha1_ret( const unsigned char *input, * stronger message digests instead. * */ -MBEDTLS_DEPRECATED static inline void mbedtls_sha1( const unsigned char *input, - size_t ilen, - unsigned char output[20] ) -{ - mbedtls_sha1_ret( input, ilen, output ); -} +MBEDTLS_DEPRECATED void mbedtls_sha1( const unsigned char *input, + size_t ilen, + unsigned char output[20] ); #undef MBEDTLS_DEPRECATED #endif /* !MBEDTLS_DEPRECATED_REMOVED */ diff --git a/thirdparty/mbedtls/include/mbedtls/sha256.h b/thirdparty/mbedtls/include/mbedtls/sha256.h index a2b6e11644..ffb16c277a 100644 --- a/thirdparty/mbedtls/include/mbedtls/sha256.h +++ b/thirdparty/mbedtls/include/mbedtls/sha256.h @@ -35,10 +35,6 @@ #define MBEDTLS_ERR_SHA256_HW_ACCEL_FAILED -0x0037 /**< SHA-256 hardware accelerator failed */ -#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ - !defined(inline) && !defined(__cplusplus) -#define inline __inline -#endif #if !defined(MBEDTLS_SHA256_ALT) // Regular implementation // @@ -156,12 +152,8 @@ int mbedtls_internal_sha256_process( mbedtls_sha256_context *ctx, * <ul><li>0: Use SHA-256.</li> * <li>1: Use SHA-224.</li></ul> */ -MBEDTLS_DEPRECATED static inline void mbedtls_sha256_starts( - mbedtls_sha256_context *ctx, - int is224 ) -{ - mbedtls_sha256_starts_ret( ctx, is224 ); -} +MBEDTLS_DEPRECATED void mbedtls_sha256_starts( mbedtls_sha256_context *ctx, + int is224 ); /** * \brief This function feeds an input buffer into an ongoing @@ -173,13 +165,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha256_starts( * \param input The buffer holding the data. * \param ilen The length of the input data. */ -MBEDTLS_DEPRECATED static inline void mbedtls_sha256_update( - mbedtls_sha256_context *ctx, - const unsigned char *input, - size_t ilen ) -{ - mbedtls_sha256_update_ret( ctx, input, ilen ); -} +MBEDTLS_DEPRECATED void mbedtls_sha256_update( mbedtls_sha256_context *ctx, + const unsigned char *input, + size_t ilen ); /** * \brief This function finishes the SHA-256 operation, and writes @@ -190,12 +178,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha256_update( * \param ctx The SHA-256 context. * \param output The SHA-224or SHA-256 checksum result. */ -MBEDTLS_DEPRECATED static inline void mbedtls_sha256_finish( - mbedtls_sha256_context *ctx, - unsigned char output[32] ) -{ - mbedtls_sha256_finish_ret( ctx, output ); -} +MBEDTLS_DEPRECATED void mbedtls_sha256_finish( mbedtls_sha256_context *ctx, + unsigned char output[32] ); /** * \brief This function processes a single data block within @@ -207,12 +191,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha256_finish( * \param ctx The SHA-256 context. * \param data The buffer holding one block of data. */ -MBEDTLS_DEPRECATED static inline void mbedtls_sha256_process( - mbedtls_sha256_context *ctx, - const unsigned char data[64] ) -{ - mbedtls_internal_sha256_process( ctx, data ); -} +MBEDTLS_DEPRECATED void mbedtls_sha256_process( mbedtls_sha256_context *ctx, + const unsigned char data[64] ); #undef MBEDTLS_DEPRECATED #endif /* !MBEDTLS_DEPRECATED_REMOVED */ @@ -276,14 +256,10 @@ int mbedtls_sha256_ret( const unsigned char *input, * <ul><li>0: Use SHA-256.</li> * <li>1: Use SHA-224.</li></ul> */ -MBEDTLS_DEPRECATED static inline void mbedtls_sha256( - const unsigned char *input, - size_t ilen, - unsigned char output[32], - int is224 ) -{ - mbedtls_sha256_ret( input, ilen, output, is224 ); -} +MBEDTLS_DEPRECATED void mbedtls_sha256( const unsigned char *input, + size_t ilen, + unsigned char output[32], + int is224 ); #undef MBEDTLS_DEPRECATED #endif /* !MBEDTLS_DEPRECATED_REMOVED */ diff --git a/thirdparty/mbedtls/include/mbedtls/sha512.h b/thirdparty/mbedtls/include/mbedtls/sha512.h index 52ae204d44..8404a2d599 100644 --- a/thirdparty/mbedtls/include/mbedtls/sha512.h +++ b/thirdparty/mbedtls/include/mbedtls/sha512.h @@ -35,10 +35,6 @@ #define MBEDTLS_ERR_SHA512_HW_ACCEL_FAILED -0x0039 /**< SHA-512 hardware accelerator failed */ -#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ - !defined(inline) && !defined(__cplusplus) -#define inline __inline -#endif #if !defined(MBEDTLS_SHA512_ALT) // Regular implementation // @@ -156,12 +152,8 @@ int mbedtls_internal_sha512_process( mbedtls_sha512_context *ctx, * <ul><li>0: Use SHA-512.</li> * <li>1: Use SHA-384.</li></ul> */ -MBEDTLS_DEPRECATED static inline void mbedtls_sha512_starts( - mbedtls_sha512_context *ctx, - int is384 ) -{ - mbedtls_sha512_starts_ret( ctx, is384 ); -} +MBEDTLS_DEPRECATED void mbedtls_sha512_starts( mbedtls_sha512_context *ctx, + int is384 ); /** * \brief This function feeds an input buffer into an ongoing @@ -173,13 +165,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha512_starts( * \param input The buffer holding the data. * \param ilen The length of the input data. */ -MBEDTLS_DEPRECATED static inline void mbedtls_sha512_update( - mbedtls_sha512_context *ctx, - const unsigned char *input, - size_t ilen ) -{ - mbedtls_sha512_update_ret( ctx, input, ilen ); -} +MBEDTLS_DEPRECATED void mbedtls_sha512_update( mbedtls_sha512_context *ctx, + const unsigned char *input, + size_t ilen ); /** * \brief This function finishes the SHA-512 operation, and writes @@ -190,12 +178,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha512_update( * \param ctx The SHA-512 context. * \param output The SHA-384 or SHA-512 checksum result. */ -MBEDTLS_DEPRECATED static inline void mbedtls_sha512_finish( - mbedtls_sha512_context *ctx, - unsigned char output[64] ) -{ - mbedtls_sha512_finish_ret( ctx, output ); -} +MBEDTLS_DEPRECATED void mbedtls_sha512_finish( mbedtls_sha512_context *ctx, + unsigned char output[64] ); /** * \brief This function processes a single data block within @@ -207,12 +191,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha512_finish( * \param ctx The SHA-512 context. * \param data The buffer holding one block of data. */ -MBEDTLS_DEPRECATED static inline void mbedtls_sha512_process( +MBEDTLS_DEPRECATED void mbedtls_sha512_process( mbedtls_sha512_context *ctx, - const unsigned char data[128] ) -{ - mbedtls_internal_sha512_process( ctx, data ); -} + const unsigned char data[128] ); #undef MBEDTLS_DEPRECATED #endif /* !MBEDTLS_DEPRECATED_REMOVED */ @@ -278,14 +259,10 @@ int mbedtls_sha512_ret( const unsigned char *input, * <ul><li>0: Use SHA-512.</li> * <li>1: Use SHA-384.</li></ul> */ -MBEDTLS_DEPRECATED static inline void mbedtls_sha512( - const unsigned char *input, - size_t ilen, - unsigned char output[64], - int is384 ) -{ - mbedtls_sha512_ret( input, ilen, output, is384 ); -} +MBEDTLS_DEPRECATED void mbedtls_sha512( const unsigned char *input, + size_t ilen, + unsigned char output[64], + int is384 ); #undef MBEDTLS_DEPRECATED #endif /* !MBEDTLS_DEPRECATED_REMOVED */ diff --git a/thirdparty/mbedtls/include/mbedtls/ssl.h b/thirdparty/mbedtls/include/mbedtls/ssl.h index 51e843ae24..dffc162191 100644 --- a/thirdparty/mbedtls/include/mbedtls/ssl.h +++ b/thirdparty/mbedtls/include/mbedtls/ssl.h @@ -49,6 +49,15 @@ #endif #if defined(MBEDTLS_ZLIB_SUPPORT) + +#if defined(MBEDTLS_DEPRECATED_WARNING) +#warning "Record compression support via MBEDTLS_ZLIB_SUPPORT is deprecated and will be removed in the next major revision of the library" +#endif + +#if defined(MBEDTLS_DEPRECATED_REMOVED) +#error "Record compression support via MBEDTLS_ZLIB_SUPPORT is deprecated and cannot be used if MBEDTLS_DEPRECATED_REMOVED is set" +#endif + #include "zlib.h" #endif @@ -971,8 +980,13 @@ void mbedtls_ssl_init( mbedtls_ssl_context *ssl ); * \note No copy of the configuration context is made, it can be * shared by many mbedtls_ssl_context structures. * - * \warning Modifying the conf structure after it has been used in this - * function is unsupported! + * \warning The conf structure will be accessed during the session. + * It must not be modified or freed as long as the session + * is active. + * + * \warning This function must be called exactly once per context. + * Calling mbedtls_ssl_setup again is not supported, even + * if no session is active. * * \param ssl SSL context * \param conf SSL configuration to use diff --git a/thirdparty/mbedtls/include/mbedtls/ssl_internal.h b/thirdparty/mbedtls/include/mbedtls/ssl_internal.h index 9f583a8777..60b431a0f4 100644 --- a/thirdparty/mbedtls/include/mbedtls/ssl_internal.h +++ b/thirdparty/mbedtls/include/mbedtls/ssl_internal.h @@ -71,6 +71,9 @@ #endif /* MBEDTLS_SSL_PROTO_TLS1 */ #endif /* MBEDTLS_SSL_PROTO_SSL3 */ +#define MBEDTLS_SSL_MIN_VALID_MINOR_VERSION MBEDTLS_SSL_MINOR_VERSION_1 +#define MBEDTLS_SSL_MIN_VALID_MAJOR_VERSION MBEDTLS_SSL_MAJOR_VERSION_3 + /* Determine maximum supported version */ #define MBEDTLS_SSL_MAX_MAJOR_VERSION MBEDTLS_SSL_MAJOR_VERSION_3 diff --git a/thirdparty/mbedtls/include/mbedtls/version.h b/thirdparty/mbedtls/include/mbedtls/version.h index 961be59c35..c3ee649f5c 100644 --- a/thirdparty/mbedtls/include/mbedtls/version.h +++ b/thirdparty/mbedtls/include/mbedtls/version.h @@ -39,7 +39,7 @@ * Major, Minor, Patchlevel */ #define MBEDTLS_VERSION_MAJOR 2 -#define MBEDTLS_VERSION_MINOR 7 +#define MBEDTLS_VERSION_MINOR 8 #define MBEDTLS_VERSION_PATCH 0 /** @@ -47,9 +47,9 @@ * MMNNPP00 * Major version | Minor version | Patch version */ -#define MBEDTLS_VERSION_NUMBER 0x02070000 -#define MBEDTLS_VERSION_STRING "2.7.0" -#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.7.0" +#define MBEDTLS_VERSION_NUMBER 0x02080000 +#define MBEDTLS_VERSION_STRING "2.8.0" +#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.8.0" #if defined(MBEDTLS_VERSION_C) diff --git a/thirdparty/mbedtls/library/aes.c b/thirdparty/mbedtls/library/aes.c index dba4a5f578..3d2eac82dd 100644 --- a/thirdparty/mbedtls/library/aes.c +++ b/thirdparty/mbedtls/library/aes.c @@ -765,12 +765,14 @@ int mbedtls_internal_aes_encrypt( mbedtls_aes_context *ctx, } #endif /* !MBEDTLS_AES_ENCRYPT_ALT */ +#if !defined(MBEDTLS_DEPRECATED_REMOVED) void mbedtls_aes_encrypt( mbedtls_aes_context *ctx, const unsigned char input[16], unsigned char output[16] ) { mbedtls_internal_aes_encrypt( ctx, input, output ); } +#endif /* !MBEDTLS_DEPRECATED_REMOVED */ /* * AES-ECB block decryption @@ -831,12 +833,14 @@ int mbedtls_internal_aes_decrypt( mbedtls_aes_context *ctx, } #endif /* !MBEDTLS_AES_DECRYPT_ALT */ +#if !defined(MBEDTLS_DEPRECATED_REMOVED) void mbedtls_aes_decrypt( mbedtls_aes_context *ctx, const unsigned char input[16], unsigned char output[16] ) { mbedtls_internal_aes_decrypt( ctx, input, output ); } +#endif /* !MBEDTLS_DEPRECATED_REMOVED */ /* * AES-ECB block encryption/decryption diff --git a/thirdparty/mbedtls/library/bignum.c b/thirdparty/mbedtls/library/bignum.c index d27c130bcb..9f13da4421 100644 --- a/thirdparty/mbedtls/library/bignum.c +++ b/thirdparty/mbedtls/library/bignum.c @@ -1623,7 +1623,7 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi mbedtls_mpi RR, T, W[ 2 << MBEDTLS_MPI_WINDOW_SIZE ], Apos; int neg; - if( mbedtls_mpi_cmp_int( N, 0 ) < 0 || ( N->p[0] & 1 ) == 0 ) + if( mbedtls_mpi_cmp_int( N, 0 ) <= 0 || ( N->p[0] & 1 ) == 0 ) return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); if( mbedtls_mpi_cmp_int( E, 0 ) < 0 ) diff --git a/thirdparty/mbedtls/library/ctr_drbg.c b/thirdparty/mbedtls/library/ctr_drbg.c index c2310cb579..ff532a0134 100644 --- a/thirdparty/mbedtls/library/ctr_drbg.c +++ b/thirdparty/mbedtls/library/ctr_drbg.c @@ -19,7 +19,7 @@ * This file is part of mbed TLS (https://tls.mbed.org) */ /* - * The NIST SP 800-90 DRBGs are described in the following publucation. + * The NIST SP 800-90 DRBGs are described in the following publication. * * http://csrc.nist.gov/publications/nistpubs/800-90/SP800-90revised_March2007.pdf */ diff --git a/thirdparty/mbedtls/library/debug.c b/thirdparty/mbedtls/library/debug.c index f9229b3606..db3924ac54 100644 --- a/thirdparty/mbedtls/library/debug.c +++ b/thirdparty/mbedtls/library/debug.c @@ -91,7 +91,7 @@ void mbedtls_debug_print_msg( const mbedtls_ssl_context *ssl, int level, va_start( argp, format ); #if defined(_WIN32) -#if defined(_TRUNCATE) +#if defined(_TRUNCATE) && !defined(__MINGW32__) ret = _vsnprintf_s( str, DEBUG_BUF_SIZE, _TRUNCATE, format, argp ); #else ret = _vsnprintf( str, DEBUG_BUF_SIZE, format, argp ); diff --git a/thirdparty/mbedtls/library/entropy_poll.c b/thirdparty/mbedtls/library/entropy_poll.c index 5e8a090b37..ed350735d0 100644 --- a/thirdparty/mbedtls/library/entropy_poll.c +++ b/thirdparty/mbedtls/library/entropy_poll.c @@ -55,16 +55,17 @@ #endif #include <windows.h> #include <bcrypt.h> -#if _MSC_VER <= 1600 -/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and <intsafe.h> are included, as they - * redefine a number of <TYPE>_MAX constants. These constants are guaranteed to be the same, though, so - * we suppress the warning when including intsafe.h. +#if defined(_MSC_VER) && _MSC_VER <= 1600 +/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and + * <intsafe.h> are included, as they redefine a number of <TYPE>_MAX constants. + * These constants are guaranteed to be the same, though, so we suppress the + * warning when including intsafe.h. */ #pragma warning( push ) #pragma warning( disable : 4005 ) #endif #include <intsafe.h> -#if _MSC_VER <= 1600 +#if defined(_MSC_VER) && _MSC_VER <= 1600 #pragma warning( pop ) #endif @@ -76,8 +77,9 @@ int mbedtls_platform_entropy_poll( void *data, unsigned char *output, size_t len *olen = 0; /* - * BCryptGenRandom takes ULONG for size, which is smaller than size_t on 64-bit platforms. - * Ensure len's value can be safely converted into a ULONG. + * BCryptGenRandom takes ULONG for size, which is smaller than size_t on + * 64-bit Windows platforms. Ensure len's value can be safely converted into + * a ULONG. */ if ( FAILED( SizeTToULong( len, &len_as_ulong ) ) ) { diff --git a/thirdparty/mbedtls/library/md2.c b/thirdparty/mbedtls/library/md2.c index 5028e8c586..b88aa406af 100644 --- a/thirdparty/mbedtls/library/md2.c +++ b/thirdparty/mbedtls/library/md2.c @@ -115,6 +115,13 @@ int mbedtls_md2_starts_ret( mbedtls_md2_context *ctx ) return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_md2_starts( mbedtls_md2_context *ctx ) +{ + mbedtls_md2_starts_ret( ctx ); +} +#endif + #if !defined(MBEDTLS_MD2_PROCESS_ALT) int mbedtls_internal_md2_process( mbedtls_md2_context *ctx ) { @@ -151,6 +158,13 @@ int mbedtls_internal_md2_process( mbedtls_md2_context *ctx ) return( 0 ); } + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_md2_process( mbedtls_md2_context *ctx ) +{ + mbedtls_internal_md2_process( ctx ); +} +#endif #endif /* !MBEDTLS_MD2_PROCESS_ALT */ /* @@ -187,6 +201,15 @@ int mbedtls_md2_update_ret( mbedtls_md2_context *ctx, return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_md2_update( mbedtls_md2_context *ctx, + const unsigned char *input, + size_t ilen ) +{ + mbedtls_md2_update_ret( ctx, input, ilen ); +} +#endif + /* * MD2 final digest */ @@ -214,6 +237,14 @@ int mbedtls_md2_finish_ret( mbedtls_md2_context *ctx, return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_md2_finish( mbedtls_md2_context *ctx, + unsigned char output[16] ) +{ + mbedtls_md2_finish_ret( ctx, output ); +} +#endif + #endif /* !MBEDTLS_MD2_ALT */ /* @@ -243,6 +274,15 @@ exit: return( ret ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_md2( const unsigned char *input, + size_t ilen, + unsigned char output[16] ) +{ + mbedtls_md2_ret( input, ilen, output ); +} +#endif + #if defined(MBEDTLS_SELF_TEST) /* diff --git a/thirdparty/mbedtls/library/md4.c b/thirdparty/mbedtls/library/md4.c index 34a4b0e24e..ba704f58e8 100644 --- a/thirdparty/mbedtls/library/md4.c +++ b/thirdparty/mbedtls/library/md4.c @@ -111,6 +111,13 @@ int mbedtls_md4_starts_ret( mbedtls_md4_context *ctx ) return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_md4_starts( mbedtls_md4_context *ctx ) +{ + mbedtls_md4_starts_ret( ctx ); +} +#endif + #if !defined(MBEDTLS_MD4_PROCESS_ALT) int mbedtls_internal_md4_process( mbedtls_md4_context *ctx, const unsigned char data[64] ) @@ -217,6 +224,14 @@ int mbedtls_internal_md4_process( mbedtls_md4_context *ctx, return( 0 ); } + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_md4_process( mbedtls_md4_context *ctx, + const unsigned char data[64] ) +{ + mbedtls_internal_md4_process( ctx, data ); +} +#endif #endif /* !MBEDTLS_MD4_PROCESS_ALT */ /* @@ -273,6 +288,15 @@ int mbedtls_md4_update_ret( mbedtls_md4_context *ctx, return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_md4_update( mbedtls_md4_context *ctx, + const unsigned char *input, + size_t ilen ) +{ + mbedtls_md4_update_ret( ctx, input, ilen ); +} +#endif + static const unsigned char md4_padding[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -318,6 +342,14 @@ int mbedtls_md4_finish_ret( mbedtls_md4_context *ctx, return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_md4_finish( mbedtls_md4_context *ctx, + unsigned char output[16] ) +{ + mbedtls_md4_finish_ret( ctx, output ); +} +#endif + #endif /* !MBEDTLS_MD4_ALT */ /* @@ -347,6 +379,15 @@ exit: return( ret ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_md4( const unsigned char *input, + size_t ilen, + unsigned char output[16] ) +{ + mbedtls_md4_ret( input, ilen, output ); +} +#endif + #if defined(MBEDTLS_SELF_TEST) /* diff --git a/thirdparty/mbedtls/library/md5.c b/thirdparty/mbedtls/library/md5.c index 8872dc467d..8440ebffcf 100644 --- a/thirdparty/mbedtls/library/md5.c +++ b/thirdparty/mbedtls/library/md5.c @@ -110,6 +110,13 @@ int mbedtls_md5_starts_ret( mbedtls_md5_context *ctx ) return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_md5_starts( mbedtls_md5_context *ctx ) +{ + mbedtls_md5_starts_ret( ctx ); +} +#endif + #if !defined(MBEDTLS_MD5_PROCESS_ALT) int mbedtls_internal_md5_process( mbedtls_md5_context *ctx, const unsigned char data[64] ) @@ -236,6 +243,14 @@ int mbedtls_internal_md5_process( mbedtls_md5_context *ctx, return( 0 ); } + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_md5_process( mbedtls_md5_context *ctx, + const unsigned char data[64] ) +{ + mbedtls_internal_md5_process( ctx, data ); +} +#endif #endif /* !MBEDTLS_MD5_PROCESS_ALT */ /* @@ -289,6 +304,15 @@ int mbedtls_md5_update_ret( mbedtls_md5_context *ctx, return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_md5_update( mbedtls_md5_context *ctx, + const unsigned char *input, + size_t ilen ) +{ + mbedtls_md5_update_ret( ctx, input, ilen ); +} +#endif + static const unsigned char md5_padding[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -332,6 +356,14 @@ int mbedtls_md5_finish_ret( mbedtls_md5_context *ctx, return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_md5_finish( mbedtls_md5_context *ctx, + unsigned char output[16] ) +{ + mbedtls_md5_finish_ret( ctx, output ); +} +#endif + #endif /* !MBEDTLS_MD5_ALT */ /* @@ -361,6 +393,15 @@ exit: return( ret ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_md5( const unsigned char *input, + size_t ilen, + unsigned char output[16] ) +{ + mbedtls_md5_ret( input, ilen, output ); +} +#endif + #if defined(MBEDTLS_SELF_TEST) /* * RFC 1321 test vectors diff --git a/thirdparty/mbedtls/library/memory_buffer_alloc.c b/thirdparty/mbedtls/library/memory_buffer_alloc.c index 545d5a2c32..821ae2c708 100644 --- a/thirdparty/mbedtls/library/memory_buffer_alloc.c +++ b/thirdparty/mbedtls/library/memory_buffer_alloc.c @@ -113,7 +113,7 @@ static void debug_header( memory_header *hdr ) #endif } -static void debug_chain() +static void debug_chain( void ) { memory_header *cur = heap.first; @@ -180,11 +180,11 @@ static int verify_header( memory_header *hdr ) return( 0 ); } -static int verify_chain() +static int verify_chain( void ) { - memory_header *prv = heap.first, *cur = heap.first->next; + memory_header *prv = heap.first, *cur; - if( verify_header( heap.first ) != 0 ) + if( prv == NULL || verify_header( prv ) != 0 ) { #if defined(MBEDTLS_MEMORY_DEBUG) mbedtls_fprintf( stderr, "FATAL: verification of first header " @@ -202,6 +202,8 @@ static int verify_chain() return( 1 ); } + cur = heap.first->next; + while( cur != NULL ) { if( verify_header( cur ) != 0 ) @@ -245,7 +247,9 @@ static void *buffer_alloc_calloc( size_t n, size_t size ) original_len = len = n * size; - if( n != 0 && len / n != size ) + if( n == 0 || size == 0 || len / n != size ) + return( NULL ); + else if( len > (size_t)-MBEDTLS_MEMORY_ALIGN_MULTIPLE ) return( NULL ); if( len % MBEDTLS_MEMORY_ALIGN_MULTIPLE ) @@ -386,7 +390,7 @@ static void buffer_alloc_free( void *ptr ) if( ptr == NULL || heap.buf == NULL || heap.first == NULL ) return; - if( p < heap.buf || p > heap.buf + heap.len ) + if( p < heap.buf || p >= heap.buf + heap.len ) { #if defined(MBEDTLS_MEMORY_DEBUG) mbedtls_fprintf( stderr, "FATAL: mbedtls_free() outside of managed " @@ -500,13 +504,13 @@ void mbedtls_memory_buffer_set_verify( int verify ) heap.verify = verify; } -int mbedtls_memory_buffer_alloc_verify() +int mbedtls_memory_buffer_alloc_verify( void ) { return verify_chain(); } #if defined(MBEDTLS_MEMORY_DEBUG) -void mbedtls_memory_buffer_alloc_status() +void mbedtls_memory_buffer_alloc_status( void ) { mbedtls_fprintf( stderr, "Current use: %zu blocks / %zu bytes, max: %zu blocks / " @@ -570,8 +574,7 @@ static void buffer_alloc_free_mutexed( void *ptr ) void mbedtls_memory_buffer_alloc_init( unsigned char *buf, size_t len ) { - memset( &heap, 0, sizeof(buffer_alloc_ctx) ); - memset( buf, 0, len ); + memset( &heap, 0, sizeof( buffer_alloc_ctx ) ); #if defined(MBEDTLS_THREADING_C) mbedtls_mutex_init( &heap.mutex ); @@ -581,26 +584,30 @@ void mbedtls_memory_buffer_alloc_init( unsigned char *buf, size_t len ) mbedtls_platform_set_calloc_free( buffer_alloc_calloc, buffer_alloc_free ); #endif - if( (size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE ) + if( len < sizeof( memory_header ) + MBEDTLS_MEMORY_ALIGN_MULTIPLE ) + return; + else if( (size_t)buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE ) { /* Adjust len first since buf is used in the computation */ len -= MBEDTLS_MEMORY_ALIGN_MULTIPLE - - (size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE; + - (size_t)buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE; buf += MBEDTLS_MEMORY_ALIGN_MULTIPLE - - (size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE; + - (size_t)buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE; } + memset( buf, 0, len ); + heap.buf = buf; heap.len = len; - heap.first = (memory_header *) buf; - heap.first->size = len - sizeof(memory_header); + heap.first = (memory_header *)buf; + heap.first->size = len - sizeof( memory_header ); heap.first->magic1 = MAGIC1; heap.first->magic2 = MAGIC2; heap.first_free = heap.first; } -void mbedtls_memory_buffer_alloc_free() +void mbedtls_memory_buffer_alloc_free( void ) { #if defined(MBEDTLS_THREADING_C) mbedtls_mutex_free( &heap.mutex ); @@ -620,7 +627,7 @@ static int check_pointer( void *p ) return( 0 ); } -static int check_all_free( ) +static int check_all_free( void ) { if( #if defined(MBEDTLS_MEMORY_DEBUG) diff --git a/thirdparty/mbedtls/library/net_sockets.c b/thirdparty/mbedtls/library/net_sockets.c index 754049005d..2fb548caa9 100644 --- a/thirdparty/mbedtls/library/net_sockets.c +++ b/thirdparty/mbedtls/library/net_sockets.c @@ -45,11 +45,14 @@ #if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \ !defined(EFI32) -#ifdef _WIN32_WINNT +/* GODOT ADDITION */ +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501) #undef _WIN32_WINNT -#endif /* Enables getaddrinfo() & Co */ -#define _WIN32_WINNT 0x0601 +#define _WIN32_WINNT 0x0501 +#endif +/* END GODOT ADDITION */ + #include <ws2tcpip.h> #include <winsock2.h> diff --git a/thirdparty/mbedtls/library/oid.c b/thirdparty/mbedtls/library/oid.c index f13826ed74..edea950f8f 100644 --- a/thirdparty/mbedtls/library/oid.c +++ b/thirdparty/mbedtls/library/oid.c @@ -625,6 +625,51 @@ static const oid_md_alg_t oid_md_alg[] = FN_OID_TYPED_FROM_ASN1(oid_md_alg_t, md_alg, oid_md_alg) FN_OID_GET_ATTR1(mbedtls_oid_get_md_alg, oid_md_alg_t, md_alg, mbedtls_md_type_t, md_alg) FN_OID_GET_OID_BY_ATTR1(mbedtls_oid_get_oid_by_md, oid_md_alg_t, oid_md_alg, mbedtls_md_type_t, md_alg) + +/* + * For HMAC digestAlgorithm + */ +typedef struct { + mbedtls_oid_descriptor_t descriptor; + mbedtls_md_type_t md_hmac; +} oid_md_hmac_t; + +static const oid_md_hmac_t oid_md_hmac[] = +{ +#if defined(MBEDTLS_SHA1_C) + { + { ADD_LEN( MBEDTLS_OID_HMAC_SHA1 ), "hmacSHA1", "HMAC-SHA-1" }, + MBEDTLS_MD_SHA1, + }, +#endif /* MBEDTLS_SHA1_C */ +#if defined(MBEDTLS_SHA256_C) + { + { ADD_LEN( MBEDTLS_OID_HMAC_SHA224 ), "hmacSHA224", "HMAC-SHA-224" }, + MBEDTLS_MD_SHA224, + }, + { + { ADD_LEN( MBEDTLS_OID_HMAC_SHA256 ), "hmacSHA256", "HMAC-SHA-256" }, + MBEDTLS_MD_SHA256, + }, +#endif /* MBEDTLS_SHA256_C */ +#if defined(MBEDTLS_SHA512_C) + { + { ADD_LEN( MBEDTLS_OID_HMAC_SHA384 ), "hmacSHA384", "HMAC-SHA-384" }, + MBEDTLS_MD_SHA384, + }, + { + { ADD_LEN( MBEDTLS_OID_HMAC_SHA512 ), "hmacSHA512", "HMAC-SHA-512" }, + MBEDTLS_MD_SHA512, + }, +#endif /* MBEDTLS_SHA512_C */ + { + { NULL, 0, NULL, NULL }, + MBEDTLS_MD_NONE, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_md_hmac_t, md_hmac, oid_md_hmac) +FN_OID_GET_ATTR1(mbedtls_oid_get_md_hmac, oid_md_hmac_t, md_hmac, mbedtls_md_type_t, md_hmac) #endif /* MBEDTLS_MD_C */ #if defined(MBEDTLS_PKCS12_C) diff --git a/thirdparty/mbedtls/library/pem.c b/thirdparty/mbedtls/library/pem.c index c09651f4a2..ac86d7e479 100644 --- a/thirdparty/mbedtls/library/pem.c +++ b/thirdparty/mbedtls/library/pem.c @@ -442,7 +442,7 @@ int mbedtls_pem_write_buffer( const char *header, const char *footer, unsigned char *buf, size_t buf_len, size_t *olen ) { int ret; - unsigned char *encode_buf, *c, *p = buf; + unsigned char *encode_buf = NULL, *c, *p = buf; size_t len = 0, use_len, add_len = 0; mbedtls_base64_encode( NULL, 0, &use_len, der_data, der_len ); @@ -454,7 +454,8 @@ int mbedtls_pem_write_buffer( const char *header, const char *footer, return( MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL ); } - if( ( encode_buf = mbedtls_calloc( 1, use_len ) ) == NULL ) + if( use_len != 0 && + ( ( encode_buf = mbedtls_calloc( 1, use_len ) ) == NULL ) ) return( MBEDTLS_ERR_PEM_ALLOC_FAILED ); if( ( ret = mbedtls_base64_encode( encode_buf, use_len, &use_len, der_data, diff --git a/thirdparty/mbedtls/library/pkcs5.c b/thirdparty/mbedtls/library/pkcs5.c index e28d5a8473..95f44fa98b 100644 --- a/thirdparty/mbedtls/library/pkcs5.c +++ b/thirdparty/mbedtls/library/pkcs5.c @@ -96,11 +96,9 @@ static int pkcs5_parse_pbkdf2_params( const mbedtls_asn1_buf *params, if( ( ret = mbedtls_asn1_get_alg_null( &p, end, &prf_alg_oid ) ) != 0 ) return( MBEDTLS_ERR_PKCS5_INVALID_FORMAT + ret ); - if( MBEDTLS_OID_CMP( MBEDTLS_OID_HMAC_SHA1, &prf_alg_oid ) != 0 ) + if( mbedtls_oid_get_md_hmac( &prf_alg_oid, md_type ) != 0 ) return( MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE ); - *md_type = MBEDTLS_MD_SHA1; - if( p != end ) return( MBEDTLS_ERR_PKCS5_INVALID_FORMAT + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); diff --git a/thirdparty/mbedtls/library/pkparse.c b/thirdparty/mbedtls/library/pkparse.c index b4def4f914..9022db2f93 100644 --- a/thirdparty/mbedtls/library/pkparse.c +++ b/thirdparty/mbedtls/library/pkparse.c @@ -181,6 +181,10 @@ static int pk_get_ecparams( unsigned char **p, const unsigned char *end, { int ret; + if ( end - *p < 1 ) + return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT + + MBEDTLS_ERR_ASN1_OUT_OF_DATA ); + /* Tag may be either OID or SEQUENCE */ params->tag = **p; if( params->tag != MBEDTLS_ASN1_OID @@ -1277,6 +1281,9 @@ int mbedtls_pk_parse_key( mbedtls_pk_context *pk, { unsigned char *key_copy; + if( keylen == 0 ) + return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT ); + if( ( key_copy = mbedtls_calloc( 1, keylen ) ) == NULL ) return( MBEDTLS_ERR_PK_ALLOC_FAILED ); @@ -1348,11 +1355,45 @@ int mbedtls_pk_parse_public_key( mbedtls_pk_context *ctx, { int ret; unsigned char *p; +#if defined(MBEDTLS_RSA_C) + const mbedtls_pk_info_t *pk_info; +#endif #if defined(MBEDTLS_PEM_PARSE_C) size_t len; mbedtls_pem_context pem; mbedtls_pem_init( &pem ); +#if defined(MBEDTLS_RSA_C) + /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ + if( keylen == 0 || key[keylen - 1] != '\0' ) + ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; + else + ret = mbedtls_pem_read_buffer( &pem, + "-----BEGIN RSA PUBLIC KEY-----", + "-----END RSA PUBLIC KEY-----", + key, NULL, 0, &len ); + + if( ret == 0 ) + { + p = pem.buf; + if( ( pk_info = mbedtls_pk_info_from_type( MBEDTLS_PK_RSA ) ) == NULL ) + return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG ); + + if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 ) + return( ret ); + + if ( ( ret = pk_get_rsapubkey( &p, p + pem.buflen, mbedtls_pk_rsa( *ctx ) ) ) != 0 ) + mbedtls_pk_free( ctx ); + + mbedtls_pem_free( &pem ); + return( ret ); + } + else if( ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) + { + mbedtls_pem_free( &pem ); + return( ret ); + } +#endif /* MBEDTLS_RSA_C */ /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ if( keylen == 0 || key[keylen - 1] != '\0' ) @@ -1368,23 +1409,43 @@ int mbedtls_pk_parse_public_key( mbedtls_pk_context *ctx, /* * Was PEM encoded */ - key = pem.buf; - keylen = pem.buflen; + p = pem.buf; + + ret = mbedtls_pk_parse_subpubkey( &p, p + pem.buflen, ctx ); + mbedtls_pem_free( &pem ); + return( ret ); } else if( ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) { mbedtls_pem_free( &pem ); return( ret ); } + mbedtls_pem_free( &pem ); #endif /* MBEDTLS_PEM_PARSE_C */ + +#if defined(MBEDTLS_RSA_C) + if( ( pk_info = mbedtls_pk_info_from_type( MBEDTLS_PK_RSA ) ) == NULL ) + return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG ); + + if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 ) + return( ret ); + + p = (unsigned char *)key; + ret = pk_get_rsapubkey( &p, p + keylen, mbedtls_pk_rsa( *ctx ) ); + if( ret == 0 ) + { + return( ret ); + } + mbedtls_pk_free( ctx ); + if( ret != ( MBEDTLS_ERR_PK_INVALID_PUBKEY + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ) ) + { + return( ret ); + } +#endif /* MBEDTLS_RSA_C */ p = (unsigned char *) key; ret = mbedtls_pk_parse_subpubkey( &p, p + keylen, ctx ); -#if defined(MBEDTLS_PEM_PARSE_C) - mbedtls_pem_free( &pem ); -#endif - return( ret ); } diff --git a/thirdparty/mbedtls/library/platform.c b/thirdparty/mbedtls/library/platform.c index 76df7fac18..a295f9b9af 100644 --- a/thirdparty/mbedtls/library/platform.c +++ b/thirdparty/mbedtls/library/platform.c @@ -82,7 +82,7 @@ int mbedtls_platform_win32_snprintf( char *s, size_t n, const char *fmt, ... ) return( -1 ); va_start( argp, fmt ); -#if defined(_TRUNCATE) +#if defined(_TRUNCATE) && !defined(__MINGW32__) ret = _vsnprintf_s( s, n, _TRUNCATE, fmt, argp ); #else ret = _vsnprintf( s, n, fmt, argp ); diff --git a/thirdparty/mbedtls/library/ripemd160.c b/thirdparty/mbedtls/library/ripemd160.c index b85b117c6a..2ba48b7fdb 100644 --- a/thirdparty/mbedtls/library/ripemd160.c +++ b/thirdparty/mbedtls/library/ripemd160.c @@ -112,6 +112,13 @@ int mbedtls_ripemd160_starts_ret( mbedtls_ripemd160_context *ctx ) return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_ripemd160_starts( mbedtls_ripemd160_context *ctx ) +{ + mbedtls_ripemd160_starts_ret( ctx ); +} +#endif + #if !defined(MBEDTLS_RIPEMD160_PROCESS_ALT) /* * Process one block @@ -295,6 +302,14 @@ int mbedtls_internal_ripemd160_process( mbedtls_ripemd160_context *ctx, return( 0 ); } + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_ripemd160_process( mbedtls_ripemd160_context *ctx, + const unsigned char data[64] ) +{ + mbedtls_internal_ripemd160_process( ctx, data ); +} +#endif #endif /* !MBEDTLS_RIPEMD160_PROCESS_ALT */ /* @@ -349,6 +364,15 @@ int mbedtls_ripemd160_update_ret( mbedtls_ripemd160_context *ctx, return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_ripemd160_update( mbedtls_ripemd160_context *ctx, + const unsigned char *input, + size_t ilen ) +{ + mbedtls_ripemd160_update_ret( ctx, input, ilen ); +} +#endif + static const unsigned char ripemd160_padding[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -395,6 +419,14 @@ int mbedtls_ripemd160_finish_ret( mbedtls_ripemd160_context *ctx, return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_ripemd160_finish( mbedtls_ripemd160_context *ctx, + unsigned char output[20] ) +{ + mbedtls_ripemd160_finish_ret( ctx, output ); +} +#endif + #endif /* ! MBEDTLS_RIPEMD160_ALT */ /* @@ -424,6 +456,15 @@ exit: return( ret ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_ripemd160( const unsigned char *input, + size_t ilen, + unsigned char output[20] ) +{ + mbedtls_ripemd160_ret( input, ilen, output ); +} +#endif + #if defined(MBEDTLS_SELF_TEST) /* * Test vectors from the RIPEMD-160 paper and diff --git a/thirdparty/mbedtls/library/rsa.c b/thirdparty/mbedtls/library/rsa.c index 6526978e26..c9f7ba91b6 100644 --- a/thirdparty/mbedtls/library/rsa.c +++ b/thirdparty/mbedtls/library/rsa.c @@ -773,16 +773,38 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx, { int ret; size_t olen; - mbedtls_mpi T, T1, T2; + + /* Temporary holding the result */ + mbedtls_mpi T; + + /* Temporaries holding P-1, Q-1 and the + * exponent blinding factor, respectively. */ mbedtls_mpi P1, Q1, R; -#if defined(MBEDTLS_RSA_NO_CRT) - mbedtls_mpi D_blind; - mbedtls_mpi *D = &ctx->D; -#else + +#if !defined(MBEDTLS_RSA_NO_CRT) + /* Temporaries holding the results mod p resp. mod q. */ + mbedtls_mpi TP, TQ; + + /* Temporaries holding the blinded exponents for + * the mod p resp. mod q computation (if used). */ mbedtls_mpi DP_blind, DQ_blind; + + /* Pointers to actual exponents to be used - either the unblinded + * or the blinded ones, depending on the presence of a PRNG. */ mbedtls_mpi *DP = &ctx->DP; mbedtls_mpi *DQ = &ctx->DQ; -#endif +#else + /* Temporary holding the blinded exponent (if used). */ + mbedtls_mpi D_blind; + + /* Pointer to actual exponent to be used - either the unblinded + * or the blinded one, depending on the presence of a PRNG. */ + mbedtls_mpi *D = &ctx->D; +#endif /* MBEDTLS_RSA_NO_CRT */ + + /* Temporaries holding the initial input and the double + * checked result; should be the same in the end. */ + mbedtls_mpi I, C; if( rsa_check_context( ctx, 1 /* private key checks */, f_rng != NULL /* blinding y/n */ ) != 0 ) @@ -790,8 +812,17 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx, return( MBEDTLS_ERR_RSA_BAD_INPUT_DATA ); } - mbedtls_mpi_init( &T ); mbedtls_mpi_init( &T1 ); mbedtls_mpi_init( &T2 ); - mbedtls_mpi_init( &P1 ); mbedtls_mpi_init( &Q1 ); mbedtls_mpi_init( &R ); +#if defined(MBEDTLS_THREADING_C) + if( ( ret = mbedtls_mutex_lock( &ctx->mutex ) ) != 0 ) + return( ret ); +#endif + + /* MPI Initialization */ + mbedtls_mpi_init( &T ); + + mbedtls_mpi_init( &P1 ); + mbedtls_mpi_init( &Q1 ); + mbedtls_mpi_init( &R ); if( f_rng != NULL ) { @@ -803,12 +834,15 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx, #endif } - -#if defined(MBEDTLS_THREADING_C) - if( ( ret = mbedtls_mutex_lock( &ctx->mutex ) ) != 0 ) - return( ret ); +#if !defined(MBEDTLS_RSA_NO_CRT) + mbedtls_mpi_init( &TP ); mbedtls_mpi_init( &TQ ); #endif + mbedtls_mpi_init( &I ); + mbedtls_mpi_init( &C ); + + /* End of MPI initialization */ + MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( &T, input, ctx->len ) ); if( mbedtls_mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) { @@ -816,6 +850,8 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx, goto cleanup; } + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &I, &T ) ); + if( f_rng != NULL ) { /* @@ -874,24 +910,25 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx, /* * Faster decryption using the CRT * - * T1 = input ^ dP mod P - * T2 = input ^ dQ mod Q + * TP = input ^ dP mod P + * TQ = input ^ dQ mod Q */ - MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &T1, &T, DP, &ctx->P, &ctx->RP ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &T2, &T, DQ, &ctx->Q, &ctx->RQ ) ); + + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &TP, &T, DP, &ctx->P, &ctx->RP ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &TQ, &T, DQ, &ctx->Q, &ctx->RQ ) ); /* - * T = (T1 - T2) * (Q^-1 mod P) mod P + * T = (TP - TQ) * (Q^-1 mod P) mod P */ - MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &T, &T1, &T2 ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &T1, &T, &ctx->QP ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &T, &T1, &ctx->P ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &T, &TP, &TQ ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &TP, &T, &ctx->QP ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &T, &TP, &ctx->P ) ); /* - * T = T2 + T * Q + * T = TQ + T * Q */ - MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &T1, &T, &ctx->Q ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &T, &T2, &T1 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &TP, &T, &ctx->Q ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &T, &TQ, &TP ) ); #endif /* MBEDTLS_RSA_NO_CRT */ if( f_rng != NULL ) @@ -904,6 +941,15 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx, MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &T, &T, &ctx->N ) ); } + /* Verify the result to prevent glitching attacks. */ + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &C, &T, &ctx->E, + &ctx->N, &ctx->RN ) ); + if( mbedtls_mpi_cmp_mpi( &C, &I ) != 0 ) + { + ret = MBEDTLS_ERR_RSA_VERIFY_FAILED; + goto cleanup; + } + olen = ctx->len; MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary( &T, output, olen ) ); @@ -913,8 +959,9 @@ cleanup: return( MBEDTLS_ERR_THREADING_MUTEX_ERROR ); #endif - mbedtls_mpi_free( &T ); mbedtls_mpi_free( &T1 ); mbedtls_mpi_free( &T2 ); - mbedtls_mpi_free( &P1 ); mbedtls_mpi_free( &Q1 ); mbedtls_mpi_free( &R ); + mbedtls_mpi_free( &P1 ); + mbedtls_mpi_free( &Q1 ); + mbedtls_mpi_free( &R ); if( f_rng != NULL ) { @@ -926,6 +973,15 @@ cleanup: #endif } + mbedtls_mpi_free( &T ); + +#if !defined(MBEDTLS_RSA_NO_CRT) + mbedtls_mpi_free( &TP ); mbedtls_mpi_free( &TQ ); +#endif + + mbedtls_mpi_free( &C ); + mbedtls_mpi_free( &I ); + if( ret != 0 ) return( MBEDTLS_ERR_RSA_PRIVATE_FAILED + ret ); @@ -2222,7 +2278,8 @@ int mbedtls_rsa_self_test( int verbose ) if( verbose != 0 ) mbedtls_printf( "failed\n" ); - return( 1 ); + ret = 1; + goto cleanup; } if( verbose != 0 ) @@ -2237,7 +2294,8 @@ int mbedtls_rsa_self_test( int verbose ) if( verbose != 0 ) mbedtls_printf( "failed\n" ); - return( 1 ); + ret = 1; + goto cleanup; } if( verbose != 0 ) @@ -2250,7 +2308,8 @@ int mbedtls_rsa_self_test( int verbose ) if( verbose != 0 ) mbedtls_printf( "failed\n" ); - return( 1 ); + ret = 1; + goto cleanup; } if( memcmp( rsa_decrypted, rsa_plaintext, len ) != 0 ) @@ -2258,7 +2317,8 @@ int mbedtls_rsa_self_test( int verbose ) if( verbose != 0 ) mbedtls_printf( "failed\n" ); - return( 1 ); + ret = 1; + goto cleanup; } if( verbose != 0 ) @@ -2283,7 +2343,8 @@ int mbedtls_rsa_self_test( int verbose ) if( verbose != 0 ) mbedtls_printf( "failed\n" ); - return( 1 ); + ret = 1; + goto cleanup; } if( verbose != 0 ) @@ -2296,7 +2357,8 @@ int mbedtls_rsa_self_test( int verbose ) if( verbose != 0 ) mbedtls_printf( "failed\n" ); - return( 1 ); + ret = 1; + goto cleanup; } if( verbose != 0 ) diff --git a/thirdparty/mbedtls/library/sha1.c b/thirdparty/mbedtls/library/sha1.c index 8432eba8bd..1f29a0fbf8 100644 --- a/thirdparty/mbedtls/library/sha1.c +++ b/thirdparty/mbedtls/library/sha1.c @@ -111,6 +111,13 @@ int mbedtls_sha1_starts_ret( mbedtls_sha1_context *ctx ) return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_sha1_starts( mbedtls_sha1_context *ctx ) +{ + mbedtls_sha1_starts_ret( ctx ); +} +#endif + #if !defined(MBEDTLS_SHA1_PROCESS_ALT) int mbedtls_internal_sha1_process( mbedtls_sha1_context *ctx, const unsigned char data[64] ) @@ -270,6 +277,14 @@ int mbedtls_internal_sha1_process( mbedtls_sha1_context *ctx, return( 0 ); } + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_sha1_process( mbedtls_sha1_context *ctx, + const unsigned char data[64] ) +{ + mbedtls_internal_sha1_process( ctx, data ); +} +#endif #endif /* !MBEDTLS_SHA1_PROCESS_ALT */ /* @@ -322,6 +337,15 @@ int mbedtls_sha1_update_ret( mbedtls_sha1_context *ctx, return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_sha1_update( mbedtls_sha1_context *ctx, + const unsigned char *input, + size_t ilen ) +{ + mbedtls_sha1_update_ret( ctx, input, ilen ); +} +#endif + static const unsigned char sha1_padding[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -365,6 +389,14 @@ int mbedtls_sha1_finish_ret( mbedtls_sha1_context *ctx, return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_sha1_finish( mbedtls_sha1_context *ctx, + unsigned char output[20] ) +{ + mbedtls_sha1_finish_ret( ctx, output ); +} +#endif + #endif /* !MBEDTLS_SHA1_ALT */ /* @@ -394,6 +426,15 @@ exit: return( ret ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_sha1( const unsigned char *input, + size_t ilen, + unsigned char output[20] ) +{ + mbedtls_sha1_ret( input, ilen, output ); +} +#endif + #if defined(MBEDTLS_SELF_TEST) /* * FIPS-180-1 test vectors diff --git a/thirdparty/mbedtls/library/sha256.c b/thirdparty/mbedtls/library/sha256.c index abcd64d134..f39bcbab6c 100644 --- a/thirdparty/mbedtls/library/sha256.c +++ b/thirdparty/mbedtls/library/sha256.c @@ -135,6 +135,14 @@ int mbedtls_sha256_starts_ret( mbedtls_sha256_context *ctx, int is224 ) return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_sha256_starts( mbedtls_sha256_context *ctx, + int is224 ) +{ + mbedtls_sha256_starts_ret( ctx, is224 ); +} +#endif + #if !defined(MBEDTLS_SHA256_PROCESS_ALT) static const uint32_t K[] = { @@ -238,6 +246,14 @@ int mbedtls_internal_sha256_process( mbedtls_sha256_context *ctx, return( 0 ); } + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_sha256_process( mbedtls_sha256_context *ctx, + const unsigned char data[64] ) +{ + mbedtls_internal_sha256_process( ctx, data ); +} +#endif #endif /* !MBEDTLS_SHA256_PROCESS_ALT */ /* @@ -290,6 +306,15 @@ int mbedtls_sha256_update_ret( mbedtls_sha256_context *ctx, return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_sha256_update( mbedtls_sha256_context *ctx, + const unsigned char *input, + size_t ilen ) +{ + mbedtls_sha256_update_ret( ctx, input, ilen ); +} +#endif + static const unsigned char sha256_padding[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -339,6 +364,14 @@ int mbedtls_sha256_finish_ret( mbedtls_sha256_context *ctx, return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_sha256_finish( mbedtls_sha256_context *ctx, + unsigned char output[32] ) +{ + mbedtls_sha256_finish_ret( ctx, output ); +} +#endif + #endif /* !MBEDTLS_SHA256_ALT */ /* @@ -369,6 +402,16 @@ exit: return( ret ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_sha256( const unsigned char *input, + size_t ilen, + unsigned char output[32], + int is224 ) +{ + mbedtls_sha256_ret( input, ilen, output, is224 ); +} +#endif + #if defined(MBEDTLS_SELF_TEST) /* * FIPS-180-2 test vectors diff --git a/thirdparty/mbedtls/library/sha512.c b/thirdparty/mbedtls/library/sha512.c index c99b6da950..97cee07c56 100644 --- a/thirdparty/mbedtls/library/sha512.c +++ b/thirdparty/mbedtls/library/sha512.c @@ -149,6 +149,14 @@ int mbedtls_sha512_starts_ret( mbedtls_sha512_context *ctx, int is384 ) return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_sha512_starts( mbedtls_sha512_context *ctx, + int is384 ) +{ + mbedtls_sha512_starts_ret( ctx, is384 ); +} +#endif + #if !defined(MBEDTLS_SHA512_PROCESS_ALT) /* @@ -269,6 +277,14 @@ int mbedtls_internal_sha512_process( mbedtls_sha512_context *ctx, return( 0 ); } + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_sha512_process( mbedtls_sha512_context *ctx, + const unsigned char data[128] ) +{ + mbedtls_internal_sha512_process( ctx, data ); +} +#endif #endif /* !MBEDTLS_SHA512_PROCESS_ALT */ /* @@ -320,6 +336,15 @@ int mbedtls_sha512_update_ret( mbedtls_sha512_context *ctx, return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_sha512_update( mbedtls_sha512_context *ctx, + const unsigned char *input, + size_t ilen ) +{ + mbedtls_sha512_update_ret( ctx, input, ilen ); +} +#endif + static const unsigned char sha512_padding[128] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -375,6 +400,14 @@ int mbedtls_sha512_finish_ret( mbedtls_sha512_context *ctx, return( 0 ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_sha512_finish( mbedtls_sha512_context *ctx, + unsigned char output[64] ) +{ + mbedtls_sha512_finish_ret( ctx, output ); +} +#endif + #endif /* !MBEDTLS_SHA512_ALT */ /* @@ -405,6 +438,16 @@ exit: return( ret ); } +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_sha512( const unsigned char *input, + size_t ilen, + unsigned char output[64], + int is384 ) +{ + mbedtls_sha512_ret( input, ilen, output, is384 ); +} +#endif + #if defined(MBEDTLS_SELF_TEST) /* diff --git a/thirdparty/mbedtls/library/ssl_cli.c b/thirdparty/mbedtls/library/ssl_cli.c index 2534346a49..88864b8136 100644 --- a/thirdparty/mbedtls/library/ssl_cli.c +++ b/thirdparty/mbedtls/library/ssl_cli.c @@ -902,6 +902,8 @@ static int ssl_write_client_hello( mbedtls_ssl_context *ssl ) *p++ = (unsigned char)( ciphersuites[i] ); } + MBEDTLS_SSL_DEBUG_MSG( 3, ( "client hello, got %d ciphersuites (excluding SCSVs)", n ) ); + /* * Add TLS_EMPTY_RENEGOTIATION_INFO_SCSV */ @@ -909,6 +911,7 @@ static int ssl_write_client_hello( mbedtls_ssl_context *ssl ) if( ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE ) #endif { + MBEDTLS_SSL_DEBUG_MSG( 3, ( "adding EMPTY_RENEGOTIATION_INFO_SCSV" ) ); *p++ = (unsigned char)( MBEDTLS_SSL_EMPTY_RENEGOTIATION_INFO >> 8 ); *p++ = (unsigned char)( MBEDTLS_SSL_EMPTY_RENEGOTIATION_INFO ); n++; @@ -928,8 +931,6 @@ static int ssl_write_client_hello( mbedtls_ssl_context *ssl ) *q++ = (unsigned char)( n >> 7 ); *q++ = (unsigned char)( n << 1 ); - MBEDTLS_SSL_DEBUG_MSG( 3, ( "client hello, got %d ciphersuites", n ) ); - #if defined(MBEDTLS_ZLIB_SUPPORT) offer_compress = 1; #else @@ -2057,10 +2058,16 @@ static int ssl_parse_server_psk_hint( mbedtls_ssl_context *ssl, * * opaque psk_identity_hint<0..2^16-1>; */ + if( (*p) > end - 2 ) + { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server key exchange message " + "(psk_identity_hint length)" ) ); + return( MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); + } len = (*p)[0] << 8 | (*p)[1]; *p += 2; - if( (*p) + len > end ) + if( (*p) > end - len ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server key exchange message " "(psk_identity_hint length)" ) ); @@ -2478,10 +2485,18 @@ static int ssl_parse_server_key_exchange( mbedtls_ssl_context *ssl ) /* * Read signature */ + + if( p > end - 2 ) + { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); + mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR ); + return( MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); + } sig_len = ( p[0] << 8 ) | p[1]; p += 2; - if( end != p + sig_len ) + if( p != end - sig_len ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, diff --git a/thirdparty/mbedtls/library/ssl_tls.c b/thirdparty/mbedtls/library/ssl_tls.c index 617dedb1b0..236e52d767 100644 --- a/thirdparty/mbedtls/library/ssl_tls.c +++ b/thirdparty/mbedtls/library/ssl_tls.c @@ -501,6 +501,7 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl ) unsigned char *key2; unsigned char *mac_enc; unsigned char *mac_dec; + size_t mac_key_len; size_t iv_copy_len; const mbedtls_cipher_info_t *cipher_info; const mbedtls_md_info_t *md_info; @@ -692,6 +693,7 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl ) cipher_info->mode == MBEDTLS_MODE_CCM ) { transform->maclen = 0; + mac_key_len = 0; transform->ivlen = 12; transform->fixed_ivlen = 4; @@ -712,7 +714,8 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl ) } /* Get MAC length */ - transform->maclen = mbedtls_md_get_size( md_info ); + mac_key_len = mbedtls_md_get_size( md_info ); + transform->maclen = mac_key_len; #if defined(MBEDTLS_SSL_TRUNCATED_HMAC) /* @@ -721,7 +724,16 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl ) * so we only need to adjust the length here. */ if( session->trunc_hmac == MBEDTLS_SSL_TRUNC_HMAC_ENABLED ) + { transform->maclen = MBEDTLS_SSL_TRUNCATED_HMAC_LEN; + +#if defined(MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT) + /* Fall back to old, non-compliant version of the truncated + * HMAC implementation which also truncates the key + * (Mbed TLS versions from 1.3 to 2.6.0) */ + mac_key_len = transform->maclen; +#endif + } #endif /* MBEDTLS_SSL_TRUNCATED_HMAC */ /* IV length */ @@ -783,11 +795,11 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl ) #if defined(MBEDTLS_SSL_CLI_C) if( ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT ) { - key1 = keyblk + transform->maclen * 2; - key2 = keyblk + transform->maclen * 2 + transform->keylen; + key1 = keyblk + mac_key_len * 2; + key2 = keyblk + mac_key_len * 2 + transform->keylen; mac_enc = keyblk; - mac_dec = keyblk + transform->maclen; + mac_dec = keyblk + mac_key_len; /* * This is not used in TLS v1.1. @@ -803,10 +815,10 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl ) #if defined(MBEDTLS_SSL_SRV_C) if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER ) { - key1 = keyblk + transform->maclen * 2 + transform->keylen; - key2 = keyblk + transform->maclen * 2; + key1 = keyblk + mac_key_len * 2 + transform->keylen; + key2 = keyblk + mac_key_len * 2; - mac_enc = keyblk + transform->maclen; + mac_enc = keyblk + mac_key_len; mac_dec = keyblk; /* @@ -828,14 +840,14 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl ) #if defined(MBEDTLS_SSL_PROTO_SSL3) if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 ) { - if( transform->maclen > sizeof transform->mac_enc ) + if( mac_key_len > sizeof transform->mac_enc ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); } - memcpy( transform->mac_enc, mac_enc, transform->maclen ); - memcpy( transform->mac_dec, mac_dec, transform->maclen ); + memcpy( transform->mac_enc, mac_enc, mac_key_len ); + memcpy( transform->mac_dec, mac_dec, mac_key_len ); } else #endif /* MBEDTLS_SSL_PROTO_SSL3 */ @@ -843,8 +855,8 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl ) defined(MBEDTLS_SSL_PROTO_TLS1_2) if( ssl->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_1 ) { - mbedtls_md_hmac_starts( &transform->md_ctx_enc, mac_enc, transform->maclen ); - mbedtls_md_hmac_starts( &transform->md_ctx_dec, mac_dec, transform->maclen ); + mbedtls_md_hmac_starts( &transform->md_ctx_enc, mac_enc, mac_key_len ); + mbedtls_md_hmac_starts( &transform->md_ctx_dec, mac_dec, mac_key_len ); } else #endif @@ -864,7 +876,7 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl ) transform->iv_enc, transform->iv_dec, iv_copy_len, mac_enc, mac_dec, - transform->maclen ) ) != 0 ) + mac_key_len ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_hw_record_init", ret ); return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED ); @@ -877,7 +889,7 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl ) { ssl->conf->f_export_keys( ssl->conf->p_export_keys, session->master, keyblk, - transform->maclen, transform->keylen, + mac_key_len, transform->keylen, iv_copy_len ); } #endif @@ -7673,8 +7685,14 @@ int mbedtls_ssl_config_defaults( mbedtls_ssl_config *conf, * Default */ default: - conf->min_major_ver = MBEDTLS_SSL_MAJOR_VERSION_3; - conf->min_minor_ver = MBEDTLS_SSL_MINOR_VERSION_1; /* TLS 1.0 */ + conf->min_major_ver = ( MBEDTLS_SSL_MIN_MAJOR_VERSION > + MBEDTLS_SSL_MIN_VALID_MAJOR_VERSION ) ? + MBEDTLS_SSL_MIN_MAJOR_VERSION : + MBEDTLS_SSL_MIN_VALID_MAJOR_VERSION; + conf->min_minor_ver = ( MBEDTLS_SSL_MIN_MINOR_VERSION > + MBEDTLS_SSL_MIN_VALID_MINOR_VERSION ) ? + MBEDTLS_SSL_MIN_MINOR_VERSION : + MBEDTLS_SSL_MIN_VALID_MINOR_VERSION; conf->max_major_ver = MBEDTLS_SSL_MAX_MAJOR_VERSION; conf->max_minor_ver = MBEDTLS_SSL_MAX_MINOR_VERSION; diff --git a/thirdparty/mbedtls/library/version.c b/thirdparty/mbedtls/library/version.c index 6ca80d4695..fd96750885 100644 --- a/thirdparty/mbedtls/library/version.c +++ b/thirdparty/mbedtls/library/version.c @@ -30,7 +30,7 @@ #include "mbedtls/version.h" #include <string.h> -unsigned int mbedtls_version_get_number() +unsigned int mbedtls_version_get_number( void ) { return( MBEDTLS_VERSION_NUMBER ); } diff --git a/thirdparty/mbedtls/library/version_features.c b/thirdparty/mbedtls/library/version_features.c index 72afec2da0..da47e3d753 100644 --- a/thirdparty/mbedtls/library/version_features.c +++ b/thirdparty/mbedtls/library/version_features.c @@ -468,6 +468,9 @@ static const char *features[] = { #if defined(MBEDTLS_SSL_TRUNCATED_HMAC) "MBEDTLS_SSL_TRUNCATED_HMAC", #endif /* MBEDTLS_SSL_TRUNCATED_HMAC */ +#if defined(MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT) + "MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT", +#endif /* MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT */ #if defined(MBEDTLS_THREADING_ALT) "MBEDTLS_THREADING_ALT", #endif /* MBEDTLS_THREADING_ALT */ diff --git a/thirdparty/mbedtls/library/x509_crl.c b/thirdparty/mbedtls/library/x509_crl.c index 55d12acd03..b0f39d428b 100644 --- a/thirdparty/mbedtls/library/x509_crl.c +++ b/thirdparty/mbedtls/library/x509_crl.c @@ -95,17 +95,23 @@ static int x509_crl_get_version( unsigned char **p, } /* - * X.509 CRL v2 extensions (no extensions parsed yet.) + * X.509 CRL v2 extensions + * + * We currently don't parse any extension's content, but we do check that the + * list of extensions is well-formed and abort on critical extensions (that + * are unsupported as we don't support any extension so far) */ static int x509_get_crl_ext( unsigned char **p, const unsigned char *end, mbedtls_x509_buf *ext ) { int ret; - size_t len = 0; - /* Get explicit tag */ - if( ( ret = mbedtls_x509_get_ext( p, end, ext, 0) ) != 0 ) + /* + * crlExtensions [0] EXPLICIT Extensions OPTIONAL + * -- if present, version MUST be v2 + */ + if( ( ret = mbedtls_x509_get_ext( p, end, ext, 0 ) ) != 0 ) { if( ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ) return( 0 ); @@ -115,11 +121,54 @@ static int x509_get_crl_ext( unsigned char **p, while( *p < end ) { + /* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING } + */ + int is_critical = 0; + const unsigned char *end_ext_data; + size_t len; + + /* Get enclosing sequence tag */ if( ( ret = mbedtls_asn1_get_tag( p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); + end_ext_data = *p + len; + + /* Get OID (currently ignored) */ + if( ( ret = mbedtls_asn1_get_tag( p, end_ext_data, &len, + MBEDTLS_ASN1_OID ) ) != 0 ) + { + return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); + } + *p += len; + + /* Get optional critical */ + if( ( ret = mbedtls_asn1_get_bool( p, end_ext_data, + &is_critical ) ) != 0 && + ( ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ) ) + { + return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); + } + + /* Data should be octet string type */ + if( ( ret = mbedtls_asn1_get_tag( p, end_ext_data, &len, + MBEDTLS_ASN1_OCTET_STRING ) ) != 0 ) + return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); + + /* Ignore data so far and just check its length */ *p += len; + if( *p != end_ext_data ) + return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); + + /* Abort on (unsupported) critical extensions */ + if( is_critical ) + return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ); } if( *p != end ) @@ -257,7 +306,7 @@ int mbedtls_x509_crl_parse_der( mbedtls_x509_crl *chain, { int ret; size_t len; - unsigned char *p, *end; + unsigned char *p = NULL, *end = NULL; mbedtls_x509_buf sig_params1, sig_params2, sig_oid2; mbedtls_x509_crl *crl = chain; @@ -294,7 +343,11 @@ int mbedtls_x509_crl_parse_der( mbedtls_x509_crl *chain, /* * Copy raw DER-encoded CRL */ - if( ( p = mbedtls_calloc( 1, buflen ) ) == NULL ) + if( buflen == 0 ) + return( MBEDTLS_ERR_X509_INVALID_FORMAT ); + + p = mbedtls_calloc( 1, buflen ); + if( p == NULL ) return( MBEDTLS_ERR_X509_ALLOC_FAILED ); memcpy( p, buf, buflen ); diff --git a/thirdparty/mbedtls/library/x509_crt.c b/thirdparty/mbedtls/library/x509_crt.c index 6c592fdb5a..2a5dbb8783 100644 --- a/thirdparty/mbedtls/library/x509_crt.c +++ b/thirdparty/mbedtls/library/x509_crt.c @@ -62,16 +62,17 @@ #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) #include <windows.h> -#if _MSC_VER <= 1600 -/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and <intsafe.h> are included, as they - * redefine a number of <TYPE>_MAX constants. These constants are guaranteed to be the same, though, so - * we suppress the warning when including intsafe.h. +#if defined(_MSC_VER) && _MSC_VER <= 1600 +/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and + * <intsafe.h> are included, as they redefine a number of <TYPE>_MAX constants. + * These constants are guaranteed to be the same, though, so we suppress the + * warning when including intsafe.h. */ #pragma warning( push ) #pragma warning( disable : 4005 ) #endif #include <intsafe.h> -#if _MSC_VER <= 1600 +#if defined(_MSC_VER) && _MSC_VER <= 1600 #pragma warning( pop ) #endif #else @@ -145,7 +146,8 @@ const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_suiteb = MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) | MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ), /* Only ECDSA */ - MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ), + MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) | + MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ), #if defined(MBEDTLS_ECP_C) /* Only NIST P-256 and P-384 */ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP256R1 ) | @@ -484,9 +486,12 @@ static int x509_get_subject_alt_name( unsigned char **p, if( ( ret = mbedtls_asn1_get_len( p, end, &tag_len ) ) != 0 ) return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); - if( ( tag & MBEDTLS_ASN1_CONTEXT_SPECIFIC ) != MBEDTLS_ASN1_CONTEXT_SPECIFIC ) + if( ( tag & MBEDTLS_ASN1_TAG_CLASS_MASK ) != + MBEDTLS_ASN1_CONTEXT_SPECIFIC ) + { return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ); + } /* Skip everything but DNS name */ if( tag != ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | 2 ) ) @@ -1140,6 +1145,14 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path ) if ( FAILED ( SizeTToInt( len, &lengthAsInt ) ) ) return( MBEDTLS_ERR_X509_FILE_IO_ERROR ); + /* + * Note this function uses the code page CP_ACP, and assumes the incoming + * string is encoded in ANSI, before translating it into Unicode. If the + * incoming string were changed to be UTF-8, then the length check needs to + * change to check the number of characters, not the number of bytes, in the + * incoming string are less than MAX_PATH to avoid a buffer overrun with + * MultiByteToWideChar(). + */ w_ret = MultiByteToWideChar( CP_ACP, 0, filename, lengthAsInt, szDir, MAX_PATH - 3 ); if( w_ret == 0 ) |