diff options
Diffstat (limited to 'core')
36 files changed, 1031 insertions, 711 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index b4ca31d77a..c872ae2162 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -32,6 +32,7 @@ #include "core/core_bind.h" #include "core/core_string_names.h" +#include "core/input/input_map.h" #include "core/io/file_access_network.h" #include "core/io/file_access_pack.h" #include "core/io/marshalls.h" @@ -124,6 +125,11 @@ void ProjectSettings::set_restart_if_changed(const String &p_name, bool p_restar props[p_name].restart_if_changed = p_restart; } +void ProjectSettings::set_as_basic(const String &p_name, bool p_basic) { + ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); + props[p_name].basic = p_basic; +} + void ProjectSettings::set_ignore_value_in_docs(const String &p_name, bool p_ignore) { ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); #ifdef DEBUG_METHODS_ENABLED @@ -269,6 +275,10 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const { vc.flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE; } + if (v->basic) { + vc.flags |= PROPERTY_USAGE_EDITOR_BASIC_SETTING; + } + if (v->restart_if_changed) { vc.flags |= PROPERTY_USAGE_RESTART_IF_CHANGED; } @@ -278,7 +288,7 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const { for (Set<_VCSort>::Element *E = vclist.front(); E; E = E->next()) { String prop_info_name = E->get().name; int dot = prop_info_name.find("."); - if (dot != -1) { + if (dot != -1 && !custom_prop_info.has(prop_info_name)) { prop_info_name = prop_info_name.substr(0, dot); } @@ -908,7 +918,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust } } -Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed, bool p_ignore_value_in_docs) { +Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed, bool p_ignore_value_in_docs, bool p_basic) { Variant ret; if (!ProjectSettings::get_singleton()->has_setting(p_var)) { ProjectSettings::get_singleton()->set(p_var, p_default); @@ -917,6 +927,7 @@ Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restar ProjectSettings::get_singleton()->set_initial_value(p_var, p_default); ProjectSettings::get_singleton()->set_builtin_order(p_var); + ProjectSettings::get_singleton()->set_as_basic(p_var, p_basic); ProjectSettings::get_singleton()->set_restart_if_changed(p_var, p_restart_if_changed); ProjectSettings::get_singleton()->set_ignore_value_in_docs(p_var, p_ignore_value_in_docs); return ret; @@ -1047,29 +1058,47 @@ void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("save_custom", "file"), &ProjectSettings::_save_custom_bnd); } +void ProjectSettings::_add_builtin_input_map() { + if (InputMap::get_singleton()) { + OrderedHashMap<String, List<Ref<InputEvent>>> builtins = InputMap::get_singleton()->get_builtins(); + + for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) { + Array events; + + // Convert list of input events into array + for (List<Ref<InputEvent>>::Element *I = E.get().front(); I; I = I->next()) { + events.push_back(I->get()); + } + + Dictionary action; + action["deadzone"] = Variant(0.5f); + action["events"] = events; + + String action_name = "input/" + E.key(); + GLOBAL_DEF(action_name, action); + input_presets.push_back(action_name); + } + } +} + ProjectSettings::ProjectSettings() { // Initialization of engine variables should be done in the setup() method, // so that the values can be overridden from project.godot or project.binary. singleton = this; - Array events; - Dictionary action; - Ref<InputEventKey> key; - Ref<InputEventJoypadButton> joyb; - - GLOBAL_DEF("application/config/name", ""); - GLOBAL_DEF("application/config/description", ""); + GLOBAL_DEF_BASIC("application/config/name", ""); + GLOBAL_DEF_BASIC("application/config/description", ""); custom_prop_info["application/config/description"] = PropertyInfo(Variant::STRING, "application/config/description", PROPERTY_HINT_MULTILINE_TEXT); - GLOBAL_DEF("application/run/main_scene", ""); + GLOBAL_DEF_BASIC("application/run/main_scene", ""); custom_prop_info["application/run/main_scene"] = PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "*.tscn,*.scn,*.res"); GLOBAL_DEF("application/run/disable_stdout", false); GLOBAL_DEF("application/run/disable_stderr", false); GLOBAL_DEF("application/config/use_custom_user_dir", false); GLOBAL_DEF("application/config/custom_user_dir_name", ""); GLOBAL_DEF("application/config/project_settings_override", ""); - GLOBAL_DEF("audio/default_bus_layout", "res://default_bus_layout.tres"); - custom_prop_info["audio/default_bus_layout"] = PropertyInfo(Variant::STRING, "audio/default_bus_layout", PROPERTY_HINT_FILE, "*.tres"); + GLOBAL_DEF_BASIC("audio/buses/default_bus_layout", "res://default_bus_layout.tres"); + custom_prop_info["audio/buses/default_bus_layout"] = PropertyInfo(Variant::STRING, "audio/buses/default_bus_layout", PROPERTY_HINT_FILE, "*.tres"); PackedStringArray extensions = PackedStringArray(); extensions.push_back("gd"); @@ -1078,172 +1107,16 @@ ProjectSettings::ProjectSettings() { } extensions.push_back("shader"); - GLOBAL_DEF("editor/search_in_file_extensions", extensions); - custom_prop_info["editor/search_in_file_extensions"] = PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/search_in_file_extensions"); - - GLOBAL_DEF("editor/script_templates_search_path", "res://script_templates"); - custom_prop_info["editor/script_templates_search_path"] = PropertyInfo(Variant::STRING, "editor/script_templates_search_path", PROPERTY_HINT_DIR); - - action = Dictionary(); - action["deadzone"] = Variant(0.5f); - events = Array(); - key.instance(); - key->set_keycode(KEY_ENTER); - events.push_back(key); - key.instance(); - key->set_keycode(KEY_KP_ENTER); - events.push_back(key); - key.instance(); - key->set_keycode(KEY_SPACE); - events.push_back(key); - joyb.instance(); - joyb->set_button_index(JOY_BUTTON_A); - events.push_back(joyb); - action["events"] = events; - GLOBAL_DEF("input/ui_accept", action); - input_presets.push_back("input/ui_accept"); - - action = Dictionary(); - action["deadzone"] = Variant(0.5f); - events = Array(); - key.instance(); - key->set_keycode(KEY_SPACE); - events.push_back(key); - joyb.instance(); - joyb->set_button_index(JOY_BUTTON_Y); - events.push_back(joyb); - action["events"] = events; - GLOBAL_DEF("input/ui_select", action); - input_presets.push_back("input/ui_select"); - - action = Dictionary(); - action["deadzone"] = Variant(0.5f); - events = Array(); - key.instance(); - key->set_keycode(KEY_ESCAPE); - events.push_back(key); - joyb.instance(); - joyb->set_button_index(JOY_BUTTON_B); - events.push_back(joyb); - action["events"] = events; - GLOBAL_DEF("input/ui_cancel", action); - input_presets.push_back("input/ui_cancel"); - - action = Dictionary(); - action["deadzone"] = Variant(0.5f); - events = Array(); - key.instance(); - key->set_keycode(KEY_TAB); - events.push_back(key); - action["events"] = events; - GLOBAL_DEF("input/ui_focus_next", action); - input_presets.push_back("input/ui_focus_next"); - - action = Dictionary(); - action["deadzone"] = Variant(0.5f); - events = Array(); - key.instance(); - key->set_keycode(KEY_TAB); - key->set_shift(true); - events.push_back(key); - action["events"] = events; - GLOBAL_DEF("input/ui_focus_prev", action); - input_presets.push_back("input/ui_focus_prev"); - - action = Dictionary(); - action["deadzone"] = Variant(0.5f); - events = Array(); - key.instance(); - key->set_keycode(KEY_LEFT); - events.push_back(key); - joyb.instance(); - joyb->set_button_index(JOY_BUTTON_DPAD_LEFT); - events.push_back(joyb); - action["events"] = events; - GLOBAL_DEF("input/ui_left", action); - input_presets.push_back("input/ui_left"); - - action = Dictionary(); - action["deadzone"] = Variant(0.5f); - events = Array(); - key.instance(); - key->set_keycode(KEY_RIGHT); - events.push_back(key); - joyb.instance(); - joyb->set_button_index(JOY_BUTTON_DPAD_RIGHT); - events.push_back(joyb); - action["events"] = events; - GLOBAL_DEF("input/ui_right", action); - input_presets.push_back("input/ui_right"); - - action = Dictionary(); - action["deadzone"] = Variant(0.5f); - events = Array(); - key.instance(); - key->set_keycode(KEY_UP); - events.push_back(key); - joyb.instance(); - joyb->set_button_index(JOY_BUTTON_DPAD_UP); - events.push_back(joyb); - action["events"] = events; - GLOBAL_DEF("input/ui_up", action); - input_presets.push_back("input/ui_up"); - - action = Dictionary(); - action["deadzone"] = Variant(0.5f); - events = Array(); - key.instance(); - key->set_keycode(KEY_DOWN); - events.push_back(key); - joyb.instance(); - joyb->set_button_index(JOY_BUTTON_DPAD_DOWN); - events.push_back(joyb); - action["events"] = events; - GLOBAL_DEF("input/ui_down", action); - input_presets.push_back("input/ui_down"); - - action = Dictionary(); - action["deadzone"] = Variant(0.5f); - events = Array(); - key.instance(); - key->set_keycode(KEY_PAGEUP); - events.push_back(key); - action["events"] = events; - GLOBAL_DEF("input/ui_page_up", action); - input_presets.push_back("input/ui_page_up"); - - action = Dictionary(); - action["deadzone"] = Variant(0.5f); - events = Array(); - key.instance(); - key->set_keycode(KEY_PAGEDOWN); - events.push_back(key); - action["events"] = events; - GLOBAL_DEF("input/ui_page_down", action); - input_presets.push_back("input/ui_page_down"); - - action = Dictionary(); - action["deadzone"] = Variant(0.5f); - events = Array(); - key.instance(); - key->set_keycode(KEY_HOME); - events.push_back(key); - action["events"] = events; - GLOBAL_DEF("input/ui_home", action); - input_presets.push_back("input/ui_home"); - - action = Dictionary(); - action["deadzone"] = Variant(0.5f); - events = Array(); - key.instance(); - key->set_keycode(KEY_END); - events.push_back(key); - action["events"] = events; - GLOBAL_DEF("input/ui_end", action); - input_presets.push_back("input/ui_end"); + GLOBAL_DEF("editor/script/search_in_file_extensions", extensions); + custom_prop_info["editor/script/search_in_file_extensions"] = PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions"); + + GLOBAL_DEF("editor/script/templates_search_path", "res://script_templates"); + custom_prop_info["editor/script/templates_search_path"] = PropertyInfo(Variant::STRING, "editor/script/templates_search_path", PROPERTY_HINT_DIR); + + _add_builtin_input_map(); custom_prop_info["display/window/handheld/orientation"] = PropertyInfo(Variant::STRING, "display/window/handheld/orientation", PROPERTY_HINT_ENUM, "landscape,portrait,reverse_landscape,reverse_portrait,sensor_landscape,sensor_portrait,sensor"); - custom_prop_info["rendering/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/threads/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded"); + custom_prop_info["rendering/driver/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/driver/threads/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded"); GLOBAL_DEF("physics/2d/run_on_thread", false); GLOBAL_DEF("physics/3d/run_on_thread", false); diff --git a/core/config/project_settings.h b/core/config/project_settings.h index 645506f302..ed8fb19fa0 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -58,6 +58,7 @@ protected: struct VariantContainer { int order = 0; bool persist = false; + bool basic = false; Variant variant; Variant initial; bool hide_from_editor = false; @@ -115,6 +116,9 @@ protected: Error _setup(const String &p_path, const String &p_main_pack, bool p_upwards = false); + void _add_builtin_input_map(); + +protected: static void _bind_methods(); public: @@ -128,6 +132,7 @@ public: String globalize_path(const String &p_path) const; void set_initial_value(const String &p_name, const Variant &p_value); + void set_as_basic(const String &p_name, bool p_basic); void set_restart_if_changed(const String &p_name, bool p_restart); void set_ignore_value_in_docs(const String &p_name, bool p_ignore); bool get_ignore_value_in_docs(const String &p_name) const; @@ -174,11 +179,16 @@ public: }; //not a macro any longer -Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed = false, bool p_ignore_value_in_docs = false); +Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed = false, bool p_ignore_value_in_docs = false, bool p_basic = false); #define GLOBAL_DEF(m_var, m_value) _GLOBAL_DEF(m_var, m_value) #define GLOBAL_DEF_RST(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true) #define GLOBAL_DEF_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, true) #define GLOBAL_DEF_RST_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true, true) #define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var) +#define GLOBAL_DEF_BASIC(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, false, true) +#define GLOBAL_DEF_RST_BASIC(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true, false, true) +#define GLOBAL_DEF_NOVAL_BASIC(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, true, true) +#define GLOBAL_DEF_RST_NOVAL_BASIC(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true, true, true) + #endif // PROJECT_SETTINGS_H diff --git a/core/core_bind.cpp b/core/core_bind.cpp index f568ee754d..0da6680a7b 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -135,7 +135,7 @@ void _ResourceLoader::_bind_methods() { ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &_ResourceLoader::load_threaded_get_status, DEFVAL(Array())); ClassDB::bind_method(D_METHOD("load_threaded_get", "path"), &_ResourceLoader::load_threaded_get); - ClassDB::bind_method(D_METHOD("load", "path", "type_hint", "no_cache"), &_ResourceLoader::load, DEFVAL(""), DEFVAL(CACHE_MODE_REUSE)); + ClassDB::bind_method(D_METHOD("load", "path", "type_hint", "cache_mode"), &_ResourceLoader::load, DEFVAL(""), DEFVAL(CACHE_MODE_REUSE)); ClassDB::bind_method(D_METHOD("get_recognized_extensions_for_type", "type"), &_ResourceLoader::get_recognized_extensions_for_type); ClassDB::bind_method(D_METHOD("set_abort_on_missing_resources", "abort"), &_ResourceLoader::set_abort_on_missing_resources); ClassDB::bind_method(D_METHOD("get_dependencies", "path"), &_ResourceLoader::get_dependencies); @@ -519,11 +519,19 @@ double _OS::get_unix_time() const { return OS::get_singleton()->get_unix_time(); } -void _OS::delay_usec(uint32_t p_usec) const { +/** This method uses a signed argument for better error reporting as it's used from the scripting API. */ +void _OS::delay_usec(int p_usec) const { + ERR_FAIL_COND_MSG( + p_usec < 0, + vformat("Can't sleep for %d microseconds. The delay provided must be greater than or equal to 0 microseconds.", p_usec)); OS::get_singleton()->delay_usec(p_usec); } -void _OS::delay_msec(uint32_t p_msec) const { +/** This method uses a signed argument for better error reporting as it's used from the scripting API. */ +void _OS::delay_msec(int p_msec) const { + ERR_FAIL_COND_MSG( + p_msec < 0, + vformat("Can't sleep for %d milliseconds. The delay provided must be greater than or equal to 0 milliseconds.", p_msec)); OS::get_singleton()->delay_usec(int64_t(p_msec) * 1000); } @@ -681,22 +689,6 @@ String _OS::get_unique_id() const { return OS::get_singleton()->get_unique_id(); } -int _OS::get_tablet_driver_count() const { - return OS::get_singleton()->get_tablet_driver_count(); -} - -String _OS::get_tablet_driver_name(int p_driver) const { - return OS::get_singleton()->get_tablet_driver_name(p_driver); -} - -String _OS::get_current_tablet_driver() const { - return OS::get_singleton()->get_current_tablet_driver(); -} - -void _OS::set_current_tablet_driver(const String &p_driver) { - OS::get_singleton()->set_current_tablet_driver(p_driver); -} - _OS *_OS::singleton = nullptr; void _OS::_bind_methods() { @@ -780,19 +772,12 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("request_permissions"), &_OS::request_permissions); ClassDB::bind_method(D_METHOD("get_granted_permissions"), &_OS::get_granted_permissions); - ClassDB::bind_method(D_METHOD("get_tablet_driver_count"), &_OS::get_tablet_driver_count); - ClassDB::bind_method(D_METHOD("get_tablet_driver_name", "idx"), &_OS::get_tablet_driver_name); - ClassDB::bind_method(D_METHOD("get_current_tablet_driver"), &_OS::get_current_tablet_driver); - ClassDB::bind_method(D_METHOD("set_current_tablet_driver", "name"), &_OS::set_current_tablet_driver); - ADD_PROPERTY(PropertyInfo(Variant::INT, "exit_code"), "set_exit_code", "get_exit_code"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "low_processor_usage_mode"), "set_low_processor_usage_mode", "is_in_low_processor_usage_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "low_processor_usage_mode_sleep_usec"), "set_low_processor_usage_mode_sleep_usec", "get_low_processor_usage_mode_sleep_usec"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "tablet_driver"), "set_current_tablet_driver", "get_current_tablet_driver"); // Those default values need to be specified for the docs generator, // to avoid using values from the documentation writer's own OS instance. - ADD_PROPERTY_DEFAULT("tablet_driver", ""); ADD_PROPERTY_DEFAULT("exit_code", 0); ADD_PROPERTY_DEFAULT("low_processor_usage_mode", false); ADD_PROPERTY_DEFAULT("low_processor_usage_mode_sleep_usec", 6900); @@ -1254,6 +1239,11 @@ Error _File::open(const String &p_path, ModeFlags p_mode_flags) { return err; } +void _File::flush() { + ERR_FAIL_COND_MSG(!f, "File must be opened before flushing."); + f->flush(); +} + void _File::close() { if (f) { memdelete(f); @@ -1547,6 +1537,7 @@ void _File::_bind_methods() { ClassDB::bind_method(D_METHOD("open_compressed", "path", "mode_flags", "compression_mode"), &_File::open_compressed, DEFVAL(0)); ClassDB::bind_method(D_METHOD("open", "path", "flags"), &_File::open); + ClassDB::bind_method(D_METHOD("flush"), &_File::flush); ClassDB::bind_method(D_METHOD("close"), &_File::close); ClassDB::bind_method(D_METHOD("get_path"), &_File::get_path); ClassDB::bind_method(D_METHOD("get_path_absolute"), &_File::get_path_absolute); @@ -1984,7 +1975,7 @@ void _Thread::_start_func(void *ud) { } Error _Thread::start(Object *p_instance, const StringName &p_method, const Variant &p_userdata, Priority p_priority) { - ERR_FAIL_COND_V_MSG(active, ERR_ALREADY_IN_USE, "Thread already started."); + ERR_FAIL_COND_V_MSG(active.is_set(), ERR_ALREADY_IN_USE, "Thread already started."); ERR_FAIL_COND_V(!p_instance, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_method == StringName(), ERR_INVALID_PARAMETER); ERR_FAIL_INDEX_V(p_priority, PRIORITY_MAX, ERR_INVALID_PARAMETER); @@ -1993,7 +1984,7 @@ Error _Thread::start(Object *p_instance, const StringName &p_method, const Varia target_method = p_method; target_instance = p_instance; userdata = p_userdata; - active = true; + active.set(); Ref<_Thread> *ud = memnew(Ref<_Thread>(this)); @@ -2009,14 +2000,14 @@ String _Thread::get_id() const { } bool _Thread::is_active() const { - return active; + return active.is_set(); } Variant _Thread::wait_to_finish() { - ERR_FAIL_COND_V_MSG(!active, Variant(), "Thread must be active to wait for its completion."); + ERR_FAIL_COND_V_MSG(!active.is_set(), Variant(), "Thread must be active to wait for its completion."); thread.wait_to_finish(); Variant r = ret; - active = false; + active.clear(); target_method = StringName(); target_instance = nullptr; userdata = Variant(); diff --git a/core/core_bind.h b/core/core_bind.h index 44e620085a..8a4885b82b 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -40,6 +40,7 @@ #include "core/os/os.h" #include "core/os/semaphore.h" #include "core/os/thread.h" +#include "core/templates/safe_refcount.h" class _ResourceLoader : public Object { GDCLASS(_ResourceLoader, Object); @@ -210,8 +211,8 @@ public: uint64_t get_static_memory_usage() const; uint64_t get_static_memory_peak_usage() const; - void delay_usec(uint32_t p_usec) const; - void delay_msec(uint32_t p_msec) const; + void delay_usec(int p_usec) const; + void delay_msec(int p_msec) const; uint32_t get_ticks_msec() const; uint64_t get_ticks_usec() const; @@ -247,11 +248,6 @@ public: bool request_permissions(); Vector<String> get_granted_permissions() const; - int get_tablet_driver_count() const; - String get_tablet_driver_name(int p_driver) const; - String get_current_tablet_driver() const; - void set_current_tablet_driver(const String &p_driver); - static _OS *get_singleton() { return singleton; } _OS() { singleton = this; } @@ -386,6 +382,7 @@ public: Error open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode = COMPRESSION_FASTLZ); Error open(const String &p_path, ModeFlags p_mode_flags); // open a file. + void flush(); // Flush a file (write its buffer to disk). void close(); // Close a file. bool is_open() const; // True when file is open. @@ -558,7 +555,7 @@ class _Thread : public Reference { protected: Variant ret; Variant userdata; - volatile bool active = false; + SafeFlag active; Object *target_instance = nullptr; StringName target_method; Thread thread; diff --git a/core/error/error_macros.h b/core/error/error_macros.h index 8eb6217ce8..f909a67d55 100644 --- a/core/error/error_macros.h +++ b/core/error/error_macros.h @@ -33,6 +33,8 @@ #include "core/typedefs.h" +#include "core/templates/safe_refcount.h" + class String; enum ErrorHandlerType { @@ -577,10 +579,10 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li */ #define WARN_DEPRECATED \ if (true) { \ - static volatile bool warning_shown = false; \ - if (!warning_shown) { \ + static SafeFlag warning_shown; \ + if (!warning_shown.is_set()) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", ERR_HANDLER_WARNING); \ - warning_shown = true; \ + warning_shown.set(); \ } \ } else \ ((void)0) @@ -590,10 +592,10 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li */ #define WARN_DEPRECATED_MSG(m_msg) \ if (true) { \ - static volatile bool warning_shown = false; \ - if (!warning_shown) { \ + static SafeFlag warning_shown; \ + if (!warning_shown.is_set()) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", DEBUG_STR(m_msg), ERR_HANDLER_WARNING); \ - warning_shown = true; \ + warning_shown.set(); \ } \ } else \ ((void)0) diff --git a/core/input/input.cpp b/core/input/input.cpp index 047aeb47fd..94a18b5b4f 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -97,11 +97,11 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed); ClassDB::bind_method(D_METHOD("is_mouse_button_pressed", "button"), &Input::is_mouse_button_pressed); ClassDB::bind_method(D_METHOD("is_joy_button_pressed", "device", "button"), &Input::is_joy_button_pressed); - ClassDB::bind_method(D_METHOD("is_action_pressed", "action"), &Input::is_action_pressed); - ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action"), &Input::is_action_just_pressed); - ClassDB::bind_method(D_METHOD("is_action_just_released", "action"), &Input::is_action_just_released); - ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &Input::get_action_strength); - ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action"), &Input::get_action_strength); + ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "exact_match"), &Input::is_action_pressed, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action", "exact_match"), &Input::is_action_just_pressed, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_just_released", "action", "exact_match"), &Input::is_action_just_released, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact_match"), &Input::get_action_strength, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action", "exact_match"), &Input::get_action_strength, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_axis", "negative_action", "positive_action"), &Input::get_axis); ClassDB::bind_method(D_METHOD("get_vector", "negative_x", "positive_x", "negative_y", "positive_y", "deadzone"), &Input::get_vector, DEFVAL(-1.0f)); ClassDB::bind_method(D_METHOD("add_joy_mapping", "mapping", "update_existing"), &Input::add_joy_mapping, DEFVAL(false)); @@ -240,16 +240,20 @@ bool Input::is_joy_button_pressed(int p_device, int p_button) const { return joy_buttons_pressed.has(_combine_device(p_button, p_device)); } -bool Input::is_action_pressed(const StringName &p_action) const { - return action_state.has(p_action) && action_state[p_action].pressed; +bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const { + return action_state.has(p_action) && action_state[p_action].pressed && (p_exact ? action_state[p_action].exact : true); } -bool Input::is_action_just_pressed(const StringName &p_action) const { +bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return false; } + if (p_exact && E->get().exact == false) { + return false; + } + if (Engine::get_singleton()->is_in_physics_frame()) { return E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames(); } else { @@ -257,12 +261,16 @@ bool Input::is_action_just_pressed(const StringName &p_action) const { } } -bool Input::is_action_just_released(const StringName &p_action) const { +bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return false; } + if (p_exact && E->get().exact == false) { + return false; + } + if (Engine::get_singleton()->is_in_physics_frame()) { return !E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames(); } else { @@ -270,21 +278,29 @@ bool Input::is_action_just_released(const StringName &p_action) const { } } -float Input::get_action_strength(const StringName &p_action) const { +float Input::get_action_strength(const StringName &p_action, bool p_exact) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return 0.0f; } + if (p_exact && E->get().exact == false) { + return 0.0f; + } + return E->get().strength; } -float Input::get_action_raw_strength(const StringName &p_action) const { +float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return 0.0f; } + if (p_exact && E->get().exact == false) { + return 0.0f; + } + return E->get().raw_strength; } @@ -588,20 +604,21 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em } } - for (const Map<StringName, InputMap::Action>::Element *E = InputMap::get_singleton()->get_action_map().front(); E; E = E->next()) { - if (InputMap::get_singleton()->event_is_action(p_event, E->key())) { - // Save the action's state - if (!p_event->is_echo() && is_action_pressed(E->key()) != p_event->is_action_pressed(E->key())) { + for (OrderedHashMap<StringName, InputMap::Action>::ConstElement E = InputMap::get_singleton()->get_action_map().front(); E; E = E.next()) { + if (InputMap::get_singleton()->event_is_action(p_event, E.key())) { + // If not echo and action pressed state has changed + if (!p_event->is_echo() && is_action_pressed(E.key(), false) != p_event->is_action_pressed(E.key())) { Action action; action.physics_frame = Engine::get_singleton()->get_physics_frames(); action.process_frame = Engine::get_singleton()->get_process_frames(); - action.pressed = p_event->is_action_pressed(E->key()); + action.pressed = p_event->is_action_pressed(E.key()); action.strength = 0.0f; action.raw_strength = 0.0f; - action_state[E->key()] = action; + action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key(), true); + action_state[E.key()] = action; } - action_state[E->key()].strength = p_event->get_action_strength(E->key()); - action_state[E->key()].raw_strength = p_event->get_action_raw_strength(E->key()); + action_state[E.key()].strength = p_event->get_action_strength(E.key()); + action_state[E.key()].raw_strength = p_event->get_action_raw_strength(E.key()); } } diff --git a/core/input/input.h b/core/input/input.h index 445b7ff0cf..0e3af42381 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -116,6 +116,7 @@ private: uint64_t physics_frame; uint64_t process_frame; bool pressed; + bool exact; float strength; float raw_strength; }; @@ -261,11 +262,11 @@ public: bool is_key_pressed(int p_keycode) const; bool is_mouse_button_pressed(int p_button) const; bool is_joy_button_pressed(int p_device, int p_button) const; - bool is_action_pressed(const StringName &p_action) const; - bool is_action_just_pressed(const StringName &p_action) const; - bool is_action_just_released(const StringName &p_action) const; - float get_action_strength(const StringName &p_action) const; - float get_action_raw_strength(const StringName &p_action) const; + bool is_action_pressed(const StringName &p_action, bool p_exact = false) const; + bool is_action_just_pressed(const StringName &p_action, bool p_exact = false) const; + bool is_action_just_released(const StringName &p_action, bool p_exact = false) const; + float get_action_strength(const StringName &p_action, bool p_exact = false) const; + float get_action_raw_strength(const StringName &p_action, bool p_exact = false) const; float get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const; Vector2 get_vector(const StringName &p_negative_x, const StringName &p_positive_x, const StringName &p_negative_y, const StringName &p_positive_y, float p_deadzone = -1.0f) const; diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index c91dc4d067..c6910d2b1f 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -44,31 +44,31 @@ int InputEvent::get_device() const { return device; } -bool InputEvent::is_action(const StringName &p_action) const { - return InputMap::get_singleton()->event_is_action(Ref<InputEvent>((InputEvent *)this), p_action); +bool InputEvent::is_action(const StringName &p_action, bool p_exact_match) const { + return InputMap::get_singleton()->event_is_action(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match); } -bool InputEvent::is_action_pressed(const StringName &p_action, bool p_allow_echo) const { +bool InputEvent::is_action_pressed(const StringName &p_action, bool p_allow_echo, bool p_exact_match) const { bool pressed; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, &pressed); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, &pressed, nullptr, nullptr); return valid && pressed && (p_allow_echo || !is_echo()); } -bool InputEvent::is_action_released(const StringName &p_action) const { +bool InputEvent::is_action_released(const StringName &p_action, bool p_exact_match) const { bool pressed; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, &pressed); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, &pressed, nullptr, nullptr); return valid && !pressed; } -float InputEvent::get_action_strength(const StringName &p_action) const { +float InputEvent::get_action_strength(const StringName &p_action, bool p_exact_match) const { float strength; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, nullptr, &strength); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, nullptr, &strength, nullptr); return valid ? strength : 0.0f; } -float InputEvent::get_action_raw_strength(const StringName &p_action) const { +float InputEvent::get_action_raw_strength(const StringName &p_action, bool p_exact_match) const { float raw_strength; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, nullptr, nullptr, &raw_strength); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, nullptr, nullptr, &raw_strength); return valid ? raw_strength : 0.0f; } @@ -100,10 +100,10 @@ void InputEvent::_bind_methods() { ClassDB::bind_method(D_METHOD("set_device", "device"), &InputEvent::set_device); ClassDB::bind_method(D_METHOD("get_device"), &InputEvent::get_device); - ClassDB::bind_method(D_METHOD("is_action", "action"), &InputEvent::is_action); - ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "allow_echo"), &InputEvent::is_action_pressed, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("is_action_released", "action"), &InputEvent::is_action_released); - ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &InputEvent::get_action_strength); + ClassDB::bind_method(D_METHOD("is_action", "action", "exact_match"), &InputEvent::is_action, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "allow_echo", "exact_match"), &InputEvent::is_action_pressed, DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_released", "action", "exact_match"), &InputEvent::is_action_released, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact_match"), &InputEvent::get_action_strength, DEFVAL(false)); ClassDB::bind_method(D_METHOD("is_pressed"), &InputEvent::is_pressed); ClassDB::bind_method(D_METHOD("is_echo"), &InputEvent::is_echo); @@ -384,6 +384,31 @@ String InputEventKey::to_string() { return vformat("InputEventKey: keycode=%s mods=%s physical=%s pressed=%s echo=%s", kc, mods, physical, p, e); } +Ref<InputEventKey> InputEventKey::create_reference(uint32_t p_keycode) { + Ref<InputEventKey> ie; + ie.instance(); + ie->set_keycode(p_keycode & KEY_CODE_MASK); + ie->set_unicode(p_keycode & KEY_CODE_MASK); + + if (p_keycode & KEY_MASK_SHIFT) { + ie->set_shift(true); + } + if (p_keycode & KEY_MASK_ALT) { + ie->set_alt(true); + } + if (p_keycode & KEY_MASK_CTRL) { + ie->set_control(true); + } + if (p_keycode & KEY_MASK_CMD) { + ie->set_command(true); + } + if (p_keycode & KEY_MASK_META) { + ie->set_metakey(true); + } + + return ie; +} + bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const { Ref<InputEventKey> key = p_event; if (key.is_null()) { @@ -1011,6 +1036,14 @@ String InputEventJoypadButton::to_string() { return "InputEventJoypadButton : button_index=" + itos(button_index) + ", pressed=" + (pressed ? "true" : "false") + ", pressure=" + String(Variant(pressure)); } +Ref<InputEventJoypadButton> InputEventJoypadButton::create_reference(int p_btn_index) { + Ref<InputEventJoypadButton> ie; + ie.instance(); + ie->set_button_index(p_btn_index); + + return ie; +} + void InputEventJoypadButton::_bind_methods() { ClassDB::bind_method(D_METHOD("set_button_index", "button_index"), &InputEventJoypadButton::set_button_index); ClassDB::bind_method(D_METHOD("get_button_index"), &InputEventJoypadButton::get_button_index); diff --git a/core/input/input_event.h b/core/input/input_event.h index a354119cf9..df81b9fc75 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -128,11 +128,11 @@ public: void set_device(int p_device); int get_device() const; - bool is_action(const StringName &p_action) const; - bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false) const; - bool is_action_released(const StringName &p_action) const; - float get_action_strength(const StringName &p_action) const; - float get_action_raw_strength(const StringName &p_action) const; + bool is_action(const StringName &p_action, bool p_exact_match = false) const; + bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false, bool p_exact_match = false) const; + bool is_action_released(const StringName &p_action, bool p_exact_match = false) const; + float get_action_strength(const StringName &p_action, bool p_exact_match = false) const; + float get_action_raw_strength(const StringName &p_action, bool p_exact_match = false) const; // To be removed someday, since they do not make sense for all events virtual bool is_pressed() const; @@ -260,6 +260,8 @@ public: virtual String as_text() const override; virtual String to_string() override; + static Ref<InputEventKey> create_reference(uint32_t p_keycode_with_modifier_masks); + InputEventKey() {} }; @@ -406,6 +408,8 @@ public: virtual String as_text() const override; virtual String to_string() override; + static Ref<InputEventJoypadButton> create_reference(int p_btn_index); + InputEventJoypadButton() {} }; diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index 53ed925c1f..b31c431ead 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -50,7 +50,7 @@ void InputMap::_bind_methods() { ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event); ClassDB::bind_method(D_METHOD("action_erase_events", "action"), &InputMap::action_erase_events); ClassDB::bind_method(D_METHOD("action_get_events", "action"), &InputMap::_action_get_events); - ClassDB::bind_method(D_METHOD("event_is_action", "event", "action"), &InputMap::event_is_action); + ClassDB::bind_method(D_METHOD("event_is_action", "event", "action", "exact_match"), &InputMap::event_is_action, DEFVAL(false)); ClassDB::bind_method(D_METHOD("load_from_project_settings"), &InputMap::load_from_project_settings); } @@ -88,14 +88,14 @@ List<StringName> InputMap::get_actions() const { return actions; } - for (Map<StringName, Action>::Element *E = input_map.front(); E; E = E->next()) { - actions.push_back(E->key()); + for (OrderedHashMap<StringName, Action>::Element E = input_map.front(); E; E = E.next()) { + actions.push_back(E.key()); } return actions; } -List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength) const { +List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const { ERR_FAIL_COND_V(!p_event.is_valid(), nullptr); for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) { @@ -106,7 +106,9 @@ List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Re int device = e->get_device(); if (device == ALL_DEVICES || device == p_event->get_device()) { - if (e->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) { + if (p_exact_match && e->shortcut_match(p_event)) { + return E; + } else if (!p_exact_match && e->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) { return E; } } @@ -134,7 +136,7 @@ void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event) { ERR_FAIL_COND_MSG(p_event.is_null(), "It's not a reference to a valid InputEvent object."); ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); - if (_find_event(input_map[p_action], p_event)) { + if (_find_event(input_map[p_action], p_event, true)) { return; // Already addded. } @@ -143,13 +145,13 @@ void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent bool InputMap::action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event) { ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); - return (_find_event(input_map[p_action], p_event) != nullptr); + return (_find_event(input_map[p_action], p_event, true) != nullptr); } void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event) { ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); - List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event); + List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event, true); if (E) { input_map[p_action].inputs.erase(E); if (Input::get_singleton()->is_action_pressed(p_action)) { @@ -177,20 +179,20 @@ Array InputMap::_action_get_events(const StringName &p_action) { } const List<Ref<InputEvent>> *InputMap::action_get_events(const StringName &p_action) { - const Map<StringName, Action>::Element *E = input_map.find(p_action); + const OrderedHashMap<StringName, Action>::Element E = input_map.find(p_action); if (!E) { return nullptr; } - return &E->get().inputs; + return &E.get().inputs; } -bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action) const { - return event_get_action_status(p_event, p_action); +bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match) const { + return event_get_action_status(p_event, p_action, p_exact_match); } -bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed, float *p_strength, float *p_raw_strength) const { - Map<StringName, Action>::Element *E = input_map.find(p_action); +bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const { + OrderedHashMap<StringName, Action>::Element E = input_map.find(p_action); ERR_FAIL_COND_V_MSG(!E, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); Ref<InputEventAction> input_event_action = p_event; @@ -207,7 +209,7 @@ bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const Str bool pressed; float strength; float raw_strength; - List<Ref<InputEvent>>::Element *event = _find_event(E->get(), p_event, &pressed, &strength, &raw_strength); + List<Ref<InputEvent>>::Element *event = _find_event(E.get(), p_event, p_exact_match, &pressed, &strength, &raw_strength); if (event != nullptr) { if (p_pressed != nullptr) { *p_pressed = pressed; @@ -224,7 +226,7 @@ bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const Str } } -const Map<StringName, InputMap::Action> &InputMap::get_action_map() const { +const OrderedHashMap<StringName, InputMap::Action> &InputMap::get_action_map() const { return input_map; } @@ -258,84 +260,444 @@ void InputMap::load_from_project_settings() { } } +struct _BuiltinActionDisplayName { + const char *name; + const char *display_name; +}; + +static const _BuiltinActionDisplayName _builtin_action_display_names[] = { + /* clang-format off */ + { "ui_accept", TTRC("Accept") }, + { "ui_select", TTRC("Select") }, + { "ui_cancel", TTRC("Cancel") }, + { "ui_focus_next", TTRC("Focus Next") }, + { "ui_focus_prev", TTRC("Focus Prev") }, + { "ui_left", TTRC("Left") }, + { "ui_right", TTRC("Right") }, + { "ui_up", TTRC("Up") }, + { "ui_down", TTRC("Down") }, + { "ui_page_up", TTRC("Page Up") }, + { "ui_page_down", TTRC("Page Down") }, + { "ui_home", TTRC("Home") }, + { "ui_end", TTRC("End") }, + { "ui_cut", TTRC("Cut") }, + { "ui_copy", TTRC("Copy") }, + { "ui_paste", TTRC("Paste") }, + { "ui_undo", TTRC("Undo") }, + { "ui_redo", TTRC("Redo") }, + { "ui_text_completion_query", TTRC("Completion Query") }, + { "ui_text_newline", TTRC("New Line") }, + { "ui_text_newline_blank", TTRC("New Blank Line") }, + { "ui_text_newline_above", TTRC("New Line Above") }, + { "ui_text_indent", TTRC("Indent") }, + { "ui_text_dedent", TTRC("Dedent") }, + { "ui_text_backspace", TTRC("Backspace") }, + { "ui_text_backspace_word", TTRC("Backspace Word") }, + { "ui_text_backspace_word.OSX", TTRC("Backspace Word") }, + { "ui_text_backspace_all_to_left", TTRC("Backspace all to Left") }, + { "ui_text_backspace_all_to_left.OSX", TTRC("Backspace all to Left") }, + { "ui_text_delete", TTRC("Delete") }, + { "ui_text_delete_word", TTRC("Delete Word") }, + { "ui_text_delete_word.OSX", TTRC("Delete Word") }, + { "ui_text_delete_all_to_right", TTRC("Delete all to Right") }, + { "ui_text_delete_all_to_right.OSX", TTRC("Delete all to Right") }, + { "ui_text_caret_left", TTRC("Caret Left") }, + { "ui_text_caret_word_left", TTRC("Caret Word Left") }, + { "ui_text_caret_word_left.OSX", TTRC("Caret Word Left") }, + { "ui_text_caret_right", TTRC("Caret Right") }, + { "ui_text_caret_word_right", TTRC("Caret Word Right") }, + { "ui_text_caret_word_right.OSX", TTRC("Caret Word Right") }, + { "ui_text_caret_up", TTRC("Caret Up") }, + { "ui_text_caret_down", TTRC("Caret Down") }, + { "ui_text_caret_line_start", TTRC("Caret Line Start") }, + { "ui_text_caret_line_start.OSX", TTRC("Caret Line Start") }, + { "ui_text_caret_line_end", TTRC("Caret Line End") }, + { "ui_text_caret_line_end.OSX", TTRC("Caret Line End") }, + { "ui_text_caret_page_up", TTRC("Caret Page Up") }, + { "ui_text_caret_page_down", TTRC("Caret Page Down") }, + { "ui_text_caret_document_start", TTRC("Caret Document Start") }, + { "ui_text_caret_document_start.OSX", TTRC("Caret Document Start") }, + { "ui_text_caret_document_end", TTRC("Caret Document End") }, + { "ui_text_caret_document_end.OSX", TTRC("Caret Document End") }, + { "ui_text_scroll_up", TTRC("Scroll Up") }, + { "ui_text_scroll_up.OSX", TTRC("Scroll Up") }, + { "ui_text_scroll_down", TTRC("Scroll Down") }, + { "ui_text_scroll_down.OSX", TTRC("Scroll Down") }, + { "ui_text_select_all", TTRC("Select All") }, + { "ui_text_toggle_insert_mode", TTRC("Toggle Insert Mode") }, + { "ui_graph_duplicate", TTRC("Duplicate Nodes") }, + { "ui_graph_delete", TTRC("Delete Nodes") }, + { "ui_filedialog_up_one_level", TTRC("Go Up One Level") }, + { "ui_filedialog_refresh", TTRC("Refresh") }, + { "ui_filedialog_show_hidden", TTRC("Show Hidden") }, + { "ui_swap_input_direction ", TTRC("Swap Input Direction") }, + { "", TTRC("")} + /* clang-format on */ +}; + +String InputMap::get_builtin_display_name(const String &p_name) const { + int len = sizeof(_builtin_action_display_names) / sizeof(_BuiltinActionDisplayName); + + for (int i = 0; i < len; i++) { + if (_builtin_action_display_names[i].name == p_name) { + return RTR(_builtin_action_display_names[i].display_name); + } + } + + return p_name; +} + +const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { + // Return cache if it has already been built. + if (default_builtin_cache.size()) { + return default_builtin_cache; + } + + List<Ref<InputEvent>> inputs; + inputs.push_back(InputEventKey::create_reference(KEY_ENTER)); + inputs.push_back(InputEventKey::create_reference(KEY_KP_ENTER)); + inputs.push_back(InputEventKey::create_reference(KEY_SPACE)); + default_builtin_cache.insert("ui_accept", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventJoypadButton::create_reference(JOY_BUTTON_Y)); + inputs.push_back(InputEventKey::create_reference(KEY_SPACE)); + default_builtin_cache.insert("ui_select", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_ESCAPE)); + default_builtin_cache.insert("ui_cancel", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_TAB)); + default_builtin_cache.insert("ui_focus_next", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_TAB | KEY_MASK_SHIFT)); + default_builtin_cache.insert("ui_focus_prev", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_LEFT)); + inputs.push_back(InputEventJoypadButton::create_reference(JOY_BUTTON_DPAD_LEFT)); + default_builtin_cache.insert("ui_left", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_RIGHT)); + inputs.push_back(InputEventJoypadButton::create_reference(JOY_BUTTON_DPAD_RIGHT)); + default_builtin_cache.insert("ui_right", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_UP)); + inputs.push_back(InputEventJoypadButton::create_reference(JOY_BUTTON_DPAD_UP)); + default_builtin_cache.insert("ui_up", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_DOWN)); + inputs.push_back(InputEventJoypadButton::create_reference(JOY_BUTTON_DPAD_DOWN)); + default_builtin_cache.insert("ui_down", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_PAGEUP)); + default_builtin_cache.insert("ui_page_up", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_PAGEDOWN)); + default_builtin_cache.insert("ui_page_down", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_HOME)); + default_builtin_cache.insert("ui_home", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_END)); + default_builtin_cache.insert("ui_end", inputs); + + // ///// UI basic Shortcuts ///// + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_X | KEY_MASK_CMD)); + inputs.push_back(InputEventKey::create_reference(KEY_DELETE | KEY_MASK_SHIFT)); + default_builtin_cache.insert("ui_cut", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_C | KEY_MASK_CMD)); + inputs.push_back(InputEventKey::create_reference(KEY_INSERT | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_copy", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_V | KEY_MASK_CMD)); + inputs.push_back(InputEventKey::create_reference(KEY_INSERT | KEY_MASK_SHIFT)); + default_builtin_cache.insert("ui_paste", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_Z | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_undo", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_Y | KEY_MASK_CMD)); + inputs.push_back(InputEventKey::create_reference(KEY_Z | KEY_MASK_CMD | KEY_MASK_SHIFT)); + default_builtin_cache.insert("ui_redo", inputs); + + // ///// UI Text Input Shortcuts ///// + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_SPACE | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_completion_query", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_TAB)); + default_builtin_cache.insert("ui_text_completion_accept", inputs); + + // Newlines + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_ENTER)); + inputs.push_back(InputEventKey::create_reference(KEY_KP_ENTER)); + default_builtin_cache.insert("ui_text_newline", inputs); + + inputs = List<Ref<InputEvent>>(); + + inputs.push_back(InputEventKey::create_reference(KEY_ENTER | KEY_MASK_CMD)); + inputs.push_back(InputEventKey::create_reference(KEY_KP_ENTER | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_newline_blank", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_ENTER | KEY_MASK_SHIFT | KEY_MASK_CMD)); + inputs.push_back(InputEventKey::create_reference(KEY_KP_ENTER | KEY_MASK_SHIFT | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_newline_above", inputs); + + // Indentation + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_TAB)); + default_builtin_cache.insert("ui_text_indent", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_TAB | KEY_MASK_SHIFT)); + default_builtin_cache.insert("ui_text_dedent", inputs); + + // Text Backspace and Delete + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE)); + default_builtin_cache.insert("ui_text_backspace", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_backspace_word", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE | KEY_MASK_ALT)); + default_builtin_cache.insert("ui_text_backspace_word.OSX", inputs); + + inputs = List<Ref<InputEvent>>(); + default_builtin_cache.insert("ui_text_backspace_all_to_left", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_backspace_all_to_left.OSX", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_DELETE)); + default_builtin_cache.insert("ui_text_delete", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_DELETE | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_delete_word", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_DELETE | KEY_MASK_ALT)); + default_builtin_cache.insert("ui_text_delete_word.OSX", inputs); + + inputs = List<Ref<InputEvent>>(); + default_builtin_cache.insert("ui_text_delete_all_to_right", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_DELETE | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_delete_all_to_right.OSX", inputs); + + // Text Caret Movement Left/Right + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_LEFT)); + default_builtin_cache.insert("ui_text_caret_left", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_LEFT | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_caret_word_left", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_LEFT | KEY_MASK_ALT)); + default_builtin_cache.insert("ui_text_caret_word_left.OSX", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_RIGHT)); + default_builtin_cache.insert("ui_text_caret_right", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_RIGHT | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_caret_word_right", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_RIGHT | KEY_MASK_ALT)); + default_builtin_cache.insert("ui_text_caret_word_right.OSX", inputs); + + // Text Caret Movement Up/Down + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_UP)); + default_builtin_cache.insert("ui_text_caret_up", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_DOWN)); + default_builtin_cache.insert("ui_text_caret_down", inputs); + + // Text Caret Movement Line Start/End + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_HOME)); + default_builtin_cache.insert("ui_text_caret_line_start", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_A | KEY_MASK_CTRL)); + inputs.push_back(InputEventKey::create_reference(KEY_LEFT | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_caret_line_start.OSX", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_END)); + default_builtin_cache.insert("ui_text_caret_line_end", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_E | KEY_MASK_CTRL)); + inputs.push_back(InputEventKey::create_reference(KEY_RIGHT | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_caret_line_end.OSX", inputs); + // Text Caret Movement Page Up/Down + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_PAGEUP)); + default_builtin_cache.insert("ui_text_caret_page_up", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_PAGEDOWN)); + default_builtin_cache.insert("ui_text_caret_page_down", inputs); + + // Text Caret Movement Document Start/End + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_HOME | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_caret_document_start", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_UP | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_caret_document_start.OSX", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_END | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_caret_document_end", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_DOWN | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_caret_document_end.OSX", inputs); + + // Text Scrolling + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_UP | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_scroll_up", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_UP | KEY_MASK_CMD | KEY_MASK_ALT)); + default_builtin_cache.insert("ui_text_scroll_up.OSX", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_DOWN | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_scroll_down", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_DOWN | KEY_MASK_CMD | KEY_MASK_ALT)); + default_builtin_cache.insert("ui_text_scroll_down.OSX", inputs); + + // Text Misc + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_A | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_text_select_all", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_INSERT)); + default_builtin_cache.insert("ui_text_toggle_insert_mode", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_MENU)); + default_builtin_cache.insert("ui_menu", inputs); + + // ///// UI Graph Shortcuts ///// + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_D | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_graph_duplicate", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_DELETE)); + default_builtin_cache.insert("ui_graph_delete", inputs); + + // ///// UI File Dialog Shortcuts ///// + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_BACKSPACE)); + default_builtin_cache.insert("ui_filedialog_up_one_level", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_F5)); + default_builtin_cache.insert("ui_filedialog_refresh", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_H)); + default_builtin_cache.insert("ui_filedialog_show_hidden", inputs); + + inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(KEY_QUOTELEFT | KEY_MASK_CMD)); + default_builtin_cache.insert("ui_swap_input_direction", inputs); + + return default_builtin_cache; +} + void InputMap::load_default() { - Ref<InputEventKey> key; - - add_action("ui_accept"); - key.instance(); - key->set_keycode(KEY_ENTER); - action_add_event("ui_accept", key); - - key.instance(); - key->set_keycode(KEY_KP_ENTER); - action_add_event("ui_accept", key); - - key.instance(); - key->set_keycode(KEY_SPACE); - action_add_event("ui_accept", key); - - add_action("ui_select"); - key.instance(); - key->set_keycode(KEY_SPACE); - action_add_event("ui_select", key); - - add_action("ui_cancel"); - key.instance(); - key->set_keycode(KEY_ESCAPE); - action_add_event("ui_cancel", key); - - add_action("ui_focus_next"); - key.instance(); - key->set_keycode(KEY_TAB); - action_add_event("ui_focus_next", key); - - add_action("ui_focus_prev"); - key.instance(); - key->set_keycode(KEY_TAB); - key->set_shift(true); - action_add_event("ui_focus_prev", key); - - add_action("ui_left"); - key.instance(); - key->set_keycode(KEY_LEFT); - action_add_event("ui_left", key); - - add_action("ui_right"); - key.instance(); - key->set_keycode(KEY_RIGHT); - action_add_event("ui_right", key); - - add_action("ui_up"); - key.instance(); - key->set_keycode(KEY_UP); - action_add_event("ui_up", key); - - add_action("ui_down"); - key.instance(); - key->set_keycode(KEY_DOWN); - action_add_event("ui_down", key); - - add_action("ui_page_up"); - key.instance(); - key->set_keycode(KEY_PAGEUP); - action_add_event("ui_page_up", key); - - add_action("ui_page_down"); - key.instance(); - key->set_keycode(KEY_PAGEDOWN); - action_add_event("ui_page_down", key); - - add_action("ui_home"); - key.instance(); - key->set_keycode(KEY_HOME); - action_add_event("ui_home", key); - - add_action("ui_end"); - key.instance(); - key->set_keycode(KEY_END); - action_add_event("ui_end", key); - - //set("display/window/handheld/orientation", "landscape"); + OrderedHashMap<String, List<Ref<InputEvent>>> builtins = get_builtins(); + + // List of Builtins which have an override for OSX. + Vector<String> osx_builtins; + for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) { + if (String(E.key()).ends_with(".OSX")) { + // Strip .OSX from name: some_input_name.OSX -> some_input_name + osx_builtins.push_back(String(E.key()).split(".")[0]); + } + } + + for (OrderedHashMap<String, List<Ref<InputEvent>>>::Element E = builtins.front(); E; E = E.next()) { + String fullname = E.key(); + String name = fullname.split(".")[0]; + String override_for = fullname.split(".").size() > 1 ? fullname.split(".")[1] : ""; + +#ifdef APPLE_STYLE_KEYS + if (osx_builtins.has(name) && override_for != "OSX") { + // Name has osx builtin but this particular one is for non-osx systems - so skip. + continue; + } +#else + if (override_for == "OSX") { + // Override for OSX - not needed on non-osx platforms. + continue; + } +#endif + + add_action(name); + + List<Ref<InputEvent>> inputs = E.get(); + for (List<Ref<InputEvent>>::Element *I = inputs.front(); I; I = I->next()) { + Ref<InputEventKey> iek = I->get(); + + // For the editor, only add keyboard actions. + if (iek.is_valid()) { + action_add_event(fullname, I->get()); + } + } + } } InputMap::InputMap() { diff --git a/core/input/input_map.h b/core/input/input_map.h index 1646e7fadb..99c71e1e53 100644 --- a/core/input/input_map.h +++ b/core/input/input_map.h @@ -33,6 +33,8 @@ #include "core/input/input_event.h" #include "core/object/class_db.h" +#include "core/object/object.h" +#include "core/templates/ordered_hash_map.h" class InputMap : public Object { GDCLASS(InputMap, Object); @@ -52,9 +54,10 @@ public: private: static InputMap *singleton; - mutable Map<StringName, Action> input_map; + mutable OrderedHashMap<StringName, Action> input_map; + OrderedHashMap<String, List<Ref<InputEvent>>> default_builtin_cache; - List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; + List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; Array _action_get_events(const StringName &p_action); Array _get_actions(); @@ -78,13 +81,17 @@ public: void action_erase_events(const StringName &p_action); const List<Ref<InputEvent>> *action_get_events(const StringName &p_action); - bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action) const; - bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; + bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const; + bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; - const Map<StringName, Action> &get_action_map() const; + const OrderedHashMap<StringName, Action> &get_action_map() const; void load_from_project_settings(); void load_default(); + String get_builtin_display_name(const String &p_name) const; + // Use an Ordered Map so insertion order is preserved. We want the elements to be 'grouped' somewhat. + const OrderedHashMap<String, List<Ref<InputEvent>>> &get_builtins(); + InputMap(); }; diff --git a/core/io/image.cpp b/core/io/image.cpp index 986c29b539..5d46d75efe 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -2985,6 +2985,26 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) { _set_color_at_ofs(data.ptrw(), ofs, p_color); } +void Image::adjust_bcs(float p_brightness, float p_contrast, float p_saturation) { + uint8_t *w = data.ptrw(); + uint32_t pixel_size = get_format_pixel_size(format); + uint32_t pixel_count = data.size() / pixel_size; + + for (uint32_t i = 0; i < pixel_count; i++) { + Color c = _get_color_at_ofs(w, i); + Vector3 rgb(c.r, c.g, c.b); + + rgb *= p_brightness; + rgb = Vector3(0.5, 0.5, 0.5).lerp(rgb, p_contrast); + float center = (rgb.x + rgb.y + rgb.z) / 3.0; + rgb = Vector3(center, center, center).lerp(rgb, p_saturation); + c.r = rgb.x; + c.g = rgb.y; + c.b = rgb.z; + _set_color_at_ofs(w, i, c); + } +} + Image::UsedChannels Image::detect_used_channels(CompressSource p_source) { ERR_FAIL_COND_V(data.size() == 0, USED_CHANNELS_RGBA); ERR_FAIL_COND_V(is_compressed(), USED_CHANNELS_RGBA); @@ -3132,6 +3152,8 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pixelv", "point", "color"), &Image::set_pixelv); ClassDB::bind_method(D_METHOD("set_pixel", "x", "y", "color"), &Image::set_pixel); + ClassDB::bind_method(D_METHOD("adjust_bcs", "brightness", "contrast", "saturation"), &Image::adjust_bcs); + ClassDB::bind_method(D_METHOD("load_png_from_buffer", "buffer"), &Image::load_png_from_buffer); ClassDB::bind_method(D_METHOD("load_jpg_from_buffer", "buffer"), &Image::load_jpg_from_buffer); ClassDB::bind_method(D_METHOD("load_webp_from_buffer", "buffer"), &Image::load_webp_from_buffer); diff --git a/core/io/image.h b/core/io/image.h index b894be7df4..df8f9b35a1 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -390,6 +390,8 @@ public: void set_pixelv(const Point2i &p_point, const Color &p_color); void set_pixel(int p_x, int p_y, const Color &p_color); + void adjust_bcs(float p_brightness, float p_contrast, float p_saturation); + void set_as_black(); void copy_internals_from(const Ref<Image> &p_image) { diff --git a/core/io/ip.cpp b/core/io/ip.cpp index df95785000..e1d9c19f10 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -40,13 +40,13 @@ VARIANT_ENUM_CAST(IP::ResolverStatus); struct _IP_ResolverPrivate { struct QueueItem { - volatile IP::ResolverStatus status; + SafeNumeric<IP::ResolverStatus> status; IP_Address response; String hostname; IP::Type type; void clear() { - status = IP::RESOLVER_STATUS_NONE; + status.set(IP::RESOLVER_STATUS_NONE); response = IP_Address(); type = IP::TYPE_NONE; hostname = ""; @@ -61,7 +61,7 @@ struct _IP_ResolverPrivate { IP::ResolverID find_empty_id() const { for (int i = 0; i < IP::RESOLVER_MAX_QUERIES; i++) { - if (queue[i].status == IP::RESOLVER_STATUS_NONE) { + if (queue[i].status.get() == IP::RESOLVER_STATUS_NONE) { return i; } } @@ -77,15 +77,15 @@ struct _IP_ResolverPrivate { void resolve_queues() { for (int i = 0; i < IP::RESOLVER_MAX_QUERIES; i++) { - if (queue[i].status != IP::RESOLVER_STATUS_WAITING) { + if (queue[i].status.get() != IP::RESOLVER_STATUS_WAITING) { continue; } queue[i].response = IP::get_singleton()->resolve_hostname(queue[i].hostname, queue[i].type); if (!queue[i].response.is_valid()) { - queue[i].status = IP::RESOLVER_STATUS_ERROR; + queue[i].status.set(IP::RESOLVER_STATUS_ERROR); } else { - queue[i].status = IP::RESOLVER_STATUS_DONE; + queue[i].status.set(IP::RESOLVER_STATUS_DONE); } } } @@ -137,10 +137,10 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Typ resolver->queue[id].type = p_type; if (resolver->cache.has(key) && resolver->cache[key].is_valid()) { resolver->queue[id].response = resolver->cache[key]; - resolver->queue[id].status = IP::RESOLVER_STATUS_DONE; + resolver->queue[id].status.set(IP::RESOLVER_STATUS_DONE); } else { resolver->queue[id].response = IP_Address(); - resolver->queue[id].status = IP::RESOLVER_STATUS_WAITING; + resolver->queue[id].status.set(IP::RESOLVER_STATUS_WAITING); if (resolver->thread.is_started()) { resolver->sem.post(); } else { @@ -156,12 +156,12 @@ IP::ResolverStatus IP::get_resolve_item_status(ResolverID p_id) const { MutexLock lock(resolver->mutex); - if (resolver->queue[p_id].status == IP::RESOLVER_STATUS_NONE) { + if (resolver->queue[p_id].status.get() == IP::RESOLVER_STATUS_NONE) { ERR_PRINT("Condition status == IP::RESOLVER_STATUS_NONE"); resolver->mutex.unlock(); return IP::RESOLVER_STATUS_NONE; } - return resolver->queue[p_id].status; + return resolver->queue[p_id].status.get(); } IP_Address IP::get_resolve_item_address(ResolverID p_id) const { @@ -169,7 +169,7 @@ IP_Address IP::get_resolve_item_address(ResolverID p_id) const { MutexLock lock(resolver->mutex); - if (resolver->queue[p_id].status != IP::RESOLVER_STATUS_DONE) { + if (resolver->queue[p_id].status.get() != IP::RESOLVER_STATUS_DONE) { ERR_PRINT("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet."); resolver->mutex.unlock(); return IP_Address(); @@ -183,7 +183,7 @@ void IP::erase_resolve_item(ResolverID p_id) { MutexLock lock(resolver->mutex); - resolver->queue[p_id].status = IP::RESOLVER_STATUS_NONE; + resolver->queue[p_id].status.set(IP::RESOLVER_STATUS_NONE); } void IP::clear_cache(const String &p_hostname) { diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 588af0f0ae..cba9a47187 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -174,6 +174,10 @@ void ResourceFormatLoader::_bind_methods() { ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_resource_type", PropertyInfo(Variant::STRING, "path"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo("get_dependencies", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "add_types"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::INT, "rename_dependencies", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "renames"))); + + BIND_ENUM_CONSTANT(CACHE_MODE_IGNORE); + BIND_ENUM_CONSTANT(CACHE_MODE_REUSE); + BIND_ENUM_CONSTANT(CACHE_MODE_REPLACE); } /////////////////////////////////// @@ -968,11 +972,11 @@ void ResourceLoader::reload_translation_remaps() { } void ResourceLoader::load_translation_remaps() { - if (!ProjectSettings::get_singleton()->has_setting("locale/translation_remaps")) { + if (!ProjectSettings::get_singleton()->has_setting("internationalization/locale/translation_remaps")) { return; } - Dictionary remaps = ProjectSettings::get_singleton()->get("locale/translation_remaps"); + Dictionary remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps"); List<Variant> keys; remaps.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 38a756c52f..914d988caa 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -66,6 +66,8 @@ public: virtual ~ResourceFormatLoader() {} }; +VARIANT_ENUM_CAST(ResourceFormatLoader::CacheMode) + typedef void (*ResourceLoadErrorNotify)(void *p_ud, const String &p_text); typedef void (*DependencyErrorNotify)(void *p_ud, const String &p_loading, const String &p_which, const String &p_type); diff --git a/core/math/transform.h b/core/math/transform.h index 60da6f5593..1c05dbe554 100644 --- a/core/math/transform.h +++ b/core/math/transform.h @@ -51,8 +51,8 @@ public: void rotate(const Vector3 &p_axis, real_t p_phi); void rotate_basis(const Vector3 &p_axis, real_t p_phi); - void set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up); - Transform looking_at(const Vector3 &p_target, const Vector3 &p_up) const; + void set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)); + Transform looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)) const; void scale(const Vector3 &p_scale); Transform scaled(const Vector3 &p_scale) const; diff --git a/core/math/vector3.h b/core/math/vector3.h index 6b4ff3f9a8..377581bb45 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -110,6 +110,7 @@ struct Vector3 { _FORCE_INLINE_ Vector3 project(const Vector3 &p_to) const; _FORCE_INLINE_ real_t angle_to(const Vector3 &p_to) const; + _FORCE_INLINE_ real_t signed_angle_to(const Vector3 &p_to, const Vector3 &p_axis) const; _FORCE_INLINE_ Vector3 direction_to(const Vector3 &p_to) const; _FORCE_INLINE_ Vector3 slide(const Vector3 &p_normal) const; @@ -230,6 +231,13 @@ real_t Vector3::angle_to(const Vector3 &p_to) const { return Math::atan2(cross(p_to).length(), dot(p_to)); } +real_t Vector3::signed_angle_to(const Vector3 &p_to, const Vector3 &p_axis) const { + Vector3 cross_to = cross(p_to); + real_t unsigned_angle = Math::atan2(cross_to.length(), dot(p_to)); + real_t sign = cross_to.dot(p_axis); + return (sign < 0) ? -unsigned_angle : unsigned_angle; +} + Vector3 Vector3::direction_to(const Vector3 &p_to) const { Vector3 ret(p_to.x - x, p_to.y - y, p_to.z - z); ret.normalize(); diff --git a/core/object/object.cpp b/core/object/object.cpp index 8f2eed3200..1a9cce49d8 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -1727,7 +1727,7 @@ void *Object::get_script_instance_binding(int p_script_language_index) { if (!_script_instance_bindings[p_script_language_index]) { void *script_data = ScriptServer::get_language(p_script_language_index)->alloc_instance_binding_data(this); if (script_data) { - atomic_increment(&instance_binding_count); + instance_binding_count.increment(); _script_instance_bindings[p_script_language_index] = script_data; } } diff --git a/core/object/object.h b/core/object/object.h index b695ce9bc3..029478873d 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -37,6 +37,7 @@ #include "core/templates/hash_map.h" #include "core/templates/list.h" #include "core/templates/map.h" +#include "core/templates/safe_refcount.h" #include "core/templates/set.h" #include "core/templates/vmap.h" #include "core/variant/callable_bind.h" @@ -125,6 +126,7 @@ enum PropertyUsageFlags { PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 25, // Used in inspector to increment property when keyed in animation player PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 26, // when loading, the resource for this property can be set at the end of loading PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 27, // For Object properties, instantiate them when creating in editor. + PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 28, //for project or editor settings, show when basic settings are selected PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK, PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED, @@ -485,7 +487,7 @@ private: friend class Reference; bool type_is_reference = false; - uint32_t instance_binding_count = 0; + SafeNumeric<uint32_t> instance_binding_count; void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS]; Object(bool p_reference); diff --git a/core/object/reference.cpp b/core/object/reference.cpp index 71a52a9ba5..22e4e8a336 100644 --- a/core/object/reference.cpp +++ b/core/object/reference.cpp @@ -62,7 +62,7 @@ bool Reference::reference() { if (get_script_instance()) { get_script_instance()->refcount_incremented(); } - if (instance_binding_count > 0 && !ScriptServer::are_languages_finished()) { + if (instance_binding_count.get() > 0 && !ScriptServer::are_languages_finished()) { for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) { if (_script_instance_bindings[i]) { ScriptServer::get_language(i)->refcount_incremented_instance_binding(this); @@ -83,7 +83,7 @@ bool Reference::unreference() { bool script_ret = get_script_instance()->refcount_decremented(); die = die && script_ret; } - if (instance_binding_count > 0 && !ScriptServer::are_languages_finished()) { + if (instance_binding_count.get() > 0 && !ScriptServer::are_languages_finished()) { for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) { if (_script_instance_bindings[i]) { bool script_ret = ScriptServer::get_language(i)->refcount_decremented_instance_binding(this); diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index d0636e8b84..42fb0a0caf 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -275,6 +275,14 @@ void ScriptServer::save_global_classes() { gcarr.push_back(d); } + Array old; + if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) { + old = ProjectSettings::get_singleton()->get("_global_script_classes"); + } + if ((!old.is_empty() || gcarr.is_empty()) && gcarr.hash() == old.hash()) { + return; + } + if (gcarr.is_empty()) { if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) { ProjectSettings::get_singleton()->clear("_global_script_classes"); diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp index 3b1165b8f6..e8735e335c 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -532,7 +532,7 @@ void UndoRedo::_bind_methods() { ClassDB::bind_method(D_METHOD("get_history_count"), &UndoRedo::get_history_count); ClassDB::bind_method(D_METHOD("get_current_action"), &UndoRedo::get_current_action); - ClassDB::bind_method(D_METHOD("get_action_name"), &UndoRedo::get_action_name); + ClassDB::bind_method(D_METHOD("get_action_name", "id"), &UndoRedo::get_action_name); ClassDB::bind_method(D_METHOD("clear_history", "increase_version"), &UndoRedo::clear_history, DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_current_action_name"), &UndoRedo::get_current_action_name); diff --git a/core/os/memory.cpp b/core/os/memory.cpp index 14808c2ce6..5910cb0e7b 100644 --- a/core/os/memory.cpp +++ b/core/os/memory.cpp @@ -60,11 +60,11 @@ void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_d #endif #ifdef DEBUG_ENABLED -uint64_t Memory::mem_usage = 0; -uint64_t Memory::max_usage = 0; +SafeNumeric<uint64_t> Memory::mem_usage; +SafeNumeric<uint64_t> Memory::max_usage; #endif -uint64_t Memory::alloc_count = 0; +SafeNumeric<uint64_t> Memory::alloc_count; void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) { #ifdef DEBUG_ENABLED @@ -77,7 +77,7 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) { ERR_FAIL_COND_V(!mem, nullptr); - atomic_increment(&alloc_count); + alloc_count.increment(); if (prepad) { uint64_t *s = (uint64_t *)mem; @@ -86,8 +86,8 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) { uint8_t *s8 = (uint8_t *)mem; #ifdef DEBUG_ENABLED - atomic_add(&mem_usage, p_bytes); - atomic_exchange_if_greater(&max_usage, mem_usage); + uint64_t new_mem_usage = mem_usage.add(p_bytes); + max_usage.exchange_if_greater(new_mem_usage); #endif return s8 + PAD_ALIGN; } else { @@ -114,10 +114,10 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) { #ifdef DEBUG_ENABLED if (p_bytes > *s) { - atomic_add(&mem_usage, p_bytes - *s); - atomic_exchange_if_greater(&max_usage, mem_usage); + uint64_t new_mem_usage = mem_usage.add(p_bytes - *s); + max_usage.exchange_if_greater(new_mem_usage); } else { - atomic_sub(&mem_usage, *s - p_bytes); + mem_usage.sub(*s - p_bytes); } #endif @@ -156,14 +156,14 @@ void Memory::free_static(void *p_ptr, bool p_pad_align) { bool prepad = p_pad_align; #endif - atomic_decrement(&alloc_count); + alloc_count.decrement(); if (prepad) { mem -= PAD_ALIGN; #ifdef DEBUG_ENABLED uint64_t *s = (uint64_t *)mem; - atomic_sub(&mem_usage, *s); + mem_usage.sub(*s); #endif free(mem); @@ -178,7 +178,7 @@ uint64_t Memory::get_mem_available() { uint64_t Memory::get_mem_usage() { #ifdef DEBUG_ENABLED - return mem_usage; + return mem_usage.get(); #else return 0; #endif @@ -186,7 +186,7 @@ uint64_t Memory::get_mem_usage() { uint64_t Memory::get_mem_max_usage() { #ifdef DEBUG_ENABLED - return max_usage; + return max_usage.get(); #else return 0; #endif diff --git a/core/os/memory.h b/core/os/memory.h index c2ae3f4ae7..10e678103d 100644 --- a/core/os/memory.h +++ b/core/os/memory.h @@ -43,11 +43,11 @@ class Memory { Memory(); #ifdef DEBUG_ENABLED - static uint64_t mem_usage; - static uint64_t max_usage; + static SafeNumeric<uint64_t> mem_usage; + static SafeNumeric<uint64_t> max_usage; #endif - static uint64_t alloc_count; + static SafeNumeric<uint64_t> alloc_count; public: static void *alloc_static(size_t p_bytes, bool p_pad_align = false); diff --git a/core/os/os.h b/core/os/os.h index e02ce7d5ec..77a54ba68a 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -149,11 +149,6 @@ public: bool is_layered_allowed() const { return _allow_layered; } bool is_hidpi_allowed() const { return _allow_hidpi; } - virtual int get_tablet_driver_count() const { return 0; }; - virtual String get_tablet_driver_name(int p_driver) const { return ""; }; - virtual String get_current_tablet_driver() const { return ""; }; - virtual void set_current_tablet_driver(const String &p_driver){}; - void ensure_user_data_dir(); virtual MainLoop *get_main_loop() const = 0; diff --git a/core/os/thread.cpp b/core/os/thread.cpp index 936e5d5500..88744eed63 100644 --- a/core/os/thread.cpp +++ b/core/os/thread.cpp @@ -34,13 +34,15 @@ #if !defined(NO_THREADS) +#include "core/templates/safe_refcount.h" + Error (*Thread::set_name_func)(const String &) = nullptr; void (*Thread::set_priority_func)(Thread::Priority) = nullptr; void (*Thread::init_func)() = nullptr; void (*Thread::term_func)() = nullptr; Thread::ID Thread::main_thread_id = 1; -Thread::ID Thread::last_thread_id = 1; +SafeNumeric<Thread::ID> Thread::last_thread_id{ 1 }; thread_local Thread::ID Thread::caller_id = 1; void Thread::_set_platform_funcs( @@ -79,7 +81,7 @@ void Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_ std::thread empty_thread; thread.swap(empty_thread); } - id = atomic_increment(&last_thread_id); + id = last_thread_id.increment(); std::thread new_thread(&Thread::callback, this, p_settings, p_callback, p_user); thread.swap(new_thread); } diff --git a/core/os/thread.h b/core/os/thread.h index b5449d2ed6..76f5be182e 100644 --- a/core/os/thread.h +++ b/core/os/thread.h @@ -34,6 +34,7 @@ #include "core/typedefs.h" #if !defined(NO_THREADS) +#include "core/templates/safe_refcount.h" #include <thread> #endif @@ -61,7 +62,7 @@ private: friend class Main; static ID main_thread_id; - static ID last_thread_id; + static SafeNumeric<Thread::ID> last_thread_id; ID id = 0; static thread_local ID caller_id; diff --git a/core/os/threaded_array_processor.h b/core/os/threaded_array_processor.h index 9538ac5957..4f270001d3 100644 --- a/core/os/threaded_array_processor.h +++ b/core/os/threaded_array_processor.h @@ -40,7 +40,7 @@ template <class C, class U> struct ThreadArrayProcessData { uint32_t elements; - uint32_t index; + SafeNumeric<uint32_t> index; C *instance; U userdata; void (C::*method)(uint32_t, U); @@ -56,7 +56,7 @@ template <class T> void process_array_thread(void *ud) { T &data = *(T *)ud; while (true) { - uint32_t index = atomic_increment(&data.index); + uint32_t index = data.index.increment(); if (index >= data.elements) { break; } @@ -70,9 +70,9 @@ void thread_process_array(uint32_t p_elements, C *p_instance, M p_method, U p_us data.method = p_method; data.instance = p_instance; data.userdata = p_userdata; - data.index = 0; + data.index.set(0); data.elements = p_elements; - data.process(data.index); //process first, let threads increment for next + data.process(0); //process first, let threads increment for next int thread_count = OS::get_singleton()->get_processor_count(); Thread *threads = memnew_arr(Thread, thread_count); diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 5337d46aa3..0901944360 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -1191,14 +1191,14 @@ bool TranslationServer::_load_translations(const String &p_from) { } void TranslationServer::setup() { - String test = GLOBAL_DEF("locale/test", ""); + String test = GLOBAL_DEF("internationalization/locale/test", ""); test = test.strip_edges(); if (test != "") { set_locale(test); } else { set_locale(OS::get_singleton()->get_locale()); } - fallback = GLOBAL_DEF("locale/fallback", "en"); + fallback = GLOBAL_DEF("internationalization/locale/fallback", "en"); #ifdef TOOLS_ENABLED { String options = ""; @@ -1210,7 +1210,7 @@ void TranslationServer::setup() { options += locale_list[idx]; idx++; } - ProjectSettings::get_singleton()->set_custom_property_info("locale/fallback", PropertyInfo(Variant::STRING, "locale/fallback", PROPERTY_HINT_ENUM, options)); + ProjectSettings::get_singleton()->set_custom_property_info("internationalization/locale/fallback", PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_ENUM, options)); } #endif } @@ -1307,11 +1307,11 @@ void TranslationServer::_bind_methods() { void TranslationServer::load_translations() { String locale = get_locale(); - _load_translations("locale/translations"); //all - _load_translations("locale/translations_" + locale.substr(0, 2)); + _load_translations("internationalization/locale/translations"); //all + _load_translations("internationalization/locale/translations_" + locale.substr(0, 2)); if (locale.substr(0, 2) != locale) { - _load_translations("locale/translations_" + locale); + _load_translations("internationalization/locale/translations_" + locale); } } diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h index 4becb1be59..525d9e77cb 100644 --- a/core/templates/cowdata.h +++ b/core/templates/cowdata.h @@ -45,6 +45,10 @@ class CharString; template <class T, class V> class VMap; +#if !defined(NO_THREADS) +SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t) +#endif + template <class T> class CowData { template <class TV> @@ -60,12 +64,12 @@ private: // internal helpers - _FORCE_INLINE_ uint32_t *_get_refcount() const { + _FORCE_INLINE_ SafeNumeric<uint32_t> *_get_refcount() const { if (!_ptr) { return nullptr; } - return reinterpret_cast<uint32_t *>(_ptr) - 2; + return reinterpret_cast<SafeNumeric<uint32_t> *>(_ptr) - 2; } _FORCE_INLINE_ uint32_t *_get_size() const { @@ -111,7 +115,7 @@ private: void _unref(void *p_data); void _ref(const CowData *p_from); void _ref(const CowData &p_from); - void _copy_on_write(); + uint32_t _copy_on_write(); public: void operator=(const CowData<T> &p_from) { _ref(p_from); } @@ -192,9 +196,9 @@ void CowData<T>::_unref(void *p_data) { return; } - uint32_t *refc = _get_refcount(); + SafeNumeric<uint32_t> *refc = _get_refcount(); - if (atomic_decrement(refc) > 0) { + if (refc->decrement() > 0) { return; // still in use } // clean up @@ -214,20 +218,21 @@ void CowData<T>::_unref(void *p_data) { } template <class T> -void CowData<T>::_copy_on_write() { +uint32_t CowData<T>::_copy_on_write() { if (!_ptr) { - return; + return 0; } - uint32_t *refc = _get_refcount(); + SafeNumeric<uint32_t> *refc = _get_refcount(); - if (unlikely(*refc > 1)) { + uint32_t rc = refc->get(); + if (unlikely(rc > 1)) { /* in use by more than me */ uint32_t current_size = *_get_size(); uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true); - *(mem_new - 2) = 1; //refcount + new (mem_new - 2, sizeof(uint32_t), "") SafeNumeric<uint32_t>(1); //refcount *(mem_new - 1) = current_size; //size T *_data = (T *)(mem_new); @@ -244,7 +249,10 @@ void CowData<T>::_copy_on_write() { _unref(_ptr); _ptr = _data; + + rc = 1; } + return rc; } template <class T> @@ -265,7 +273,7 @@ Error CowData<T>::resize(int p_size) { } // possibly changing size, copy on write - _copy_on_write(); + uint32_t rc = _copy_on_write(); size_t current_alloc_size = _get_alloc_size(current_size); size_t alloc_size; @@ -278,13 +286,15 @@ Error CowData<T>::resize(int p_size) { uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true); ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY); *(ptr - 1) = 0; //size, currently none - *(ptr - 2) = 1; //refcount + new (ptr - 2, sizeof(uint32_t), "") SafeNumeric<uint32_t>(1); //refcount _ptr = (T *)ptr; } else { - void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); + uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true); ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + new (_ptrnew - 2, sizeof(uint32_t), "") SafeNumeric<uint32_t>(rc); //refcount + _ptr = (T *)(_ptrnew); } } @@ -311,8 +321,9 @@ Error CowData<T>::resize(int p_size) { } if (alloc_size != current_alloc_size) { - void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); + uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true); ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + new (_ptrnew - 2, sizeof(uint32_t), "") SafeNumeric<uint32_t>(rc); //refcount _ptr = (T *)(_ptrnew); } @@ -359,7 +370,7 @@ void CowData<T>::_ref(const CowData &p_from) { return; //nothing to do } - if (atomic_conditional_increment(p_from._get_refcount()) > 0) { // could reference + if (p_from._get_refcount()->conditional_increment() > 0) { // could reference _ptr = p_from._ptr; } } diff --git a/core/templates/rid_owner.cpp b/core/templates/rid_owner.cpp index f75a2eb9df..56f39ab779 100644 --- a/core/templates/rid_owner.cpp +++ b/core/templates/rid_owner.cpp @@ -30,4 +30,4 @@ #include "rid_owner.h" -volatile uint64_t RID_AllocBase::base_id = 1; +SafeNumeric<uint64_t> RID_AllocBase::base_id{ 1 }; diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h index 3edc73b1a9..c4aa93c394 100644 --- a/core/templates/rid_owner.h +++ b/core/templates/rid_owner.h @@ -44,7 +44,7 @@ #include <typeinfo> class RID_AllocBase { - static volatile uint64_t base_id; + static SafeNumeric<uint64_t> base_id; protected: static RID _make_from_id(uint64_t p_id) { @@ -54,7 +54,7 @@ protected: } static uint64_t _gen_id() { - return atomic_increment(&base_id); + return base_id.increment(); } static RID _gen_rid() { diff --git a/core/templates/safe_refcount.cpp b/core/templates/safe_refcount.cpp deleted file mode 100644 index a915ee662f..0000000000 --- a/core/templates/safe_refcount.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/*************************************************************************/ -/* safe_refcount.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "safe_refcount.h" - -#if defined(_MSC_VER) - -/* Implementation for MSVC-Windows */ - -// don't pollute my namespace! -#include <windows.h> - -#define ATOMIC_CONDITIONAL_INCREMENT_BODY(m_pw, m_win_type, m_win_cmpxchg, m_cpp_type) \ - /* try to increment until it actually works */ \ - /* taken from boost */ \ - while (true) { \ - m_cpp_type tmp = static_cast<m_cpp_type const volatile &>(*(m_pw)); \ - if (tmp == 0) { \ - return 0; /* if zero, can't add to it anymore */ \ - } \ - if (m_win_cmpxchg((m_win_type volatile *)(m_pw), tmp + 1, tmp) == tmp) { \ - return tmp + 1; \ - } \ - } - -#define ATOMIC_EXCHANGE_IF_GREATER_BODY(m_pw, m_val, m_win_type, m_win_cmpxchg, m_cpp_type) \ - while (true) { \ - m_cpp_type tmp = static_cast<m_cpp_type const volatile &>(*(m_pw)); \ - if (tmp >= m_val) { \ - return tmp; /* already greater, or equal */ \ - } \ - if (m_win_cmpxchg((m_win_type volatile *)(m_pw), m_val, tmp) == tmp) { \ - return m_val; \ - } \ - } - -_ALWAYS_INLINE_ uint32_t _atomic_conditional_increment_impl(volatile uint32_t *pw) { - ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONG, InterlockedCompareExchange, uint32_t); -} - -_ALWAYS_INLINE_ uint32_t _atomic_decrement_impl(volatile uint32_t *pw) { - return InterlockedDecrement((LONG volatile *)pw); -} - -_ALWAYS_INLINE_ uint32_t _atomic_increment_impl(volatile uint32_t *pw) { - return InterlockedIncrement((LONG volatile *)pw); -} - -_ALWAYS_INLINE_ uint32_t _atomic_sub_impl(volatile uint32_t *pw, volatile uint32_t val) { - return InterlockedExchangeAdd((LONG volatile *)pw, -(int32_t)val) - val; -} - -_ALWAYS_INLINE_ uint32_t _atomic_add_impl(volatile uint32_t *pw, volatile uint32_t val) { - return InterlockedAdd((LONG volatile *)pw, val); -} - -_ALWAYS_INLINE_ uint32_t _atomic_exchange_if_greater_impl(volatile uint32_t *pw, volatile uint32_t val) { - ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONG, InterlockedCompareExchange, uint32_t); -} - -_ALWAYS_INLINE_ uint64_t _atomic_conditional_increment_impl(volatile uint64_t *pw) { - ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONGLONG, InterlockedCompareExchange64, uint64_t); -} - -_ALWAYS_INLINE_ uint64_t _atomic_decrement_impl(volatile uint64_t *pw) { - return InterlockedDecrement64((LONGLONG volatile *)pw); -} - -_ALWAYS_INLINE_ uint64_t _atomic_increment_impl(volatile uint64_t *pw) { - return InterlockedIncrement64((LONGLONG volatile *)pw); -} - -_ALWAYS_INLINE_ uint64_t _atomic_sub_impl(volatile uint64_t *pw, volatile uint64_t val) { - return InterlockedExchangeAdd64((LONGLONG volatile *)pw, -(int64_t)val) - val; -} - -_ALWAYS_INLINE_ uint64_t _atomic_add_impl(volatile uint64_t *pw, volatile uint64_t val) { - return InterlockedAdd64((LONGLONG volatile *)pw, val); -} - -_ALWAYS_INLINE_ uint64_t _atomic_exchange_if_greater_impl(volatile uint64_t *pw, volatile uint64_t val) { - ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONGLONG, InterlockedCompareExchange64, uint64_t); -} - -// The actual advertised functions; they'll call the right implementation - -uint32_t atomic_conditional_increment(volatile uint32_t *pw) { - return _atomic_conditional_increment_impl(pw); -} - -uint32_t atomic_decrement(volatile uint32_t *pw) { - return _atomic_decrement_impl(pw); -} - -uint32_t atomic_increment(volatile uint32_t *pw) { - return _atomic_increment_impl(pw); -} - -uint32_t atomic_sub(volatile uint32_t *pw, volatile uint32_t val) { - return _atomic_sub_impl(pw, val); -} - -uint32_t atomic_add(volatile uint32_t *pw, volatile uint32_t val) { - return _atomic_add_impl(pw, val); -} - -uint32_t atomic_exchange_if_greater(volatile uint32_t *pw, volatile uint32_t val) { - return _atomic_exchange_if_greater_impl(pw, val); -} - -uint64_t atomic_conditional_increment(volatile uint64_t *pw) { - return _atomic_conditional_increment_impl(pw); -} - -uint64_t atomic_decrement(volatile uint64_t *pw) { - return _atomic_decrement_impl(pw); -} - -uint64_t atomic_increment(volatile uint64_t *pw) { - return _atomic_increment_impl(pw); -} - -uint64_t atomic_sub(volatile uint64_t *pw, volatile uint64_t val) { - return _atomic_sub_impl(pw, val); -} - -uint64_t atomic_add(volatile uint64_t *pw, volatile uint64_t val) { - return _atomic_add_impl(pw, val); -} - -uint64_t atomic_exchange_if_greater(volatile uint64_t *pw, volatile uint64_t val) { - return _atomic_exchange_if_greater_impl(pw, val); -} -#endif diff --git a/core/templates/safe_refcount.h b/core/templates/safe_refcount.h index d94444fad6..cdc9908a5f 100644 --- a/core/templates/safe_refcount.h +++ b/core/templates/safe_refcount.h @@ -31,167 +31,291 @@ #ifndef SAFE_REFCOUNT_H #define SAFE_REFCOUNT_H -#include "core/os/mutex.h" #include "core/typedefs.h" -#include "platform_config.h" -// Atomic functions, these are used for multithread safe reference counters! +#if !defined(NO_THREADS) -#ifdef NO_THREADS +#include <atomic> -/* Bogus implementation unaware of multiprocessing */ +// Design goals for these classes: +// - No automatic conversions or arithmetic operators, +// to keep explicit the use of atomics everywhere. +// - Using acquire-release semantics, even to set the first value. +// The first value may be set relaxedly in many cases, but adding the distinction +// between relaxed and unrelaxed operation to the interface would make it needlessly +// flexible. There's negligible waste in having release semantics for the initial +// value and, as an important benefit, you can be sure the value is properly synchronized +// even with threads that are already running. + +// This is used in very specific areas of the engine where it's critical that these guarantees are held +#define SAFE_NUMERIC_TYPE_PUN_GUARANTEES(m_type) \ + static_assert(sizeof(SafeNumeric<m_type>) == sizeof(m_type)); \ + static_assert(alignof(SafeNumeric<m_type>) == alignof(m_type)); \ + static_assert(std::is_trivially_destructible<std::atomic<m_type>>::value); template <class T> -static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) { - if (*pw == 0) { - return 0; +class SafeNumeric { + std::atomic<T> value; + + static_assert(std::atomic<T>::is_always_lock_free); + +public: + _ALWAYS_INLINE_ void set(T p_value) { + value.store(p_value, std::memory_order_release); } - (*pw)++; + _ALWAYS_INLINE_ T get() const { + return value.load(std::memory_order_acquire); + } - return *pw; -} + _ALWAYS_INLINE_ T increment() { + return value.fetch_add(1, std::memory_order_acq_rel) + 1; + } -template <class T> -static _ALWAYS_INLINE_ T atomic_decrement(volatile T *pw) { - (*pw)--; + // Returns the original value instead of the new one + _ALWAYS_INLINE_ T postincrement() { + return value.fetch_add(1, std::memory_order_acq_rel); + } - return *pw; -} + _ALWAYS_INLINE_ T decrement() { + return value.fetch_sub(1, std::memory_order_acq_rel) - 1; + } -template <class T> -static _ALWAYS_INLINE_ T atomic_increment(volatile T *pw) { - (*pw)++; + // Returns the original value instead of the new one + _ALWAYS_INLINE_ T postdecrement() { + return value.fetch_sub(1, std::memory_order_acq_rel); + } - return *pw; -} + _ALWAYS_INLINE_ T add(T p_value) { + return value.fetch_add(p_value, std::memory_order_acq_rel) + p_value; + } -template <class T, class V> -static _ALWAYS_INLINE_ T atomic_sub(volatile T *pw, volatile V val) { - (*pw) -= val; + // Returns the original value instead of the new one + _ALWAYS_INLINE_ T postadd(T p_value) { + return value.fetch_add(p_value, std::memory_order_acq_rel); + } - return *pw; -} + _ALWAYS_INLINE_ T sub(T p_value) { + return value.fetch_sub(p_value, std::memory_order_acq_rel) - p_value; + } -template <class T, class V> -static _ALWAYS_INLINE_ T atomic_add(volatile T *pw, volatile V val) { - (*pw) += val; + // 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); + } - return *pw; -} + _ALWAYS_INLINE_ T exchange_if_greater(T p_value) { + while (true) { + T tmp = value.load(std::memory_order_acquire); + if (tmp >= p_value) { + return tmp; // already greater, or equal + } + if (value.compare_exchange_weak(tmp, p_value, std::memory_order_release)) { + return p_value; + } + } + } -template <class T, class V> -static _ALWAYS_INLINE_ T atomic_exchange_if_greater(volatile T *pw, volatile V val) { - if (val > *pw) { - *pw = val; + _ALWAYS_INLINE_ T conditional_increment() { + while (true) { + T c = value.load(std::memory_order_acquire); + if (c == 0) { + return 0; + } + if (value.compare_exchange_weak(c, c + 1, std::memory_order_release)) { + return c + 1; + } + } } - return *pw; -} + _ALWAYS_INLINE_ explicit SafeNumeric<T>(T p_value = static_cast<T>(0)) { + set(p_value); + } +}; -#elif defined(__GNUC__) +class SafeFlag { + std::atomic_bool flag; -/* Implementation for GCC & Clang */ + static_assert(std::atomic_bool::is_always_lock_free); -// GCC guarantees atomic intrinsics for sizes of 1, 2, 4 and 8 bytes. -// Clang states it supports GCC atomic builtins. +public: + _ALWAYS_INLINE_ bool is_set() const { + return flag.load(std::memory_order_acquire); + } -template <class T> -static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) { - while (true) { - T tmp = static_cast<T const volatile &>(*pw); - if (tmp == 0) { - return 0; // if zero, can't add to it anymore - } - if (__sync_val_compare_and_swap(pw, tmp, tmp + 1) == tmp) { - return tmp + 1; - } + _ALWAYS_INLINE_ void set() { + flag.store(true, std::memory_order_release); } -} -template <class T> -static _ALWAYS_INLINE_ T atomic_decrement(volatile T *pw) { - return __sync_sub_and_fetch(pw, 1); -} + _ALWAYS_INLINE_ void clear() { + flag.store(false, std::memory_order_release); + } + + _ALWAYS_INLINE_ void set_to(bool p_value) { + flag.store(p_value, std::memory_order_release); + } + + _ALWAYS_INLINE_ explicit SafeFlag(bool p_value = false) { + set_to(p_value); + } +}; + +class SafeRefCount { + SafeNumeric<uint32_t> count; + +public: + _ALWAYS_INLINE_ bool ref() { // true on success + return count.conditional_increment() != 0; + } + + _ALWAYS_INLINE_ uint32_t refval() { // none-zero on success + return count.conditional_increment(); + } + + _ALWAYS_INLINE_ bool unref() { // true if must be disposed of + return count.decrement() == 0; + } + + _ALWAYS_INLINE_ uint32_t unrefval() { // 0 if must be disposed of + return count.decrement(); + } + + _ALWAYS_INLINE_ uint32_t get() const { + return count.get(); + } + + _ALWAYS_INLINE_ void init(uint32_t p_value = 1) { + count.set(p_value); + } +}; + +#else template <class T> -static _ALWAYS_INLINE_ T atomic_increment(volatile T *pw) { - return __sync_add_and_fetch(pw, 1); -} - -template <class T, class V> -static _ALWAYS_INLINE_ T atomic_sub(volatile T *pw, volatile V val) { - return __sync_sub_and_fetch(pw, val); -} - -template <class T, class V> -static _ALWAYS_INLINE_ T atomic_add(volatile T *pw, volatile V val) { - return __sync_add_and_fetch(pw, val); -} - -template <class T, class V> -static _ALWAYS_INLINE_ T atomic_exchange_if_greater(volatile T *pw, volatile V val) { - while (true) { - T tmp = static_cast<T const volatile &>(*pw); - if (tmp >= val) { - return tmp; // already greater, or equal +class SafeNumeric { +protected: + T value; + +public: + _ALWAYS_INLINE_ void set(T p_value) { + value = p_value; + } + + _ALWAYS_INLINE_ T get() const { + return value; + } + + _ALWAYS_INLINE_ T increment() { + return ++value; + } + + _ALWAYS_INLINE_ T postincrement() { + return value++; + } + + _ALWAYS_INLINE_ T decrement() { + return --value; + } + + _ALWAYS_INLINE_ T postdecrement() { + return value--; + } + + _ALWAYS_INLINE_ T add(T p_value) { + return value += p_value; + } + + _ALWAYS_INLINE_ T postadd(T p_value) { + T old = value; + value += p_value; + return old; + } + + _ALWAYS_INLINE_ T sub(T p_value) { + return value -= p_value; + } + + _ALWAYS_INLINE_ T postsub(T p_value) { + T old = value; + value -= p_value; + return old; + } + + _ALWAYS_INLINE_ T exchange_if_greater(T p_value) { + if (value < p_value) { + value = p_value; } - if (__sync_val_compare_and_swap(pw, tmp, val) == tmp) { - return val; + return value; + } + + _ALWAYS_INLINE_ T conditional_increment() { + if (value != 0) { + return 0; + } else { + return ++value; } } -} - -#elif defined(_MSC_VER) -// For MSVC use a separate compilation unit to prevent windows.h from polluting -// the global namespace. -uint32_t atomic_conditional_increment(volatile uint32_t *pw); -uint32_t atomic_decrement(volatile uint32_t *pw); -uint32_t atomic_increment(volatile uint32_t *pw); -uint32_t atomic_sub(volatile uint32_t *pw, volatile uint32_t val); -uint32_t atomic_add(volatile uint32_t *pw, volatile uint32_t val); -uint32_t atomic_exchange_if_greater(volatile uint32_t *pw, volatile uint32_t val); - -uint64_t atomic_conditional_increment(volatile uint64_t *pw); -uint64_t atomic_decrement(volatile uint64_t *pw); -uint64_t atomic_increment(volatile uint64_t *pw); -uint64_t atomic_sub(volatile uint64_t *pw, volatile uint64_t val); -uint64_t atomic_add(volatile uint64_t *pw, volatile uint64_t val); -uint64_t atomic_exchange_if_greater(volatile uint64_t *pw, volatile uint64_t val); -#else -//no threads supported? -#error Must provide atomic functions for this platform or compiler! -#endif + _ALWAYS_INLINE_ explicit SafeNumeric<T>(T p_value = static_cast<T>(0)) : + value(p_value) { + } +}; -struct SafeRefCount { - uint32_t count = 0; +class SafeFlag { +protected: + bool flag; public: - // destroy() is called when weak_count_ drops to zero. - - _ALWAYS_INLINE_ bool ref() { // true on success + _ALWAYS_INLINE_ bool is_set() const { + return flag; + } - return atomic_conditional_increment(&count) != 0; + _ALWAYS_INLINE_ void set() { + flag = true; } - _ALWAYS_INLINE_ uint32_t refval() { // none-zero on success + _ALWAYS_INLINE_ void clear() { + flag = false; + } - return atomic_conditional_increment(&count); + _ALWAYS_INLINE_ void set_to(bool p_value) { + flag = p_value; } - _ALWAYS_INLINE_ bool unref() { // true if must be disposed of + _ALWAYS_INLINE_ explicit SafeFlag(bool p_value = false) : + flag(p_value) {} +}; + +class SafeRefCount { + uint32_t count = 0; - return atomic_decrement(&count) == 0; +public: + _ALWAYS_INLINE_ bool ref() { // true on success + if (count != 0) { + ++count; + return true; + } else { + return false; + } } - _ALWAYS_INLINE_ uint32_t unrefval() { // 0 if must be disposed of + _ALWAYS_INLINE_ uint32_t refval() { // none-zero on success + if (count != 0) { + return ++count; + } else { + return 0; + } + } - return atomic_decrement(&count); + _ALWAYS_INLINE_ bool unref() { // true if must be disposed of + return --count == 0; } - _ALWAYS_INLINE_ uint32_t get() const { // nothrow + _ALWAYS_INLINE_ uint32_t unrefval() { // 0 if must be disposed of + return --count; + } + _ALWAYS_INLINE_ uint32_t get() const { return count; } @@ -200,4 +324,6 @@ public: } }; +#endif + #endif // SAFE_REFCOUNT_H diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 8f2cba138b..54ca1a911d 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1059,6 +1059,7 @@ static void _register_variant_builtin_methods() { bind_method(Vector3, min_axis, sarray(), varray()); bind_method(Vector3, max_axis, sarray(), varray()); bind_method(Vector3, angle_to, sarray("to"), varray()); + bind_method(Vector3, signed_angle_to, sarray("to", "axis"), varray()); bind_method(Vector3, direction_to, sarray("b"), varray()); bind_method(Vector3, distance_to, sarray("b"), varray()); bind_method(Vector3, distance_squared_to, sarray("b"), varray()); @@ -1256,7 +1257,7 @@ static void _register_variant_builtin_methods() { bind_method(Transform, rotated, sarray("axis", "phi"), varray()); bind_method(Transform, scaled, sarray("scale"), varray()); bind_method(Transform, translated, sarray("offset"), varray()); - bind_method(Transform, looking_at, sarray("target", "up"), varray()); + bind_method(Transform, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0))); bind_method(Transform, interpolate_with, sarray("xform", "weight"), varray()); bind_method(Transform, is_equal_approx, sarray("xform"), varray()); |