diff options
53 files changed, 1605 insertions, 286 deletions
diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 8afca5a2df..47d40f2742 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -1317,11 +1317,11 @@ Variant ClassDB::instantiate(const StringName &p_class) const { } } -bool ClassDB::has_signal(StringName p_class, StringName p_signal) const { +bool ClassDB::class_has_signal(StringName p_class, StringName p_signal) const { return ::ClassDB::has_signal(p_class, p_signal); } -Dictionary ClassDB::get_signal(StringName p_class, StringName p_signal) const { +Dictionary ClassDB::class_get_signal(StringName p_class, StringName p_signal) const { MethodInfo signal; if (::ClassDB::get_signal(p_class, p_signal, &signal)) { return signal.operator Dictionary(); @@ -1330,7 +1330,7 @@ Dictionary ClassDB::get_signal(StringName p_class, StringName p_signal) const { } } -TypedArray<Dictionary> ClassDB::get_signal_list(StringName p_class, bool p_no_inheritance) const { +TypedArray<Dictionary> ClassDB::class_get_signal_list(StringName p_class, bool p_no_inheritance) const { List<MethodInfo> signals; ::ClassDB::get_signal_list(p_class, &signals, p_no_inheritance); TypedArray<Dictionary> ret; @@ -1342,7 +1342,7 @@ TypedArray<Dictionary> ClassDB::get_signal_list(StringName p_class, bool p_no_in return ret; } -TypedArray<Dictionary> ClassDB::get_property_list(StringName p_class, bool p_no_inheritance) const { +TypedArray<Dictionary> ClassDB::class_get_property_list(StringName p_class, bool p_no_inheritance) const { List<PropertyInfo> plist; ::ClassDB::get_property_list(p_class, &plist, p_no_inheritance); TypedArray<Dictionary> ret; @@ -1353,13 +1353,13 @@ TypedArray<Dictionary> ClassDB::get_property_list(StringName p_class, bool p_no_ return ret; } -Variant ClassDB::get_property(Object *p_object, const StringName &p_property) const { +Variant ClassDB::class_get_property(Object *p_object, const StringName &p_property) const { Variant ret; ::ClassDB::get_property(p_object, p_property, ret); return ret; } -Error ClassDB::set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const { +Error ClassDB::class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const { Variant ret; bool valid; if (!::ClassDB::set_property(p_object, p_property, p_value, &valid)) { @@ -1370,11 +1370,11 @@ Error ClassDB::set_property(Object *p_object, const StringName &p_property, cons return OK; } -bool ClassDB::has_method(StringName p_class, StringName p_method, bool p_no_inheritance) const { +bool ClassDB::class_has_method(StringName p_class, StringName p_method, bool p_no_inheritance) const { return ::ClassDB::has_method(p_class, p_method, p_no_inheritance); } -TypedArray<Dictionary> ClassDB::get_method_list(StringName p_class, bool p_no_inheritance) const { +TypedArray<Dictionary> ClassDB::class_get_method_list(StringName p_class, bool p_no_inheritance) const { List<MethodInfo> methods; ::ClassDB::get_method_list(p_class, &methods, p_no_inheritance); TypedArray<Dictionary> ret; @@ -1392,7 +1392,7 @@ TypedArray<Dictionary> ClassDB::get_method_list(StringName p_class, bool p_no_in return ret; } -PackedStringArray ClassDB::get_integer_constant_list(const StringName &p_class, bool p_no_inheritance) const { +PackedStringArray ClassDB::class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance) const { List<String> constants; ::ClassDB::get_integer_constant_list(p_class, &constants, p_no_inheritance); @@ -1406,24 +1406,24 @@ PackedStringArray ClassDB::get_integer_constant_list(const StringName &p_class, return ret; } -bool ClassDB::has_integer_constant(const StringName &p_class, const StringName &p_name) const { +bool ClassDB::class_has_integer_constant(const StringName &p_class, const StringName &p_name) const { bool success; ::ClassDB::get_integer_constant(p_class, p_name, &success); return success; } -int64_t ClassDB::get_integer_constant(const StringName &p_class, const StringName &p_name) const { +int64_t ClassDB::class_get_integer_constant(const StringName &p_class, const StringName &p_name) const { bool found; int64_t c = ::ClassDB::get_integer_constant(p_class, p_name, &found); ERR_FAIL_COND_V(!found, 0); return c; } -bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const { +bool ClassDB::class_has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const { return ::ClassDB::has_enum(p_class, p_name, p_no_inheritance); } -PackedStringArray ClassDB::get_enum_list(const StringName &p_class, bool p_no_inheritance) const { +PackedStringArray ClassDB::class_get_enum_list(const StringName &p_class, bool p_no_inheritance) const { List<StringName> enums; ::ClassDB::get_enum_list(p_class, &enums, p_no_inheritance); @@ -1437,7 +1437,7 @@ PackedStringArray ClassDB::get_enum_list(const StringName &p_class, bool p_no_in return ret; } -PackedStringArray ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance) const { +PackedStringArray ClassDB::class_get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance) const { List<StringName> constants; ::ClassDB::get_enum_constants(p_class, p_enum, &constants, p_no_inheritance); @@ -1451,7 +1451,7 @@ PackedStringArray ClassDB::get_enum_constants(const StringName &p_class, const S return ret; } -StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const { +StringName ClassDB::class_get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) const { return ::ClassDB::get_integer_constant_enum(p_class, p_name, p_no_inheritance); } @@ -1468,27 +1468,27 @@ void ClassDB::_bind_methods() { ::ClassDB::bind_method(D_METHOD("can_instantiate", "class"), &ClassDB::can_instantiate); ::ClassDB::bind_method(D_METHOD("instantiate", "class"), &ClassDB::instantiate); - ::ClassDB::bind_method(D_METHOD("class_has_signal", "class", "signal"), &ClassDB::has_signal); - ::ClassDB::bind_method(D_METHOD("class_get_signal", "class", "signal"), &ClassDB::get_signal); - ::ClassDB::bind_method(D_METHOD("class_get_signal_list", "class", "no_inheritance"), &ClassDB::get_signal_list, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_has_signal", "class", "signal"), &ClassDB::class_has_signal); + ::ClassDB::bind_method(D_METHOD("class_get_signal", "class", "signal"), &ClassDB::class_get_signal); + ::ClassDB::bind_method(D_METHOD("class_get_signal_list", "class", "no_inheritance"), &ClassDB::class_get_signal_list, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_get_property_list", "class", "no_inheritance"), &ClassDB::get_property_list, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_get_property", "object", "property"), &ClassDB::get_property); - ::ClassDB::bind_method(D_METHOD("class_set_property", "object", "property", "value"), &ClassDB::set_property); + ::ClassDB::bind_method(D_METHOD("class_get_property_list", "class", "no_inheritance"), &ClassDB::class_get_property_list, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_property", "object", "property"), &ClassDB::class_get_property); + ::ClassDB::bind_method(D_METHOD("class_set_property", "object", "property", "value"), &ClassDB::class_set_property); - ::ClassDB::bind_method(D_METHOD("class_has_method", "class", "method", "no_inheritance"), &ClassDB::has_method, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_has_method", "class", "method", "no_inheritance"), &ClassDB::class_has_method, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &ClassDB::get_method_list, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &ClassDB::class_get_method_list, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &ClassDB::get_integer_constant_list, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &ClassDB::class_get_integer_constant_list, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_has_integer_constant", "class", "name"), &ClassDB::has_integer_constant); - ::ClassDB::bind_method(D_METHOD("class_get_integer_constant", "class", "name"), &ClassDB::get_integer_constant); + ::ClassDB::bind_method(D_METHOD("class_has_integer_constant", "class", "name"), &ClassDB::class_has_integer_constant); + ::ClassDB::bind_method(D_METHOD("class_get_integer_constant", "class", "name"), &ClassDB::class_get_integer_constant); - ::ClassDB::bind_method(D_METHOD("class_has_enum", "class", "name", "no_inheritance"), &ClassDB::has_enum, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_get_enum_list", "class", "no_inheritance"), &ClassDB::get_enum_list, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_get_enum_constants", "class", "enum", "no_inheritance"), &ClassDB::get_enum_constants, DEFVAL(false)); - ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_enum", "class", "name", "no_inheritance"), &ClassDB::get_integer_constant_enum, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_has_enum", "class", "name", "no_inheritance"), &ClassDB::class_has_enum, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_enum_list", "class", "no_inheritance"), &ClassDB::class_get_enum_list, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_enum_constants", "class", "enum", "no_inheritance"), &ClassDB::class_get_enum_constants, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_enum", "class", "name", "no_inheritance"), &ClassDB::class_get_integer_constant_enum, DEFVAL(false)); ::ClassDB::bind_method(D_METHOD("is_class_enabled", "class"), &ClassDB::is_class_enabled); } diff --git a/core/core_bind.h b/core/core_bind.h index be43ae2c9d..6083cf2e2f 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -424,26 +424,26 @@ public: bool can_instantiate(const StringName &p_class) const; Variant instantiate(const StringName &p_class) const; - bool has_signal(StringName p_class, StringName p_signal) const; - Dictionary get_signal(StringName p_class, StringName p_signal) const; - TypedArray<Dictionary> get_signal_list(StringName p_class, bool p_no_inheritance = false) const; + bool class_has_signal(StringName p_class, StringName p_signal) const; + Dictionary class_get_signal(StringName p_class, StringName p_signal) const; + TypedArray<Dictionary> class_get_signal_list(StringName p_class, bool p_no_inheritance = false) const; - TypedArray<Dictionary> get_property_list(StringName p_class, bool p_no_inheritance = false) const; - Variant get_property(Object *p_object, const StringName &p_property) const; - Error set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const; + TypedArray<Dictionary> class_get_property_list(StringName p_class, bool p_no_inheritance = false) const; + Variant class_get_property(Object *p_object, const StringName &p_property) const; + Error class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const; - bool has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false) const; + bool class_has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false) const; - TypedArray<Dictionary> get_method_list(StringName p_class, bool p_no_inheritance = false) const; + TypedArray<Dictionary> class_get_method_list(StringName p_class, bool p_no_inheritance = false) const; - PackedStringArray get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const; - bool has_integer_constant(const StringName &p_class, const StringName &p_name) const; - int64_t get_integer_constant(const StringName &p_class, const StringName &p_name) const; + PackedStringArray class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const; + bool class_has_integer_constant(const StringName &p_class, const StringName &p_name) const; + int64_t class_get_integer_constant(const StringName &p_class, const StringName &p_name) const; - bool has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; - PackedStringArray get_enum_list(const StringName &p_class, bool p_no_inheritance = false) const; - PackedStringArray get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance = false) const; - StringName get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; + bool class_has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; + PackedStringArray class_get_enum_list(const StringName &p_class, bool p_no_inheritance = false) const; + PackedStringArray class_get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance = false) const; + StringName class_get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; bool is_class_enabled(StringName p_class) const; diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 91038b9bdf..af7c37b4b6 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -425,10 +425,10 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra self->extension_classes.erase(class_name); } -void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExtensionStringPtr r_path) { +void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringPtr r_path) { GDExtension *self = reinterpret_cast<GDExtension *>(p_library); - *(String *)r_path = self->library_path; + memnew_placement(r_path, String(self->library_path)); } Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol) { diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index 96a57115bf..b5ec04b12d 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -80,10 +80,10 @@ uint64_t gdextension_get_native_struct_size(GDExtensionConstStringNamePtr p_name // Variant functions -static void gdextension_variant_new_copy(GDExtensionVariantPtr r_dest, GDExtensionConstVariantPtr p_src) { +static void gdextension_variant_new_copy(GDExtensionUninitializedVariantPtr r_dest, GDExtensionConstVariantPtr p_src) { memnew_placement(reinterpret_cast<Variant *>(r_dest), Variant(*reinterpret_cast<const Variant *>(p_src))); } -static void gdextension_variant_new_nil(GDExtensionVariantPtr r_dest) { +static void gdextension_variant_new_nil(GDExtensionUninitializedVariantPtr r_dest) { memnew_placement(reinterpret_cast<Variant *>(r_dest), Variant); } static void gdextension_variant_destroy(GDExtensionVariantPtr p_self) { @@ -92,14 +92,14 @@ static void gdextension_variant_destroy(GDExtensionVariantPtr p_self) { // variant type -static void gdextension_variant_call(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) { +static void gdextension_variant_call(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error) { Variant *self = (Variant *)p_self; const StringName method = *reinterpret_cast<const StringName *>(p_method); const Variant **args = (const Variant **)p_args; - Variant ret; Callable::CallError error; - self->callp(method, args, p_argcount, ret, error); - memnew_placement(r_return, Variant(ret)); + memnew_placement(r_return, Variant); + Variant *ret = reinterpret_cast<Variant *>(r_return); + self->callp(method, args, p_argcount, *ret, error); if (r_error) { r_error->error = (GDExtensionCallErrorType)(error.error); @@ -108,14 +108,14 @@ static void gdextension_variant_call(GDExtensionVariantPtr p_self, GDExtensionCo } } -static void gdextension_variant_call_static(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) { +static void gdextension_variant_call_static(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error) { Variant::Type type = (Variant::Type)p_type; const StringName method = *reinterpret_cast<const StringName *>(p_method); const Variant **args = (const Variant **)p_args; - Variant ret; Callable::CallError error; - Variant::call_static(type, method, args, p_argcount, ret, error); - memnew_placement(r_return, Variant(ret)); + memnew_placement(r_return, Variant); + Variant *ret = reinterpret_cast<Variant *>(r_return); + Variant::call_static(type, method, args, p_argcount, *ret, error); if (r_error) { r_error->error = (GDExtensionCallErrorType)error.error; @@ -124,12 +124,13 @@ static void gdextension_variant_call_static(GDExtensionVariantType p_type, GDExt } } -static void gdextension_variant_evaluate(GDExtensionVariantOperator p_op, GDExtensionConstVariantPtr p_a, GDExtensionConstVariantPtr p_b, GDExtensionVariantPtr r_return, GDExtensionBool *r_valid) { +static void gdextension_variant_evaluate(GDExtensionVariantOperator p_op, GDExtensionConstVariantPtr p_a, GDExtensionConstVariantPtr p_b, GDExtensionUninitializedVariantPtr r_return, GDExtensionBool *r_valid) { Variant::Operator op = (Variant::Operator)p_op; const Variant *a = (const Variant *)p_a; const Variant *b = (const Variant *)p_b; - Variant *ret = (Variant *)r_return; bool valid; + memnew_placement(r_return, Variant); + Variant *ret = reinterpret_cast<Variant *>(r_return); Variant::evaluate(op, *a, *b, *ret, valid); *r_valid = valid; } @@ -175,7 +176,7 @@ static void gdextension_variant_set_indexed(GDExtensionVariantPtr p_self, GDExte *r_oob = oob; } -static void gdextension_variant_get(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid) { +static void gdextension_variant_get(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid) { const Variant *self = (const Variant *)p_self; const Variant *key = (const Variant *)p_key; @@ -184,7 +185,7 @@ static void gdextension_variant_get(GDExtensionConstVariantPtr p_self, GDExtensi *r_valid = valid; } -static void gdextension_variant_get_named(GDExtensionConstVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid) { +static void gdextension_variant_get_named(GDExtensionConstVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid) { const Variant *self = (const Variant *)p_self; const StringName *key = (const StringName *)p_key; @@ -193,7 +194,7 @@ static void gdextension_variant_get_named(GDExtensionConstVariantPtr p_self, GDE *r_valid = valid; } -static void gdextension_variant_get_keyed(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid) { +static void gdextension_variant_get_keyed(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid) { const Variant *self = (const Variant *)p_self; const Variant *key = (const Variant *)p_key; @@ -202,7 +203,7 @@ static void gdextension_variant_get_keyed(GDExtensionConstVariantPtr p_self, GDE *r_valid = valid; } -static void gdextension_variant_get_indexed(GDExtensionConstVariantPtr p_self, GDExtensionInt p_index, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid, GDExtensionBool *r_oob) { +static void gdextension_variant_get_indexed(GDExtensionConstVariantPtr p_self, GDExtensionInt p_index, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid, GDExtensionBool *r_oob) { const Variant *self = (const Variant *)p_self; bool valid; @@ -213,9 +214,10 @@ static void gdextension_variant_get_indexed(GDExtensionConstVariantPtr p_self, G } /// Iteration. -static GDExtensionBool gdextension_variant_iter_init(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionBool *r_valid) { +static GDExtensionBool gdextension_variant_iter_init(GDExtensionConstVariantPtr p_self, GDExtensionUninitializedVariantPtr r_iter, GDExtensionBool *r_valid) { const Variant *self = (const Variant *)p_self; - Variant *iter = (Variant *)r_iter; + memnew_placement(r_iter, Variant); + Variant *iter = reinterpret_cast<Variant *>(r_iter); bool valid; bool ret = self->iter_init(*iter, valid); @@ -233,7 +235,7 @@ static GDExtensionBool gdextension_variant_iter_next(GDExtensionConstVariantPtr return ret; } -static void gdextension_variant_iter_get(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid) { +static void gdextension_variant_iter_get(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid) { const Variant *self = (const Variant *)p_self; Variant *iter = (Variant *)r_iter; @@ -264,12 +266,12 @@ static GDExtensionBool gdextension_variant_booleanize(GDExtensionConstVariantPtr return self->booleanize(); } -static void gdextension_variant_duplicate(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_ret, GDExtensionBool p_deep) { +static void gdextension_variant_duplicate(GDExtensionConstVariantPtr p_self, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool p_deep) { const Variant *self = (const Variant *)p_self; memnew_placement(r_ret, Variant(self->duplicate(p_deep))); } -static void gdextension_variant_stringify(GDExtensionConstVariantPtr p_self, GDExtensionStringPtr r_ret) { +static void gdextension_variant_stringify(GDExtensionConstVariantPtr p_self, GDExtensionUninitializedVariantPtr r_ret) { const Variant *self = (const Variant *)p_self; memnew_placement(r_ret, String(*self)); } @@ -298,7 +300,7 @@ static GDExtensionBool gdextension_variant_has_key(GDExtensionConstVariantPtr p_ return ret; } -static void gdextension_variant_get_type_name(GDExtensionVariantType p_type, GDExtensionStringPtr r_ret) { +static void gdextension_variant_get_type_name(GDExtensionVariantType p_type, GDExtensionUninitializedVariantPtr r_ret) { String name = Variant::get_type_name((Variant::Type)p_type); memnew_placement(r_ret, String(name)); } @@ -498,11 +500,12 @@ static GDExtensionPtrConstructor gdextension_variant_get_ptr_constructor(GDExten static GDExtensionPtrDestructor gdextension_variant_get_ptr_destructor(GDExtensionVariantType p_type) { return (GDExtensionPtrDestructor)Variant::get_ptr_destructor(Variant::Type(p_type)); } -static void gdextension_variant_construct(GDExtensionVariantType p_type, GDExtensionVariantPtr p_base, const GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error) { - memnew_placement(p_base, Variant); +static void gdextension_variant_construct(GDExtensionVariantType p_type, GDExtensionUninitializedVariantPtr r_base, const GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error) { + memnew_placement(r_base, Variant); + Variant *base = reinterpret_cast<Variant *>(r_base); Callable::CallError error; - Variant::construct(Variant::Type(p_type), *(Variant *)p_base, (const Variant **)p_args, p_argument_count, error); + Variant::construct(Variant::Type(p_type), *base, (const Variant **)p_args, p_argument_count, error); if (r_error) { r_error->error = (GDExtensionCallErrorType)(error.error); @@ -533,7 +536,7 @@ static GDExtensionPtrKeyedGetter gdextension_variant_get_ptr_keyed_getter(GDExte static GDExtensionPtrKeyedChecker gdextension_variant_get_ptr_keyed_checker(GDExtensionVariantType p_type) { return (GDExtensionPtrKeyedChecker)Variant::get_member_ptr_keyed_checker(Variant::Type(p_type)); } -static void gdextension_variant_get_constant_value(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_constant, GDExtensionVariantPtr r_ret) { +static void gdextension_variant_get_constant_value(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_constant, GDExtensionUninitializedVariantPtr r_ret) { StringName constant = *reinterpret_cast<const StringName *>(p_constant); memnew_placement(r_ret, Variant(Variant::get_constant_value(Variant::Type(p_type), constant))); } @@ -549,77 +552,67 @@ static GDExtensionPtrUtilityFunction gdextension_variant_get_ptr_utility_functio //string helpers -static void gdextension_string_new_with_latin1_chars(GDExtensionStringPtr r_dest, const char *p_contents) { - String *dest = (String *)r_dest; - memnew_placement(dest, String); - *dest = String(p_contents); +static void gdextension_string_new_with_latin1_chars(GDExtensionUninitializedStringPtr r_dest, const char *p_contents) { + memnew_placement(r_dest, String(p_contents)); } -static void gdextension_string_new_with_utf8_chars(GDExtensionStringPtr r_dest, const char *p_contents) { - String *dest = (String *)r_dest; - memnew_placement(dest, String); +static void gdextension_string_new_with_utf8_chars(GDExtensionUninitializedStringPtr r_dest, const char *p_contents) { + memnew_placement(r_dest, String); + String *dest = reinterpret_cast<String *>(r_dest); dest->parse_utf8(p_contents); } -static void gdextension_string_new_with_utf16_chars(GDExtensionStringPtr r_dest, const char16_t *p_contents) { - String *dest = (String *)r_dest; - memnew_placement(dest, String); +static void gdextension_string_new_with_utf16_chars(GDExtensionUninitializedStringPtr r_dest, const char16_t *p_contents) { + memnew_placement(r_dest, String); + String *dest = reinterpret_cast<String *>(r_dest); dest->parse_utf16(p_contents); } -static void gdextension_string_new_with_utf32_chars(GDExtensionStringPtr r_dest, const char32_t *p_contents) { - String *dest = (String *)r_dest; - memnew_placement(dest, String); - *dest = String((const char32_t *)p_contents); +static void gdextension_string_new_with_utf32_chars(GDExtensionUninitializedStringPtr r_dest, const char32_t *p_contents) { + memnew_placement(r_dest, String((const char32_t *)p_contents)); } -static void gdextension_string_new_with_wide_chars(GDExtensionStringPtr r_dest, const wchar_t *p_contents) { - String *dest = (String *)r_dest; +static void gdextension_string_new_with_wide_chars(GDExtensionUninitializedStringPtr r_dest, const wchar_t *p_contents) { if constexpr (sizeof(wchar_t) == 2) { // wchar_t is 16 bit, parse. - memnew_placement(dest, String); + memnew_placement(r_dest, String); + String *dest = reinterpret_cast<String *>(r_dest); dest->parse_utf16((const char16_t *)p_contents); } else { // wchar_t is 32 bit, copy. - memnew_placement(dest, String); - *dest = String((const char32_t *)p_contents); + memnew_placement(r_dest, String((const char32_t *)p_contents)); } } -static void gdextension_string_new_with_latin1_chars_and_len(GDExtensionStringPtr r_dest, const char *p_contents, GDExtensionInt p_size) { - String *dest = (String *)r_dest; - memnew_placement(dest, String); - *dest = String(p_contents, p_size); +static void gdextension_string_new_with_latin1_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char *p_contents, GDExtensionInt p_size) { + memnew_placement(r_dest, String(p_contents, p_size)); } -static void gdextension_string_new_with_utf8_chars_and_len(GDExtensionStringPtr r_dest, const char *p_contents, GDExtensionInt p_size) { - String *dest = (String *)r_dest; - memnew_placement(dest, String); +static void gdextension_string_new_with_utf8_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char *p_contents, GDExtensionInt p_size) { + memnew_placement(r_dest, String); + String *dest = reinterpret_cast<String *>(r_dest); dest->parse_utf8(p_contents, p_size); } -static void gdextension_string_new_with_utf16_chars_and_len(GDExtensionStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_size) { - String *dest = (String *)r_dest; - memnew_placement(dest, String); +static void gdextension_string_new_with_utf16_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_size) { + memnew_placement(r_dest, String); + String *dest = reinterpret_cast<String *>(r_dest); dest->parse_utf16(p_contents, p_size); } -static void gdextension_string_new_with_utf32_chars_and_len(GDExtensionStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_size) { - String *dest = (String *)r_dest; - memnew_placement(dest, String); - *dest = String((const char32_t *)p_contents, p_size); +static void gdextension_string_new_with_utf32_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_size) { + memnew_placement(r_dest, String((const char32_t *)p_contents, p_size)); } -static void gdextension_string_new_with_wide_chars_and_len(GDExtensionStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_size) { - String *dest = (String *)r_dest; +static void gdextension_string_new_with_wide_chars_and_len(GDExtensionUninitializedStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_size) { if constexpr (sizeof(wchar_t) == 2) { // wchar_t is 16 bit, parse. - memnew_placement(dest, String); + memnew_placement(r_dest, String); + String *dest = reinterpret_cast<String *>(r_dest); dest->parse_utf16((const char16_t *)p_contents, p_size); } else { // wchar_t is 32 bit, copy. - memnew_placement(dest, String); - *dest = String((const char32_t *)p_contents, p_size); + memnew_placement(r_dest, String((const char32_t *)p_contents, p_size)); } } @@ -936,14 +929,13 @@ static GDExtensionVariantPtr gdextension_dictionary_operator_index_const(GDExten /* OBJECT API */ -static void gdextension_object_method_bind_call(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) { +static void gdextension_object_method_bind_call(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error) { const MethodBind *mb = reinterpret_cast<const MethodBind *>(p_method_bind); Object *o = (Object *)p_instance; const Variant **args = (const Variant **)p_args; Callable::CallError error; - Variant ret = mb->call(o, args, p_arg_count, error); - memnew_placement(r_return, Variant(ret)); + memnew_placement(r_return, Variant(mb->call(o, args, p_arg_count, error))); if (r_error) { r_error->error = (GDExtensionCallErrorType)(error.error); diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index f1412d667f..839221c24e 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -139,16 +139,37 @@ typedef enum { } GDExtensionVariantOperator; +// In this API there are multiple functions which expect the caller to pass a pointer +// on return value as parameter. +// In order to make it clear if the caller should initialize the return value or not +// we have two flavor of types: +// - `GDExtensionXXXPtr` for pointer on an initialized value +// - `GDExtensionUninitializedXXXPtr` for pointer on uninitialized value +// +// Notes: +// - Not respecting those requirements can seems harmless, but will lead to unexpected +// segfault or memory leak (for instance with a specific compiler/OS, or when two +// native extensions start doing ptrcall on each other). +// - Initialization must be done with the function pointer returned by `variant_get_ptr_constructor`, +// zero-initializing the variable should not be considered a valid initialization method here ! +// - Some types have no destructor (see `extension_api.json`'s `has_destructor` field), for +// them it is always safe to skip the constructor for the return value if you are in a hurry ;-) + typedef void *GDExtensionVariantPtr; typedef const void *GDExtensionConstVariantPtr; +typedef void *GDExtensionUninitializedVariantPtr; typedef void *GDExtensionStringNamePtr; typedef const void *GDExtensionConstStringNamePtr; +typedef void *GDExtensionUninitializedStringNamePtr; typedef void *GDExtensionStringPtr; typedef const void *GDExtensionConstStringPtr; +typedef void *GDExtensionUninitializedStringPtr; typedef void *GDExtensionObjectPtr; typedef const void *GDExtensionConstObjectPtr; +typedef void *GDExtensionUninitializedObjectPtr; typedef void *GDExtensionTypePtr; typedef const void *GDExtensionConstTypePtr; +typedef void *GDExtensionUninitializedTypePtr; typedef const void *GDExtensionMethodBindPtr; typedef int64_t GDExtensionInt; typedef uint8_t GDExtensionBool; @@ -427,37 +448,37 @@ typedef struct { /* GODOT VARIANT */ /* variant general */ - void (*variant_new_copy)(GDExtensionVariantPtr r_dest, GDExtensionConstVariantPtr p_src); - void (*variant_new_nil)(GDExtensionVariantPtr r_dest); + void (*variant_new_copy)(GDExtensionUninitializedVariantPtr r_dest, GDExtensionConstVariantPtr p_src); + void (*variant_new_nil)(GDExtensionUninitializedVariantPtr r_dest); void (*variant_destroy)(GDExtensionVariantPtr p_self); /* variant type */ - void (*variant_call)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); - void (*variant_call_static)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); - void (*variant_evaluate)(GDExtensionVariantOperator p_op, GDExtensionConstVariantPtr p_a, GDExtensionConstVariantPtr p_b, GDExtensionVariantPtr r_return, GDExtensionBool *r_valid); + void (*variant_call)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error); + void (*variant_call_static)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error); + void (*variant_evaluate)(GDExtensionVariantOperator p_op, GDExtensionConstVariantPtr p_a, GDExtensionConstVariantPtr p_b, GDExtensionUninitializedVariantPtr r_return, GDExtensionBool *r_valid); void (*variant_set)(GDExtensionVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid); void (*variant_set_named)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid); void (*variant_set_keyed)(GDExtensionVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid); void (*variant_set_indexed)(GDExtensionVariantPtr p_self, GDExtensionInt p_index, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid, GDExtensionBool *r_oob); - void (*variant_get)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid); - void (*variant_get_named)(GDExtensionConstVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid); - void (*variant_get_keyed)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid); - void (*variant_get_indexed)(GDExtensionConstVariantPtr p_self, GDExtensionInt p_index, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid, GDExtensionBool *r_oob); - GDExtensionBool (*variant_iter_init)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionBool *r_valid); + void (*variant_get)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid); + void (*variant_get_named)(GDExtensionConstVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid); + void (*variant_get_keyed)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid); + void (*variant_get_indexed)(GDExtensionConstVariantPtr p_self, GDExtensionInt p_index, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid, GDExtensionBool *r_oob); + GDExtensionBool (*variant_iter_init)(GDExtensionConstVariantPtr p_self, GDExtensionUninitializedVariantPtr r_iter, GDExtensionBool *r_valid); GDExtensionBool (*variant_iter_next)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionBool *r_valid); - void (*variant_iter_get)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid); + void (*variant_iter_get)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool *r_valid); GDExtensionInt (*variant_hash)(GDExtensionConstVariantPtr p_self); GDExtensionInt (*variant_recursive_hash)(GDExtensionConstVariantPtr p_self, GDExtensionInt p_recursion_count); GDExtensionBool (*variant_hash_compare)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_other); GDExtensionBool (*variant_booleanize)(GDExtensionConstVariantPtr p_self); - void (*variant_duplicate)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_ret, GDExtensionBool p_deep); - void (*variant_stringify)(GDExtensionConstVariantPtr p_self, GDExtensionStringPtr r_ret); + void (*variant_duplicate)(GDExtensionConstVariantPtr p_self, GDExtensionUninitializedVariantPtr r_ret, GDExtensionBool p_deep); + void (*variant_stringify)(GDExtensionConstVariantPtr p_self, GDExtensionUninitializedStringPtr r_ret); GDExtensionVariantType (*variant_get_type)(GDExtensionConstVariantPtr p_self); GDExtensionBool (*variant_has_method)(GDExtensionConstVariantPtr p_self, GDExtensionConstStringNamePtr p_method); GDExtensionBool (*variant_has_member)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member); GDExtensionBool (*variant_has_key)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionBool *r_valid); - void (*variant_get_type_name)(GDExtensionVariantType p_type, GDExtensionStringPtr r_name); + void (*variant_get_type_name)(GDExtensionVariantType p_type, GDExtensionUninitializedStringPtr r_name); GDExtensionBool (*variant_can_convert)(GDExtensionVariantType p_from, GDExtensionVariantType p_to); GDExtensionBool (*variant_can_convert_strict)(GDExtensionVariantType p_from, GDExtensionVariantType p_to); @@ -468,7 +489,7 @@ typedef struct { GDExtensionPtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, GDExtensionInt p_hash); GDExtensionPtrConstructor (*variant_get_ptr_constructor)(GDExtensionVariantType p_type, int32_t p_constructor); GDExtensionPtrDestructor (*variant_get_ptr_destructor)(GDExtensionVariantType p_type); - void (*variant_construct)(GDExtensionVariantType p_type, GDExtensionVariantPtr p_base, const GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error); + void (*variant_construct)(GDExtensionVariantType p_type, GDExtensionUninitializedVariantPtr r_base, const GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error); GDExtensionPtrSetter (*variant_get_ptr_setter)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member); GDExtensionPtrGetter (*variant_get_ptr_getter)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member); GDExtensionPtrIndexedSetter (*variant_get_ptr_indexed_setter)(GDExtensionVariantType p_type); @@ -476,20 +497,20 @@ typedef struct { GDExtensionPtrKeyedSetter (*variant_get_ptr_keyed_setter)(GDExtensionVariantType p_type); GDExtensionPtrKeyedGetter (*variant_get_ptr_keyed_getter)(GDExtensionVariantType p_type); GDExtensionPtrKeyedChecker (*variant_get_ptr_keyed_checker)(GDExtensionVariantType p_type); - void (*variant_get_constant_value)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_constant, GDExtensionVariantPtr r_ret); + void (*variant_get_constant_value)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_constant, GDExtensionUninitializedVariantPtr r_ret); GDExtensionPtrUtilityFunction (*variant_get_ptr_utility_function)(GDExtensionConstStringNamePtr p_function, GDExtensionInt p_hash); /* extra utilities */ - void (*string_new_with_latin1_chars)(GDExtensionStringPtr r_dest, const char *p_contents); - void (*string_new_with_utf8_chars)(GDExtensionStringPtr r_dest, const char *p_contents); - void (*string_new_with_utf16_chars)(GDExtensionStringPtr r_dest, const char16_t *p_contents); - void (*string_new_with_utf32_chars)(GDExtensionStringPtr r_dest, const char32_t *p_contents); - void (*string_new_with_wide_chars)(GDExtensionStringPtr r_dest, const wchar_t *p_contents); - void (*string_new_with_latin1_chars_and_len)(GDExtensionStringPtr r_dest, const char *p_contents, GDExtensionInt p_size); - void (*string_new_with_utf8_chars_and_len)(GDExtensionStringPtr r_dest, const char *p_contents, GDExtensionInt p_size); - void (*string_new_with_utf16_chars_and_len)(GDExtensionStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_size); - void (*string_new_with_utf32_chars_and_len)(GDExtensionStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_size); - void (*string_new_with_wide_chars_and_len)(GDExtensionStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_size); + void (*string_new_with_latin1_chars)(GDExtensionUninitializedStringPtr r_dest, const char *p_contents); + void (*string_new_with_utf8_chars)(GDExtensionUninitializedStringPtr r_dest, const char *p_contents); + void (*string_new_with_utf16_chars)(GDExtensionUninitializedStringPtr r_dest, const char16_t *p_contents); + void (*string_new_with_utf32_chars)(GDExtensionUninitializedStringPtr r_dest, const char32_t *p_contents); + void (*string_new_with_wide_chars)(GDExtensionUninitializedStringPtr r_dest, const wchar_t *p_contents); + void (*string_new_with_latin1_chars_and_len)(GDExtensionUninitializedStringPtr r_dest, const char *p_contents, GDExtensionInt p_size); + void (*string_new_with_utf8_chars_and_len)(GDExtensionUninitializedStringPtr r_dest, const char *p_contents, GDExtensionInt p_size); + void (*string_new_with_utf16_chars_and_len)(GDExtensionUninitializedStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_size); + void (*string_new_with_utf32_chars_and_len)(GDExtensionUninitializedStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_size); + void (*string_new_with_wide_chars_and_len)(GDExtensionUninitializedStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_size); /* Information about the following functions: * - The return value is the resulting encoded string length. * - The length returned is in characters, not in bytes. It also does not include a trailing zero. @@ -564,7 +585,7 @@ typedef struct { /* OBJECT */ - void (*object_method_bind_call)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionVariantPtr r_ret, GDExtensionCallError *r_error); + void (*object_method_bind_call)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionUninitializedVariantPtr r_ret, GDExtensionCallError *r_error); void (*object_method_bind_ptrcall)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret); void (*object_destroy)(GDExtensionObjectPtr p_o); GDExtensionObjectPtr (*global_get_singleton)(GDExtensionConstStringNamePtr p_name); @@ -605,7 +626,7 @@ typedef struct { void (*classdb_register_extension_class_signal)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_signal_name, const GDExtensionPropertyInfo *p_argument_info, GDExtensionInt p_argument_count); void (*classdb_unregister_extension_class)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */ - void (*get_library_path)(GDExtensionClassLibraryPtr p_library, GDExtensionStringPtr r_path); + void (*get_library_path)(GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringPtr r_path); } GDExtensionInterface; diff --git a/core/object/object.h b/core/object/object.h index ae22851c15..c4d287a71c 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -836,14 +836,21 @@ public: /* SCRIPT */ - void set_script(const Variant &p_script); - Variant get_script() const; +// When in debug, some non-virtual functions can be overridden for multithreaded guards. +#ifdef DEBUG_ENABLED +#define MTVIRTUAL virtual +#else +#define MTVIRTUAL +#endif + + MTVIRTUAL void set_script(const Variant &p_script); + MTVIRTUAL Variant get_script() const; - bool has_meta(const StringName &p_name) const; - void set_meta(const StringName &p_name, const Variant &p_value); - void remove_meta(const StringName &p_name); - Variant get_meta(const StringName &p_name, const Variant &p_default = Variant()) const; - void get_meta_list(List<StringName> *p_list) const; + MTVIRTUAL bool has_meta(const StringName &p_name) const; + MTVIRTUAL void set_meta(const StringName &p_name, const Variant &p_value); + MTVIRTUAL void remove_meta(const StringName &p_name); + MTVIRTUAL Variant get_meta(const StringName &p_name, const Variant &p_default = Variant()) const; + MTVIRTUAL void get_meta_list(List<StringName> *p_list) const; #ifdef TOOLS_ENABLED void set_edited(bool p_edited); @@ -870,17 +877,17 @@ public: return emit_signalp(p_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); } - Error emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount); - bool has_signal(const StringName &p_name) const; - void get_signal_list(List<MethodInfo> *p_signals) const; - void get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const; - void get_all_signal_connections(List<Connection> *p_connections) const; - int get_persistent_signal_connection_count() const; - void get_signals_connected_to_this(List<Connection> *p_connections) const; + MTVIRTUAL Error emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount); + MTVIRTUAL bool has_signal(const StringName &p_name) const; + MTVIRTUAL void get_signal_list(List<MethodInfo> *p_signals) const; + MTVIRTUAL void get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const; + MTVIRTUAL void get_all_signal_connections(List<Connection> *p_connections) const; + MTVIRTUAL int get_persistent_signal_connection_count() const; + MTVIRTUAL void get_signals_connected_to_this(List<Connection> *p_connections) const; - Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0); - void disconnect(const StringName &p_signal, const Callable &p_callable); - bool is_connected(const StringName &p_signal, const Callable &p_callable) const; + MTVIRTUAL Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0); + MTVIRTUAL void disconnect(const StringName &p_signal, const Callable &p_callable); + MTVIRTUAL bool is_connected(const StringName &p_signal, const Callable &p_callable) const; template <typename... VarArgs> void call_deferred(const StringName &p_name, VarArgs... p_args) { diff --git a/core/templates/safe_refcount.h b/core/templates/safe_refcount.h index 58ed019287..8669bcaeeb 100644 --- a/core/templates/safe_refcount.h +++ b/core/templates/safe_refcount.h @@ -102,6 +102,17 @@ public: return value.fetch_sub(p_value, std::memory_order_acq_rel) - p_value; } + _ALWAYS_INLINE_ T bit_or(T p_value) { + return value.fetch_or(p_value, std::memory_order_acq_rel); + } + _ALWAYS_INLINE_ T bit_and(T p_value) { + return value.fetch_and(p_value, std::memory_order_acq_rel); + } + + _ALWAYS_INLINE_ T bit_xor(T p_value) { + return value.fetch_xor(p_value, std::memory_order_acq_rel); + } + // Returns the original value instead of the new one _ALWAYS_INLINE_ T postsub(T p_value) { return value.fetch_sub(p_value, std::memory_order_acq_rel); diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index b7bd2a9c8c..8013c1a32a 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -1534,14 +1534,14 @@ struct VariantTypeAdjust<Object *> { template <class T> struct VariantTypeConstructor { - _FORCE_INLINE_ static void variant_from_type(void *p_variant, void *p_value) { - Variant *variant = reinterpret_cast<Variant *>(p_variant); - VariantInitializer<T>::init(variant); - VariantInternalAccessor<T>::set(variant, *((T *)p_value)); + _FORCE_INLINE_ static void variant_from_type(void *r_variant, void *p_value) { + // r_variant is provided by caller as uninitialized memory + memnew_placement(r_variant, Variant(*((T *)p_value))); } - _FORCE_INLINE_ static void type_from_variant(void *p_value, void *p_variant) { - *((T *)p_value) = VariantInternalAccessor<T>::get(reinterpret_cast<Variant *>(p_variant)); + _FORCE_INLINE_ static void type_from_variant(void *r_value, void *p_variant) { + // r_value is provided by caller as uninitialized memory + memnew_placement(r_value, T(VariantInternalAccessor<T>::get(reinterpret_cast<Variant *>(p_variant)))); } }; diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index 9bc75d34d1..94f310ebfa 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -196,7 +196,7 @@ <param index="6" name="max_lines" type="int" default="-1" /> <param index="7" name="modulate" type="Color" default="Color(1, 1, 1, 1)" /> <param index="8" name="brk_flags" type="int" enum="TextServer.LineBreakFlag" default="3" /> - <param index="9" name="jst_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> + <param index="9" name="justification_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> <param index="10" name="direction" type="int" enum="TextServer.Direction" default="0" /> <param index="11" name="orientation" type="int" enum="TextServer.Orientation" default="0" /> <description> @@ -215,7 +215,7 @@ <param index="7" name="size" type="int" default="1" /> <param index="8" name="modulate" type="Color" default="Color(1, 1, 1, 1)" /> <param index="9" name="brk_flags" type="int" enum="TextServer.LineBreakFlag" default="3" /> - <param index="10" name="jst_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> + <param index="10" name="justification_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> <param index="11" name="direction" type="int" enum="TextServer.Direction" default="0" /> <param index="12" name="orientation" type="int" enum="TextServer.Orientation" default="0" /> <description> @@ -311,7 +311,7 @@ <param index="4" name="width" type="float" default="-1" /> <param index="5" name="font_size" type="int" default="16" /> <param index="6" name="modulate" type="Color" default="Color(1, 1, 1, 1)" /> - <param index="7" name="jst_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> + <param index="7" name="justification_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> <param index="8" name="direction" type="int" enum="TextServer.Direction" default="0" /> <param index="9" name="orientation" type="int" enum="TextServer.Orientation" default="0" /> <description> @@ -348,7 +348,7 @@ <param index="5" name="font_size" type="int" default="16" /> <param index="6" name="size" type="int" default="1" /> <param index="7" name="modulate" type="Color" default="Color(1, 1, 1, 1)" /> - <param index="8" name="jst_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> + <param index="8" name="justification_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> <param index="9" name="direction" type="int" enum="TextServer.Direction" default="0" /> <param index="10" name="orientation" type="int" enum="TextServer.Orientation" default="0" /> <description> diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 513414e695..beccaab048 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -855,6 +855,15 @@ [b]Note:[/b] This method is implemented on Android, Linux (X11), macOS and Windows. Returns [code]72[/code] on unsupported platforms. </description> </method> + <method name="screen_get_image" qualifiers="const"> + <return type="Image" /> + <param index="0" name="screen" type="int" default="-1" /> + <description> + Returns screenshot of the [param screen]. + [b]Note:[/b] This method is implemented on Linux (X11), macOS, and Windows. + [b]Note:[/b] On macOS, this method requires "Screen Recording" permission, if permission is not granted it will return desktop wallpaper color. + </description> + </method> <method name="screen_get_max_scale" qualifiers="const"> <return type="float" /> <description> diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml index 548376f262..03cd7f835a 100644 --- a/doc/classes/Font.xml +++ b/doc/classes/Font.xml @@ -45,7 +45,7 @@ <param index="6" name="max_lines" type="int" default="-1" /> <param index="7" name="modulate" type="Color" default="Color(1, 1, 1, 1)" /> <param index="8" name="brk_flags" type="int" enum="TextServer.LineBreakFlag" default="3" /> - <param index="9" name="jst_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> + <param index="9" name="justification_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> <param index="10" name="direction" type="int" enum="TextServer.Direction" default="0" /> <param index="11" name="orientation" type="int" enum="TextServer.Orientation" default="0" /> <description> @@ -65,7 +65,7 @@ <param index="7" name="size" type="int" default="1" /> <param index="8" name="modulate" type="Color" default="Color(1, 1, 1, 1)" /> <param index="9" name="brk_flags" type="int" enum="TextServer.LineBreakFlag" default="3" /> - <param index="10" name="jst_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> + <param index="10" name="justification_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> <param index="11" name="direction" type="int" enum="TextServer.Direction" default="0" /> <param index="12" name="orientation" type="int" enum="TextServer.Orientation" default="0" /> <description> @@ -82,7 +82,7 @@ <param index="4" name="width" type="float" default="-1" /> <param index="5" name="font_size" type="int" default="16" /> <param index="6" name="modulate" type="Color" default="Color(1, 1, 1, 1)" /> - <param index="7" name="jst_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> + <param index="7" name="justification_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> <param index="8" name="direction" type="int" enum="TextServer.Direction" default="0" /> <param index="9" name="orientation" type="int" enum="TextServer.Orientation" default="0" /> <description> @@ -100,7 +100,7 @@ <param index="5" name="font_size" type="int" default="16" /> <param index="6" name="size" type="int" default="1" /> <param index="7" name="modulate" type="Color" default="Color(1, 1, 1, 1)" /> - <param index="8" name="jst_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> + <param index="8" name="justification_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> <param index="9" name="direction" type="int" enum="TextServer.Direction" default="0" /> <param index="10" name="orientation" type="int" enum="TextServer.Orientation" default="0" /> <description> @@ -201,7 +201,7 @@ <param index="3" name="font_size" type="int" default="16" /> <param index="4" name="max_lines" type="int" default="-1" /> <param index="5" name="brk_flags" type="int" enum="TextServer.LineBreakFlag" default="3" /> - <param index="6" name="jst_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> + <param index="6" name="justification_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> <param index="7" name="direction" type="int" enum="TextServer.Direction" default="0" /> <param index="8" name="orientation" type="int" enum="TextServer.Orientation" default="0" /> <description> @@ -234,7 +234,7 @@ <param index="1" name="alignment" type="int" enum="HorizontalAlignment" default="0" /> <param index="2" name="width" type="float" default="-1" /> <param index="3" name="font_size" type="int" default="16" /> - <param index="4" name="jst_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> + <param index="4" name="justification_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> <param index="5" name="direction" type="int" enum="TextServer.Direction" default="0" /> <param index="6" name="orientation" type="int" enum="TextServer.Orientation" default="0" /> <description> diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml index b002d456f1..6a26da9f4f 100644 --- a/doc/classes/Label.xml +++ b/doc/classes/Label.xml @@ -49,6 +49,9 @@ <member name="horizontal_alignment" type="int" setter="set_horizontal_alignment" getter="get_horizontal_alignment" enum="HorizontalAlignment" default="0"> Controls the text's horizontal alignment. Supports left, center, right, and fill, or justify. Set it to one of the [enum HorizontalAlignment] constants. </member> + <member name="justification_flags" type="int" setter="set_justification_flags" getter="get_justification_flags" enum="TextServer.JustificationFlag" default="163"> + Line fill alignment rules. For more info see [enum TextServer.JustificationFlag]. + </member> <member name="label_settings" type="LabelSettings" setter="set_label_settings" getter="get_label_settings"> A [LabelSettings] resource that can be shared between multiple [Label] nodes. Takes priority over theme properties. </member> diff --git a/doc/classes/Label3D.xml b/doc/classes/Label3D.xml index c605f8bec1..7499074dc9 100644 --- a/doc/classes/Label3D.xml +++ b/doc/classes/Label3D.xml @@ -71,6 +71,9 @@ <member name="horizontal_alignment" type="int" setter="set_horizontal_alignment" getter="get_horizontal_alignment" enum="HorizontalAlignment" default="1"> Controls the text's horizontal alignment. Supports left, center, right, and fill, or justify. Set it to one of the [enum HorizontalAlignment] constants. </member> + <member name="justification_flags" type="int" setter="set_justification_flags" getter="get_justification_flags" enum="TextServer.JustificationFlag" default="163"> + Line fill alignment rules. For more info see [enum TextServer.JustificationFlag]. + </member> <member name="language" type="String" setter="set_language" getter="get_language" default=""""> Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead. </member> diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index a93fdfd296..c44f54749b 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -409,6 +409,7 @@ <param index="1" name="base_direction" type="int" enum="Control.TextDirection" default="0" /> <param index="2" name="language" type="String" default="""" /> <param index="3" name="st_parser" type="int" enum="TextServer.StructuredTextParser" default="0" /> + <param index="4" name="justification_flags" type="int" enum="TextServer.JustificationFlag" default="163" /> <description> Adds a [code][p][/code] tag to the tag stack. </description> diff --git a/doc/classes/TextMesh.xml b/doc/classes/TextMesh.xml index 48b2a9c943..9258dca657 100644 --- a/doc/classes/TextMesh.xml +++ b/doc/classes/TextMesh.xml @@ -29,6 +29,9 @@ <member name="horizontal_alignment" type="int" setter="set_horizontal_alignment" getter="get_horizontal_alignment" enum="HorizontalAlignment" default="1"> Controls the text's horizontal alignment. Supports left, center, right, and fill, or justify. Set it to one of the [enum HorizontalAlignment] constants. </member> + <member name="justification_flags" type="int" setter="set_justification_flags" getter="get_justification_flags" enum="TextServer.JustificationFlag" default="163"> + Line fill alignment rules. For more info see [enum TextServer.JustificationFlag]. + </member> <member name="language" type="String" setter="set_language" getter="get_language" default=""""> Language code used for text shaping algorithms, if left empty current locale is used instead. </member> diff --git a/doc/classes/TextParagraph.xml b/doc/classes/TextParagraph.xml index caba9d0a46..a6eabd415c 100644 --- a/doc/classes/TextParagraph.xml +++ b/doc/classes/TextParagraph.xml @@ -274,8 +274,8 @@ <member name="direction" type="int" setter="set_direction" getter="get_direction" enum="TextServer.Direction" default="0"> Text writing direction. </member> - <member name="justification_flags" type="int" setter="set_justification_flags" getter="get_justification_flags" enum="TextServer.JustificationFlag" default="3"> - Line alignment rules. For more info see [TextServer]. + <member name="justification_flags" type="int" setter="set_justification_flags" getter="get_justification_flags" enum="TextServer.JustificationFlag" default="163"> + Line fill alignment rules. For more info see [enum TextServer.JustificationFlag]. </member> <member name="max_lines_visible" type="int" setter="set_max_lines_visible" getter="get_max_lines_visible" default="-1"> Limits the lines of text shown. diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 1763df938a..207349a76a 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -1161,7 +1161,7 @@ <return type="float" /> <param index="0" name="shaped" type="RID" /> <param index="1" name="width" type="float" /> - <param index="2" name="jst_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> + <param index="2" name="justification_flags" type="int" enum="TextServer.JustificationFlag" default="3" /> <description> Adjusts text width to fit to specified width, returns new text width. </description> @@ -1395,6 +1395,13 @@ Breaks text into words and returns array of character ranges. Use [param grapheme_flags] to set what characters are used for breaking (see [enum GraphemeFlag]). </description> </method> + <method name="shaped_text_has_visible_chars" qualifiers="const"> + <return type="bool" /> + <param index="0" name="shaped" type="RID" /> + <description> + Returns [code]true[/code], if text buffer contents any visible characters. + </description> + </method> <method name="shaped_text_hit_test_grapheme" qualifiers="const"> <return type="int" /> <param index="0" name="shaped" type="RID" /> @@ -1671,6 +1678,15 @@ <constant name="JUSTIFICATION_CONSTRAIN_ELLIPSIS" value="16" enum="JustificationFlag" is_bitfield="true"> Apply justification to the trimmed line with ellipsis. </constant> + <constant name="JUSTIFICATION_SKIP_LAST_LINE" value="32" enum="JustificationFlag" is_bitfield="true"> + Do not apply justification to the last line of the paragraph. + </constant> + <constant name="JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS" value="64" enum="JustificationFlag" is_bitfield="true"> + Do not apply justification to the last line of the paragraph with visible characters (takes precedence over [constant JUSTIFICATION_SKIP_LAST_LINE]). + </constant> + <constant name="JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE" value="128" enum="JustificationFlag" is_bitfield="true"> + Always apply justification to the paragraphs with a single line ([constant JUSTIFICATION_SKIP_LAST_LINE] and [constant JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS] are ignored). + </constant> <constant name="AUTOWRAP_OFF" value="0" enum="AutowrapMode"> Autowrap is disabled. </constant> diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml index a3891442b7..0f04c3a10d 100644 --- a/doc/classes/TextServerExtension.xml +++ b/doc/classes/TextServerExtension.xml @@ -1002,7 +1002,7 @@ <return type="float" /> <param index="0" name="shaped" type="RID" /> <param index="1" name="width" type="float" /> - <param index="2" name="jst_flags" type="int" enum="TextServer.JustificationFlag" /> + <param index="2" name="justification_flags" type="int" enum="TextServer.JustificationFlag" /> <description> </description> </method> diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index 45a100a4e5..f5da9da8e7 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -158,8 +158,6 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref< } r_texture = generated; - int small_thumbnail_size = EditorNode::get_singleton()->get_theme_base()->get_theme_icon(SNAME("Object"), SNAME("EditorIcons"))->get_width(); // Kind of a workaround to retrieve the default icon size - if (preview_generators[i]->can_generate_small_preview()) { Ref<Texture2D> generated_small; Dictionary d; @@ -340,9 +338,16 @@ void EditorResourcePreview::_thread() { exited.set(); } +void EditorResourcePreview::_update_thumbnail_sizes() { + if (small_thumbnail_size == -1) { + small_thumbnail_size = EditorNode::get_singleton()->get_theme_base()->get_theme_icon(SNAME("Object"), SNAME("EditorIcons"))->get_width(); // Kind of a workaround to retrieve the default icon size + } +} + void EditorResourcePreview::queue_edited_resource_preview(const Ref<Resource> &p_res, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata) { ERR_FAIL_NULL(p_receiver); ERR_FAIL_COND(!p_res.is_valid()); + _update_thumbnail_sizes(); { MutexLock lock(preview_mutex); @@ -370,6 +375,8 @@ void EditorResourcePreview::queue_edited_resource_preview(const Ref<Resource> &p } void EditorResourcePreview::queue_resource_preview(const String &p_path, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata) { + _update_thumbnail_sizes(); + ERR_FAIL_NULL(p_receiver); { MutexLock lock(preview_mutex); diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h index 96ce672d0e..84835094bb 100644 --- a/editor/editor_resource_preview.h +++ b/editor/editor_resource_preview.h @@ -97,6 +97,8 @@ class EditorResourcePreview : public Node { void _preview_ready(const String &p_path, int p_hash, const Ref<Texture2D> &p_texture, const Ref<Texture2D> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud, const Dictionary &p_metadata); void _generate_preview(Ref<ImageTexture> &r_texture, Ref<ImageTexture> &r_small_texture, const QueueItem &p_item, const String &cache_base, Dictionary &p_metadata); + int small_thumbnail_size = -1; + static void _thread_func(void *ud); void _thread(); void _iterate(); @@ -106,6 +108,8 @@ class EditorResourcePreview : public Node { Vector<Ref<EditorResourcePreviewGenerator>> preview_generators; + void _update_thumbnail_sizes(); + protected: static void _bind_methods(); diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 13f261aa43..9506189573 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -1206,6 +1206,105 @@ Color DisplayServerX11::screen_get_pixel(const Point2i &p_position) const { return Color(); } +Ref<Image> DisplayServerX11::screen_get_image(int p_screen) const { + ERR_FAIL_INDEX_V(p_screen, get_screen_count(), Ref<Image>()); + + switch (p_screen) { + case SCREEN_PRIMARY: { + p_screen = get_primary_screen(); + } break; + case SCREEN_OF_MAIN_WINDOW: { + p_screen = window_get_current_screen(MAIN_WINDOW_ID); + } break; + default: + break; + } + + ERR_FAIL_COND_V(p_screen < 0, Ref<Image>()); + + XImage *image = nullptr; + + int event_base, error_base; + if (XineramaQueryExtension(x11_display, &event_base, &error_base)) { + int xin_count; + XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &xin_count); + if (p_screen < xin_count) { + int x_count = XScreenCount(x11_display); + for (int i = 0; i < x_count; i++) { + Window root = XRootWindow(x11_display, i); + XWindowAttributes root_attrs; + XGetWindowAttributes(x11_display, root, &root_attrs); + if ((xsi[p_screen].x_org >= root_attrs.x) && (xsi[p_screen].x_org <= root_attrs.x + root_attrs.width) && (xsi[p_screen].y_org >= root_attrs.y) && (xsi[p_screen].y_org <= root_attrs.y + root_attrs.height)) { + image = XGetImage(x11_display, root, xsi[p_screen].x_org, xsi[p_screen].y_org, xsi[p_screen].width, xsi[p_screen].height, AllPlanes, ZPixmap); + break; + } + } + } else { + ERR_FAIL_V_MSG(Ref<Image>(), "Invalid screen index: " + itos(p_screen) + "(count: " + itos(xin_count) + ")."); + } + } else { + int x_count = XScreenCount(x11_display); + if (p_screen < x_count) { + Window root = XRootWindow(x11_display, p_screen); + + XWindowAttributes root_attrs; + XGetWindowAttributes(x11_display, root, &root_attrs); + + image = XGetImage(x11_display, root, root_attrs.x, root_attrs.y, root_attrs.width, root_attrs.height, AllPlanes, ZPixmap); + } else { + ERR_FAIL_V_MSG(Ref<Image>(), "Invalid screen index: " + itos(p_screen) + "(count: " + itos(x_count) + ")."); + } + } + + Ref<Image> img; + if (image) { + int width = image->width; + int height = image->height; + + Vector<uint8_t> img_data; + img_data.resize(height * width * 4); + + uint8_t *sr = (uint8_t *)image->data; + uint8_t *wr = (uint8_t *)img_data.ptrw(); + + if (image->bits_per_pixel == 24 && image->red_mask == 0xff0000 && image->green_mask == 0x00ff00 && image->blue_mask == 0x0000ff) { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + wr[(y * width + x) * 4 + 0] = sr[(y * width + x) * 3 + 2]; + wr[(y * width + x) * 4 + 1] = sr[(y * width + x) * 3 + 1]; + wr[(y * width + x) * 4 + 2] = sr[(y * width + x) * 3 + 0]; + wr[(y * width + x) * 4 + 3] = 255; + } + } + } else if (image->bits_per_pixel == 24 && image->red_mask == 0x0000ff && image->green_mask == 0x00ff00 && image->blue_mask == 0xff0000) { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + wr[(y * width + x) * 4 + 0] = sr[(y * width + x) * 3 + 2]; + wr[(y * width + x) * 4 + 1] = sr[(y * width + x) * 3 + 1]; + wr[(y * width + x) * 4 + 2] = sr[(y * width + x) * 3 + 0]; + wr[(y * width + x) * 4 + 3] = 255; + } + } + } else if (image->bits_per_pixel == 32 && image->red_mask == 0xff0000 && image->green_mask == 0x00ff00 && image->blue_mask == 0x0000ff) { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + wr[(y * width + x) * 4 + 0] = sr[(y * width + x) * 4 + 2]; + wr[(y * width + x) * 4 + 1] = sr[(y * width + x) * 4 + 1]; + wr[(y * width + x) * 4 + 2] = sr[(y * width + x) * 4 + 0]; + wr[(y * width + x) * 4 + 3] = 255; + } + } + } else { + XFree(image); + ERR_FAIL_V_MSG(Ref<Image>(), vformat("XImage with RGB mask %x %x %x and depth %d is not supported.", image->red_mask, image->green_mask, image->blue_mask, image->bits_per_pixel)); + } + img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data); + XFree(image); + } + + return img; +} + float DisplayServerX11::screen_get_refresh_rate(int p_screen) const { _THREAD_SAFE_METHOD_ diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index 6d343be3ab..fd3a5dccfa 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -412,6 +412,7 @@ public: virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual Color screen_get_pixel(const Point2i &p_position) const override; + virtual Ref<Image> screen_get_image(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; #if defined(DBUS_ENABLED) virtual void screen_set_keep_on(bool p_enable) override; diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index a1cd83280d..495dc43c55 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -336,6 +336,7 @@ public: virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual Color screen_get_pixel(const Point2i &p_position) const override; + virtual Ref<Image> screen_get_image(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual void screen_set_keep_on(bool p_enable) override; virtual bool screen_is_kept_on() const override; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index 32f9441484..4657461dca 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -2234,6 +2234,49 @@ Color DisplayServerMacOS::screen_get_pixel(const Point2i &p_position) const { return Color(); } +Ref<Image> DisplayServerMacOS::screen_get_image(int p_screen) const { + ERR_FAIL_INDEX_V(p_screen, get_screen_count(), Ref<Image>()); + + switch (p_screen) { + case SCREEN_PRIMARY: { + p_screen = get_primary_screen(); + } break; + case SCREEN_OF_MAIN_WINDOW: { + p_screen = window_get_current_screen(MAIN_WINDOW_ID); + } break; + default: + break; + } + + Ref<Image> img; + NSArray *screenArray = [NSScreen screens]; + if ((NSUInteger)p_screen < [screenArray count]) { + NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; + NSDictionary *screenDescription = [[screenArray objectAtIndex:p_screen] deviceDescription]; + CGDirectDisplayID display_id = [[screenDescription objectForKey:@"NSScreenNumber"] unsignedIntValue]; + CGImageRef image = CGDisplayCreateImageForRect(display_id, CGRectMake(0, 0, nsrect.size.width, nsrect.size.height)); + if (image) { + CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB(); + if (color_space) { + NSUInteger width = CGImageGetWidth(image); + NSUInteger height = CGImageGetHeight(image); + + Vector<uint8_t> img_data; + img_data.resize(height * width * 4); + CGContextRef context = CGBitmapContextCreate(img_data.ptrw(), width, height, 8, 4 * width, color_space, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + if (context) { + CGContextDrawImage(context, CGRectMake(0, 0, width, height), image); + img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data); + CGContextRelease(context); + } + CGColorSpaceRelease(color_space); + } + CGImageRelease(image); + } + } + return img; +} + float DisplayServerMacOS::screen_get_refresh_rate(int p_screen) const { _THREAD_SAFE_METHOD_ diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 02bb30ed38..ff32a318a2 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -614,15 +614,87 @@ Color DisplayServerWindows::screen_get_pixel(const Point2i &p_position) const { win81p_LogicalToPhysicalPointForPerMonitorDPI(0, &p); } HDC dc = GetDC(0); - COLORREF col = GetPixel(dc, p.x, p.y); - if (col != CLR_INVALID) { - return Color(float(col & 0x000000FF) / 256.0, float((col & 0x0000FF00) >> 8) / 256.0, float((col & 0x00FF0000) >> 16) / 256.0, 1.0); + if (dc) { + COLORREF col = GetPixel(dc, p.x, p.y); + if (col != CLR_INVALID) { + ReleaseDC(NULL, dc); + return Color(float(col & 0x000000FF) / 256.0, float((col & 0x0000FF00) >> 8) / 256.0, float((col & 0x00FF0000) >> 16) / 256.0, 1.0); + } + ReleaseDC(NULL, dc); } - ReleaseDC(NULL, dc); return Color(); } +Ref<Image> DisplayServerWindows::screen_get_image(int p_screen) const { + ERR_FAIL_INDEX_V(p_screen, get_screen_count(), Ref<Image>()); + + switch (p_screen) { + case SCREEN_PRIMARY: { + p_screen = get_primary_screen(); + } break; + case SCREEN_OF_MAIN_WINDOW: { + p_screen = window_get_current_screen(MAIN_WINDOW_ID); + } break; + default: + break; + } + + Point2i pos = screen_get_position(p_screen) + _get_screens_origin(); + Size2i size = screen_get_size(p_screen); + + POINT p1; + p1.x = pos.x; + p1.y = pos.y; + + POINT p2; + p2.x = pos.x + size.x; + p2.y = pos.y + size.y; + if (win81p_LogicalToPhysicalPointForPerMonitorDPI) { + win81p_LogicalToPhysicalPointForPerMonitorDPI(0, &p1); + win81p_LogicalToPhysicalPointForPerMonitorDPI(0, &p2); + } + + Ref<Image> img; + HDC dc = GetDC(0); + if (dc) { + HDC hdc = CreateCompatibleDC(dc); + int width = p2.x - p1.x; + int height = p2.y - p1.y; + if (hdc) { + HBITMAP hbm = CreateCompatibleBitmap(dc, width, height); + if (hbm) { + SelectObject(hdc, hbm); + BitBlt(hdc, 0, 0, width, height, dc, p1.x, p1.y, SRCCOPY); + + BITMAPINFO bmp_info = { 0 }; + bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader); + bmp_info.bmiHeader.biWidth = width; + bmp_info.bmiHeader.biHeight = -height; + bmp_info.bmiHeader.biPlanes = 1; + bmp_info.bmiHeader.biBitCount = 32; + bmp_info.bmiHeader.biCompression = BI_RGB; + + Vector<uint8_t> img_data; + img_data.resize(width * height * 4); + GetDIBits(hdc, hbm, 0, height, img_data.ptrw(), &bmp_info, DIB_RGB_COLORS); + + uint8_t *wr = (uint8_t *)img_data.ptrw(); + for (int i = 0; i < width * height; i++) { + SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); + } + img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data); + + DeleteObject(hbm); + } + DeleteDC(hdc); + } + ReleaseDC(NULL, dc); + } + + return img; +} + float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const { _THREAD_SAFE_METHOD_ diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index a8dcc6bad0..4a1619f331 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -529,6 +529,7 @@ public: virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual Color screen_get_pixel(const Point2i &p_position) const override; + virtual Ref<Image> screen_get_image(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual void screen_set_keep_on(bool p_enable) override; //disable screensaver virtual bool screen_is_kept_on() const override; diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 43a2f62163..24478fd847 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -113,11 +113,11 @@ void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) { #endif void Node2D::_update_xform_values() { - position = transform.columns[2]; rotation = transform.get_rotation(); - scale = transform.get_scale(); skew = transform.get_skew(); - _xform_dirty = false; + position = transform.columns[2]; + scale = transform.get_scale(); + xform_dirty.clear(); } void Node2D::_update_transform() { @@ -134,6 +134,7 @@ void Node2D::_update_transform() { } void Node2D::reparent(Node *p_parent, bool p_keep_global_transform) { + ERR_THREAD_GUARD; Transform2D temp = get_global_transform(); Node::reparent(p_parent); if (p_keep_global_transform) { @@ -142,7 +143,8 @@ void Node2D::reparent(Node *p_parent, bool p_keep_global_transform) { } void Node2D::set_position(const Point2 &p_pos) { - if (_xform_dirty) { + ERR_THREAD_GUARD; + if (xform_dirty.is_set()) { const_cast<Node2D *>(this)->_update_xform_values(); } position = p_pos; @@ -150,7 +152,8 @@ void Node2D::set_position(const Point2 &p_pos) { } void Node2D::set_rotation(real_t p_radians) { - if (_xform_dirty) { + ERR_THREAD_GUARD; + if (xform_dirty.is_set()) { const_cast<Node2D *>(this)->_update_xform_values(); } rotation = p_radians; @@ -158,11 +161,13 @@ void Node2D::set_rotation(real_t p_radians) { } void Node2D::set_rotation_degrees(real_t p_degrees) { + ERR_THREAD_GUARD; set_rotation(Math::deg_to_rad(p_degrees)); } void Node2D::set_skew(real_t p_radians) { - if (_xform_dirty) { + ERR_THREAD_GUARD; + if (xform_dirty.is_set()) { const_cast<Node2D *>(this)->_update_xform_values(); } skew = p_radians; @@ -170,7 +175,8 @@ void Node2D::set_skew(real_t p_radians) { } void Node2D::set_scale(const Size2 &p_scale) { - if (_xform_dirty) { + ERR_THREAD_GUARD; + if (xform_dirty.is_set()) { const_cast<Node2D *>(this)->_update_xform_values(); } scale = p_scale; @@ -185,14 +191,17 @@ void Node2D::set_scale(const Size2 &p_scale) { } Point2 Node2D::get_position() const { - if (_xform_dirty) { + ERR_READ_THREAD_GUARD_V(Point2()); + if (xform_dirty.is_set()) { const_cast<Node2D *>(this)->_update_xform_values(); } + return position; } real_t Node2D::get_rotation() const { - if (_xform_dirty) { + ERR_READ_THREAD_GUARD_V(0); + if (xform_dirty.is_set()) { const_cast<Node2D *>(this)->_update_xform_values(); } @@ -200,11 +209,13 @@ real_t Node2D::get_rotation() const { } real_t Node2D::get_rotation_degrees() const { + ERR_READ_THREAD_GUARD_V(0); return Math::rad_to_deg(get_rotation()); } real_t Node2D::get_skew() const { - if (_xform_dirty) { + ERR_READ_THREAD_GUARD_V(0); + if (xform_dirty.is_set()) { const_cast<Node2D *>(this)->_update_xform_values(); } @@ -212,7 +223,8 @@ real_t Node2D::get_skew() const { } Size2 Node2D::get_scale() const { - if (_xform_dirty) { + ERR_READ_THREAD_GUARD_V(Size2()); + if (xform_dirty.is_set()) { const_cast<Node2D *>(this)->_update_xform_values(); } @@ -220,26 +232,32 @@ Size2 Node2D::get_scale() const { } Transform2D Node2D::get_transform() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); return transform; } void Node2D::rotate(real_t p_radians) { + ERR_THREAD_GUARD; set_rotation(get_rotation() + p_radians); } void Node2D::translate(const Vector2 &p_amount) { + ERR_THREAD_GUARD; set_position(get_position() + p_amount); } void Node2D::global_translate(const Vector2 &p_amount) { + ERR_THREAD_GUARD; set_global_position(get_global_position() + p_amount); } void Node2D::apply_scale(const Size2 &p_amount) { + ERR_THREAD_GUARD; set_scale(get_scale() * p_amount); } void Node2D::move_x(real_t p_delta, bool p_scaled) { + ERR_THREAD_GUARD; Transform2D t = get_transform(); Vector2 m = t[0]; if (!p_scaled) { @@ -249,6 +267,7 @@ void Node2D::move_x(real_t p_delta, bool p_scaled) { } void Node2D::move_y(real_t p_delta, bool p_scaled) { + ERR_THREAD_GUARD; Transform2D t = get_transform(); Vector2 m = t[1]; if (!p_scaled) { @@ -258,10 +277,12 @@ void Node2D::move_y(real_t p_delta, bool p_scaled) { } Point2 Node2D::get_global_position() const { + ERR_READ_THREAD_GUARD_V(Point2()); return get_global_transform().get_origin(); } void Node2D::set_global_position(const Point2 &p_pos) { + ERR_THREAD_GUARD; CanvasItem *parent = get_parent_item(); if (parent) { Transform2D inv = parent->get_global_transform().affine_inverse(); @@ -272,18 +293,22 @@ void Node2D::set_global_position(const Point2 &p_pos) { } real_t Node2D::get_global_rotation() const { + ERR_READ_THREAD_GUARD_V(0); return get_global_transform().get_rotation(); } real_t Node2D::get_global_rotation_degrees() const { + ERR_READ_THREAD_GUARD_V(0); return Math::rad_to_deg(get_global_rotation()); } real_t Node2D::get_global_skew() const { + ERR_READ_THREAD_GUARD_V(0); return get_global_transform().get_skew(); } void Node2D::set_global_rotation(const real_t p_radians) { + ERR_THREAD_GUARD; CanvasItem *parent = get_parent_item(); if (parent) { Transform2D parent_global_transform = parent->get_global_transform(); @@ -297,10 +322,12 @@ void Node2D::set_global_rotation(const real_t p_radians) { } void Node2D::set_global_rotation_degrees(const real_t p_degrees) { + ERR_THREAD_GUARD; set_global_rotation(Math::deg_to_rad(p_degrees)); } void Node2D::set_global_skew(const real_t p_radians) { + ERR_THREAD_GUARD; CanvasItem *parent = get_parent_item(); if (parent) { Transform2D parent_global_transform = parent->get_global_transform(); @@ -314,10 +341,12 @@ void Node2D::set_global_skew(const real_t p_radians) { } Size2 Node2D::get_global_scale() const { + ERR_READ_THREAD_GUARD_V(Size2()); return get_global_transform().get_scale(); } void Node2D::set_global_scale(const Size2 &p_scale) { + ERR_THREAD_GUARD; CanvasItem *parent = get_parent_item(); if (parent) { Transform2D parent_global_transform = parent->get_global_transform(); @@ -331,8 +360,9 @@ void Node2D::set_global_scale(const Size2 &p_scale) { } void Node2D::set_transform(const Transform2D &p_transform) { + ERR_THREAD_GUARD; transform = p_transform; - _xform_dirty = true; + xform_dirty.set(); RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), transform); @@ -344,6 +374,7 @@ void Node2D::set_transform(const Transform2D &p_transform) { } void Node2D::set_global_transform(const Transform2D &p_transform) { + ERR_THREAD_GUARD; CanvasItem *parent = get_parent_item(); if (parent) { set_transform(parent->get_global_transform().affine_inverse() * p_transform); @@ -353,6 +384,7 @@ void Node2D::set_global_transform(const Transform2D &p_transform) { } Transform2D Node2D::get_relative_transform_to_parent(const Node *p_parent) const { + ERR_READ_THREAD_GUARD_V(Transform2D()); if (p_parent == this) { return Transform2D(); } @@ -368,22 +400,27 @@ Transform2D Node2D::get_relative_transform_to_parent(const Node *p_parent) const } void Node2D::look_at(const Vector2 &p_pos) { + ERR_THREAD_GUARD; rotate(get_angle_to(p_pos)); } real_t Node2D::get_angle_to(const Vector2 &p_pos) const { + ERR_READ_THREAD_GUARD_V(0); return (to_local(p_pos) * get_scale()).angle(); } Point2 Node2D::to_local(Point2 p_global) const { + ERR_READ_THREAD_GUARD_V(Point2()); return get_global_transform().affine_inverse().xform(p_global); } Point2 Node2D::to_global(Point2 p_local) const { + ERR_READ_THREAD_GUARD_V(Point2()); return get_global_transform().xform(p_local); } void Node2D::_notification(int p_notification) { + ERR_THREAD_GUARD; switch (p_notification) { case NOTIFICATION_ENTER_TREE: { if (get_viewport()) { diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h index 119e23cd98..6bdf5d6eb5 100644 --- a/scene/2d/node_2d.h +++ b/scene/2d/node_2d.h @@ -36,6 +36,7 @@ class Node2D : public CanvasItem { GDCLASS(Node2D, CanvasItem); + SafeFlag xform_dirty; Point2 position; real_t rotation = 0.0; Size2 scale = Vector2(1, 1); @@ -43,8 +44,6 @@ class Node2D : public CanvasItem { Transform2D transform; - bool _xform_dirty = false; - void _update_transform(); void _update_xform_values(); diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index 5fc36abb76..198fb70ad7 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -88,6 +88,9 @@ void Label3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &Label3D::set_autowrap_mode); ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &Label3D::get_autowrap_mode); + ClassDB::bind_method(D_METHOD("set_justification_flags", "justification_flags"), &Label3D::set_justification_flags); + ClassDB::bind_method(D_METHOD("get_justification_flags"), &Label3D::get_justification_flags); + ClassDB::bind_method(D_METHOD("set_width", "width"), &Label3D::set_width); ClassDB::bind_method(D_METHOD("get_width"), &Label3D::get_width); @@ -157,6 +160,7 @@ void Label3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing"); ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "justification_flags", PROPERTY_HINT_FLAGS, "Kashida Justification:1,Word Justification:2,Justify Only After Last Tab:8,Skip Last Line:32,Skip Last Line With Visible Characters:64,Do Not Skip Single Line:128"), "set_justification_flags", "get_justification_flags"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_NONE, "suffix:px"), "set_width", "get_width"); ADD_GROUP("BiDi", ""); @@ -533,8 +537,24 @@ void Label3D::_shape() { } if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { - for (int i = 0; i < lines_rid.size() - 1; i++) { - TS->shaped_text_fit_to_width(lines_rid[i], (width > 0) ? width : max_line_w, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA); + int jst_to_line = lines_rid.size(); + if (lines_rid.size() == 1 && jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) { + jst_to_line = lines_rid.size(); + } else { + if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) { + jst_to_line = lines_rid.size() - 1; + } + if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) { + for (int i = lines_rid.size() - 1; i >= 0; i--) { + if (TS->shaped_text_has_visible_chars(lines_rid[i])) { + jst_to_line = i; + break; + } + } + } + } + for (int i = 0; i < jst_to_line; i++) { + TS->shaped_text_fit_to_width(lines_rid[i], (width > 0) ? width : max_line_w, jst_flags); } } dirty_lines = false; @@ -871,6 +891,18 @@ TextServer::AutowrapMode Label3D::get_autowrap_mode() const { return autowrap_mode; } +void Label3D::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) { + if (jst_flags != p_flags) { + jst_flags = p_flags; + dirty_lines = true; + _queue_update(); + } +} + +BitField<TextServer::JustificationFlag> Label3D::get_justification_flags() const { + return jst_flags; +} + void Label3D::set_width(float p_width) { if (width != p_width) { width = p_width; diff --git a/scene/3d/label_3d.h b/scene/3d/label_3d.h index 912f485354..332eaac18d 100644 --- a/scene/3d/label_3d.h +++ b/scene/3d/label_3d.h @@ -112,6 +112,7 @@ private: bool uppercase = false; TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF; + BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE; float width = 500.0; int font_size = 32; @@ -215,6 +216,9 @@ public: void set_autowrap_mode(TextServer::AutowrapMode p_mode); TextServer::AutowrapMode get_autowrap_mode() const; + void set_justification_flags(BitField<TextServer::JustificationFlag> p_flags); + BitField<TextServer::JustificationFlag> get_justification_flags() const; + void set_width(float p_width); float get_width() const; diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 66e8831d15..446d9f6ee8 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -86,10 +86,8 @@ void Node3D::_notify_dirty() { void Node3D::_update_local_transform() const { // This function is called when the local transform (data.local_transform) is dirty and the right value is contained in the Euler rotation and scale. - data.local_transform.basis.set_euler_scale(data.euler_rotation, data.scale, data.euler_rotation_order); - - data.dirty &= ~DIRTY_LOCAL_TRANSFORM; + data.dirty.bit_and(~DIRTY_LOCAL_TRANSFORM); } void Node3D::_update_rotation_and_scale() const { @@ -97,8 +95,13 @@ void Node3D::_update_rotation_and_scale() const { data.scale = data.local_transform.basis.get_scale(); data.euler_rotation = data.local_transform.basis.get_euler_normalized(data.euler_rotation_order); + data.dirty.bit_and(~DIRTY_EULER_ROTATION_AND_SCALE); +} - data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE; +void Node3D::_propagate_transform_changed_deferred() { + if (is_inside_tree() && !xform_change.in_list()) { + get_tree()->xform_change_list.add(&xform_change); + } } void Node3D::_propagate_transform_changed(Node3D *p_origin) { @@ -106,8 +109,6 @@ void Node3D::_propagate_transform_changed(Node3D *p_origin) { return; } - data.children_lock++; - for (Node3D *&E : data.children) { if (E->data.top_level_active) { continue; //don't propagate to a top_level @@ -119,14 +120,19 @@ void Node3D::_propagate_transform_changed(Node3D *p_origin) { #else if (data.notify_transform && !data.ignore_notification && !xform_change.in_list()) { #endif - get_tree()->xform_change_list.add(&xform_change); + if (likely(is_accessible_from_caller_thread())) { + get_tree()->xform_change_list.add(&xform_change); + } else { + // This should very rarely happen, but if it does at least make sure the notification is received eventually. + MessageQueue::get_singleton()->push_callable(callable_mp(this, &Node3D::_propagate_transform_changed_deferred)); + } } - data.dirty |= DIRTY_GLOBAL_TRANSFORM; - - data.children_lock--; + data.dirty.bit_or(DIRTY_GLOBAL_TRANSFORM); } void Node3D::_notification(int p_what) { + ERR_THREAD_GUARD; + switch (p_what) { case NOTIFICATION_ENTER_TREE: { ERR_FAIL_COND(!get_tree()); @@ -145,12 +151,12 @@ void Node3D::_notification(int p_what) { if (data.top_level && !Engine::get_singleton()->is_editor_hint()) { if (data.parent) { data.local_transform = data.parent->get_global_transform() * get_transform(); - data.dirty = DIRTY_EULER_ROTATION_AND_SCALE; // As local transform was updated, rot/scale should be dirty. + data.dirty.set(DIRTY_EULER_ROTATION_AND_SCALE); // As local transform was updated, rot/scale should be dirty. } data.top_level_active = true; } - data.dirty |= DIRTY_GLOBAL_TRANSFORM; // Global is always dirty upon entering a scene. + data.dirty.bit_or(DIRTY_GLOBAL_TRANSFORM); // Global is always dirty upon entering a scene. _notify_dirty(); notification(NOTIFICATION_ENTER_WORLD); @@ -217,19 +223,23 @@ void Node3D::_notification(int p_what) { } void Node3D::set_basis(const Basis &p_basis) { + ERR_THREAD_GUARD; + set_transform(Transform3D(p_basis, data.local_transform.origin)); } void Node3D::set_quaternion(const Quaternion &p_quaternion) { - if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + ERR_THREAD_GUARD; + + if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) { // We need the scale part, so if these are dirty, update it data.scale = data.local_transform.basis.get_scale(); - data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE; + data.dirty.bit_and(~DIRTY_EULER_ROTATION_AND_SCALE); } data.local_transform.basis = Basis(p_quaternion, data.scale); // Rotscale should not be marked dirty because that would cause precision loss issues with the scale. Instead reconstruct rotation now. data.euler_rotation = data.local_transform.basis.get_euler_normalized(data.euler_rotation_order); - data.dirty = DIRTY_NONE; + data.dirty.set(DIRTY_NONE); _propagate_transform_changed(this); if (data.notify_local_transform) { @@ -238,38 +248,45 @@ void Node3D::set_quaternion(const Quaternion &p_quaternion) { } Vector3 Node3D::get_global_position() const { + ERR_READ_THREAD_GUARD_V(Vector3()); return get_global_transform().get_origin(); } void Node3D::set_global_position(const Vector3 &p_position) { + ERR_THREAD_GUARD; Transform3D transform = get_global_transform(); transform.set_origin(p_position); set_global_transform(transform); } Vector3 Node3D::get_global_rotation() const { + ERR_READ_THREAD_GUARD_V(Vector3()); return get_global_transform().get_basis().get_euler(); } Vector3 Node3D::get_global_rotation_degrees() const { + ERR_READ_THREAD_GUARD_V(Vector3()); Vector3 radians = get_global_rotation(); return Vector3(Math::rad_to_deg(radians.x), Math::rad_to_deg(radians.y), Math::rad_to_deg(radians.z)); } void Node3D::set_global_rotation(const Vector3 &p_euler_rad) { + ERR_THREAD_GUARD; Transform3D transform = get_global_transform(); transform.basis = Basis::from_euler(p_euler_rad); set_global_transform(transform); } void Node3D::set_global_rotation_degrees(const Vector3 &p_euler_degrees) { + ERR_THREAD_GUARD; Vector3 radians(Math::deg_to_rad(p_euler_degrees.x), Math::deg_to_rad(p_euler_degrees.y), Math::deg_to_rad(p_euler_degrees.z)); set_global_rotation(radians); } void Node3D::set_transform(const Transform3D &p_transform) { + ERR_THREAD_GUARD; data.local_transform = p_transform; - data.dirty = DIRTY_EULER_ROTATION_AND_SCALE; // Make rot/scale dirty. + data.dirty.set(DIRTY_EULER_ROTATION_AND_SCALE); // Make rot/scale dirty. _propagate_transform_changed(this); if (data.notify_local_transform) { @@ -278,18 +295,16 @@ void Node3D::set_transform(const Transform3D &p_transform) { } Basis Node3D::get_basis() const { + ERR_READ_THREAD_GUARD_V(Basis()); return get_transform().basis; } Quaternion Node3D::get_quaternion() const { - if (data.dirty & DIRTY_LOCAL_TRANSFORM) { - _update_local_transform(); - } - - return data.local_transform.basis.get_rotation_quaternion(); + return get_transform().basis.get_rotation_quaternion(); } void Node3D::set_global_transform(const Transform3D &p_transform) { + ERR_THREAD_GUARD; Transform3D xform = (data.parent && !data.top_level_active) ? data.parent->get_global_transform().affine_inverse() * p_transform : p_transform; @@ -298,31 +313,42 @@ void Node3D::set_global_transform(const Transform3D &p_transform) { } Transform3D Node3D::get_transform() const { - if (data.dirty & DIRTY_LOCAL_TRANSFORM) { + ERR_READ_THREAD_GUARD_V(Transform3D()); + if (data.dirty.get() & DIRTY_LOCAL_TRANSFORM) { + // This update can happen if needed over multiple threads. _update_local_transform(); } return data.local_transform; } + Transform3D Node3D::get_global_transform() const { ERR_FAIL_COND_V(!is_inside_tree(), Transform3D()); - if (data.dirty & DIRTY_GLOBAL_TRANSFORM) { - if (data.dirty & DIRTY_LOCAL_TRANSFORM) { - _update_local_transform(); + /* Due to how threads work at scene level, while this global transform won't be able to be changed from outside a thread, + * it is possible that multiple threads can access it while it's dirty from previous work. Due to this, we must ensure that + * the dirty/update process is thread safe by utilizing atomic copies. + */ + + uint32_t dirty = data.dirty.get(); + if (dirty & DIRTY_GLOBAL_TRANSFORM) { + if (dirty & DIRTY_LOCAL_TRANSFORM) { + _update_local_transform(); // Update local transform atomically. } + Transform3D new_global; if (data.parent && !data.top_level_active) { - data.global_transform = data.parent->get_global_transform() * data.local_transform; + new_global = data.parent->get_global_transform() * data.local_transform; } else { - data.global_transform = data.local_transform; + new_global = data.local_transform; } if (data.disable_scale) { - data.global_transform.basis.orthonormalize(); + new_global.basis.orthonormalize(); } - data.dirty &= ~DIRTY_GLOBAL_TRANSFORM; + data.global_transform = new_global; + data.dirty.bit_and(~DIRTY_GLOBAL_TRANSFORM); } return data.global_transform; @@ -339,6 +365,7 @@ Transform3D Node3D::get_local_gizmo_transform() const { #endif Node3D *Node3D::get_parent_node_3d() const { + ERR_READ_THREAD_GUARD_V(nullptr); // This can't be changed on threads anyway. if (data.top_level) { return nullptr; } @@ -347,6 +374,7 @@ Node3D *Node3D::get_parent_node_3d() const { } Transform3D Node3D::get_relative_transform(const Node *p_parent) const { + ERR_READ_THREAD_GUARD_V(Transform3D()); if (p_parent == this) { return Transform3D(); } @@ -361,6 +389,7 @@ Transform3D Node3D::get_relative_transform(const Node *p_parent) const { } void Node3D::set_position(const Vector3 &p_position) { + ERR_THREAD_GUARD; data.local_transform.origin = p_position; _propagate_transform_changed(this); if (data.notify_local_transform) { @@ -369,19 +398,20 @@ void Node3D::set_position(const Vector3 &p_position) { } void Node3D::set_rotation_edit_mode(RotationEditMode p_mode) { + ERR_THREAD_GUARD; if (data.rotation_edit_mode == p_mode) { return; } bool transform_changed = false; - if (data.rotation_edit_mode == ROTATION_EDIT_MODE_BASIS && !(data.dirty & DIRTY_LOCAL_TRANSFORM)) { + if (data.rotation_edit_mode == ROTATION_EDIT_MODE_BASIS && !(data.dirty.get() & DIRTY_LOCAL_TRANSFORM)) { data.local_transform.orthogonalize(); transform_changed = true; } data.rotation_edit_mode = p_mode; - if (p_mode == ROTATION_EDIT_MODE_EULER && (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE)) { + if (p_mode == ROTATION_EDIT_MODE_EULER && (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE)) { // If going to Euler mode, ensure that vectors are _not_ dirty, else the retrieved value may be wrong. // Otherwise keep what is there, so switching back and forth between modes does not break the vectors. @@ -399,10 +429,12 @@ void Node3D::set_rotation_edit_mode(RotationEditMode p_mode) { } Node3D::RotationEditMode Node3D::get_rotation_edit_mode() const { + ERR_READ_THREAD_GUARD_V(ROTATION_EDIT_MODE_EULER); return data.rotation_edit_mode; } void Node3D::set_rotation_order(EulerOrder p_order) { + ERR_THREAD_GUARD; if (data.euler_rotation_order == p_order) { return; } @@ -410,13 +442,13 @@ void Node3D::set_rotation_order(EulerOrder p_order) { ERR_FAIL_INDEX(int32_t(p_order), 6); bool transform_changed = false; - if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) { _update_rotation_and_scale(); - } else if (data.dirty & DIRTY_LOCAL_TRANSFORM) { + } else if (data.dirty.get() & DIRTY_LOCAL_TRANSFORM) { data.euler_rotation = Basis::from_euler(data.euler_rotation, data.euler_rotation_order).get_euler_normalized(p_order); transform_changed = true; } else { - data.dirty |= DIRTY_LOCAL_TRANSFORM; + data.dirty.bit_or(DIRTY_LOCAL_TRANSFORM); transform_changed = true; } @@ -432,18 +464,20 @@ void Node3D::set_rotation_order(EulerOrder p_order) { } EulerOrder Node3D::get_rotation_order() const { + ERR_READ_THREAD_GUARD_V(EulerOrder::XYZ); return data.euler_rotation_order; } void Node3D::set_rotation(const Vector3 &p_euler_rad) { - if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + ERR_THREAD_GUARD; + if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) { // Update scale only if rotation and scale are dirty, as rotation will be overridden. data.scale = data.local_transform.basis.get_scale(); - data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE; + data.dirty.bit_and(~DIRTY_EULER_ROTATION_AND_SCALE); } data.euler_rotation = p_euler_rad; - data.dirty = DIRTY_LOCAL_TRANSFORM; + data.dirty.set(DIRTY_LOCAL_TRANSFORM); _propagate_transform_changed(this); if (data.notify_local_transform) { notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); @@ -451,19 +485,21 @@ void Node3D::set_rotation(const Vector3 &p_euler_rad) { } void Node3D::set_rotation_degrees(const Vector3 &p_euler_degrees) { + ERR_THREAD_GUARD; Vector3 radians(Math::deg_to_rad(p_euler_degrees.x), Math::deg_to_rad(p_euler_degrees.y), Math::deg_to_rad(p_euler_degrees.z)); set_rotation(radians); } void Node3D::set_scale(const Vector3 &p_scale) { - if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + ERR_THREAD_GUARD; + if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) { // Update rotation only if rotation and scale are dirty, as scale will be overridden. data.euler_rotation = data.local_transform.basis.get_euler_normalized(data.euler_rotation_order); - data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE; + data.dirty.bit_and(~DIRTY_EULER_ROTATION_AND_SCALE); } data.scale = p_scale; - data.dirty = DIRTY_LOCAL_TRANSFORM; + data.dirty.set(DIRTY_LOCAL_TRANSFORM); _propagate_transform_changed(this); if (data.notify_local_transform) { notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); @@ -471,11 +507,13 @@ void Node3D::set_scale(const Vector3 &p_scale) { } Vector3 Node3D::get_position() const { + ERR_READ_THREAD_GUARD_V(Vector3()); return data.local_transform.origin; } Vector3 Node3D::get_rotation() const { - if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + ERR_READ_THREAD_GUARD_V(Vector3()); + if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) { _update_rotation_and_scale(); } @@ -483,12 +521,14 @@ Vector3 Node3D::get_rotation() const { } Vector3 Node3D::get_rotation_degrees() const { + ERR_READ_THREAD_GUARD_V(Vector3()); Vector3 radians = get_rotation(); return Vector3(Math::rad_to_deg(radians.x), Math::rad_to_deg(radians.y), Math::rad_to_deg(radians.z)); } Vector3 Node3D::get_scale() const { - if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) { + ERR_READ_THREAD_GUARD_V(Vector3()); + if (data.dirty.get() & DIRTY_EULER_ROTATION_AND_SCALE) { _update_rotation_and_scale(); } @@ -496,6 +536,7 @@ Vector3 Node3D::get_scale() const { } void Node3D::update_gizmos() { + ERR_THREAD_GUARD; #ifdef TOOLS_ENABLED if (!is_inside_world()) { return; @@ -514,6 +555,7 @@ void Node3D::update_gizmos() { } void Node3D::set_subgizmo_selection(Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform) { + ERR_THREAD_GUARD; #ifdef TOOLS_ENABLED if (!is_inside_world()) { return; @@ -526,6 +568,7 @@ void Node3D::set_subgizmo_selection(Ref<Node3DGizmo> p_gizmo, int p_id, Transfor } void Node3D::clear_subgizmo_selection() { + ERR_THREAD_GUARD; #ifdef TOOLS_ENABLED if (!is_inside_world()) { return; @@ -542,6 +585,7 @@ void Node3D::clear_subgizmo_selection() { } void Node3D::add_gizmo(Ref<Node3DGizmo> p_gizmo) { + ERR_THREAD_GUARD; #ifdef TOOLS_ENABLED if (data.gizmos_disabled || p_gizmo.is_null()) { return; @@ -559,6 +603,7 @@ void Node3D::add_gizmo(Ref<Node3DGizmo> p_gizmo) { } void Node3D::remove_gizmo(Ref<Node3DGizmo> p_gizmo) { + ERR_THREAD_GUARD; #ifdef TOOLS_ENABLED int idx = data.gizmos.find(p_gizmo); if (idx != -1) { @@ -569,6 +614,7 @@ void Node3D::remove_gizmo(Ref<Node3DGizmo> p_gizmo) { } void Node3D::clear_gizmos() { + ERR_THREAD_GUARD; #ifdef TOOLS_ENABLED for (int i = 0; i < data.gizmos.size(); i++) { data.gizmos.write[i]->free(); @@ -578,6 +624,7 @@ void Node3D::clear_gizmos() { } TypedArray<Node3DGizmo> Node3D::get_gizmos_bind() const { + ERR_THREAD_GUARD_V(TypedArray<Node3DGizmo>()); TypedArray<Node3DGizmo> ret; #ifdef TOOLS_ENABLED @@ -590,6 +637,7 @@ TypedArray<Node3DGizmo> Node3D::get_gizmos_bind() const { } Vector<Ref<Node3DGizmo>> Node3D::get_gizmos() const { + ERR_THREAD_GUARD_V(Vector<Ref<Node3DGizmo>>()); #ifdef TOOLS_ENABLED return data.gizmos; #else @@ -615,6 +663,7 @@ void Node3D::_update_gizmos() { } void Node3D::set_disable_gizmos(bool p_enabled) { + ERR_THREAD_GUARD; #ifdef TOOLS_ENABLED data.gizmos_disabled = p_enabled; if (!p_enabled) { @@ -624,6 +673,7 @@ void Node3D::set_disable_gizmos(bool p_enabled) { } void Node3D::reparent(Node *p_parent, bool p_keep_global_transform) { + ERR_THREAD_GUARD; Transform3D temp = get_global_transform(); Node::reparent(p_parent); if (p_keep_global_transform) { @@ -632,14 +682,17 @@ void Node3D::reparent(Node *p_parent, bool p_keep_global_transform) { } void Node3D::set_disable_scale(bool p_enabled) { + ERR_THREAD_GUARD; data.disable_scale = p_enabled; } bool Node3D::is_scale_disabled() const { + ERR_READ_THREAD_GUARD_V(false); return data.disable_scale; } void Node3D::set_as_top_level(bool p_enabled) { + ERR_THREAD_GUARD; if (data.top_level == p_enabled) { return; } @@ -658,10 +711,12 @@ void Node3D::set_as_top_level(bool p_enabled) { } bool Node3D::is_set_as_top_level() const { + ERR_READ_THREAD_GUARD_V(false); return data.top_level; } Ref<World3D> Node3D::get_world_3d() const { + ERR_READ_THREAD_GUARD_V(Ref<World3D>()); // World3D can only be set from main thread, so it's safe to obtain on threads. ERR_FAIL_COND_V(!is_inside_world(), Ref<World3D>()); ERR_FAIL_COND_V(!data.viewport, Ref<World3D>()); @@ -688,14 +743,17 @@ void Node3D::_propagate_visibility_changed() { } void Node3D::show() { + ERR_MAIN_THREAD_GUARD; set_visible(true); } void Node3D::hide() { + ERR_MAIN_THREAD_GUARD; set_visible(false); } void Node3D::set_visible(bool p_visible) { + ERR_MAIN_THREAD_GUARD; if (data.visible == p_visible) { return; } @@ -709,10 +767,12 @@ void Node3D::set_visible(bool p_visible) { } bool Node3D::is_visible() const { + ERR_READ_THREAD_GUARD_V(false); return data.visible; } bool Node3D::is_visible_in_tree() const { + ERR_READ_THREAD_GUARD_V(false); // Since visibility can only be changed from main thread, this is safe to call. const Node3D *s = this; while (s) { @@ -726,42 +786,49 @@ bool Node3D::is_visible_in_tree() const { } void Node3D::rotate_object_local(const Vector3 &p_axis, real_t p_angle) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.basis.rotate_local(p_axis, p_angle); set_transform(t); } void Node3D::rotate(const Vector3 &p_axis, real_t p_angle) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.basis.rotate(p_axis, p_angle); set_transform(t); } void Node3D::rotate_x(real_t p_angle) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.basis.rotate(Vector3(1, 0, 0), p_angle); set_transform(t); } void Node3D::rotate_y(real_t p_angle) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.basis.rotate(Vector3(0, 1, 0), p_angle); set_transform(t); } void Node3D::rotate_z(real_t p_angle) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.basis.rotate(Vector3(0, 0, 1), p_angle); set_transform(t); } void Node3D::translate(const Vector3 &p_offset) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.translate_local(p_offset); set_transform(t); } void Node3D::translate_object_local(const Vector3 &p_offset) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); Transform3D s; @@ -770,52 +837,61 @@ void Node3D::translate_object_local(const Vector3 &p_offset) { } void Node3D::scale(const Vector3 &p_ratio) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.basis.scale(p_ratio); set_transform(t); } void Node3D::scale_object_local(const Vector3 &p_scale) { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.basis.scale_local(p_scale); set_transform(t); } void Node3D::global_rotate(const Vector3 &p_axis, real_t p_angle) { + ERR_THREAD_GUARD; Transform3D t = get_global_transform(); t.basis.rotate(p_axis, p_angle); set_global_transform(t); } void Node3D::global_scale(const Vector3 &p_scale) { + ERR_THREAD_GUARD; Transform3D t = get_global_transform(); t.basis.scale(p_scale); set_global_transform(t); } void Node3D::global_translate(const Vector3 &p_offset) { + ERR_THREAD_GUARD; Transform3D t = get_global_transform(); t.origin += p_offset; set_global_transform(t); } void Node3D::orthonormalize() { + ERR_THREAD_GUARD; Transform3D t = get_transform(); t.orthonormalize(); set_transform(t); } void Node3D::set_identity() { + ERR_THREAD_GUARD; set_transform(Transform3D()); } void Node3D::look_at(const Vector3 &p_target, const Vector3 &p_up) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!is_inside_tree(), "Node not inside tree. Use look_at_from_position() instead."); Vector3 origin = get_global_transform().origin; look_at_from_position(origin, p_target, p_up); } void Node3D::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(p_pos.is_equal_approx(p_target), "Node origin and target are in the same position, look_at() failed."); ERR_FAIL_COND_MSG(p_up.is_zero_approx(), "The up vector can't be zero, look_at() failed."); ERR_FAIL_COND_MSG(p_up.cross(p_target - p_pos).is_zero_approx(), "Up vector and direction between node origin and target are aligned, look_at() failed."); @@ -827,30 +903,37 @@ void Node3D::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target } Vector3 Node3D::to_local(Vector3 p_global) const { + ERR_READ_THREAD_GUARD_V(Vector3()); return get_global_transform().affine_inverse().xform(p_global); } Vector3 Node3D::to_global(Vector3 p_local) const { + ERR_READ_THREAD_GUARD_V(Vector3()); return get_global_transform().xform(p_local); } void Node3D::set_notify_transform(bool p_enabled) { + ERR_THREAD_GUARD; data.notify_transform = p_enabled; } bool Node3D::is_transform_notification_enabled() const { + ERR_READ_THREAD_GUARD_V(false); return data.notify_transform; } void Node3D::set_notify_local_transform(bool p_enabled) { + ERR_THREAD_GUARD; data.notify_local_transform = p_enabled; } bool Node3D::is_local_transform_notification_enabled() const { + ERR_READ_THREAD_GUARD_V(false); return data.notify_local_transform; } void Node3D::force_update_transform() { + ERR_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); if (!xform_change.in_list()) { return; //nothing to update @@ -894,6 +977,7 @@ void Node3D::_update_visibility_parent(bool p_update_root) { } void Node3D::set_visibility_parent(const NodePath &p_path) { + ERR_MAIN_THREAD_GUARD; visibility_parent_path = p_path; if (is_inside_tree()) { _update_visibility_parent(true); @@ -901,6 +985,7 @@ void Node3D::set_visibility_parent(const NodePath &p_path) { } NodePath Node3D::get_visibility_parent() const { + ERR_READ_THREAD_GUARD_V(NodePath()); return visibility_parent_path; } diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index 98bcab5fd4..4fb77eeb9f 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -88,6 +88,7 @@ private: mutable SelfList<Node> xform_change; // This Data struct is to avoid namespace pollution in derived classes. + struct Data { mutable Transform3D global_transform; mutable Transform3D local_transform; @@ -96,7 +97,7 @@ private: mutable Vector3 scale = Vector3(1, 1, 1); mutable RotationEditMode rotation_edit_mode = ROTATION_EDIT_MODE_EULER; - mutable int dirty = DIRTY_NONE; + mutable SafeNumeric<uint32_t> dirty; Viewport *viewport = nullptr; @@ -106,7 +107,6 @@ private: RID visibility_parent; - int children_lock = 0; Node3D *parent = nullptr; List<Node3D *> children; List<Node3D *>::Element *C = nullptr; @@ -137,6 +137,7 @@ private: void _propagate_visibility_parent(); void _update_visibility_parent(bool p_update_root); + void _propagate_transform_changed_deferred(); protected: _FORCE_INLINE_ void set_ignore_transform_notification(bool p_ignore) { data.ignore_notification = p_ignore; } diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 6ef39c88d8..39bfa556e5 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -187,6 +187,7 @@ Size2 Control::_edit_get_minimum_size() const { #endif void Control::reparent(Node *p_parent, bool p_keep_global_transform) { + ERR_MAIN_THREAD_GUARD; Transform2D temp = get_global_transform(); Node::reparent(p_parent); if (p_keep_global_transform) { @@ -197,6 +198,7 @@ void Control::reparent(Node *p_parent, bool p_keep_global_transform) { // Editor integration. void Control::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + ERR_READ_THREAD_GUARD; Node::get_argument_options(p_function, p_idx, r_options); if (p_idx == 0) { @@ -222,6 +224,7 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List } PackedStringArray Control::get_configuration_warnings() const { + ERR_READ_THREAD_GUARD_V(PackedStringArray()); PackedStringArray warnings = Node::get_configuration_warnings(); if (data.mouse_filter == MOUSE_FILTER_IGNORE && !data.tooltip.is_empty()) { @@ -236,6 +239,7 @@ PackedStringArray Control::get_configuration_warnings() const { } bool Control::is_text_field() const { + ERR_READ_THREAD_GUARD_V(false); return false; } @@ -257,6 +261,7 @@ String Control::properties_managed_by_container[] = { }; bool Control::_set(const StringName &p_name, const Variant &p_value) { + ERR_MAIN_THREAD_GUARD_V(false); String name = p_name; if (!name.begins_with("theme_override")) { return false; @@ -327,6 +332,7 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) { } bool Control::_get(const StringName &p_name, Variant &r_ret) const { + ERR_MAIN_THREAD_GUARD_V(false); String sname = p_name; if (!sname.begins_with("theme_override")) { return false; @@ -358,6 +364,7 @@ bool Control::_get(const StringName &p_name, Variant &r_ret) const { } void Control::_get_property_list(List<PropertyInfo> *p_list) const { + ERR_MAIN_THREAD_GUARD; Ref<Theme> default_theme = ThemeDB::get_singleton()->get_default_theme(); p_list->push_back(PropertyInfo(Variant::NIL, GNAME("Theme Overrides", "theme_override_"), PROPERTY_HINT_NONE, "theme_override_", PROPERTY_USAGE_GROUP)); @@ -603,18 +610,22 @@ bool Control::_property_get_revert(const StringName &p_name, Variant &r_property // Global relations. bool Control::is_top_level_control() const { + ERR_READ_THREAD_GUARD_V(false); return is_inside_tree() && (!data.parent_canvas_item && !data.RI && is_set_as_top_level()); } Control *Control::get_parent_control() const { + ERR_READ_THREAD_GUARD_V(nullptr); return data.parent_control; } Window *Control::get_parent_window() const { + ERR_READ_THREAD_GUARD_V(nullptr); return data.parent_window; } Control *Control::get_root_parent_control() const { + ERR_READ_THREAD_GUARD_V(nullptr); const CanvasItem *ci = this; const Control *root = this; @@ -635,6 +646,7 @@ Control *Control::get_root_parent_control() const { } Rect2 Control::get_parent_anchorable_rect() const { + ERR_READ_THREAD_GUARD_V(Rect2()); if (!is_inside_tree()) { return Rect2(); } @@ -662,6 +674,7 @@ Rect2 Control::get_parent_anchorable_rect() const { } Size2 Control::get_parent_area_size() const { + ERR_READ_THREAD_GUARD_V(Size2()); return get_parent_anchorable_rect().size; } @@ -689,6 +702,7 @@ void Control::_update_canvas_item_transform() { } Transform2D Control::get_transform() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); Transform2D xform = _get_internal_transform(); xform[2] += get_position(); return xform; @@ -707,6 +721,7 @@ void Control::_set_anchor(Side p_side, real_t p_anchor) { } void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX((int)p_side, 4); Rect2 parent_rect = get_parent_anchorable_rect(); @@ -739,12 +754,14 @@ void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool } real_t Control::get_anchor(Side p_side) const { + ERR_READ_THREAD_GUARD_V(0); ERR_FAIL_INDEX_V(int(p_side), 4, 0.0); return data.anchor[p_side]; } void Control::set_offset(Side p_side, real_t p_value) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX((int)p_side, 4); if (data.offset[p_side] == p_value) { return; @@ -755,17 +772,20 @@ void Control::set_offset(Side p_side, real_t p_value) { } real_t Control::get_offset(Side p_side) const { + ERR_READ_THREAD_GUARD_V(0); ERR_FAIL_INDEX_V((int)p_side, 4, 0); return data.offset[p_side]; } void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, bool p_push_opposite_anchor) { + ERR_MAIN_THREAD_GUARD; set_anchor(p_side, p_anchor, false, p_push_opposite_anchor); set_offset(p_side, p_pos); } void Control::set_begin(const Point2 &p_point) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!isfinite(p_point.x) || !isfinite(p_point.y)); if (data.offset[0] == p_point.x && data.offset[1] == p_point.y) { return; @@ -777,10 +797,12 @@ void Control::set_begin(const Point2 &p_point) { } Point2 Control::get_begin() const { + ERR_READ_THREAD_GUARD_V(Vector2()); return Point2(data.offset[0], data.offset[1]); } void Control::set_end(const Point2 &p_point) { + ERR_MAIN_THREAD_GUARD; if (data.offset[2] == p_point.x && data.offset[3] == p_point.y) { return; } @@ -791,10 +813,12 @@ void Control::set_end(const Point2 &p_point) { } Point2 Control::get_end() const { + ERR_READ_THREAD_GUARD_V(Point2()); return Point2(data.offset[2], data.offset[3]); } void Control::set_h_grow_direction(GrowDirection p_direction) { + ERR_MAIN_THREAD_GUARD; if (data.h_grow == p_direction) { return; } @@ -806,10 +830,12 @@ void Control::set_h_grow_direction(GrowDirection p_direction) { } Control::GrowDirection Control::get_h_grow_direction() const { + ERR_READ_THREAD_GUARD_V(GROW_DIRECTION_BEGIN); return data.h_grow; } void Control::set_v_grow_direction(GrowDirection p_direction) { + ERR_MAIN_THREAD_GUARD; if (data.v_grow == p_direction) { return; } @@ -821,6 +847,7 @@ void Control::set_v_grow_direction(GrowDirection p_direction) { } Control::GrowDirection Control::get_v_grow_direction() const { + ERR_READ_THREAD_GUARD_V(GROW_DIRECTION_BEGIN); return data.v_grow; } @@ -1045,6 +1072,7 @@ int Control::_get_anchors_layout_preset() const { } void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX((int)p_preset, 16); //Left @@ -1161,6 +1189,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) { } void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode, int p_margin) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX((int)p_preset, 16); ERR_FAIL_INDEX((int)p_resize_mode, 4); @@ -1296,11 +1325,13 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz } void Control::set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode, int p_margin) { + ERR_MAIN_THREAD_GUARD; set_anchors_preset(p_preset); set_offsets_preset(p_preset, p_resize_mode, p_margin); } void Control::set_grow_direction_preset(LayoutPreset p_preset) { + ERR_MAIN_THREAD_GUARD; // Select correct horizontal grow direction. switch (p_preset) { case PRESET_TOP_LEFT: @@ -1363,6 +1394,7 @@ void Control::_set_position(const Point2 &p_point) { } void Control::set_position(const Point2 &p_point, bool p_keep_offsets) { + ERR_MAIN_THREAD_GUARD; if (p_keep_offsets) { _compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor); } else { @@ -1372,6 +1404,7 @@ void Control::set_position(const Point2 &p_point, bool p_keep_offsets) { } Size2 Control::get_position() const { + ERR_READ_THREAD_GUARD_V(Size2()); return data.pos_cache; } @@ -1380,6 +1413,7 @@ void Control::_set_global_position(const Point2 &p_point) { } void Control::set_global_position(const Point2 &p_point, bool p_keep_offsets) { + ERR_MAIN_THREAD_GUARD; Transform2D inv; if (data.parent_canvas_item) { @@ -1390,10 +1424,12 @@ void Control::set_global_position(const Point2 &p_point, bool p_keep_offsets) { } Point2 Control::get_global_position() const { + ERR_READ_THREAD_GUARD_V(Point2()); return get_global_transform().get_origin(); } Point2 Control::get_screen_position() const { + ERR_READ_THREAD_GUARD_V(Point2()); ERR_FAIL_COND_V(!is_inside_tree(), Point2()); return get_screen_transform().get_origin(); } @@ -1408,6 +1444,7 @@ void Control::_set_size(const Size2 &p_size) { } void Control::set_size(const Size2 &p_size, bool p_keep_offsets) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!isfinite(p_size.x) || !isfinite(p_size.y)); Size2 new_size = p_size; Size2 min = get_combined_minimum_size(); @@ -1427,14 +1464,17 @@ void Control::set_size(const Size2 &p_size, bool p_keep_offsets) { } Size2 Control::get_size() const { + ERR_READ_THREAD_GUARD_V(Size2()); return data.size_cache; } void Control::reset_size() { + ERR_MAIN_THREAD_GUARD; set_size(Size2()); } void Control::set_rect(const Rect2 &p_rect) { + ERR_MAIN_THREAD_GUARD; for (int i = 0; i < 4; i++) { data.anchor[i] = ANCHOR_BEGIN; } @@ -1446,16 +1486,19 @@ void Control::set_rect(const Rect2 &p_rect) { } Rect2 Control::get_rect() const { + ERR_READ_THREAD_GUARD_V(Rect2()); Transform2D xform = get_transform(); return Rect2(xform.get_origin(), xform.get_scale() * get_size()); } Rect2 Control::get_global_rect() const { + ERR_READ_THREAD_GUARD_V(Rect2()); Transform2D xform = get_global_transform(); return Rect2(xform.get_origin(), xform.get_scale() * get_size()); } Rect2 Control::get_screen_rect() const { + ERR_READ_THREAD_GUARD_V(Rect2()); ERR_FAIL_COND_V(!is_inside_tree(), Rect2()); Transform2D xform = get_screen_transform(); @@ -1463,10 +1506,12 @@ Rect2 Control::get_screen_rect() const { } Rect2 Control::get_anchorable_rect() const { + ERR_READ_THREAD_GUARD_V(Rect2()); return Rect2(Point2(), get_size()); } void Control::set_scale(const Vector2 &p_scale) { + ERR_MAIN_THREAD_GUARD; if (data.scale == p_scale) { return; } @@ -1484,10 +1529,12 @@ void Control::set_scale(const Vector2 &p_scale) { } Vector2 Control::get_scale() const { + ERR_READ_THREAD_GUARD_V(Vector2()); return data.scale; } void Control::set_rotation(real_t p_radians) { + ERR_MAIN_THREAD_GUARD; if (data.rotation == p_radians) { return; } @@ -1498,18 +1545,22 @@ void Control::set_rotation(real_t p_radians) { } void Control::set_rotation_degrees(real_t p_degrees) { + ERR_MAIN_THREAD_GUARD; set_rotation(Math::deg_to_rad(p_degrees)); } real_t Control::get_rotation() const { + ERR_READ_THREAD_GUARD_V(0); return data.rotation; } real_t Control::get_rotation_degrees() const { + ERR_READ_THREAD_GUARD_V(0); return Math::rad_to_deg(get_rotation()); } void Control::set_pivot_offset(const Vector2 &p_pivot) { + ERR_MAIN_THREAD_GUARD; if (data.pivot_offset == p_pivot) { return; } @@ -1520,6 +1571,7 @@ void Control::set_pivot_offset(const Vector2 &p_pivot) { } Vector2 Control::get_pivot_offset() const { + ERR_READ_THREAD_GUARD_V(Vector2()); return data.pivot_offset; } @@ -1541,6 +1593,7 @@ void Control::_update_minimum_size() { } void Control::update_minimum_size() { + ERR_MAIN_THREAD_GUARD; if (!is_inside_tree() || data.block_minimum_size_adjust) { return; } @@ -1577,16 +1630,19 @@ void Control::update_minimum_size() { } void Control::set_block_minimum_size_adjust(bool p_block) { + ERR_MAIN_THREAD_GUARD; data.block_minimum_size_adjust = p_block; } Size2 Control::get_minimum_size() const { + ERR_READ_THREAD_GUARD_V(Size2()); Vector2 ms; GDVIRTUAL_CALL(_get_minimum_size, ms); return ms; } void Control::set_custom_minimum_size(const Size2 &p_custom) { + ERR_MAIN_THREAD_GUARD; if (p_custom == data.custom_minimum_size) { return; } @@ -1601,6 +1657,7 @@ void Control::set_custom_minimum_size(const Size2 &p_custom) { } Size2 Control::get_custom_minimum_size() const { + ERR_READ_THREAD_GUARD_V(Size2()); return data.custom_minimum_size; } @@ -1623,6 +1680,7 @@ void Control::_update_minimum_size_cache() { } Size2 Control::get_combined_minimum_size() const { + ERR_READ_THREAD_GUARD_V(Size2()); if (!data.minimum_size_valid) { const_cast<Control *>(this)->_update_minimum_size_cache(); } @@ -1696,6 +1754,7 @@ void Control::_clear_size_warning() { // Container sizing. void Control::set_h_size_flags(BitField<SizeFlags> p_flags) { + ERR_MAIN_THREAD_GUARD; if ((int)data.h_size_flags == (int)p_flags) { return; } @@ -1704,10 +1763,12 @@ void Control::set_h_size_flags(BitField<SizeFlags> p_flags) { } BitField<Control::SizeFlags> Control::get_h_size_flags() const { + ERR_READ_THREAD_GUARD_V(SIZE_EXPAND_FILL); return data.h_size_flags; } void Control::set_v_size_flags(BitField<SizeFlags> p_flags) { + ERR_MAIN_THREAD_GUARD; if ((int)data.v_size_flags == (int)p_flags) { return; } @@ -1716,10 +1777,12 @@ void Control::set_v_size_flags(BitField<SizeFlags> p_flags) { } BitField<Control::SizeFlags> Control::get_v_size_flags() const { + ERR_READ_THREAD_GUARD_V(SIZE_EXPAND_FILL); return data.v_size_flags; } void Control::set_stretch_ratio(real_t p_ratio) { + ERR_MAIN_THREAD_GUARD; if (data.expand == p_ratio) { return; } @@ -1729,6 +1792,7 @@ void Control::set_stretch_ratio(real_t p_ratio) { } real_t Control::get_stretch_ratio() const { + ERR_READ_THREAD_GUARD_V(0); return data.expand; } @@ -1755,12 +1819,14 @@ void Control::gui_input(const Ref<InputEvent> &p_event) { } void Control::accept_event() { + ERR_MAIN_THREAD_GUARD; if (is_inside_tree()) { get_viewport()->_gui_accept_event(); } } bool Control::has_point(const Point2 &p_point) const { + ERR_READ_THREAD_GUARD_V(false); bool ret; if (GDVIRTUAL_CALL(_has_point, p_point, ret)) { return ret; @@ -1769,6 +1835,7 @@ bool Control::has_point(const Point2 &p_point) const { } void Control::set_mouse_filter(MouseFilter p_filter) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(p_filter, 3); data.mouse_filter = p_filter; notify_property_list_changed(); @@ -1776,23 +1843,28 @@ void Control::set_mouse_filter(MouseFilter p_filter) { } Control::MouseFilter Control::get_mouse_filter() const { + ERR_READ_THREAD_GUARD_V(MOUSE_FILTER_IGNORE); return data.mouse_filter; } void Control::set_force_pass_scroll_events(bool p_force_pass_scroll_events) { + ERR_MAIN_THREAD_GUARD; data.force_pass_scroll_events = p_force_pass_scroll_events; } bool Control::is_force_pass_scroll_events() const { + ERR_READ_THREAD_GUARD_V(false); return data.force_pass_scroll_events; } void Control::warp_mouse(const Point2 &p_position) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); get_viewport()->warp_mouse(get_global_transform_with_canvas().xform(p_position)); } void Control::set_shortcut_context(const Node *p_node) { + ERR_MAIN_THREAD_GUARD; if (p_node != nullptr) { data.shortcut_context = p_node->get_instance_id(); } else { @@ -1801,6 +1873,7 @@ void Control::set_shortcut_context(const Node *p_node) { } Node *Control::get_shortcut_context() const { + ERR_READ_THREAD_GUARD_V(nullptr); Object *ctx_obj = ObjectDB::get_instance(data.shortcut_context); Node *ctx_node = Object::cast_to<Node>(ctx_obj); @@ -1808,6 +1881,7 @@ Node *Control::get_shortcut_context() const { } bool Control::is_focus_owner_in_shortcut_context() const { + ERR_READ_THREAD_GUARD_V(false); if (data.shortcut_context == ObjectID()) { // No context, therefore global - always "in" context. return true; @@ -1823,12 +1897,14 @@ bool Control::is_focus_owner_in_shortcut_context() const { // Drag and drop handling. void Control::set_drag_forwarding(const Callable &p_drag, const Callable &p_can_drop, const Callable &p_drop) { + ERR_MAIN_THREAD_GUARD; data.forward_drag = p_drag; data.forward_can_drop = p_can_drop; data.forward_drop = p_drop; } Variant Control::get_drag_data(const Point2 &p_point) { + ERR_READ_THREAD_GUARD_V(Variant()); Variant ret; if (data.forward_drag.is_valid()) { Variant p = p_point; @@ -1846,6 +1922,7 @@ Variant Control::get_drag_data(const Point2 &p_point) { } bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const { + ERR_READ_THREAD_GUARD_V(false); if (data.forward_can_drop.is_valid()) { Variant ret; Variant p = p_point; @@ -1864,6 +1941,7 @@ bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const } void Control::drop_data(const Point2 &p_point, const Variant &p_data) { + ERR_READ_THREAD_GUARD; if (data.forward_drop.is_valid()) { Variant ret; Variant p = p_point; @@ -1880,6 +1958,7 @@ void Control::drop_data(const Point2 &p_point, const Variant &p_data) { } void Control::force_drag(const Variant &p_data, Control *p_control) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND(p_data.get_type() == Variant::NIL); @@ -1887,18 +1966,21 @@ void Control::force_drag(const Variant &p_data, Control *p_control) { } void Control::set_drag_preview(Control *p_control) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND(!get_viewport()->gui_is_dragging()); get_viewport()->_gui_set_drag_preview(this, p_control); } bool Control::is_drag_successful() const { + ERR_READ_THREAD_GUARD_V(false); return is_inside_tree() && get_viewport()->gui_is_drag_successful(); } // Focus. void Control::set_focus_mode(FocusMode p_focus_mode) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX((int)p_focus_mode, 3); if (is_inside_tree() && p_focus_mode == FOCUS_NONE && data.focus_mode != FOCUS_NONE && has_focus()) { @@ -1909,14 +1991,17 @@ void Control::set_focus_mode(FocusMode p_focus_mode) { } Control::FocusMode Control::get_focus_mode() const { + ERR_READ_THREAD_GUARD_V(FOCUS_NONE); return data.focus_mode; } bool Control::has_focus() const { + ERR_READ_THREAD_GUARD_V(false); return is_inside_tree() && get_viewport()->_gui_control_has_focus(this); } void Control::grab_focus() { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); if (data.focus_mode == FOCUS_NONE) { @@ -1928,12 +2013,14 @@ void Control::grab_focus() { } void Control::grab_click_focus() { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); get_viewport()->_gui_grab_click_focus(this); } void Control::release_focus() { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); if (!has_focus()) { @@ -1970,6 +2057,7 @@ static Control *_next_control(Control *p_from) { } Control *Control::find_next_valid_focus() const { + ERR_READ_THREAD_GUARD_V(nullptr); Control *from = const_cast<Control *>(this); while (true) { @@ -2060,6 +2148,7 @@ static Control *_prev_control(Control *p_from) { } Control *Control::find_prev_valid_focus() const { + ERR_READ_THREAD_GUARD_V(nullptr); Control *from = const_cast<Control *>(this); while (true) { @@ -2122,28 +2211,34 @@ Control *Control::find_prev_valid_focus() const { } void Control::set_focus_neighbor(Side p_side, const NodePath &p_neighbor) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX((int)p_side, 4); data.focus_neighbor[p_side] = p_neighbor; } NodePath Control::get_focus_neighbor(Side p_side) const { + ERR_READ_THREAD_GUARD_V(NodePath()); ERR_FAIL_INDEX_V((int)p_side, 4, NodePath()); return data.focus_neighbor[p_side]; } void Control::set_focus_next(const NodePath &p_next) { + ERR_MAIN_THREAD_GUARD; data.focus_next = p_next; } NodePath Control::get_focus_next() const { + ERR_READ_THREAD_GUARD_V(NodePath()); return data.focus_next; } void Control::set_focus_previous(const NodePath &p_prev) { + ERR_MAIN_THREAD_GUARD; data.focus_prev = p_prev; } NodePath Control::get_focus_previous() const { + ERR_READ_THREAD_GUARD_V(NodePath()); return data.focus_prev; } @@ -2290,6 +2385,7 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons // Rendering. void Control::set_default_cursor_shape(CursorShape p_shape) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(int(p_shape), CURSOR_MAX); if (data.default_cursor == p_shape) { @@ -2308,14 +2404,17 @@ void Control::set_default_cursor_shape(CursorShape p_shape) { } Control::CursorShape Control::get_default_cursor_shape() const { + ERR_READ_THREAD_GUARD_V(CURSOR_ARROW); return data.default_cursor; } Control::CursorShape Control::get_cursor_shape(const Point2 &p_pos) const { + ERR_READ_THREAD_GUARD_V(CURSOR_ARROW); return data.default_cursor; } void Control::set_disable_visibility_clip(bool p_ignore) { + ERR_MAIN_THREAD_GUARD; if (data.disable_visibility_clip == p_ignore) { return; } @@ -2324,10 +2423,12 @@ void Control::set_disable_visibility_clip(bool p_ignore) { } bool Control::is_visibility_clip_disabled() const { + ERR_READ_THREAD_GUARD_V(false); return data.disable_visibility_clip; } void Control::set_clip_contents(bool p_clip) { + ERR_MAIN_THREAD_GUARD; if (data.clip_contents == p_clip) { return; } @@ -2336,6 +2437,7 @@ void Control::set_clip_contents(bool p_clip) { } bool Control::is_clipping_contents() { + ERR_READ_THREAD_GUARD_V(false); return data.clip_contents; } @@ -2366,18 +2468,22 @@ void Control::_update_theme_item_cache() { } void Control::set_theme_owner_node(Node *p_node) { + ERR_MAIN_THREAD_GUARD; data.theme_owner->set_owner_node(p_node); } Node *Control::get_theme_owner_node() const { + ERR_READ_THREAD_GUARD_V(nullptr); return data.theme_owner->get_owner_node(); } bool Control::has_theme_owner_node() const { + ERR_READ_THREAD_GUARD_V(false); return data.theme_owner->has_owner_node(); } void Control::set_theme(const Ref<Theme> &p_theme) { + ERR_MAIN_THREAD_GUARD; if (data.theme == p_theme) { return; } @@ -2409,10 +2515,12 @@ void Control::set_theme(const Ref<Theme> &p_theme) { } Ref<Theme> Control::get_theme() const { + ERR_READ_THREAD_GUARD_V(Ref<Theme>()); return data.theme; } void Control::set_theme_type_variation(const StringName &p_theme_type) { + ERR_MAIN_THREAD_GUARD; if (data.theme_type_variation == p_theme_type) { return; } @@ -2423,12 +2531,14 @@ void Control::set_theme_type_variation(const StringName &p_theme_type) { } StringName Control::get_theme_type_variation() const { + ERR_READ_THREAD_GUARD_V(StringName()); return data.theme_type_variation; } /// Theme property lookup. Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(Ref<Texture2D>()); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2452,6 +2562,7 @@ Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringNam } Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(Ref<StyleBox>()); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2475,6 +2586,7 @@ Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const String } Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(Ref<Font>()); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2498,6 +2610,7 @@ Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_ } int Control::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(0); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2521,6 +2634,7 @@ int Control::get_theme_font_size(const StringName &p_name, const StringName &p_t } Color Control::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(Color()); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2544,6 +2658,7 @@ Color Control::get_theme_color(const StringName &p_name, const StringName &p_the } int Control::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(0); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2567,6 +2682,7 @@ int Control::get_theme_constant(const StringName &p_name, const StringName &p_th } bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2583,6 +2699,7 @@ bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme } bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2599,6 +2716,7 @@ bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_t } bool Control::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2615,6 +2733,7 @@ bool Control::has_theme_font(const StringName &p_name, const StringName &p_theme } bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2631,6 +2750,7 @@ bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_ } bool Control::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2647,6 +2767,7 @@ bool Control::has_theme_color(const StringName &p_name, const StringName &p_them } bool Control::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2665,6 +2786,7 @@ bool Control::has_theme_constant(const StringName &p_name, const StringName &p_t /// Local property overrides. void Control::add_theme_icon_override(const StringName &p_name, const Ref<Texture2D> &p_icon) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!p_icon.is_valid()); if (data.theme_icon_override.has(p_name)) { @@ -2677,6 +2799,7 @@ void Control::add_theme_icon_override(const StringName &p_name, const Ref<Textur } void Control::add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!p_style.is_valid()); if (data.theme_style_override.has(p_name)) { @@ -2689,6 +2812,7 @@ void Control::add_theme_style_override(const StringName &p_name, const Ref<Style } void Control::add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!p_font.is_valid()); if (data.theme_font_override.has(p_name)) { @@ -2701,21 +2825,25 @@ void Control::add_theme_font_override(const StringName &p_name, const Ref<Font> } void Control::add_theme_font_size_override(const StringName &p_name, int p_font_size) { + ERR_MAIN_THREAD_GUARD; data.theme_font_size_override[p_name] = p_font_size; _notify_theme_override_changed(); } void Control::add_theme_color_override(const StringName &p_name, const Color &p_color) { + ERR_MAIN_THREAD_GUARD; data.theme_color_override[p_name] = p_color; _notify_theme_override_changed(); } void Control::add_theme_constant_override(const StringName &p_name, int p_constant) { + ERR_MAIN_THREAD_GUARD; data.theme_constant_override[p_name] = p_constant; _notify_theme_override_changed(); } void Control::remove_theme_icon_override(const StringName &p_name) { + ERR_MAIN_THREAD_GUARD; if (data.theme_icon_override.has(p_name)) { data.theme_icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); } @@ -2725,6 +2853,7 @@ void Control::remove_theme_icon_override(const StringName &p_name) { } void Control::remove_theme_style_override(const StringName &p_name) { + ERR_MAIN_THREAD_GUARD; if (data.theme_style_override.has(p_name)) { data.theme_style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); } @@ -2734,6 +2863,7 @@ void Control::remove_theme_style_override(const StringName &p_name) { } void Control::remove_theme_font_override(const StringName &p_name) { + ERR_MAIN_THREAD_GUARD; if (data.theme_font_override.has(p_name)) { data.theme_font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed)); } @@ -2743,46 +2873,55 @@ void Control::remove_theme_font_override(const StringName &p_name) { } void Control::remove_theme_font_size_override(const StringName &p_name) { + ERR_MAIN_THREAD_GUARD; data.theme_font_size_override.erase(p_name); _notify_theme_override_changed(); } void Control::remove_theme_color_override(const StringName &p_name) { + ERR_MAIN_THREAD_GUARD; data.theme_color_override.erase(p_name); _notify_theme_override_changed(); } void Control::remove_theme_constant_override(const StringName &p_name) { + ERR_MAIN_THREAD_GUARD; data.theme_constant_override.erase(p_name); _notify_theme_override_changed(); } bool Control::has_theme_icon_override(const StringName &p_name) const { + ERR_READ_THREAD_GUARD_V(false); const Ref<Texture2D> *tex = data.theme_icon_override.getptr(p_name); return tex != nullptr; } bool Control::has_theme_stylebox_override(const StringName &p_name) const { + ERR_READ_THREAD_GUARD_V(false); const Ref<StyleBox> *style = data.theme_style_override.getptr(p_name); return style != nullptr; } bool Control::has_theme_font_override(const StringName &p_name) const { + ERR_READ_THREAD_GUARD_V(false); const Ref<Font> *font = data.theme_font_override.getptr(p_name); return font != nullptr; } bool Control::has_theme_font_size_override(const StringName &p_name) const { + ERR_READ_THREAD_GUARD_V(false); const int *font_size = data.theme_font_size_override.getptr(p_name); return font_size != nullptr; } bool Control::has_theme_color_override(const StringName &p_name) const { + ERR_READ_THREAD_GUARD_V(false); const Color *color = data.theme_color_override.getptr(p_name); return color != nullptr; } bool Control::has_theme_constant_override(const StringName &p_name) const { + ERR_READ_THREAD_GUARD_V(false); const int *constant = data.theme_constant_override.getptr(p_name); return constant != nullptr; } @@ -2790,24 +2929,29 @@ bool Control::has_theme_constant_override(const StringName &p_name) const { /// Default theme properties. float Control::get_theme_default_base_scale() const { + ERR_READ_THREAD_GUARD_V(0); return data.theme_owner->get_theme_default_base_scale(); } Ref<Font> Control::get_theme_default_font() const { + ERR_READ_THREAD_GUARD_V(Ref<Font>()); return data.theme_owner->get_theme_default_font(); } int Control::get_theme_default_font_size() const { + ERR_READ_THREAD_GUARD_V(0); return data.theme_owner->get_theme_default_font_size(); } /// Bulk actions. void Control::begin_bulk_theme_override() { + ERR_MAIN_THREAD_GUARD; data.bulk_theme_override = true; } void Control::end_bulk_theme_override() { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!data.bulk_theme_override); data.bulk_theme_override = false; @@ -2817,6 +2961,7 @@ void Control::end_bulk_theme_override() { // Internationalization. TypedArray<Vector3i> Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const { + ERR_READ_THREAD_GUARD_V(TypedArray<Vector3i>()); if (p_parser_type == TextServer::STRUCTURED_TEXT_CUSTOM) { TypedArray<Vector3i> ret; GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret); @@ -2827,6 +2972,7 @@ TypedArray<Vector3i> Control::structured_text_parser(TextServer::StructuredTextP } void Control::set_layout_direction(Control::LayoutDirection p_direction) { + ERR_MAIN_THREAD_GUARD; if (data.layout_dir == p_direction) { return; } @@ -2839,10 +2985,12 @@ void Control::set_layout_direction(Control::LayoutDirection p_direction) { } Control::LayoutDirection Control::get_layout_direction() const { + ERR_READ_THREAD_GUARD_V(LAYOUT_DIRECTION_INHERITED); return data.layout_dir; } bool Control::is_layout_rtl() const { + ERR_READ_THREAD_GUARD_V(false); if (data.is_rtl_dirty) { const_cast<Control *>(this)->data.is_rtl_dirty = false; if (data.layout_dir == LAYOUT_DIRECTION_INHERITED) { @@ -2890,6 +3038,7 @@ bool Control::is_layout_rtl() const { } void Control::set_localize_numeral_system(bool p_enable) { + ERR_MAIN_THREAD_GUARD; if (p_enable == data.localize_numeral_system) { return; } @@ -2900,10 +3049,12 @@ void Control::set_localize_numeral_system(bool p_enable) { } bool Control::is_localizing_numeral_system() const { + ERR_READ_THREAD_GUARD_V(false); return data.localize_numeral_system; } void Control::set_auto_translate(bool p_enable) { + ERR_MAIN_THREAD_GUARD; if (p_enable == data.auto_translate) { return; } @@ -2914,21 +3065,25 @@ void Control::set_auto_translate(bool p_enable) { } bool Control::is_auto_translating() const { + ERR_READ_THREAD_GUARD_V(false); return data.auto_translate; } // Extra properties. void Control::set_tooltip_text(const String &p_hint) { + ERR_MAIN_THREAD_GUARD; data.tooltip = p_hint; update_configuration_warnings(); } String Control::get_tooltip_text() const { + ERR_READ_THREAD_GUARD_V(String()); return data.tooltip; } String Control::get_tooltip(const Point2 &p_pos) const { + ERR_READ_THREAD_GUARD_V(String()); String ret; if (GDVIRTUAL_CALL(_get_tooltip, p_pos, ret)) { return ret; @@ -2937,6 +3092,7 @@ String Control::get_tooltip(const Point2 &p_pos) const { } Control *Control::make_custom_tooltip(const String &p_text) const { + ERR_READ_THREAD_GUARD_V(nullptr); Object *ret = nullptr; GDVIRTUAL_CALL(_make_custom_tooltip, p_text, ret); return Object::cast_to<Control>(ret); @@ -2945,6 +3101,7 @@ Control *Control::make_custom_tooltip(const String &p_text) const { // Base object overrides. void Control::_notification(int p_notification) { + ERR_MAIN_THREAD_GUARD; switch (p_notification) { case NOTIFICATION_POSTINITIALIZE: { data.initialized = true; diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 59f7511894..a9f704e904 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -55,6 +55,20 @@ TextServer::AutowrapMode Label::get_autowrap_mode() const { return autowrap_mode; } +void Label::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) { + if (jst_flags == p_flags) { + return; + } + + jst_flags = p_flags; + lines_dirty = true; + queue_redraw(); +} + +BitField<TextServer::JustificationFlag> Label::get_justification_flags() const { + return jst_flags; +} + void Label::set_uppercase(bool p_uppercase) { if (uppercase == p_uppercase) { return; @@ -198,11 +212,27 @@ void Label::_shape() { overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS); } if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { + int jst_to_line = visible_lines; + if (lines_rid.size() == 1 && jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) { + jst_to_line = lines_rid.size(); + } else { + if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) { + jst_to_line = visible_lines - 1; + } + if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) { + for (int i = visible_lines - 1; i >= 0; i--) { + if (TS->shaped_text_has_visible_chars(lines_rid[i])) { + jst_to_line = i; + break; + } + } + } + } for (int i = 0; i < lines_rid.size(); i++) { - if (i < visible_lines - 1 || lines_rid.size() == 1) { - TS->shaped_text_fit_to_width(lines_rid[i], width); + if (i < jst_to_line) { + TS->shaped_text_fit_to_width(lines_rid[i], width, jst_flags); } else if (i == (visible_lines - 1)) { - TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags); + TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); } } } else if (lines_hidden) { @@ -210,12 +240,28 @@ void Label::_shape() { } } else { // Autowrap disabled. + int jst_to_line = lines_rid.size(); + if (lines_rid.size() == 1 && jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) { + jst_to_line = lines_rid.size(); + } else { + if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) { + jst_to_line = lines_rid.size() - 1; + } + if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) { + for (int i = lines_rid.size() - 1; i >= 0; i--) { + if (TS->shaped_text_has_visible_chars(lines_rid[i])) { + jst_to_line = i; + break; + } + } + } + } for (int i = 0; i < lines_rid.size(); i++) { - if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { - TS->shaped_text_fit_to_width(lines_rid[i], width); + if (i < jst_to_line && horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { + TS->shaped_text_fit_to_width(lines_rid[i], width, jst_flags); overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE); TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); - TS->shaped_text_fit_to_width(lines_rid[i], width, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS); + TS->shaped_text_fit_to_width(lines_rid[i], width, jst_flags | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS); } else { TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); } @@ -936,6 +982,8 @@ void Label::_bind_methods() { ClassDB::bind_method(D_METHOD("get_language"), &Label::get_language); ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &Label::set_autowrap_mode); ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &Label::get_autowrap_mode); + ClassDB::bind_method(D_METHOD("set_justification_flags", "justification_flags"), &Label::set_justification_flags); + ClassDB::bind_method(D_METHOD("get_justification_flags"), &Label::get_justification_flags); ClassDB::bind_method(D_METHOD("set_clip_text", "enable"), &Label::set_clip_text); ClassDB::bind_method(D_METHOD("is_clipping_text"), &Label::is_clipping_text); ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &Label::set_text_overrun_behavior); @@ -966,6 +1014,8 @@ void Label::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_vertical_alignment", "get_vertical_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "justification_flags", PROPERTY_HINT_FLAGS, "Kashida Justification:1,Word Justification:2,Justify Only After Last Tab:8,Skip Last Line:32,Skip Last Line With Visible Characters:64,Do Not Skip Single Line:128"), "set_justification_flags", "get_justification_flags"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "is_clipping_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase"); diff --git a/scene/gui/label.h b/scene/gui/label.h index 2350525236..5102750bc3 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -43,6 +43,7 @@ private: String text; String xl_text; TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF; + BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE; bool clip = false; TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING; Size2 minsize; @@ -122,6 +123,9 @@ public: void set_autowrap_mode(TextServer::AutowrapMode p_mode); TextServer::AutowrapMode get_autowrap_mode() const; + void set_justification_flags(BitField<TextServer::JustificationFlag> p_flags); + BitField<TextServer::JustificationFlag> get_justification_flags() const; + void set_uppercase(bool p_uppercase); bool is_uppercase() const; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 43c68a8eca..185db1b972 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -471,7 +471,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> // Clear cache. l.text_buf->clear(); l.text_buf->set_break_flags(autowrap_flags); - l.text_buf->set_justification_flags(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES); + l.text_buf->set_justification_flags(_find_jst_flags(l.from)); l.char_offset = *r_char_offset; l.char_count = 0; @@ -2453,6 +2453,21 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int return margin; } +BitField<TextServer::JustificationFlag> RichTextLabel::_find_jst_flags(Item *p_item) { + Item *item = p_item; + + while (item) { + if (item->type == ITEM_PARAGRAPH) { + ItemParagraph *p = static_cast<ItemParagraph *>(item); + return p->jst_flags; + } + + item = item->parent; + } + + return default_jst_flags; +} + HorizontalAlignment RichTextLabel::_find_alignment(Item *p_item) { Item *item = p_item; @@ -3297,7 +3312,7 @@ void RichTextLabel::push_strikethrough() { _add_item(item, true); } -void RichTextLabel::push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction, const String &p_language, TextServer::StructuredTextParser p_st_parser) { +void RichTextLabel::push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction, const String &p_language, TextServer::StructuredTextParser p_st_parser, BitField<TextServer::JustificationFlag> p_jst_flags) { _stop_thread(); MutexLock data_lock(data_mutex); @@ -3308,6 +3323,7 @@ void RichTextLabel::push_paragraph(HorizontalAlignment p_alignment, Control::Tex item->direction = p_direction; item->language = p_language; item->st_parser = p_st_parser; + item->jst_flags = p_jst_flags; _add_item(item, true, true); } @@ -4079,10 +4095,30 @@ void RichTextLabel::append_text(const String &p_bbcode) { Control::TextDirection dir = Control::TEXT_DIRECTION_INHERITED; String lang; TextServer::StructuredTextParser st_parser_type = TextServer::STRUCTURED_TEXT_DEFAULT; + BitField<TextServer::JustificationFlag> jst_flags = default_jst_flags; for (int i = 0; i < subtag.size(); i++) { Vector<String> subtag_a = subtag[i].split("="); if (subtag_a.size() == 2) { - if (subtag_a[0] == "align") { + if (subtag_a[0] == "justification_flags" || subtag_a[0] == "jst") { + Vector<String> subtag_b = subtag_a[1].split(","); + for (const String &E : subtag_b) { + if (E == "kashida" || E == "k") { + jst_flags.set_flag(TextServer::JUSTIFICATION_KASHIDA); + } else if (E == "word" || E == "w") { + jst_flags.set_flag(TextServer::JUSTIFICATION_WORD_BOUND); + } else if (E == "trim" || E == "tr") { + jst_flags.set_flag(TextServer::JUSTIFICATION_TRIM_EDGE_SPACES); + } else if (E == "after_last_tab" || E == "lt") { + jst_flags.set_flag(TextServer::JUSTIFICATION_AFTER_LAST_TAB); + } else if (E == "skip_last" || E == "sl") { + jst_flags.set_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE); + } else if (E == "skip_last_with_chars" || E == "sv") { + jst_flags.set_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS); + } else if (E == "do_not_skip_singe" || E == "ns") { + jst_flags.set_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE); + } + } + } else if (subtag_a[0] == "align") { if (subtag_a[1] == "l" || subtag_a[1] == "left") { alignment = HORIZONTAL_ALIGNMENT_LEFT; } else if (subtag_a[1] == "c" || subtag_a[1] == "center") { @@ -4121,7 +4157,7 @@ void RichTextLabel::append_text(const String &p_bbcode) { } } } - push_paragraph(alignment, dir, lang, st_parser_type); + push_paragraph(alignment, dir, lang, st_parser_type, jst_flags); pos = brk_end + 1; tag_stack.push_front("p"); } else if (tag == "url") { @@ -5370,7 +5406,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("push_color", "color"), &RichTextLabel::push_color); ClassDB::bind_method(D_METHOD("push_outline_size", "outline_size"), &RichTextLabel::push_outline_size); ClassDB::bind_method(D_METHOD("push_outline_color", "color"), &RichTextLabel::push_outline_color); - ClassDB::bind_method(D_METHOD("push_paragraph", "alignment", "base_direction", "language", "st_parser"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(TextServer::STRUCTURED_TEXT_DEFAULT)); + ClassDB::bind_method(D_METHOD("push_paragraph", "alignment", "base_direction", "language", "st_parser", "justification_flags"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(TextServer::STRUCTURED_TEXT_DEFAULT), DEFVAL(TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)); ClassDB::bind_method(D_METHOD("push_indent", "level"), &RichTextLabel::push_indent); ClassDB::bind_method(D_METHOD("push_list", "level", "type", "capitalize", "bullet"), &RichTextLabel::push_list, DEFVAL(String::utf8("•"))); ClassDB::bind_method(D_METHOD("push_meta", "data"), &RichTextLabel::push_meta); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 567528652c..3e3413d47a 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -240,6 +240,7 @@ private: String language; Control::TextDirection direction = Control::TEXT_DIRECTION_AUTO; TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; + BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE; ItemParagraph() { type = ITEM_PARAGRAPH; } }; @@ -405,6 +406,7 @@ private: bool use_selected_font_color = false; HorizontalAlignment default_alignment = HORIZONTAL_ALIGNMENT_LEFT; + BitField<TextServer::JustificationFlag> default_jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE; ItemMeta *meta_hovering = nullptr; Variant current_meta; @@ -492,6 +494,7 @@ private: int _find_list(Item *p_item, Vector<int> &r_index, Vector<ItemList *> &r_list); int _find_margin(Item *p_item, const Ref<Font> &p_base_font, int p_base_font_size); HorizontalAlignment _find_alignment(Item *p_item); + BitField<TextServer::JustificationFlag> _find_jst_flags(Item *p_item); TextServer::Direction _find_direction(Item *p_item); TextServer::StructuredTextParser _find_stt(Item *p_item); String _find_language(Item *p_item); @@ -593,7 +596,7 @@ public: void push_outline_color(const Color &p_color); void push_underline(); void push_strikethrough(); - void push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction = Control::TEXT_DIRECTION_INHERITED, const String &p_language = "", TextServer::StructuredTextParser p_st_parser = TextServer::STRUCTURED_TEXT_DEFAULT); + void push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction = Control::TEXT_DIRECTION_INHERITED, const String &p_language = "", TextServer::StructuredTextParser p_st_parser = TextServer::STRUCTURED_TEXT_DEFAULT, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE); void push_indent(int p_level); void push_list(int p_level, ListType p_list, bool p_capitalize, const String &p_bullet = String::utf8("•")); void push_meta(const Variant &p_meta); diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index cb115a6d94..598f6aa4c6 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -56,6 +56,7 @@ Transform2D CanvasItem::_edit_get_transform() const { #endif bool CanvasItem::is_visible_in_tree() const { + ERR_READ_THREAD_GUARD_V(false); return visible && parent_visible_in_tree; } @@ -69,6 +70,7 @@ void CanvasItem::_propagate_visibility_changed(bool p_parent_visible_in_tree) { } void CanvasItem::set_visible(bool p_visible) { + ERR_MAIN_THREAD_GUARD; if (visible == p_visible) { return; } @@ -105,14 +107,17 @@ void CanvasItem::_handle_visibility_change(bool p_visible) { } void CanvasItem::show() { + ERR_MAIN_THREAD_GUARD; set_visible(true); } void CanvasItem::hide() { + ERR_MAIN_THREAD_GUARD; set_visible(false); } bool CanvasItem::is_visible() const { + ERR_READ_THREAD_GUARD_V(false); return visible; } @@ -143,10 +148,11 @@ void CanvasItem::_redraw_callback() { } void CanvasItem::_invalidate_global_transform() { - global_invalid = true; + global_invalid.set(); } Transform2D CanvasItem::get_global_transform_with_canvas() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); if (canvas_layer) { return canvas_layer->get_final_transform() * get_global_transform(); } else if (is_inside_tree()) { @@ -157,20 +163,26 @@ Transform2D CanvasItem::get_global_transform_with_canvas() const { } Transform2D CanvasItem::get_screen_transform() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); ERR_FAIL_COND_V(!is_inside_tree(), Transform2D()); return get_viewport()->get_popup_base_transform() * get_global_transform_with_canvas(); } Transform2D CanvasItem::get_global_transform() const { - if (global_invalid) { + ERR_READ_THREAD_GUARD_V(Transform2D()); + + if (global_invalid.is_set()) { + // This code can enter multiple times from threads if dirty, this is expected. const CanvasItem *pi = get_parent_item(); + Transform2D new_global; if (pi) { - global_transform = pi->get_global_transform() * get_transform(); + new_global = pi->get_global_transform() * get_transform(); } else { - global_transform = get_transform(); + new_global = get_transform(); } - global_invalid = false; + global_transform = new_global; + global_invalid.clear(); } return global_transform; @@ -253,6 +265,8 @@ void CanvasItem::_exit_canvas() { } void CanvasItem::_notification(int p_what) { + ERR_MAIN_THREAD_GUARD; + switch (p_what) { case NOTIFICATION_ENTER_TREE: { ERR_FAIL_COND(!is_inside_tree()); @@ -294,7 +308,7 @@ void CanvasItem::_notification(int p_what) { } } - global_invalid = true; + global_invalid.set(); _enter_canvas(); RenderingServer::get_singleton()->canvas_item_set_visible(canvas_item, is_visible_in_tree()); // The visibility of the parent may change. @@ -327,7 +341,7 @@ void CanvasItem::_notification(int p_what) { window->disconnect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed)); window = nullptr; } - global_invalid = true; + global_invalid.set(); parent_visible_in_tree = false; if (get_viewport()) { @@ -346,6 +360,8 @@ void CanvasItem::_notification(int p_what) { } void CanvasItem::update_draw_order() { + ERR_MAIN_THREAD_GUARD; + if (!is_inside_tree()) { return; } @@ -363,6 +379,7 @@ void CanvasItem::_window_visibility_changed() { } void CanvasItem::queue_redraw() { + ERR_THREAD_GUARD; // Calling from thread is safe. if (!is_inside_tree()) { return; } @@ -376,6 +393,7 @@ void CanvasItem::queue_redraw() { } void CanvasItem::move_to_front() { + ERR_MAIN_THREAD_GUARD; if (!get_parent()) { return; } @@ -383,6 +401,7 @@ void CanvasItem::move_to_front() { } void CanvasItem::set_modulate(const Color &p_modulate) { + ERR_THREAD_GUARD; if (modulate == p_modulate) { return; } @@ -392,10 +411,12 @@ void CanvasItem::set_modulate(const Color &p_modulate) { } Color CanvasItem::get_modulate() const { + ERR_READ_THREAD_GUARD_V(Color()); return modulate; } Color CanvasItem::get_modulate_in_tree() const { + ERR_READ_THREAD_GUARD_V(Color()); Color final_modulate = modulate; CanvasItem *parent_item = get_parent_item(); while (parent_item) { @@ -406,6 +427,7 @@ Color CanvasItem::get_modulate_in_tree() const { } void CanvasItem::set_as_top_level(bool p_top_level) { + ERR_MAIN_THREAD_GUARD; if (top_level == p_top_level) { return; } @@ -445,6 +467,7 @@ bool CanvasItem::is_set_as_top_level() const { } CanvasItem *CanvasItem::get_parent_item() const { + ERR_READ_THREAD_GUARD_V(nullptr); if (top_level) { return nullptr; } @@ -453,6 +476,7 @@ CanvasItem *CanvasItem::get_parent_item() const { } void CanvasItem::set_self_modulate(const Color &p_self_modulate) { + ERR_THREAD_GUARD; if (self_modulate == p_self_modulate) { return; } @@ -462,10 +486,12 @@ void CanvasItem::set_self_modulate(const Color &p_self_modulate) { } Color CanvasItem::get_self_modulate() const { + ERR_READ_THREAD_GUARD_V(Color()); return self_modulate; } void CanvasItem::set_light_mask(int p_light_mask) { + ERR_THREAD_GUARD; if (light_mask == p_light_mask) { return; } @@ -475,10 +501,12 @@ void CanvasItem::set_light_mask(int p_light_mask) { } int CanvasItem::get_light_mask() const { + ERR_READ_THREAD_GUARD_V(0); return light_mask; } void CanvasItem::item_rect_changed(bool p_size_changed) { + ERR_MAIN_THREAD_GUARD; if (p_size_changed) { queue_redraw(); } @@ -486,6 +514,7 @@ void CanvasItem::item_rect_changed(bool p_size_changed) { } void CanvasItem::set_z_index(int p_z) { + ERR_THREAD_GUARD; ERR_FAIL_COND(p_z < RS::CANVAS_ITEM_Z_MIN); ERR_FAIL_COND(p_z > RS::CANVAS_ITEM_Z_MAX); z_index = p_z; @@ -494,6 +523,7 @@ void CanvasItem::set_z_index(int p_z) { } void CanvasItem::set_z_as_relative(bool p_enabled) { + ERR_THREAD_GUARD; if (z_relative == p_enabled) { return; } @@ -502,14 +532,17 @@ void CanvasItem::set_z_as_relative(bool p_enabled) { } bool CanvasItem::is_z_relative() const { + ERR_READ_THREAD_GUARD_V(false); return z_relative; } int CanvasItem::get_z_index() const { + ERR_READ_THREAD_GUARD_V(0); return z_index; } int CanvasItem::get_effective_z_index() const { + ERR_READ_THREAD_GUARD_V(0); int effective_z_index = z_index; if (is_z_relative()) { CanvasItem *p = get_parent_item(); @@ -521,15 +554,18 @@ int CanvasItem::get_effective_z_index() const { } void CanvasItem::set_y_sort_enabled(bool p_enabled) { + ERR_THREAD_GUARD; y_sort_enabled = p_enabled; RS::get_singleton()->canvas_item_set_sort_children_by_y(canvas_item, y_sort_enabled); } bool CanvasItem::is_y_sort_enabled() const { + ERR_READ_THREAD_GUARD_V(false); return y_sort_enabled; } void CanvasItem::draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width, real_t p_dash, bool p_aligned) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_dash <= 0.0); @@ -565,12 +601,14 @@ void CanvasItem::draw_dashed_line(const Point2 &p_from, const Point2 &p_to, cons } void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width, bool p_antialiased) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); RenderingServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width, p_antialiased); } void CanvasItem::draw_polyline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width, bool p_antialiased) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); Vector<Color> colors = { p_color }; @@ -578,12 +616,14 @@ void CanvasItem::draw_polyline(const Vector<Point2> &p_points, const Color &p_co } void CanvasItem::draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width, bool p_antialiased) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); RenderingServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, p_colors, p_width, p_antialiased); } void CanvasItem::draw_arc(const Vector2 &p_center, real_t p_radius, real_t p_start_angle, real_t p_end_angle, int p_point_count, const Color &p_color, real_t p_width, bool p_antialiased) { + ERR_THREAD_GUARD; Vector<Point2> points; points.resize(p_point_count); Point2 *points_ptr = points.ptrw(); @@ -599,6 +639,7 @@ void CanvasItem::draw_arc(const Vector2 &p_center, real_t p_radius, real_t p_sta } void CanvasItem::draw_multiline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); Vector<Color> colors = { p_color }; @@ -606,12 +647,14 @@ void CanvasItem::draw_multiline(const Vector<Point2> &p_points, const Color &p_c } void CanvasItem::draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); RenderingServer::get_singleton()->canvas_item_add_multiline(canvas_item, p_points, p_colors, p_width); } void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled, real_t p_width) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); Rect2 rect = p_rect.abs(); @@ -640,12 +683,14 @@ void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_fil } void CanvasItem::draw_circle(const Point2 &p_pos, real_t p_radius, const Color &p_color) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); RenderingServer::get_singleton()->canvas_item_add_circle(canvas_item, p_pos, p_radius, p_color); } void CanvasItem::draw_texture(const Ref<Texture2D> &p_texture, const Point2 &p_pos, const Color &p_modulate) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_texture.is_null()); @@ -654,6 +699,7 @@ void CanvasItem::draw_texture(const Ref<Texture2D> &p_texture, const Point2 &p_p } void CanvasItem::draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_texture.is_null()); @@ -661,24 +707,28 @@ void CanvasItem::draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 } void CanvasItem::draw_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_texture.is_null()); p_texture->draw_rect_region(canvas_item, p_rect, p_src_rect, p_modulate, p_transpose, p_clip_uv); } void CanvasItem::draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, double p_outline, double p_pixel_range, double p_scale) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_texture.is_null()); RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(canvas_item, p_rect, p_texture->get_rid(), p_src_rect, p_modulate, p_outline, p_pixel_range, p_scale); } void CanvasItem::draw_lcd_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_texture.is_null()); RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(canvas_item, p_rect, p_texture->get_rid(), p_src_rect, p_modulate); } void CanvasItem::draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_style_box.is_null()); @@ -687,6 +737,7 @@ void CanvasItem::draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p } void CanvasItem::draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); @@ -694,6 +745,7 @@ void CanvasItem::draw_primitive(const Vector<Point2> &p_points, const Vector<Col } void CanvasItem::draw_set_transform(const Point2 &p_offset, real_t p_rot, const Size2 &p_scale) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); Transform2D xform(p_rot, p_offset); @@ -702,23 +754,27 @@ void CanvasItem::draw_set_transform(const Point2 &p_offset, real_t p_rot, const } void CanvasItem::draw_set_transform_matrix(const Transform2D &p_matrix) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); RenderingServer::get_singleton()->canvas_item_add_set_transform(canvas_item, p_matrix); } void CanvasItem::draw_animation_slice(double p_animation_length, double p_slice_begin, double p_slice_end, double p_offset) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); RenderingServer::get_singleton()->canvas_item_add_animation_slice(canvas_item, p_animation_length, p_slice_begin, p_slice_end, p_offset); } void CanvasItem::draw_end_animation() { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); RenderingServer::get_singleton()->canvas_item_add_animation_slice(canvas_item, 1, 0, 2, 0); } void CanvasItem::draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); @@ -727,6 +783,7 @@ void CanvasItem::draw_polygon(const Vector<Point2> &p_points, const Vector<Color } void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture) { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); Vector<Color> colors = { p_color }; @@ -735,6 +792,7 @@ void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Colo } void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Transform2D &p_transform, const Color &p_modulate) { + ERR_THREAD_GUARD; ERR_FAIL_COND(p_mesh.is_null()); RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); @@ -742,12 +800,14 @@ void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_text } void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture) { + ERR_THREAD_GUARD; ERR_FAIL_COND(p_multimesh.is_null()); RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); RenderingServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid); } void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_font.is_null()); @@ -755,6 +815,7 @@ void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const } void CanvasItem::draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_font.is_null()); @@ -762,6 +823,7 @@ void CanvasItem::draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_ } void CanvasItem::draw_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_font.is_null()); @@ -769,6 +831,7 @@ void CanvasItem::draw_string_outline(const Ref<Font> &p_font, const Point2 &p_po } void CanvasItem::draw_multiline_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, int p_size, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_font.is_null()); @@ -776,6 +839,7 @@ void CanvasItem::draw_multiline_string_outline(const Ref<Font> &p_font, const Po } void CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size, const Color &p_modulate) const { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_char.length() != 1); ERR_FAIL_COND(p_font.is_null()); @@ -784,6 +848,7 @@ void CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const S } void CanvasItem::draw_char_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size, int p_size, const Color &p_modulate) const { + ERR_THREAD_GUARD; ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_char.length() != 1); ERR_FAIL_COND(p_font.is_null()); @@ -791,6 +856,12 @@ void CanvasItem::draw_char_outline(const Ref<Font> &p_font, const Point2 &p_pos, p_font->draw_char_outline(canvas_item, p_pos, p_char[0], p_font_size, p_size, p_modulate); } +void CanvasItem::_notify_transform_deferred() { + if (is_inside_tree() && notify_transform && !xform_change.in_list()) { + get_tree()->xform_change_list.add(&xform_change); + } +} + 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 @@ -798,16 +869,21 @@ void CanvasItem::_notify_transform(CanvasItem *p_node) { * notification anyway). */ - if (/*p_node->xform_change.in_list() &&*/ p_node->global_invalid) { + if (/*p_node->xform_change.in_list() &&*/ p_node->global_invalid.is_set()) { return; //nothing to do } - p_node->global_invalid = true; + p_node->global_invalid.set(); if (p_node->notify_transform && !p_node->xform_change.in_list()) { if (!p_node->block_transform_notify) { if (p_node->is_inside_tree()) { - get_tree()->xform_change_list.add(&p_node->xform_change); + if (is_accessible_from_caller_thread()) { + get_tree()->xform_change_list.add(&p_node->xform_change); + } else { + // Should be rare, but still needs to be handled. + MessageQueue::get_singleton()->push_callable(callable_mp(p_node, &CanvasItem::_notify_transform_deferred)); + } } } } @@ -821,11 +897,13 @@ void CanvasItem::_notify_transform(CanvasItem *p_node) { } Rect2 CanvasItem::get_viewport_rect() const { + ERR_READ_THREAD_GUARD_V(Rect2()); ERR_FAIL_COND_V(!is_inside_tree(), Rect2()); return get_viewport()->get_visible_rect(); } RID CanvasItem::get_canvas() const { + ERR_READ_THREAD_GUARD_V(RID()); ERR_FAIL_COND_V(!is_inside_tree(), RID()); if (canvas_layer) { @@ -836,6 +914,7 @@ RID CanvasItem::get_canvas() const { } ObjectID CanvasItem::get_canvas_layer_instance_id() const { + ERR_READ_THREAD_GUARD_V(ObjectID()); if (canvas_layer) { return canvas_layer->get_instance_id(); } else { @@ -844,6 +923,7 @@ ObjectID CanvasItem::get_canvas_layer_instance_id() const { } CanvasItem *CanvasItem::get_top_level() const { + ERR_READ_THREAD_GUARD_V(nullptr); CanvasItem *ci = const_cast<CanvasItem *>(this); while (!ci->top_level && Object::cast_to<CanvasItem>(ci->get_parent())) { ci = Object::cast_to<CanvasItem>(ci->get_parent()); @@ -853,6 +933,7 @@ CanvasItem *CanvasItem::get_top_level() const { } Ref<World2D> CanvasItem::get_world_2d() const { + ERR_READ_THREAD_GUARD_V(Ref<World2D>()); ERR_FAIL_COND_V(!is_inside_tree(), Ref<World2D>()); CanvasItem *tl = get_top_level(); @@ -865,19 +946,23 @@ Ref<World2D> CanvasItem::get_world_2d() const { } RID CanvasItem::get_viewport_rid() const { + ERR_READ_THREAD_GUARD_V(RID()); ERR_FAIL_COND_V(!is_inside_tree(), RID()); return get_viewport()->get_viewport_rid(); } void CanvasItem::set_block_transform_notify(bool p_enable) { + ERR_THREAD_GUARD; block_transform_notify = p_enable; } bool CanvasItem::is_block_transform_notify_enabled() const { + ERR_READ_THREAD_GUARD_V(false); return block_transform_notify; } void CanvasItem::set_draw_behind_parent(bool p_enable) { + ERR_THREAD_GUARD; if (behind == p_enable) { return; } @@ -886,10 +971,12 @@ void CanvasItem::set_draw_behind_parent(bool p_enable) { } bool CanvasItem::is_draw_behind_parent_enabled() const { + ERR_READ_THREAD_GUARD_V(false); return behind; } void CanvasItem::set_material(const Ref<Material> &p_material) { + ERR_THREAD_GUARD; material = p_material; RID rid; if (material.is_valid()) { @@ -900,19 +987,23 @@ void CanvasItem::set_material(const Ref<Material> &p_material) { } void CanvasItem::set_use_parent_material(bool p_use_parent_material) { + ERR_THREAD_GUARD; use_parent_material = p_use_parent_material; RS::get_singleton()->canvas_item_set_use_parent_material(canvas_item, p_use_parent_material); } bool CanvasItem::get_use_parent_material() const { + ERR_READ_THREAD_GUARD_V(false); return use_parent_material; } Ref<Material> CanvasItem::get_material() const { + ERR_READ_THREAD_GUARD_V(Ref<Material>()); return material; } Vector2 CanvasItem::make_canvas_position_local(const Vector2 &screen_point) const { + ERR_READ_THREAD_GUARD_V(Vector2()); ERR_FAIL_COND_V(!is_inside_tree(), screen_point); Transform2D local_matrix = (get_canvas_transform() * get_global_transform()).affine_inverse(); @@ -921,6 +1012,7 @@ Vector2 CanvasItem::make_canvas_position_local(const Vector2 &screen_point) cons } Ref<InputEvent> CanvasItem::make_input_local(const Ref<InputEvent> &p_event) const { + ERR_READ_THREAD_GUARD_V(Ref<InputEvent>()); ERR_FAIL_COND_V(p_event.is_null(), p_event); ERR_FAIL_COND_V(!is_inside_tree(), p_event); @@ -928,17 +1020,20 @@ Ref<InputEvent> CanvasItem::make_input_local(const Ref<InputEvent> &p_event) con } Vector2 CanvasItem::get_global_mouse_position() const { + ERR_READ_THREAD_GUARD_V(Vector2()); ERR_FAIL_COND_V(!get_viewport(), Vector2()); return get_canvas_transform().affine_inverse().xform(get_viewport()->get_mouse_position()); } Vector2 CanvasItem::get_local_mouse_position() const { + ERR_READ_THREAD_GUARD_V(Vector2()); ERR_FAIL_COND_V(!get_viewport(), Vector2()); return get_global_transform().affine_inverse().xform(get_global_mouse_position()); } void CanvasItem::force_update_transform() { + ERR_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); if (!xform_change.in_list()) { return; @@ -1031,10 +1126,10 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture"), &CanvasItem::draw_primitive, DEFVAL(Ref<Texture2D>())); ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture"), &CanvasItem::draw_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>())); ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture"), &CanvasItem::draw_colored_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>())); - ClassDB::bind_method(D_METHOD("draw_string", "font", "pos", "text", "alignment", "width", "font_size", "modulate", "jst_flags", "direction", "orientation"), &CanvasItem::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("draw_multiline_string", "font", "pos", "text", "alignment", "width", "font_size", "max_lines", "modulate", "brk_flags", "jst_flags", "direction", "orientation"), &CanvasItem::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("draw_string_outline", "font", "pos", "text", "alignment", "width", "font_size", "size", "modulate", "jst_flags", "direction", "orientation"), &CanvasItem::draw_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("draw_multiline_string_outline", "font", "pos", "text", "alignment", "width", "font_size", "max_lines", "size", "modulate", "brk_flags", "jst_flags", "direction", "orientation"), &CanvasItem::draw_multiline_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_string", "font", "pos", "text", "alignment", "width", "font_size", "modulate", "justification_flags", "direction", "orientation"), &CanvasItem::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string", "font", "pos", "text", "alignment", "width", "font_size", "max_lines", "modulate", "brk_flags", "justification_flags", "direction", "orientation"), &CanvasItem::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_string_outline", "font", "pos", "text", "alignment", "width", "font_size", "size", "modulate", "justification_flags", "direction", "orientation"), &CanvasItem::draw_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string_outline", "font", "pos", "text", "alignment", "width", "font_size", "max_lines", "size", "modulate", "brk_flags", "justification_flags", "direction", "orientation"), &CanvasItem::draw_multiline_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); ClassDB::bind_method(D_METHOD("draw_char", "font", "pos", "char", "font_size", "modulate"), &CanvasItem::draw_char, DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0))); ClassDB::bind_method(D_METHOD("draw_char_outline", "font", "pos", "char", "font_size", "size", "modulate"), &CanvasItem::draw_char_outline, DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0))); ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "transform", "modulate"), &CanvasItem::draw_mesh, DEFVAL(Transform2D()), DEFVAL(Color(1, 1, 1, 1))); @@ -1148,6 +1243,7 @@ void CanvasItem::_bind_methods() { } Transform2D CanvasItem::get_canvas_transform() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); ERR_FAIL_COND_V(!is_inside_tree(), Transform2D()); if (canvas_layer) { @@ -1160,6 +1256,7 @@ Transform2D CanvasItem::get_canvas_transform() const { } Transform2D CanvasItem::get_viewport_transform() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); ERR_FAIL_COND_V(!is_inside_tree(), Transform2D()); if (canvas_layer) { @@ -1170,14 +1267,17 @@ Transform2D CanvasItem::get_viewport_transform() const { } void CanvasItem::set_notify_local_transform(bool p_enable) { + ERR_THREAD_GUARD; notify_local_transform = p_enable; } bool CanvasItem::is_local_transform_notification_enabled() const { + ERR_READ_THREAD_GUARD_V(false); return notify_local_transform; } void CanvasItem::set_notify_transform(bool p_enable) { + ERR_THREAD_GUARD; if (notify_transform == p_enable) { return; } @@ -1191,10 +1291,12 @@ void CanvasItem::set_notify_transform(bool p_enable) { } bool CanvasItem::is_transform_notification_enabled() const { + ERR_READ_THREAD_GUARD_V(false); return notify_transform; } int CanvasItem::get_canvas_layer() const { + ERR_READ_THREAD_GUARD_V(0); if (canvas_layer) { return canvas_layer->get_layer(); } else { @@ -1203,15 +1305,18 @@ int CanvasItem::get_canvas_layer() const { } void CanvasItem::set_visibility_layer(uint32_t p_visibility_layer) { + ERR_THREAD_GUARD; visibility_layer = p_visibility_layer; RenderingServer::get_singleton()->canvas_item_set_visibility_layer(canvas_item, p_visibility_layer); } uint32_t CanvasItem::get_visibility_layer() const { + ERR_READ_THREAD_GUARD_V(0); return visibility_layer; } void CanvasItem::set_visibility_layer_bit(uint32_t p_visibility_layer, bool p_enable) { + ERR_THREAD_GUARD; ERR_FAIL_UNSIGNED_INDEX(p_visibility_layer, 32); if (p_enable) { set_visibility_layer(visibility_layer | (1 << p_visibility_layer)); @@ -1221,6 +1326,7 @@ void CanvasItem::set_visibility_layer_bit(uint32_t p_visibility_layer, bool p_en } bool CanvasItem::get_visibility_layer_bit(uint32_t p_visibility_layer) const { + ERR_READ_THREAD_GUARD_V(0); ERR_FAIL_UNSIGNED_INDEX_V(p_visibility_layer, 32, false); return (visibility_layer & (1 << p_visibility_layer)); } @@ -1261,6 +1367,7 @@ void CanvasItem::_update_texture_filter_changed(bool p_propagate) { } void CanvasItem::set_texture_filter(TextureFilter p_texture_filter) { + ERR_MAIN_THREAD_GUARD; // Goes down in the tree, so only main thread can set. ERR_FAIL_INDEX(p_texture_filter, TEXTURE_FILTER_MAX); if (texture_filter == p_texture_filter) { return; @@ -1271,6 +1378,7 @@ void CanvasItem::set_texture_filter(TextureFilter p_texture_filter) { } CanvasItem::TextureFilter CanvasItem::get_texture_filter() const { + ERR_READ_THREAD_GUARD_V(TEXTURE_FILTER_NEAREST); return texture_filter; } @@ -1309,6 +1417,7 @@ void CanvasItem::_update_texture_repeat_changed(bool p_propagate) { } void CanvasItem::set_texture_repeat(TextureRepeat p_texture_repeat) { + ERR_MAIN_THREAD_GUARD; // Goes down in the tree, so only main thread can set. ERR_FAIL_INDEX(p_texture_repeat, TEXTURE_REPEAT_MAX); if (texture_repeat == p_texture_repeat) { return; @@ -1319,6 +1428,7 @@ void CanvasItem::set_texture_repeat(TextureRepeat p_texture_repeat) { } void CanvasItem::set_clip_children_mode(ClipChildrenMode p_clip_mode) { + ERR_THREAD_GUARD; ERR_FAIL_COND(p_clip_mode >= CLIP_CHILDREN_MAX); if (clip_children_mode == p_clip_mode) { @@ -1334,19 +1444,23 @@ void CanvasItem::set_clip_children_mode(ClipChildrenMode p_clip_mode) { RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), RS::CanvasGroupMode(clip_children_mode)); } CanvasItem::ClipChildrenMode CanvasItem::get_clip_children_mode() const { + ERR_READ_THREAD_GUARD_V(CLIP_CHILDREN_DISABLED); return clip_children_mode; } CanvasItem::TextureRepeat CanvasItem::get_texture_repeat() const { + ERR_READ_THREAD_GUARD_V(TEXTURE_REPEAT_DISABLED); return texture_repeat; } CanvasItem::TextureFilter CanvasItem::get_texture_filter_in_tree() { + ERR_READ_THREAD_GUARD_V(TEXTURE_FILTER_NEAREST); _refresh_texture_filter_cache(); return (TextureFilter)texture_filter_cache; } CanvasItem::TextureRepeat CanvasItem::get_texture_repeat_in_tree() { + ERR_READ_THREAD_GUARD_V(TEXTURE_REPEAT_DISABLED); _refresh_texture_repeat_cache(); return (TextureRepeat)texture_repeat_cache; } diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index d6ca98d4de..55267abab8 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -118,7 +118,7 @@ private: Ref<Material> material; mutable Transform2D global_transform; - mutable bool global_invalid = true; + mutable SafeFlag global_invalid; void _top_level_raise_self(); @@ -145,6 +145,8 @@ private: void _refresh_texture_filter_cache(); void _update_texture_filter_changed(bool p_propagate); + void _notify_transform_deferred(); + protected: _FORCE_INLINE_ void _notify_transform() { if (!is_inside_tree()) { diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 5e4c6c8483..ad7e445b5c 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -3523,3 +3523,93 @@ Node::~Node() { } //////////////////////////////// +// Multithreaded locked version of Object functions. + +#ifdef DEBUG_ENABLED + +void Node::set_script(const Variant &p_script) { + ERR_THREAD_GUARD; + Object::set_script(p_script); +} + +Variant Node::get_script() const { + ERR_THREAD_GUARD_V(Variant()); + return Object::get_script(); +} + +bool Node::has_meta(const StringName &p_name) const { + ERR_THREAD_GUARD_V(false); + return Object::has_meta(p_name); +} + +void Node::set_meta(const StringName &p_name, const Variant &p_value) { + ERR_THREAD_GUARD; + Object::set_meta(p_name, p_value); +} + +void Node::remove_meta(const StringName &p_name) { + ERR_THREAD_GUARD; + Object::remove_meta(p_name); +} + +Variant Node::get_meta(const StringName &p_name, const Variant &p_default) const { + ERR_THREAD_GUARD_V(Variant()); + return Object::get_meta(p_name, p_default); +} + +void Node::get_meta_list(List<StringName> *p_list) const { + ERR_THREAD_GUARD; + Object::get_meta_list(p_list); +} + +Error Node::emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount) { + ERR_THREAD_GUARD_V(ERR_INVALID_PARAMETER); + return Object::emit_signalp(p_name, p_args, p_argcount); +} + +bool Node::has_signal(const StringName &p_name) const { + ERR_THREAD_GUARD_V(false); + return Object::has_signal(p_name); +} + +void Node::get_signal_list(List<MethodInfo> *p_signals) const { + ERR_THREAD_GUARD; + Object::get_signal_list(p_signals); +} + +void Node::get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const { + ERR_THREAD_GUARD; + Object::get_signal_connection_list(p_signal, p_connections); +} + +void Node::get_all_signal_connections(List<Connection> *p_connections) const { + ERR_THREAD_GUARD; + Object::get_all_signal_connections(p_connections); +} + +int Node::get_persistent_signal_connection_count() const { + ERR_THREAD_GUARD_V(0); + return Object::get_persistent_signal_connection_count(); +} + +void Node::get_signals_connected_to_this(List<Connection> *p_connections) const { + ERR_THREAD_GUARD; + Object::get_signals_connected_to_this(p_connections); +} + +Error Node::connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags) { + ERR_THREAD_GUARD_V(ERR_INVALID_PARAMETER); + return Object::connect(p_signal, p_callable, p_flags); +} + +void Node::disconnect(const StringName &p_signal, const Callable &p_callable) { + ERR_THREAD_GUARD; + Object::disconnect(p_signal, p_callable); +} + +bool Node::is_connected(const StringName &p_signal, const Callable &p_callable) const { + ERR_THREAD_GUARD_V(false); + return Object::is_connected(p_signal, p_callable); +} + +#endif diff --git a/scene/main/node.h b/scene/main/node.h index 0a3fbf5d3b..b7462b4468 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -530,6 +530,14 @@ public: } } + _FORCE_INLINE_ bool is_readable_from_caller_thread() const { + if (current_process_thread_group == nullptr) { + return Thread::is_main_thread(); + } else { + return true; + } + } + void set_process_thread_messages(BitField<ProcessThreadMessages> p_flags); BitField<ProcessThreadMessages> get_process_thread_messages() const; @@ -648,6 +656,30 @@ public: void set_thread_safe(const StringName &p_property, const Variant &p_value); void notify_thread_safe(int p_notification); + // These inherited functions need proper multithread locking when overridden in Node. +#ifdef DEBUG_ENABLED + + virtual void set_script(const Variant &p_script) override; + virtual Variant get_script() const override; + + virtual bool has_meta(const StringName &p_name) const override; + virtual void set_meta(const StringName &p_name, const Variant &p_value) override; + virtual void remove_meta(const StringName &p_name) override; + virtual Variant get_meta(const StringName &p_name, const Variant &p_default = Variant()) const override; + virtual void get_meta_list(List<StringName> *p_list) const override; + + virtual Error emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount) override; + virtual bool has_signal(const StringName &p_name) const override; + virtual void get_signal_list(List<MethodInfo> *p_signals) const override; + virtual void get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const override; + virtual void get_all_signal_connections(List<Connection> *p_connections) const override; + virtual int get_persistent_signal_connection_count() const override; + virtual void get_signals_connected_to_this(List<Connection> *p_connections) const override; + + virtual Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0) override; + virtual void disconnect(const StringName &p_signal, const Callable &p_callable) override; + virtual bool is_connected(const StringName &p_signal, const Callable &p_callable) const override; +#endif Node(); ~Node(); }; @@ -679,11 +711,15 @@ Error Node::rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) #define ERR_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(!is_accessible_from_caller_thread(), (m_ret), "Caller thread can't call this function in this node. Use call_deferred() or call_thread_group() instead.") #define ERR_MAIN_THREAD_GUARD ERR_FAIL_COND_MSG(is_inside_tree() && !Thread::is_main_thread(), "This function in this node can only be accessed from the main thread. Use call_deferred() instead."); #define ERR_MAIN_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(is_inside_tree() && !Thread::is_main_thread(), (m_ret), "This function in this node can only be accessed from the main thread. Use call_deferred() instead.") +#define ERR_READ_THREAD_GUARD ERR_FAIL_COND_MSG(!is_readable_from_caller_thread(), "This function in this node can only be accessed from either the main thread or a thread group. Use call_deferred() instead.") +#define ERR_READ_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(!is_readable_from_caller_thread(), (m_ret), "This function in this node can only be accessed from either the main thread or a thread group. Use call_deferred() instead.") #else #define ERR_THREAD_GUARD #define ERR_THREAD_GUARD_V(m_ret) #define ERR_MAIN_THREAD_GUARD #define ERR_MAIN_THREAD_GUARD_V(m_ret) +#define ERR_READ_THREAD_GUARD +#define ERR_READ_THREAD_GUARD_V(m_ret) #endif // Add these macro to your class's 'get_configuration_warnings' function to have warnings show up in the scene tree inspector. diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index e6043531ac..a303cc8820 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -468,6 +468,8 @@ void Viewport::_update_viewport_path() { } void Viewport::_notification(int p_what) { + ERR_MAIN_THREAD_GUARD; + switch (p_what) { case NOTIFICATION_ENTER_TREE: { _update_viewport_path(); @@ -922,10 +924,12 @@ void Viewport::_process_picking() { } RID Viewport::get_viewport_rid() const { + ERR_READ_THREAD_GUARD_V(RID()); return viewport; } void Viewport::update_canvas_items() { + ERR_MAIN_THREAD_GUARD; if (!is_inside_tree()) { return; } @@ -1013,6 +1017,7 @@ bool Viewport::_is_size_allocated() const { } Rect2 Viewport::get_visible_rect() const { + ERR_READ_THREAD_GUARD_V(Rect2()); Rect2 r; if (size == Size2()) { @@ -1029,6 +1034,7 @@ Rect2 Viewport::get_visible_rect() const { } void Viewport::canvas_parent_mark_dirty(Node *p_node) { + ERR_MAIN_THREAD_GUARD; bool request_update = gui.canvas_parents_with_dirty_order.is_empty(); gui.canvas_parents_with_dirty_order.insert(p_node->get_instance_id()); if (request_update) { @@ -1043,6 +1049,7 @@ void Viewport::_update_audio_listener_2d() { } void Viewport::set_as_audio_listener_2d(bool p_enable) { + ERR_MAIN_THREAD_GUARD; if (p_enable == is_audio_listener_2d_enabled) { return; } @@ -1052,14 +1059,17 @@ void Viewport::set_as_audio_listener_2d(bool p_enable) { } bool Viewport::is_audio_listener_2d() const { + ERR_READ_THREAD_GUARD_V(false); return is_audio_listener_2d_enabled; } AudioListener2D *Viewport::get_audio_listener_2d() const { + ERR_READ_THREAD_GUARD_V(nullptr); return audio_listener_2d; } void Viewport::enable_canvas_transform_override(bool p_enable) { + ERR_MAIN_THREAD_GUARD; if (override_canvas_transform == p_enable) { return; } @@ -1073,10 +1083,12 @@ void Viewport::enable_canvas_transform_override(bool p_enable) { } bool Viewport::is_canvas_transform_override_enbled() const { + ERR_READ_THREAD_GUARD_V(false); return override_canvas_transform; } void Viewport::set_canvas_transform_override(const Transform2D &p_transform) { + ERR_MAIN_THREAD_GUARD; if (canvas_transform_override == p_transform) { return; } @@ -1088,10 +1100,12 @@ void Viewport::set_canvas_transform_override(const Transform2D &p_transform) { } Transform2D Viewport::get_canvas_transform_override() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); return canvas_transform_override; } void Viewport::set_canvas_transform(const Transform2D &p_transform) { + ERR_MAIN_THREAD_GUARD; canvas_transform = p_transform; if (!override_canvas_transform) { @@ -1100,6 +1114,7 @@ void Viewport::set_canvas_transform(const Transform2D &p_transform) { } Transform2D Viewport::get_canvas_transform() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); return canvas_transform; } @@ -1110,12 +1125,14 @@ void Viewport::_update_global_transform() { } void Viewport::set_global_canvas_transform(const Transform2D &p_transform) { + ERR_MAIN_THREAD_GUARD; global_canvas_transform = p_transform; _update_global_transform(); } Transform2D Viewport::get_global_canvas_transform() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); return global_canvas_transform; } @@ -1147,15 +1164,18 @@ void Viewport::_canvas_layer_remove(CanvasLayer *p_canvas_layer) { } void Viewport::set_transparent_background(bool p_enable) { + ERR_MAIN_THREAD_GUARD; transparent_bg = p_enable; RS::get_singleton()->viewport_set_transparent_background(viewport, p_enable); } bool Viewport::has_transparent_background() const { + ERR_READ_THREAD_GUARD_V(false); return transparent_bg; } void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) { + ERR_MAIN_THREAD_GUARD; if (world_2d == p_world_2d) { return; } @@ -1184,6 +1204,7 @@ void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) { } Ref<World2D> Viewport::find_world_2d() const { + ERR_READ_THREAD_GUARD_V(Ref<World2D>()); if (world_2d.is_valid()) { return world_2d; } else if (parent) { @@ -1205,18 +1226,22 @@ void Viewport::_propagate_viewport_notification(Node *p_node, int p_what) { } Ref<World2D> Viewport::get_world_2d() const { + ERR_READ_THREAD_GUARD_V(Ref<World2D>()); return world_2d; } Camera2D *Viewport::get_camera_2d() const { + ERR_READ_THREAD_GUARD_V(nullptr); return camera_2d; } Transform2D Viewport::get_final_transform() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); return stretch_transform * global_canvas_transform; } void Viewport::assign_next_enabled_camera_2d(const StringName &p_camera_group) { + ERR_MAIN_THREAD_GUARD; List<Node *> camera_list; get_tree()->get_nodes_in_group(p_camera_group, &camera_list); @@ -1260,19 +1285,23 @@ void Viewport::_update_canvas_items(Node *p_node) { } Ref<ViewportTexture> Viewport::get_texture() const { + ERR_READ_THREAD_GUARD_V(Ref<ViewportTexture>()); return default_texture; } void Viewport::set_positional_shadow_atlas_size(int p_size) { + ERR_MAIN_THREAD_GUARD; positional_shadow_atlas_size = p_size; RS::get_singleton()->viewport_set_positional_shadow_atlas_size(viewport, p_size, positional_shadow_atlas_16_bits); } int Viewport::get_positional_shadow_atlas_size() const { + ERR_READ_THREAD_GUARD_V(0); return positional_shadow_atlas_size; } void Viewport::set_positional_shadow_atlas_16_bits(bool p_16_bits) { + ERR_MAIN_THREAD_GUARD; if (positional_shadow_atlas_16_bits == p_16_bits) { return; } @@ -1282,9 +1311,11 @@ void Viewport::set_positional_shadow_atlas_16_bits(bool p_16_bits) { } bool Viewport::get_positional_shadow_atlas_16_bits() const { + ERR_READ_THREAD_GUARD_V(false); return positional_shadow_atlas_16_bits; } void Viewport::set_positional_shadow_atlas_quadrant_subdiv(int p_quadrant, PositionalShadowAtlasQuadrantSubdiv p_subdiv) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(p_quadrant, 4); ERR_FAIL_INDEX(p_subdiv, SHADOW_ATLAS_QUADRANT_SUBDIV_MAX); @@ -1299,6 +1330,7 @@ void Viewport::set_positional_shadow_atlas_quadrant_subdiv(int p_quadrant, Posit } Viewport::PositionalShadowAtlasQuadrantSubdiv Viewport::get_positional_shadow_atlas_quadrant_subdiv(int p_quadrant) const { + ERR_READ_THREAD_GUARD_V(SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED); ERR_FAIL_INDEX_V(p_quadrant, 4, SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED); return positional_shadow_atlas_quadrant_subdiv[p_quadrant]; } @@ -1313,6 +1345,7 @@ Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) { } Vector2 Viewport::get_mouse_position() const { + ERR_READ_THREAD_GUARD_V(Vector2()); if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_MOUSE)) { return get_screen_transform_internal(true).affine_inverse().xform(DisplayServer::get_singleton()->mouse_get_position()); } else { @@ -1322,6 +1355,7 @@ Vector2 Viewport::get_mouse_position() const { } void Viewport::warp_mouse(const Vector2 &p_position) { + ERR_MAIN_THREAD_GUARD; Transform2D xform = get_screen_transform_internal(); Vector2 gpos = xform.xform(p_position); Input::get_singleton()->warp_mouse(gpos); @@ -1565,6 +1599,7 @@ void Viewport::_gui_call_notification(Control *p_control, int p_what) { } Control *Viewport::gui_find_control(const Point2 &p_global) { + ERR_MAIN_THREAD_GUARD_V(nullptr); // Handle subwindows. _gui_sort_roots(); @@ -2274,6 +2309,7 @@ List<Control *>::Element *Viewport::_gui_add_root_control(Control *p_control) { } void Viewport::gui_set_root_order_dirty() { + ERR_MAIN_THREAD_GUARD; gui.roots_order_dirty = true; } @@ -2374,6 +2410,7 @@ void Viewport::_gui_remove_control(Control *p_control) { } Window *Viewport::get_base_window() const { + ERR_READ_THREAD_GUARD_V(nullptr); ERR_FAIL_COND_V(!is_inside_tree(), nullptr); Viewport *v = const_cast<Viewport *>(this); @@ -2576,6 +2613,7 @@ void Viewport::_post_gui_grab_click_focus() { /////////////////////////////// void Viewport::push_text_input(const String &p_text) { + ERR_MAIN_THREAD_GUARD; if (gui.subwindow_focused) { gui.subwindow_focused->push_text_input(p_text); return; @@ -2903,6 +2941,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { } void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); if (disable_input) { @@ -2955,6 +2994,7 @@ void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) { } void Viewport::push_unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coords) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(p_event.is_null()); ERR_FAIL_COND(!is_inside_tree()); local_input_handled = false; @@ -3004,6 +3044,7 @@ void Viewport::push_unhandled_input(const Ref<InputEvent> &p_event, bool p_local } void Viewport::set_physics_object_picking(bool p_enable) { + ERR_MAIN_THREAD_GUARD; physics_object_picking = p_enable; if (physics_object_picking) { add_to_group("_picking_viewports"); @@ -3016,27 +3057,33 @@ void Viewport::set_physics_object_picking(bool p_enable) { } bool Viewport::get_physics_object_picking() { + ERR_READ_THREAD_GUARD_V(false); return physics_object_picking; } void Viewport::set_physics_object_picking_sort(bool p_enable) { + ERR_MAIN_THREAD_GUARD; physics_object_picking_sort = p_enable; } bool Viewport::get_physics_object_picking_sort() { + ERR_READ_THREAD_GUARD_V(false); return physics_object_picking_sort; } Vector2 Viewport::get_camera_coords(const Vector2 &p_viewport_coords) const { + ERR_READ_THREAD_GUARD_V(Vector2()); Transform2D xf = stretch_transform * global_canvas_transform; return xf.xform(p_viewport_coords); } Vector2 Viewport::get_camera_rect_size() const { + ERR_READ_THREAD_GUARD_V(Vector2()); return size; } void Viewport::set_disable_input(bool p_disable) { + ERR_MAIN_THREAD_GUARD; if (p_disable == disable_input) { return; } @@ -3049,14 +3096,17 @@ void Viewport::set_disable_input(bool p_disable) { } bool Viewport::is_input_disabled() const { + ERR_READ_THREAD_GUARD_V(false); return disable_input; } Variant Viewport::gui_get_drag_data() const { + ERR_READ_THREAD_GUARD_V(Variant()); return gui.drag_data; } PackedStringArray Viewport::get_configuration_warnings() const { + ERR_MAIN_THREAD_GUARD_V(PackedStringArray()); PackedStringArray warnings = Node::get_configuration_warnings(); if (size.x <= 1 || size.y <= 1) { @@ -3066,14 +3116,17 @@ PackedStringArray Viewport::get_configuration_warnings() const { } void Viewport::gui_reset_canvas_sort_index() { + ERR_MAIN_THREAD_GUARD; gui.canvas_sort_index = 0; } int Viewport::gui_get_canvas_sort_index() { + ERR_MAIN_THREAD_GUARD_V(0); return gui.canvas_sort_index++; } void Viewport::gui_release_focus() { + ERR_MAIN_THREAD_GUARD; if (gui.key_focus) { Control *f = gui.key_focus; gui.key_focus = nullptr; @@ -3083,10 +3136,12 @@ void Viewport::gui_release_focus() { } Control *Viewport::gui_get_focus_owner() { + ERR_READ_THREAD_GUARD_V(nullptr); return gui.key_focus; } void Viewport::set_msaa_2d(MSAA p_msaa) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(p_msaa, MSAA_MAX); if (msaa_2d == p_msaa) { return; @@ -3096,10 +3151,12 @@ void Viewport::set_msaa_2d(MSAA p_msaa) { } Viewport::MSAA Viewport::get_msaa_2d() const { + ERR_READ_THREAD_GUARD_V(MSAA_DISABLED); return msaa_2d; } void Viewport::set_msaa_3d(MSAA p_msaa) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(p_msaa, MSAA_MAX); if (msaa_3d == p_msaa) { return; @@ -3109,10 +3166,12 @@ void Viewport::set_msaa_3d(MSAA p_msaa) { } Viewport::MSAA Viewport::get_msaa_3d() const { + ERR_READ_THREAD_GUARD_V(MSAA_DISABLED); return msaa_3d; } void Viewport::set_screen_space_aa(ScreenSpaceAA p_screen_space_aa) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(p_screen_space_aa, SCREEN_SPACE_AA_MAX); if (screen_space_aa == p_screen_space_aa) { return; @@ -3122,10 +3181,12 @@ void Viewport::set_screen_space_aa(ScreenSpaceAA p_screen_space_aa) { } Viewport::ScreenSpaceAA Viewport::get_screen_space_aa() const { + ERR_READ_THREAD_GUARD_V(SCREEN_SPACE_AA_DISABLED); return screen_space_aa; } void Viewport::set_use_taa(bool p_use_taa) { + ERR_MAIN_THREAD_GUARD; if (use_taa == p_use_taa) { return; } @@ -3134,10 +3195,12 @@ void Viewport::set_use_taa(bool p_use_taa) { } bool Viewport::is_using_taa() const { + ERR_READ_THREAD_GUARD_V(false); return use_taa; } void Viewport::set_use_debanding(bool p_use_debanding) { + ERR_MAIN_THREAD_GUARD; if (use_debanding == p_use_debanding) { return; } @@ -3146,19 +3209,23 @@ void Viewport::set_use_debanding(bool p_use_debanding) { } bool Viewport::is_using_debanding() const { + ERR_READ_THREAD_GUARD_V(false); return use_debanding; } void Viewport::set_mesh_lod_threshold(float p_pixels) { + ERR_MAIN_THREAD_GUARD; mesh_lod_threshold = p_pixels; RS::get_singleton()->viewport_set_mesh_lod_threshold(viewport, mesh_lod_threshold); } float Viewport::get_mesh_lod_threshold() const { + ERR_READ_THREAD_GUARD_V(0); return mesh_lod_threshold; } void Viewport::set_use_occlusion_culling(bool p_use_occlusion_culling) { + ERR_MAIN_THREAD_GUARD; if (use_occlusion_culling == p_use_occlusion_culling) { return; } @@ -3170,57 +3237,70 @@ void Viewport::set_use_occlusion_culling(bool p_use_occlusion_culling) { } bool Viewport::is_using_occlusion_culling() const { + ERR_READ_THREAD_GUARD_V(false); return use_occlusion_culling; } void Viewport::set_debug_draw(DebugDraw p_debug_draw) { + ERR_MAIN_THREAD_GUARD; debug_draw = p_debug_draw; RS::get_singleton()->viewport_set_debug_draw(viewport, RS::ViewportDebugDraw(p_debug_draw)); } Viewport::DebugDraw Viewport::get_debug_draw() const { + ERR_READ_THREAD_GUARD_V(DEBUG_DRAW_DISABLED); return debug_draw; } int Viewport::get_render_info(RenderInfoType p_type, RenderInfo p_info) { + ERR_READ_THREAD_GUARD_V(0); return RS::get_singleton()->viewport_get_render_info(viewport, RS::ViewportRenderInfoType(p_type), RS::ViewportRenderInfo(p_info)); } void Viewport::set_snap_controls_to_pixels(bool p_enable) { + ERR_MAIN_THREAD_GUARD; snap_controls_to_pixels = p_enable; } bool Viewport::is_snap_controls_to_pixels_enabled() const { + ERR_READ_THREAD_GUARD_V(false); return snap_controls_to_pixels; } void Viewport::set_snap_2d_transforms_to_pixel(bool p_enable) { + ERR_MAIN_THREAD_GUARD; snap_2d_transforms_to_pixel = p_enable; RS::get_singleton()->viewport_set_snap_2d_transforms_to_pixel(viewport, snap_2d_transforms_to_pixel); } bool Viewport::is_snap_2d_transforms_to_pixel_enabled() const { + ERR_READ_THREAD_GUARD_V(false); return snap_2d_transforms_to_pixel; } void Viewport::set_snap_2d_vertices_to_pixel(bool p_enable) { + ERR_MAIN_THREAD_GUARD; snap_2d_vertices_to_pixel = p_enable; RS::get_singleton()->viewport_set_snap_2d_vertices_to_pixel(viewport, snap_2d_vertices_to_pixel); } bool Viewport::is_snap_2d_vertices_to_pixel_enabled() const { + ERR_READ_THREAD_GUARD_V(false); return snap_2d_vertices_to_pixel; } bool Viewport::gui_is_dragging() const { + ERR_READ_THREAD_GUARD_V(false); return gui.dragging; } bool Viewport::gui_is_drag_successful() const { + ERR_READ_THREAD_GUARD_V(false); return gui.drag_successful; } void Viewport::set_input_as_handled() { + ERR_MAIN_THREAD_GUARD; if (!handle_input_locally) { ERR_FAIL_COND(!is_inside_tree()); Viewport *vp = this; @@ -3243,6 +3323,7 @@ void Viewport::set_input_as_handled() { } bool Viewport::is_input_handled() const { + ERR_READ_THREAD_GUARD_V(false); if (!handle_input_locally) { ERR_FAIL_COND_V(!is_inside_tree(), false); const Viewport *vp = this; @@ -3263,14 +3344,17 @@ bool Viewport::is_input_handled() const { } void Viewport::set_handle_input_locally(bool p_enable) { + ERR_MAIN_THREAD_GUARD; handle_input_locally = p_enable; } bool Viewport::is_handling_input_locally() const { + ERR_READ_THREAD_GUARD_V(false); return handle_input_locally; } void Viewport::set_default_canvas_item_texture_filter(DefaultCanvasItemTextureFilter p_filter) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(p_filter, DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_MAX); if (default_canvas_item_texture_filter == p_filter) { @@ -3296,10 +3380,12 @@ void Viewport::set_default_canvas_item_texture_filter(DefaultCanvasItemTextureFi } Viewport::DefaultCanvasItemTextureFilter Viewport::get_default_canvas_item_texture_filter() const { + ERR_READ_THREAD_GUARD_V(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST); return default_canvas_item_texture_filter; } void Viewport::set_default_canvas_item_texture_repeat(DefaultCanvasItemTextureRepeat p_repeat) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(p_repeat, DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MAX); if (default_canvas_item_texture_repeat == p_repeat) { @@ -3324,10 +3410,12 @@ void Viewport::set_default_canvas_item_texture_repeat(DefaultCanvasItemTextureRe } Viewport::DefaultCanvasItemTextureRepeat Viewport::get_default_canvas_item_texture_repeat() const { + ERR_READ_THREAD_GUARD_V(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); return default_canvas_item_texture_repeat; } void Viewport::set_vrs_mode(Viewport::VRSMode p_vrs_mode) { + ERR_MAIN_THREAD_GUARD; // Note, set this even if not supported on this hardware, it will only be used if it is but we want to save the value as set by the user. vrs_mode = p_vrs_mode; @@ -3347,10 +3435,12 @@ void Viewport::set_vrs_mode(Viewport::VRSMode p_vrs_mode) { } Viewport::VRSMode Viewport::get_vrs_mode() const { + ERR_READ_THREAD_GUARD_V(VRS_DISABLED); return vrs_mode; } void Viewport::set_vrs_texture(Ref<Texture2D> p_texture) { + ERR_MAIN_THREAD_GUARD; vrs_texture = p_texture; // TODO need to add something here in case the RID changes @@ -3359,14 +3449,17 @@ void Viewport::set_vrs_texture(Ref<Texture2D> p_texture) { } Ref<Texture2D> Viewport::get_vrs_texture() const { + ERR_READ_THREAD_GUARD_V(Ref<Texture2D>()); return vrs_texture; } DisplayServer::WindowID Viewport::get_window_id() const { + ERR_READ_THREAD_GUARD_V(DisplayServer::INVALID_WINDOW_ID); return DisplayServer::MAIN_WINDOW_ID; } Viewport *Viewport::get_parent_viewport() const { + ERR_READ_THREAD_GUARD_V(nullptr); ERR_FAIL_COND_V(!is_inside_tree(), nullptr); if (!get_parent()) { return nullptr; //root viewport @@ -3376,14 +3469,17 @@ Viewport *Viewport::get_parent_viewport() const { } void Viewport::set_embedding_subwindows(bool p_embed) { + ERR_THREAD_GUARD; gui.embed_subwindows_hint = p_embed; } bool Viewport::is_embedding_subwindows() const { + ERR_READ_THREAD_GUARD_V(false); return gui.embed_subwindows_hint; } void Viewport::pass_mouse_focus_to(Viewport *p_viewport, Control *p_control) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_NULL(p_viewport); ERR_FAIL_NULL(p_control); @@ -3400,43 +3496,52 @@ void Viewport::pass_mouse_focus_to(Viewport *p_viewport, Control *p_control) { } void Viewport::set_sdf_oversize(SDFOversize p_sdf_oversize) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(p_sdf_oversize, SDF_OVERSIZE_MAX); sdf_oversize = p_sdf_oversize; RS::get_singleton()->viewport_set_sdf_oversize_and_scale(viewport, RS::ViewportSDFOversize(sdf_oversize), RS::ViewportSDFScale(sdf_scale)); } Viewport::SDFOversize Viewport::get_sdf_oversize() const { + ERR_READ_THREAD_GUARD_V(SDF_OVERSIZE_100_PERCENT); return sdf_oversize; } void Viewport::set_sdf_scale(SDFScale p_sdf_scale) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(p_sdf_scale, SDF_SCALE_MAX); sdf_scale = p_sdf_scale; RS::get_singleton()->viewport_set_sdf_oversize_and_scale(viewport, RS::ViewportSDFOversize(sdf_oversize), RS::ViewportSDFScale(sdf_scale)); } Viewport::SDFScale Viewport::get_sdf_scale() const { + ERR_READ_THREAD_GUARD_V(SDF_SCALE_100_PERCENT); return sdf_scale; } Transform2D Viewport::get_screen_transform() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); return get_screen_transform_internal(); } Transform2D Viewport::get_screen_transform_internal(bool p_absolute_position) const { + ERR_READ_THREAD_GUARD_V(Transform2D()); return get_final_transform(); } void Viewport::set_canvas_cull_mask(uint32_t p_canvas_cull_mask) { + ERR_MAIN_THREAD_GUARD; canvas_cull_mask = p_canvas_cull_mask; RenderingServer::get_singleton()->viewport_set_canvas_cull_mask(viewport, canvas_cull_mask); } uint32_t Viewport::get_canvas_cull_mask() const { + ERR_READ_THREAD_GUARD_V(0); return canvas_cull_mask; } void Viewport::set_canvas_cull_mask_bit(uint32_t p_layer, bool p_enable) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_UNSIGNED_INDEX(p_layer, 32); if (p_enable) { set_canvas_cull_mask(canvas_cull_mask | (1 << p_layer)); @@ -3446,16 +3551,19 @@ void Viewport::set_canvas_cull_mask_bit(uint32_t p_layer, bool p_enable) { } bool Viewport::get_canvas_cull_mask_bit(uint32_t p_layer) const { + ERR_READ_THREAD_GUARD_V(false); ERR_FAIL_UNSIGNED_INDEX_V(p_layer, 32, false); return (canvas_cull_mask & (1 << p_layer)); } #ifndef _3D_DISABLED AudioListener3D *Viewport::get_audio_listener_3d() const { + ERR_READ_THREAD_GUARD_V(nullptr); return audio_listener_3d; } void Viewport::set_as_audio_listener_3d(bool p_enable) { + ERR_MAIN_THREAD_GUARD; if (p_enable == is_audio_listener_3d_enabled) { return; } @@ -3465,6 +3573,7 @@ void Viewport::set_as_audio_listener_3d(bool p_enable) { } bool Viewport::is_audio_listener_3d() const { + ERR_READ_THREAD_GUARD_V(false); return is_audio_listener_3d_enabled; } @@ -3543,6 +3652,7 @@ void Viewport::_collision_object_3d_input_event(CollisionObject3D *p_object, Cam } Camera3D *Viewport::get_camera_3d() const { + ERR_READ_THREAD_GUARD_V(nullptr); return camera_3d; } @@ -3606,6 +3716,7 @@ void Viewport::_camera_3d_make_next_current(Camera3D *p_exclude) { } void Viewport::enable_camera_3d_override(bool p_enable) { + ERR_MAIN_THREAD_GUARD; if (p_enable == camera_3d_override) { return; } @@ -3627,6 +3738,7 @@ void Viewport::enable_camera_3d_override(bool p_enable) { } void Viewport::set_camera_3d_override_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far) { + ERR_MAIN_THREAD_GUARD; if (camera_3d_override) { if (camera_3d_override.fov == p_fovy_degrees && camera_3d_override.z_near == p_z_near && camera_3d_override.z_far == p_z_far && camera_3d_override.projection == Camera3DOverrideData::PROJECTION_PERSPECTIVE) { @@ -3643,6 +3755,7 @@ void Viewport::set_camera_3d_override_perspective(real_t p_fovy_degrees, real_t } void Viewport::set_camera_3d_override_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far) { + ERR_MAIN_THREAD_GUARD; if (camera_3d_override) { if (camera_3d_override.size == p_size && camera_3d_override.z_near == p_z_near && camera_3d_override.z_far == p_z_far && camera_3d_override.projection == Camera3DOverrideData::PROJECTION_ORTHOGONAL) { @@ -3659,19 +3772,23 @@ void Viewport::set_camera_3d_override_orthogonal(real_t p_size, real_t p_z_near, } void Viewport::set_disable_3d(bool p_disable) { + ERR_MAIN_THREAD_GUARD; disable_3d = p_disable; RenderingServer::get_singleton()->viewport_set_disable_3d(viewport, disable_3d); } bool Viewport::is_3d_disabled() const { + ERR_READ_THREAD_GUARD_V(false); return disable_3d; } bool Viewport::is_camera_3d_override_enabled() const { + ERR_READ_THREAD_GUARD_V(false); return camera_3d_override; } void Viewport::set_camera_3d_override_transform(const Transform3D &p_transform) { + ERR_MAIN_THREAD_GUARD; if (camera_3d_override) { camera_3d_override.transform = p_transform; RenderingServer::get_singleton()->camera_set_transform(camera_3d_override.rid, p_transform); @@ -3679,6 +3796,7 @@ void Viewport::set_camera_3d_override_transform(const Transform3D &p_transform) } Transform3D Viewport::get_camera_3d_override_transform() const { + ERR_READ_THREAD_GUARD_V(Transform3D()); if (camera_3d_override) { return camera_3d_override.transform; } @@ -3687,10 +3805,12 @@ Transform3D Viewport::get_camera_3d_override_transform() const { } Ref<World3D> Viewport::get_world_3d() const { + ERR_READ_THREAD_GUARD_V(Ref<World3D>()); return world_3d; } Ref<World3D> Viewport::find_world_3d() const { + ERR_READ_THREAD_GUARD_V(Ref<World3D>()); if (own_world_3d.is_valid()) { return own_world_3d; } else if (world_3d.is_valid()) { @@ -3703,6 +3823,7 @@ Ref<World3D> Viewport::find_world_3d() const { } void Viewport::set_world_3d(const Ref<World3D> &p_world_3d) { + ERR_MAIN_THREAD_GUARD; if (world_3d == p_world_3d) { return; } @@ -3759,6 +3880,7 @@ void Viewport::_own_world_3d_changed() { } void Viewport::set_use_own_world_3d(bool p_use_own_world_3d) { + ERR_MAIN_THREAD_GUARD; if (p_use_own_world_3d == own_world_3d.is_valid()) { return; } @@ -3793,6 +3915,7 @@ void Viewport::set_use_own_world_3d(bool p_use_own_world_3d) { } bool Viewport::is_using_own_world_3d() const { + ERR_READ_THREAD_GUARD_V(false); return own_world_3d.is_valid(); } @@ -3843,6 +3966,7 @@ void Viewport::_propagate_exit_world_3d(Node *p_node) { } void Viewport::set_use_xr(bool p_use_xr) { + ERR_MAIN_THREAD_GUARD; if (use_xr != p_use_xr) { use_xr = p_use_xr; @@ -3860,10 +3984,12 @@ void Viewport::set_use_xr(bool p_use_xr) { } bool Viewport::is_using_xr() { + ERR_READ_THREAD_GUARD_V(false); return use_xr; } void Viewport::set_scaling_3d_mode(Scaling3DMode p_scaling_3d_mode) { + ERR_MAIN_THREAD_GUARD; if (scaling_3d_mode == p_scaling_3d_mode) { return; } @@ -3873,10 +3999,12 @@ void Viewport::set_scaling_3d_mode(Scaling3DMode p_scaling_3d_mode) { } Viewport::Scaling3DMode Viewport::get_scaling_3d_mode() const { + ERR_READ_THREAD_GUARD_V(SCALING_3D_MODE_BILINEAR); return scaling_3d_mode; } void Viewport::set_scaling_3d_scale(float p_scaling_3d_scale) { + ERR_MAIN_THREAD_GUARD; // Clamp to reasonable values that are actually useful. // Values above 2.0 don't serve a practical purpose since the viewport // isn't displayed with mipmaps. @@ -3886,10 +4014,12 @@ void Viewport::set_scaling_3d_scale(float p_scaling_3d_scale) { } float Viewport::get_scaling_3d_scale() const { + ERR_READ_THREAD_GUARD_V(0); return scaling_3d_scale; } void Viewport::set_fsr_sharpness(float p_fsr_sharpness) { + ERR_MAIN_THREAD_GUARD; if (fsr_sharpness == p_fsr_sharpness) { return; } @@ -3903,10 +4033,12 @@ void Viewport::set_fsr_sharpness(float p_fsr_sharpness) { } float Viewport::get_fsr_sharpness() const { + ERR_READ_THREAD_GUARD_V(0); return fsr_sharpness; } void Viewport::set_texture_mipmap_bias(float p_texture_mipmap_bias) { + ERR_MAIN_THREAD_GUARD; if (texture_mipmap_bias == p_texture_mipmap_bias) { return; } @@ -3916,6 +4048,7 @@ void Viewport::set_texture_mipmap_bias(float p_texture_mipmap_bias) { } float Viewport::get_texture_mipmap_bias() const { + ERR_READ_THREAD_GUARD_V(0); return texture_mipmap_bias; } @@ -4309,10 +4442,12 @@ Viewport::~Viewport() { ///////////////////////////////// void SubViewport::set_size(const Size2i &p_size) { + ERR_MAIN_THREAD_GUARD; _internal_set_size(p_size); } void SubViewport::set_size_force(const Size2i &p_size) { + ERR_MAIN_THREAD_GUARD; // Use only for setting the size from the parent SubViewportContainer with enabled stretch mode. // Don't expose function to scripting. _internal_set_size(p_size, true); @@ -4335,18 +4470,22 @@ void SubViewport::_internal_set_size(const Size2i &p_size, bool p_force) { } Size2i SubViewport::get_size() const { + ERR_READ_THREAD_GUARD_V(Size2()); return _get_size(); } void SubViewport::set_size_2d_override(const Size2i &p_size) { + ERR_MAIN_THREAD_GUARD; _set_size(_get_size(), p_size, true); } Size2i SubViewport::get_size_2d_override() const { + ERR_READ_THREAD_GUARD_V(Size2i()); return _get_size_2d_override(); } void SubViewport::set_size_2d_override_stretch(bool p_enable) { + ERR_MAIN_THREAD_GUARD; if (p_enable == size_2d_override_stretch) { return; } @@ -4356,32 +4495,39 @@ void SubViewport::set_size_2d_override_stretch(bool p_enable) { } bool SubViewport::is_size_2d_override_stretch_enabled() const { + ERR_READ_THREAD_GUARD_V(false); return size_2d_override_stretch; } void SubViewport::set_update_mode(UpdateMode p_mode) { + ERR_MAIN_THREAD_GUARD; update_mode = p_mode; RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::ViewportUpdateMode(p_mode)); } SubViewport::UpdateMode SubViewport::get_update_mode() const { + ERR_READ_THREAD_GUARD_V(UPDATE_DISABLED); return update_mode; } void SubViewport::set_clear_mode(ClearMode p_mode) { + ERR_MAIN_THREAD_GUARD; clear_mode = p_mode; RS::get_singleton()->viewport_set_clear_mode(get_viewport_rid(), RS::ViewportClearMode(p_mode)); } SubViewport::ClearMode SubViewport::get_clear_mode() const { + ERR_READ_THREAD_GUARD_V(CLEAR_MODE_ALWAYS); return clear_mode; } DisplayServer::WindowID SubViewport::get_window_id() const { + ERR_READ_THREAD_GUARD_V(DisplayServer::INVALID_WINDOW_ID); return DisplayServer::INVALID_WINDOW_ID; } Transform2D SubViewport::get_screen_transform_internal(bool p_absolute_position) const { + ERR_READ_THREAD_GUARD_V(Transform2D()); Transform2D container_transform; SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent()); if (c) { @@ -4396,6 +4542,7 @@ Transform2D SubViewport::get_screen_transform_internal(bool p_absolute_position) } Transform2D SubViewport::get_popup_base_transform() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); if (is_embedding_subwindows()) { return Transform2D(); } @@ -4411,6 +4558,7 @@ Transform2D SubViewport::get_popup_base_transform() const { } void SubViewport::_notification(int p_what) { + ERR_MAIN_THREAD_GUARD; switch (p_what) { case NOTIFICATION_ENTER_TREE: { RS::get_singleton()->viewport_set_active(get_viewport_rid(), true); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 406246788e..e2f9aa8937 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -43,6 +43,8 @@ // Dynamic properties. bool Window::_set(const StringName &p_name, const Variant &p_value) { + ERR_MAIN_THREAD_GUARD_V(false); + String name = p_name; if (!name.begins_with("theme_override")) { return false; @@ -113,6 +115,8 @@ bool Window::_set(const StringName &p_name, const Variant &p_value) { } bool Window::_get(const StringName &p_name, Variant &r_ret) const { + ERR_READ_THREAD_GUARD_V(false); + String sname = p_name; if (!sname.begins_with("theme_override")) { return false; @@ -144,6 +148,8 @@ bool Window::_get(const StringName &p_name, Variant &r_ret) const { } void Window::_get_property_list(List<PropertyInfo> *p_list) const { + ERR_READ_THREAD_GUARD; + Ref<Theme> default_theme = ThemeDB::get_singleton()->get_default_theme(); p_list->push_back(PropertyInfo(Variant::NIL, GNAME("Theme Overrides", "theme_override_"), PROPERTY_HINT_NONE, "theme_override_", PROPERTY_USAGE_GROUP)); @@ -261,6 +267,8 @@ void Window::_validate_property(PropertyInfo &p_property) const { // void Window::set_title(const String &p_title) { + ERR_MAIN_THREAD_GUARD; + title = p_title; if (embedder) { @@ -280,19 +288,25 @@ void Window::set_title(const String &p_title) { } String Window::get_title() const { + ERR_READ_THREAD_GUARD_V(String()); return title; } void Window::set_initial_position(Window::WindowInitialPosition p_initial_position) { + ERR_MAIN_THREAD_GUARD; + initial_position = p_initial_position; notify_property_list_changed(); } Window::WindowInitialPosition Window::get_initial_position() const { + ERR_READ_THREAD_GUARD_V(WINDOW_INITIAL_POSITION_ABSOLUTE); return initial_position; } void Window::set_current_screen(int p_screen) { + ERR_MAIN_THREAD_GUARD; + current_screen = p_screen; if (window_id == DisplayServer::INVALID_WINDOW_ID) { return; @@ -301,6 +315,8 @@ void Window::set_current_screen(int p_screen) { } int Window::get_current_screen() const { + ERR_READ_THREAD_GUARD_V(0); + if (window_id != DisplayServer::INVALID_WINDOW_ID) { current_screen = DisplayServer::get_singleton()->window_get_current_screen(window_id); } @@ -308,6 +324,8 @@ int Window::get_current_screen() const { } void Window::set_position(const Point2i &p_position) { + ERR_MAIN_THREAD_GUARD; + position = p_position; if (embedder) { @@ -319,23 +337,30 @@ void Window::set_position(const Point2i &p_position) { } Point2i Window::get_position() const { + ERR_READ_THREAD_GUARD_V(Point2i()); + return position; } void Window::set_size(const Size2i &p_size) { + ERR_MAIN_THREAD_GUARD; + size = p_size; _update_window_size(); } Size2i Window::get_size() const { + ERR_READ_THREAD_GUARD_V(Size2i()); return size; } void Window::reset_size() { + ERR_MAIN_THREAD_GUARD; set_size(Size2i()); } Point2i Window::get_position_with_decorations() const { + ERR_READ_THREAD_GUARD_V(Point2i()); if (window_id != DisplayServer::INVALID_WINDOW_ID) { return DisplayServer::get_singleton()->window_get_position_with_decorations(window_id); } @@ -343,6 +368,7 @@ Point2i Window::get_position_with_decorations() const { } Size2i Window::get_size_with_decorations() const { + ERR_READ_THREAD_GUARD_V(Size2i()); if (window_id != DisplayServer::INVALID_WINDOW_ID) { return DisplayServer::get_singleton()->window_get_size_with_decorations(window_id); } @@ -366,6 +392,7 @@ void Window::_validate_limit_size() { } void Window::set_max_size(const Size2i &p_max_size) { + ERR_MAIN_THREAD_GUARD; Size2i max_size_clamped = _clamp_limit_size(p_max_size); if (max_size == max_size_clamped) { return; @@ -377,10 +404,12 @@ void Window::set_max_size(const Size2i &p_max_size) { } Size2i Window::get_max_size() const { + ERR_READ_THREAD_GUARD_V(Size2i()); return max_size; } void Window::set_min_size(const Size2i &p_min_size) { + ERR_MAIN_THREAD_GUARD; Size2i min_size_clamped = _clamp_limit_size(p_min_size); if (min_size == min_size_clamped) { return; @@ -392,10 +421,12 @@ void Window::set_min_size(const Size2i &p_min_size) { } Size2i Window::get_min_size() const { + ERR_READ_THREAD_GUARD_V(Size2i()); return min_size; } void Window::set_mode(Mode p_mode) { + ERR_MAIN_THREAD_GUARD; mode = p_mode; if (embedder) { @@ -407,6 +438,7 @@ void Window::set_mode(Mode p_mode) { } Window::Mode Window::get_mode() const { + ERR_READ_THREAD_GUARD_V(MODE_WINDOWED); if (window_id != DisplayServer::INVALID_WINDOW_ID) { mode = (Mode)DisplayServer::get_singleton()->window_get_mode(window_id); } @@ -414,6 +446,7 @@ Window::Mode Window::get_mode() const { } void Window::set_flag(Flags p_flag, bool p_enabled) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(p_flag, FLAG_MAX); flags[p_flag] = p_enabled; @@ -428,6 +461,7 @@ void Window::set_flag(Flags p_flag, bool p_enabled) { } bool Window::get_flag(Flags p_flag) const { + ERR_READ_THREAD_GUARD_V(false); ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false); if (window_id != DisplayServer::INVALID_WINDOW_ID) { if (!is_in_edited_scene_root()) { @@ -438,6 +472,7 @@ bool Window::get_flag(Flags p_flag) const { } bool Window::is_maximize_allowed() const { + ERR_READ_THREAD_GUARD_V(false); if (window_id != DisplayServer::INVALID_WINDOW_ID) { return DisplayServer::get_singleton()->window_is_maximize_allowed(window_id); } @@ -445,12 +480,14 @@ bool Window::is_maximize_allowed() const { } void Window::request_attention() { + ERR_MAIN_THREAD_GUARD; if (window_id != DisplayServer::INVALID_WINDOW_ID) { DisplayServer::get_singleton()->window_request_attention(window_id); } } void Window::move_to_foreground() { + ERR_MAIN_THREAD_GUARD; if (embedder) { embedder->_sub_window_grab_focus(this); @@ -460,6 +497,7 @@ void Window::move_to_foreground() { } bool Window::can_draw() const { + ERR_READ_THREAD_GUARD_V(false); if (!is_inside_tree()) { return false; } @@ -471,24 +509,28 @@ bool Window::can_draw() const { } void Window::set_ime_active(bool p_active) { + ERR_MAIN_THREAD_GUARD; if (window_id != DisplayServer::INVALID_WINDOW_ID) { DisplayServer::get_singleton()->window_set_ime_active(p_active, window_id); } } void Window::set_ime_position(const Point2i &p_pos) { + ERR_MAIN_THREAD_GUARD; if (window_id != DisplayServer::INVALID_WINDOW_ID) { DisplayServer::get_singleton()->window_set_ime_position(p_pos, window_id); } } bool Window::is_embedded() const { + ERR_READ_THREAD_GUARD_V(false); ERR_FAIL_COND_V(!is_inside_tree(), false); return get_embedder() != nullptr; } bool Window::is_in_edited_scene_root() const { + ERR_READ_THREAD_GUARD_V(false); #ifdef TOOLS_ENABLED return is_part_of_edited_scene(); #else @@ -680,6 +722,7 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) { } void Window::update_mouse_cursor_shape() { + ERR_MAIN_THREAD_GUARD; // The default shape is set in Viewport::_gui_input_event. To instantly // see the shape in the viewport we need to trigger a mouse motion event. Ref<InputEventMouseMotion> mm; @@ -693,14 +736,17 @@ void Window::update_mouse_cursor_shape() { } void Window::show() { + ERR_MAIN_THREAD_GUARD; set_visible(true); } void Window::hide() { + ERR_MAIN_THREAD_GUARD; set_visible(false); } void Window::set_visible(bool p_visible) { + ERR_MAIN_THREAD_GUARD; if (visible == p_visible) { return; } @@ -816,6 +862,7 @@ void Window::_make_transient() { } void Window::set_transient(bool p_transient) { + ERR_MAIN_THREAD_GUARD; if (transient == p_transient) { return; } @@ -834,10 +881,12 @@ void Window::set_transient(bool p_transient) { } bool Window::is_transient() const { + ERR_READ_THREAD_GUARD_V(false); return transient; } void Window::set_exclusive(bool p_exclusive) { + ERR_MAIN_THREAD_GUARD; if (exclusive == p_exclusive) { return; } @@ -867,10 +916,12 @@ void Window::set_exclusive(bool p_exclusive) { } bool Window::is_exclusive() const { + ERR_READ_THREAD_GUARD_V(false); return exclusive; } bool Window::is_visible() const { + ERR_READ_THREAD_GUARD_V(false); return visible; } @@ -1061,6 +1112,7 @@ void Window::_update_window_callbacks() { } Viewport *Window::get_embedder() const { + ERR_READ_THREAD_GUARD_V(nullptr); Viewport *vp = get_parent_viewport(); while (vp) { @@ -1078,6 +1130,7 @@ Viewport *Window::get_embedder() const { } void Window::_notification(int p_what) { + ERR_MAIN_THREAD_GUARD; switch (p_what) { case NOTIFICATION_POSTINITIALIZE: { initialized = true; @@ -1224,6 +1277,7 @@ void Window::_notification(int p_what) { } void Window::set_content_scale_size(const Size2i &p_size) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(p_size.x < 0); ERR_FAIL_COND(p_size.y < 0); content_scale_size = p_size; @@ -1231,38 +1285,46 @@ void Window::set_content_scale_size(const Size2i &p_size) { } Size2i Window::get_content_scale_size() const { + ERR_READ_THREAD_GUARD_V(Size2i()); return content_scale_size; } void Window::set_content_scale_mode(ContentScaleMode p_mode) { + ERR_MAIN_THREAD_GUARD; content_scale_mode = p_mode; _update_viewport_size(); } Window::ContentScaleMode Window::get_content_scale_mode() const { + ERR_READ_THREAD_GUARD_V(CONTENT_SCALE_MODE_DISABLED); return content_scale_mode; } void Window::set_content_scale_aspect(ContentScaleAspect p_aspect) { + ERR_MAIN_THREAD_GUARD; content_scale_aspect = p_aspect; _update_viewport_size(); } Window::ContentScaleAspect Window::get_content_scale_aspect() const { + ERR_READ_THREAD_GUARD_V(CONTENT_SCALE_ASPECT_IGNORE); return content_scale_aspect; } void Window::set_content_scale_factor(real_t p_factor) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(p_factor <= 0); content_scale_factor = p_factor; _update_viewport_size(); } real_t Window::get_content_scale_factor() const { + ERR_READ_THREAD_GUARD_V(0); return content_scale_factor; } void Window::set_use_font_oversampling(bool p_oversampling) { + ERR_MAIN_THREAD_GUARD; if (is_inside_tree() && window_id != DisplayServer::MAIN_WINDOW_ID) { ERR_FAIL_MSG("Only the root window can set and use font oversampling."); } @@ -1271,10 +1333,12 @@ void Window::set_use_font_oversampling(bool p_oversampling) { } bool Window::is_using_font_oversampling() const { + ERR_READ_THREAD_GUARD_V(false); return use_font_oversampling; } DisplayServer::WindowID Window::get_window_id() const { + ERR_READ_THREAD_GUARD_V(DisplayServer::INVALID_WINDOW_ID); if (embedder) { return parent->get_window_id(); } @@ -1282,6 +1346,7 @@ DisplayServer::WindowID Window::get_window_id() const { } void Window::set_mouse_passthrough_polygon(const Vector<Vector2> &p_region) { + ERR_MAIN_THREAD_GUARD; mpath = p_region; if (window_id == DisplayServer::INVALID_WINDOW_ID) { return; @@ -1294,6 +1359,7 @@ Vector<Vector2> Window::get_mouse_passthrough_polygon() const { } void Window::set_wrap_controls(bool p_enable) { + ERR_MAIN_THREAD_GUARD; wrap_controls = p_enable; if (!is_inside_tree()) { @@ -1308,6 +1374,7 @@ void Window::set_wrap_controls(bool p_enable) { } bool Window::is_wrapping_controls() const { + ERR_READ_THREAD_GUARD_V(false); return wrap_controls; } @@ -1329,6 +1396,7 @@ Size2 Window::_get_contents_minimum_size() const { } void Window::child_controls_changed() { + ERR_MAIN_THREAD_GUARD; if (!is_inside_tree() || !visible || updating_child_controls) { return; } @@ -1406,6 +1474,7 @@ void Window::_window_drop_files(const Vector<String> &p_files) { } Viewport *Window::get_parent_viewport() const { + ERR_READ_THREAD_GUARD_V(nullptr); if (get_parent()) { return get_parent()->get_viewport(); } else { @@ -1414,6 +1483,7 @@ Viewport *Window::get_parent_viewport() const { } Window *Window::get_parent_visible_window() const { + ERR_READ_THREAD_GUARD_V(nullptr); Viewport *vp = get_parent_viewport(); Window *window = nullptr; while (vp) { @@ -1431,6 +1501,7 @@ Window *Window::get_parent_visible_window() const { } void Window::popup_on_parent(const Rect2i &p_parent_rect) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND_MSG(window_id == DisplayServer::MAIN_WINDOW_ID, "Can't popup the main window."); @@ -1448,6 +1519,7 @@ void Window::popup_on_parent(const Rect2i &p_parent_rect) { } void Window::popup_centered_clamped(const Size2i &p_size, float p_fallback_ratio) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND_MSG(window_id == DisplayServer::MAIN_WINDOW_ID, "Can't popup the main window."); @@ -1479,6 +1551,7 @@ void Window::popup_centered_clamped(const Size2i &p_size, float p_fallback_ratio } void Window::popup_centered(const Size2i &p_minsize) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND_MSG(window_id == DisplayServer::MAIN_WINDOW_ID, "Can't popup the main window."); @@ -1507,6 +1580,7 @@ void Window::popup_centered(const Size2i &p_minsize) { } void Window::popup_centered_ratio(float p_ratio) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND_MSG(window_id == DisplayServer::MAIN_WINDOW_ID, "Can't popup the main window."); ERR_FAIL_COND_MSG(p_ratio <= 0.0 || p_ratio > 1.0, "Ratio must be between 0.0 and 1.0!"); @@ -1533,6 +1607,7 @@ void Window::popup_centered_ratio(float p_ratio) { } void Window::popup(const Rect2i &p_screen_rect) { + ERR_MAIN_THREAD_GUARD; emit_signal(SNAME("about_to_popup")); if (!get_embedder() && get_flag(FLAG_POPUP)) { @@ -1633,6 +1708,7 @@ void Window::popup_exclusive_centered_clamped(Node *p_from_node, const Size2i &p } Rect2i Window::fit_rect_in_parent(Rect2i p_rect, const Rect2i &p_parent_rect) const { + ERR_READ_THREAD_GUARD_V(Rect2i()); Size2i limit = p_parent_rect.size; if (p_rect.position.x + p_rect.size.x > limit.x) { p_rect.position.x = limit.x - p_rect.size.x; @@ -1655,10 +1731,12 @@ Rect2i Window::fit_rect_in_parent(Rect2i p_rect, const Rect2i &p_parent_rect) co } Size2 Window::get_contents_minimum_size() const { + ERR_READ_THREAD_GUARD_V(Size2()); return _get_contents_minimum_size(); } Size2 Window::get_clamped_minimum_size() const { + ERR_READ_THREAD_GUARD_V(Size2()); if (!wrap_controls) { return min_size; } @@ -1667,6 +1745,7 @@ Size2 Window::get_clamped_minimum_size() const { } void Window::grab_focus() { + ERR_MAIN_THREAD_GUARD; if (embedder) { embedder->_sub_window_grab_focus(this); } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { @@ -1675,10 +1754,12 @@ void Window::grab_focus() { } bool Window::has_focus() const { + ERR_READ_THREAD_GUARD_V(false); return focused; } Rect2i Window::get_usable_parent_rect() const { + ERR_READ_THREAD_GUARD_V(Rect2i()); ERR_FAIL_COND_V(!is_inside_tree(), Rect2()); Rect2i parent_rect; if (is_embedded()) { @@ -1708,18 +1789,22 @@ void Window::remove_child_notify(Node *p_child) { // Theming. void Window::set_theme_owner_node(Node *p_node) { + ERR_MAIN_THREAD_GUARD; theme_owner->set_owner_node(p_node); } Node *Window::get_theme_owner_node() const { + ERR_READ_THREAD_GUARD_V(nullptr); return theme_owner->get_owner_node(); } bool Window::has_theme_owner_node() const { + ERR_READ_THREAD_GUARD_V(false); return theme_owner->has_owner_node(); } void Window::set_theme(const Ref<Theme> &p_theme) { + ERR_MAIN_THREAD_GUARD; if (theme == p_theme) { return; } @@ -1751,6 +1836,7 @@ void Window::set_theme(const Ref<Theme> &p_theme) { } Ref<Theme> Window::get_theme() const { + ERR_READ_THREAD_GUARD_V(Ref<Theme>()); return theme; } @@ -1799,6 +1885,7 @@ void Window::_update_embedded_window() { } void Window::set_theme_type_variation(const StringName &p_theme_type) { + ERR_MAIN_THREAD_GUARD; theme_type_variation = p_theme_type; if (is_inside_tree()) { notification(NOTIFICATION_THEME_CHANGED); @@ -1806,12 +1893,14 @@ void Window::set_theme_type_variation(const StringName &p_theme_type) { } StringName Window::get_theme_type_variation() const { + ERR_READ_THREAD_GUARD_V(StringName()); return theme_type_variation; } /// Theme property lookup. Ref<Texture2D> Window::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(Ref<Texture2D>()); if (!initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -1835,6 +1924,7 @@ Ref<Texture2D> Window::get_theme_icon(const StringName &p_name, const StringName } Ref<StyleBox> Window::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(Ref<StyleBox>()); if (!initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -1858,6 +1948,7 @@ Ref<StyleBox> Window::get_theme_stylebox(const StringName &p_name, const StringN } Ref<Font> Window::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(Ref<Font>()); if (!initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -1881,6 +1972,7 @@ Ref<Font> Window::get_theme_font(const StringName &p_name, const StringName &p_t } int Window::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(0); if (!initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -1904,6 +1996,7 @@ int Window::get_theme_font_size(const StringName &p_name, const StringName &p_th } Color Window::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(Color()); if (!initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -1927,6 +2020,7 @@ Color Window::get_theme_color(const StringName &p_name, const StringName &p_them } int Window::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(0); if (!initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -1950,6 +2044,7 @@ int Window::get_theme_constant(const StringName &p_name, const StringName &p_the } bool Window::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -1966,6 +2061,7 @@ bool Window::has_theme_icon(const StringName &p_name, const StringName &p_theme_ } bool Window::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -1982,6 +2078,7 @@ bool Window::has_theme_stylebox(const StringName &p_name, const StringName &p_th } bool Window::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -1998,6 +2095,7 @@ bool Window::has_theme_font(const StringName &p_name, const StringName &p_theme_ } bool Window::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2014,6 +2112,7 @@ bool Window::has_theme_font_size(const StringName &p_name, const StringName &p_t } bool Window::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2030,6 +2129,7 @@ bool Window::has_theme_color(const StringName &p_name, const StringName &p_theme } bool Window::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + ERR_READ_THREAD_GUARD_V(false); if (!initialized) { WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); } @@ -2048,6 +2148,7 @@ bool Window::has_theme_constant(const StringName &p_name, const StringName &p_th /// Local property overrides. void Window::add_theme_icon_override(const StringName &p_name, const Ref<Texture2D> &p_icon) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!p_icon.is_valid()); if (theme_icon_override.has(p_name)) { @@ -2060,6 +2161,7 @@ void Window::add_theme_icon_override(const StringName &p_name, const Ref<Texture } void Window::add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!p_style.is_valid()); if (theme_style_override.has(p_name)) { @@ -2072,6 +2174,7 @@ void Window::add_theme_style_override(const StringName &p_name, const Ref<StyleB } void Window::add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!p_font.is_valid()); if (theme_font_override.has(p_name)) { @@ -2084,21 +2187,25 @@ void Window::add_theme_font_override(const StringName &p_name, const Ref<Font> & } void Window::add_theme_font_size_override(const StringName &p_name, int p_font_size) { + ERR_MAIN_THREAD_GUARD; theme_font_size_override[p_name] = p_font_size; _notify_theme_override_changed(); } void Window::add_theme_color_override(const StringName &p_name, const Color &p_color) { + ERR_MAIN_THREAD_GUARD; theme_color_override[p_name] = p_color; _notify_theme_override_changed(); } void Window::add_theme_constant_override(const StringName &p_name, int p_constant) { + ERR_MAIN_THREAD_GUARD; theme_constant_override[p_name] = p_constant; _notify_theme_override_changed(); } void Window::remove_theme_icon_override(const StringName &p_name) { + ERR_MAIN_THREAD_GUARD; if (theme_icon_override.has(p_name)) { theme_icon_override[p_name]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed)); } @@ -2108,6 +2215,7 @@ void Window::remove_theme_icon_override(const StringName &p_name) { } void Window::remove_theme_style_override(const StringName &p_name) { + ERR_MAIN_THREAD_GUARD; if (theme_style_override.has(p_name)) { theme_style_override[p_name]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed)); } @@ -2117,6 +2225,7 @@ void Window::remove_theme_style_override(const StringName &p_name) { } void Window::remove_theme_font_override(const StringName &p_name) { + ERR_MAIN_THREAD_GUARD; if (theme_font_override.has(p_name)) { theme_font_override[p_name]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed)); } @@ -2126,46 +2235,55 @@ void Window::remove_theme_font_override(const StringName &p_name) { } void Window::remove_theme_font_size_override(const StringName &p_name) { + ERR_MAIN_THREAD_GUARD; theme_font_size_override.erase(p_name); _notify_theme_override_changed(); } void Window::remove_theme_color_override(const StringName &p_name) { + ERR_MAIN_THREAD_GUARD; theme_color_override.erase(p_name); _notify_theme_override_changed(); } void Window::remove_theme_constant_override(const StringName &p_name) { + ERR_MAIN_THREAD_GUARD; theme_constant_override.erase(p_name); _notify_theme_override_changed(); } bool Window::has_theme_icon_override(const StringName &p_name) const { + ERR_READ_THREAD_GUARD_V(false); const Ref<Texture2D> *tex = theme_icon_override.getptr(p_name); return tex != nullptr; } bool Window::has_theme_stylebox_override(const StringName &p_name) const { + ERR_READ_THREAD_GUARD_V(false); const Ref<StyleBox> *style = theme_style_override.getptr(p_name); return style != nullptr; } bool Window::has_theme_font_override(const StringName &p_name) const { + ERR_READ_THREAD_GUARD_V(false); const Ref<Font> *font = theme_font_override.getptr(p_name); return font != nullptr; } bool Window::has_theme_font_size_override(const StringName &p_name) const { + ERR_READ_THREAD_GUARD_V(false); const int *font_size = theme_font_size_override.getptr(p_name); return font_size != nullptr; } bool Window::has_theme_color_override(const StringName &p_name) const { + ERR_READ_THREAD_GUARD_V(false); const Color *color = theme_color_override.getptr(p_name); return color != nullptr; } bool Window::has_theme_constant_override(const StringName &p_name) const { + ERR_READ_THREAD_GUARD_V(false); const int *constant = theme_constant_override.getptr(p_name); return constant != nullptr; } @@ -2173,24 +2291,29 @@ bool Window::has_theme_constant_override(const StringName &p_name) const { /// Default theme properties. float Window::get_theme_default_base_scale() const { + ERR_READ_THREAD_GUARD_V(0); return theme_owner->get_theme_default_base_scale(); } Ref<Font> Window::get_theme_default_font() const { + ERR_READ_THREAD_GUARD_V(Ref<Font>()); return theme_owner->get_theme_default_font(); } int Window::get_theme_default_font_size() const { + ERR_READ_THREAD_GUARD_V(0); return theme_owner->get_theme_default_font_size(); } /// Bulk actions. void Window::begin_bulk_theme_override() { + ERR_MAIN_THREAD_GUARD; bulk_theme_override = true; } void Window::end_bulk_theme_override() { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(!bulk_theme_override); bulk_theme_override = false; @@ -2200,6 +2323,7 @@ void Window::end_bulk_theme_override() { // Rect2i Window::get_parent_rect() const { + ERR_READ_THREAD_GUARD_V(Rect2i()); ERR_FAIL_COND_V(!is_inside_tree(), Rect2i()); if (is_embedded()) { //viewport @@ -2236,10 +2360,12 @@ Rect2i Window::get_parent_rect() const { } void Window::set_clamp_to_embedder(bool p_enable) { + ERR_MAIN_THREAD_GUARD; clamp_to_embedder = p_enable; } bool Window::is_clamped_to_embedder() const { + ERR_READ_THREAD_GUARD_V(false); return clamp_to_embedder; } @@ -2248,6 +2374,7 @@ void Window::set_unparent_when_invisible(bool p_unparent) { } void Window::set_layout_direction(Window::LayoutDirection p_direction) { + ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX((int)p_direction, 4); layout_dir = p_direction; @@ -2255,10 +2382,12 @@ void Window::set_layout_direction(Window::LayoutDirection p_direction) { } Window::LayoutDirection Window::get_layout_direction() const { + ERR_READ_THREAD_GUARD_V(LAYOUT_DIRECTION_INHERITED); return layout_dir; } bool Window::is_layout_rtl() const { + ERR_READ_THREAD_GUARD_V(false); if (layout_dir == LAYOUT_DIRECTION_INHERITED) { if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) { return true; @@ -2299,6 +2428,7 @@ bool Window::is_layout_rtl() const { } void Window::set_auto_translate(bool p_enable) { + ERR_MAIN_THREAD_GUARD; if (p_enable == auto_translate) { return; } @@ -2309,14 +2439,17 @@ void Window::set_auto_translate(bool p_enable) { } bool Window::is_auto_translating() const { + ERR_READ_THREAD_GUARD_V(false); return auto_translate; } Transform2D Window::get_final_transform() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); return window_transform * stretch_transform * global_canvas_transform; } Transform2D Window::get_screen_transform_internal(bool p_absolute_position) const { + ERR_READ_THREAD_GUARD_V(Transform2D()); Transform2D embedder_transform; if (get_embedder()) { embedder_transform.translate_local(get_position()); @@ -2328,6 +2461,7 @@ Transform2D Window::get_screen_transform_internal(bool p_absolute_position) cons } Transform2D Window::get_popup_base_transform() const { + ERR_READ_THREAD_GUARD_V(Transform2D()); if (is_embedding_subwindows()) { return Transform2D(); } diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index c4b5834ad5..a9d8fc38a4 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -72,14 +72,14 @@ void Font::_bind_methods() { // Drawing string. ClassDB::bind_method(D_METHOD("set_cache_capacity", "single_line", "multi_line"), &Font::set_cache_capacity); - ClassDB::bind_method(D_METHOD("get_string_size", "text", "alignment", "width", "font_size", "jst_flags", "direction", "orientation"), &Font::get_string_size, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "alignment", "width", "font_size", "max_lines", "brk_flags", "jst_flags", "direction", "orientation"), &Font::get_multiline_string_size, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("get_string_size", "text", "alignment", "width", "font_size", "justification_flags", "direction", "orientation"), &Font::get_string_size, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "alignment", "width", "font_size", "max_lines", "brk_flags", "justification_flags", "direction", "orientation"), &Font::get_multiline_string_size, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "alignment", "width", "font_size", "modulate", "jst_flags", "direction", "orientation"), &Font::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "modulate", "brk_flags", "jst_flags", "direction", "orientation"), &Font::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "alignment", "width", "font_size", "modulate", "justification_flags", "direction", "orientation"), &Font::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "modulate", "brk_flags", "justification_flags", "direction", "orientation"), &Font::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("draw_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "size", "modulate", "jst_flags", "direction", "orientation"), &Font::draw_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("draw_multiline_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "size", "modulate", "brk_flags", "jst_flags", "direction", "orientation"), &Font::draw_multiline_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "size", "modulate", "justification_flags", "direction", "orientation"), &Font::draw_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "size", "modulate", "brk_flags", "justification_flags", "direction", "orientation"), &Font::draw_multiline_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); // Drawing char. ClassDB::bind_method(D_METHOD("get_char_size", "char", "font_size"), &Font::get_char_size); diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 8ed68626a8..6a4f7d082e 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -2967,8 +2967,24 @@ void TextMesh::_create_mesh_array(Array &p_arr) const { } if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { - for (int i = 0; i < lines_rid.size() - 1; i++) { - TS->shaped_text_fit_to_width(lines_rid[i], (width > 0) ? width : max_line_w, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA); + int jst_to_line = lines_rid.size(); + if (lines_rid.size() == 1 && jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) { + jst_to_line = lines_rid.size(); + } else { + if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) { + jst_to_line = lines_rid.size() - 1; + } + if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) { + for (int i = lines_rid.size() - 1; i >= 0; i--) { + if (TS->shaped_text_has_visible_chars(lines_rid[i])) { + jst_to_line = i; + break; + } + } + } + } + for (int i = 0; i < jst_to_line; i++) { + TS->shaped_text_fit_to_width(lines_rid[i], (width > 0) ? width : max_line_w, jst_flags); } } dirty_lines = false; @@ -3293,6 +3309,9 @@ void TextMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &TextMesh::set_autowrap_mode); ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &TextMesh::get_autowrap_mode); + ClassDB::bind_method(D_METHOD("set_justification_flags", "justification_flags"), &TextMesh::set_justification_flags); + ClassDB::bind_method(D_METHOD("get_justification_flags"), &TextMesh::get_justification_flags); + ClassDB::bind_method(D_METHOD("set_depth", "depth"), &TextMesh::set_depth); ClassDB::bind_method(D_METHOD("get_depth"), &TextMesh::get_depth); @@ -3335,6 +3354,7 @@ void TextMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing"); ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "justification_flags", PROPERTY_HINT_FLAGS, "Kashida Justification:1,Word Justification:2,Justify Only After Last Tab:8,Skip Last Line:32,Skip Last Line With Visible Characters:64,Do Not Skip Single Line:128"), "set_justification_flags", "get_justification_flags"); ADD_GROUP("Mesh", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001,suffix:m"), "set_pixel_size", "get_pixel_size"); @@ -3512,6 +3532,18 @@ TextServer::AutowrapMode TextMesh::get_autowrap_mode() const { return autowrap_mode; } +void TextMesh::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) { + if (jst_flags != p_flags) { + jst_flags = p_flags; + dirty_lines = true; + _request_update(); + } +} + +BitField<TextServer::JustificationFlag> TextMesh::get_justification_flags() const { + return jst_flags; +} + void TextMesh::set_depth(real_t p_depth) { if (depth != p_depth) { depth = MAX(p_depth, 0.0); diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index e62f26b17c..5b788b726e 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -591,6 +591,7 @@ private: Ref<Font> font_override; TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF; + BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE; float width = 500.0; float line_spacing = 0.f; Point2 lbl_offset; @@ -649,6 +650,9 @@ public: void set_autowrap_mode(TextServer::AutowrapMode p_mode); TextServer::AutowrapMode get_autowrap_mode() const; + void set_justification_flags(BitField<TextServer::JustificationFlag> p_flags); + BitField<TextServer::JustificationFlag> get_justification_flags() const; + void set_text_direction(TextServer::Direction p_text_direction); TextServer::Direction get_text_direction() const; diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 191b71f301..33f11371fe 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -82,7 +82,7 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("set_justification_flags", "flags"), &TextParagraph::set_justification_flags); ClassDB::bind_method(D_METHOD("get_justification_flags"), &TextParagraph::get_justification_flags); - ADD_PROPERTY(PropertyInfo(Variant::INT, "justification_flags", PROPERTY_HINT_FLAGS, "Kashida Justification,Word Justification,Trim Edge Spaces After Justification,Justify Only After Last Tab,Constrain Ellipsis"), "set_justification_flags", "get_justification_flags"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "justification_flags", PROPERTY_HINT_FLAGS, "Kashida Justification:1,Word Justification:2,Trim Edge Spaces After Justification:4,Justify Only After Last Tab:8,Constrain Ellipsis:16,Skip Last Line:32,Skip Last Line With Visible Characters:64,Do Not Skip Single Line:128"), "set_justification_flags", "get_justification_flags"); ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &TextParagraph::set_text_overrun_behavior); ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &TextParagraph::get_text_overrun_behavior); @@ -232,9 +232,25 @@ void TextParagraph::_shape_lines() { overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS); } if (alignment == HORIZONTAL_ALIGNMENT_FILL) { + int jst_to_line = visible_lines; + if (lines_rid.size() == 1 && jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) { + jst_to_line = lines_rid.size(); + } else { + if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) { + jst_to_line = visible_lines - 1; + } + if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) { + for (int i = visible_lines - 1; i >= 0; i--) { + if (TS->shaped_text_has_visible_chars(lines_rid[i])) { + jst_to_line = i; + break; + } + } + } + } for (int i = 0; i < (int)lines_rid.size(); i++) { float line_w = (i <= dropcap_lines) ? (width - h_offset) : width; - if (i < visible_lines - 1 || (int)lines_rid.size() == 1) { + if (i < jst_to_line) { TS->shaped_text_fit_to_width(lines_rid[i], line_w, jst_flags); } else if (i == (visible_lines - 1)) { TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], line_w, overrun_flags); @@ -245,9 +261,25 @@ void TextParagraph::_shape_lines() { } } else { // Autowrap disabled. + int jst_to_line = lines_rid.size(); + if (lines_rid.size() == 1 && jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) { + jst_to_line = lines_rid.size(); + } else { + if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) { + jst_to_line = lines_rid.size() - 1; + } + if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) { + for (int i = lines_rid.size() - 1; i >= 0; i--) { + if (TS->shaped_text_has_visible_chars(lines_rid[i])) { + jst_to_line = i; + break; + } + } + } + } for (int i = 0; i < (int)lines_rid.size(); i++) { float line_w = (i <= dropcap_lines) ? (width - h_offset) : width; - if (alignment == HORIZONTAL_ALIGNMENT_FILL) { + if (i < jst_to_line && alignment == HORIZONTAL_ALIGNMENT_FILL) { TS->shaped_text_fit_to_width(lines_rid[i], line_w, jst_flags); overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE); TS->shaped_text_overrun_trim_to_width(lines_rid[i], line_w, overrun_flags); diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index 5723dabeca..31fc95ac02 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -55,7 +55,7 @@ private: int max_lines_visible = -1; BitField<TextServer::LineBreakFlag> brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND; - BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; + BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE; TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING; HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; diff --git a/servers/display_server.cpp b/servers/display_server.cpp index dc8c308389..516f2f1be6 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -660,6 +660,7 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("screen_get_max_scale"), &DisplayServer::screen_get_max_scale); ClassDB::bind_method(D_METHOD("screen_get_refresh_rate", "screen"), &DisplayServer::screen_get_refresh_rate, DEFVAL(SCREEN_OF_MAIN_WINDOW)); ClassDB::bind_method(D_METHOD("screen_get_pixel", "position"), &DisplayServer::screen_get_pixel); + ClassDB::bind_method(D_METHOD("screen_get_image", "screen"), &DisplayServer::screen_get_image, DEFVAL(SCREEN_OF_MAIN_WINDOW)); ClassDB::bind_method(D_METHOD("screen_set_orientation", "orientation", "screen"), &DisplayServer::screen_set_orientation, DEFVAL(SCREEN_OF_MAIN_WINDOW)); ClassDB::bind_method(D_METHOD("screen_get_orientation", "screen"), &DisplayServer::screen_get_orientation, DEFVAL(SCREEN_OF_MAIN_WINDOW)); diff --git a/servers/display_server.h b/servers/display_server.h index 8f2df62094..d8e67b4f92 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -301,6 +301,7 @@ public: } virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0; virtual Color screen_get_pixel(const Point2i &p_position) const { return Color(); }; + virtual Ref<Image> screen_get_image(int p_screen = SCREEN_OF_MAIN_WINDOW) const { return Ref<Image>(); }; virtual bool is_touchscreen_available() const; // Keep the ScreenOrientation enum values in sync with the `display/window/handheld/orientation` diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp index c1078d9493..511417664f 100644 --- a/servers/text/text_server_extension.cpp +++ b/servers/text/text_server_extension.cpp @@ -251,7 +251,7 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND(_shaped_text_substr, "shaped", "start", "length"); GDVIRTUAL_BIND(_shaped_text_get_parent, "shaped"); - GDVIRTUAL_BIND(_shaped_text_fit_to_width, "shaped", "width", "jst_flags"); + GDVIRTUAL_BIND(_shaped_text_fit_to_width, "shaped", "width", "justification_flags"); GDVIRTUAL_BIND(_shaped_text_tab_align, "shaped", "tab_stops"); GDVIRTUAL_BIND(_shaped_text_shape, "shaped"); diff --git a/servers/text_server.cpp b/servers/text_server.cpp index 36c3137472..30c601bc0d 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -404,11 +404,12 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("shaped_text_substr", "shaped", "start", "length"), &TextServer::shaped_text_substr); ClassDB::bind_method(D_METHOD("shaped_text_get_parent", "shaped"), &TextServer::shaped_text_get_parent); - ClassDB::bind_method(D_METHOD("shaped_text_fit_to_width", "shaped", "width", "jst_flags"), &TextServer::shaped_text_fit_to_width, DEFVAL(JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA)); + ClassDB::bind_method(D_METHOD("shaped_text_fit_to_width", "shaped", "width", "justification_flags"), &TextServer::shaped_text_fit_to_width, DEFVAL(JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA)); ClassDB::bind_method(D_METHOD("shaped_text_tab_align", "shaped", "tab_stops"), &TextServer::shaped_text_tab_align); ClassDB::bind_method(D_METHOD("shaped_text_shape", "shaped"), &TextServer::shaped_text_shape); ClassDB::bind_method(D_METHOD("shaped_text_is_ready", "shaped"), &TextServer::shaped_text_is_ready); + ClassDB::bind_method(D_METHOD("shaped_text_has_visible_chars", "shaped"), &TextServer::shaped_text_has_visible_chars); ClassDB::bind_method(D_METHOD("shaped_text_get_glyphs", "shaped"), &TextServer::_shaped_text_get_glyphs_wrapper); ClassDB::bind_method(D_METHOD("shaped_text_sort_logical", "shaped"), &TextServer::_shaped_text_sort_logical_wrapper); @@ -497,6 +498,9 @@ void TextServer::_bind_methods() { BIND_BITFIELD_FLAG(JUSTIFICATION_TRIM_EDGE_SPACES); BIND_BITFIELD_FLAG(JUSTIFICATION_AFTER_LAST_TAB); BIND_BITFIELD_FLAG(JUSTIFICATION_CONSTRAIN_ELLIPSIS); + BIND_BITFIELD_FLAG(JUSTIFICATION_SKIP_LAST_LINE); + BIND_BITFIELD_FLAG(JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS); + BIND_BITFIELD_FLAG(JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE); /* AutowrapMode */ BIND_ENUM_CONSTANT(AUTOWRAP_OFF); @@ -684,6 +688,21 @@ void TextServer::draw_hex_code_box(const RID &p_canvas, int64_t p_size, const Ve } } +bool TextServer::shaped_text_has_visible_chars(const RID &p_shaped) const { + int v_size = shaped_text_get_glyph_count(p_shaped); + if (v_size == 0) { + return false; + } + + const Glyph *glyphs = shaped_text_get_glyphs(p_shaped); + for (int i = 0; i < v_size; i++) { + if (glyphs[i].index != 0 && (glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL) { + return true; + } + } + return false; +} + PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped, const PackedFloat32Array &p_width, int64_t p_start, bool p_once, BitField<TextServer::LineBreakFlag> p_break_flags) const { PackedInt32Array lines; diff --git a/servers/text_server.h b/servers/text_server.h index af6efb8c7d..170249bf77 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -81,6 +81,9 @@ public: JUSTIFICATION_TRIM_EDGE_SPACES = 1 << 2, JUSTIFICATION_AFTER_LAST_TAB = 1 << 3, JUSTIFICATION_CONSTRAIN_ELLIPSIS = 1 << 4, + JUSTIFICATION_SKIP_LAST_LINE = 1 << 5, + JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS = 1 << 6, + JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE = 1 << 7, }; enum VisibleCharactersBehavior { @@ -441,6 +444,7 @@ public: virtual bool shaped_text_update_justification_ops(const RID &p_shaped) = 0; virtual bool shaped_text_is_ready(const RID &p_shaped) const = 0; + bool shaped_text_has_visible_chars(const RID &p_shaped) const; virtual const Glyph *shaped_text_get_glyphs(const RID &p_shaped) const = 0; TypedArray<Dictionary> _shaped_text_get_glyphs_wrapper(const RID &p_shaped) const; |