diff options
79 files changed, 2712 insertions, 2564 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 541dbfa212..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" @@ -1057,17 +1058,35 @@ 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_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); @@ -1094,163 +1113,7 @@ ProjectSettings::ProjectSettings() { 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"); + _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/driver/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/driver/threads/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded"); diff --git a/core/config/project_settings.h b/core/config/project_settings.h index 2b230e06f9..ed8fb19fa0 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -116,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: diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 037cfa60c6..b446f4c827 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -1967,7 +1967,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); @@ -1976,7 +1976,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)); @@ -1992,14 +1992,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 dd97021221..435269dac5 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); @@ -554,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 48e573626d..90d96e3876 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -604,21 +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())) { + 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())) { + 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.exact = InputMap::get_singleton()->event_is_action(p_event, E->key(), true); - 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_event.cpp b/core/input/input_event.cpp index b1c8f1ad66..c6910d2b1f 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -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 1ce1fad9c2..df81b9fc75 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -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 c03e64eaf4..b31c431ead 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -88,8 +88,8 @@ 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; @@ -179,12 +179,12 @@ 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, bool p_exact_match) const { @@ -192,7 +192,7 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName } 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 { - Map<StringName, Action>::Element *E = input_map.find(p_action); + 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; @@ -209,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, p_exact_match, &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; @@ -226,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; } @@ -260,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 28c692c7ba..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,7 +54,8 @@ 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_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; @@ -81,10 +84,14 @@ public: 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/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/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 5021fa47a1..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" @@ -486,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/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/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/templates/cowdata.h b/core/templates/cowdata.h index 4becb1be59..6077f5f107 100644 --- a/core/templates/cowdata.h +++ b/core/templates/cowdata.h @@ -45,6 +45,9 @@ class CharString; template <class T, class V> class VMap; +// CowData is relying on this to be true +static_assert(sizeof(SafeNumeric<uint32_t>) == sizeof(uint32_t)); + template <class T> class CowData { template <class TV> @@ -60,12 +63,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 { @@ -192,9 +195,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 @@ -219,15 +222,15 @@ void CowData<T>::_copy_on_write() { return; } - uint32_t *refc = _get_refcount(); + SafeNumeric<uint32_t> *refc = _get_refcount(); - if (unlikely(*refc > 1)) { + if (unlikely(refc->get() > 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 + reinterpret_cast<SafeNumeric<uint32_t> *>(mem_new - 2)->set(1); //refcount *(mem_new - 1) = current_size; //size T *_data = (T *)(mem_new); @@ -278,7 +281,7 @@ 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 + reinterpret_cast<SafeNumeric<uint32_t> *>(ptr - 2)->set(1); //refcount _ptr = (T *)ptr; @@ -359,7 +362,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()->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..6aebc24ec3 100644 --- a/core/templates/safe_refcount.h +++ b/core/templates/safe_refcount.h @@ -31,167 +31,281 @@ #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. template <class T> -static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) { - if (*pw == 0) { - return 0; +class SafeNumeric { + std::atomic<T> value; + +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 */ +public: + _ALWAYS_INLINE_ bool is_set() const { + return flag.load(std::memory_order_acquire); + } -// GCC guarantees atomic intrinsics for sizes of 1, 2, 4 and 8 bytes. -// Clang states it supports GCC atomic builtins. + _ALWAYS_INLINE_ void set() { + flag.store(true, std::memory_order_release); + } -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 clear() { + flag.store(false, 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 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 +314,6 @@ public: } }; +#endif + #endif // SAFE_REFCOUNT_H diff --git a/editor/audio_stream_preview.cpp b/editor/audio_stream_preview.cpp index 8be8735f3e..539657afd7 100644 --- a/editor/audio_stream_preview.cpp +++ b/editor/audio_stream_preview.cpp @@ -155,7 +155,7 @@ void AudioStreamPreviewGenerator::_preview_thread(void *p_preview) { preview->playback->stop(); - preview->generating = false; + preview->generating.clear(); } Ref<AudioStreamPreview> AudioStreamPreviewGenerator::generate_preview(const Ref<AudioStream> &p_stream) { @@ -172,7 +172,7 @@ Ref<AudioStreamPreview> AudioStreamPreviewGenerator::generate_preview(const Ref< Preview *preview = &previews[p_stream->get_instance_id()]; preview->base_stream = p_stream; preview->playback = preview->base_stream->instance_playback(); - preview->generating = true; + preview->generating.set(); preview->id = p_stream->get_instance_id(); float len_s = preview->base_stream->get_length(); @@ -217,7 +217,7 @@ void AudioStreamPreviewGenerator::_notification(int p_what) { if (p_what == NOTIFICATION_PROCESS) { List<ObjectID> to_erase; for (Map<ObjectID, Preview>::Element *E = previews.front(); E; E = E->next()) { - if (!E->get().generating) { + if (!E->get().generating.is_set()) { if (E->get().thread) { E->get().thread->wait_to_finish(); memdelete(E->get().thread); diff --git a/editor/audio_stream_preview.h b/editor/audio_stream_preview.h index 21c9ea203e..accc7275c0 100644 --- a/editor/audio_stream_preview.h +++ b/editor/audio_stream_preview.h @@ -32,6 +32,7 @@ #define AUDIO_STREAM_PREVIEW_H #include "core/os/thread.h" +#include "core/templates/safe_refcount.h" #include "scene/main/node.h" #include "servers/audio/audio_stream.h" @@ -60,9 +61,20 @@ class AudioStreamPreviewGenerator : public Node { Ref<AudioStreamPreview> preview; Ref<AudioStream> base_stream; Ref<AudioStreamPlayback> playback; - volatile bool generating = false; + SafeFlag generating; ObjectID id; Thread *thread = nullptr; + + // Needed for the bookkeeping of the Map + Preview &operator=(const Preview &p_rhs) { + preview = p_rhs.preview; + base_stream = p_rhs.base_stream; + playback = p_rhs.playback; + generating.set_to(generating.is_set()); + id = p_rhs.id; + thread = p_rhs.thread; + return *this; + } }; Map<ObjectID, Preview> previews; diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 093c6f4279..3c6649a66a 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1413,11 +1413,11 @@ void EditorFileSystem::_scan_script_classes(EditorFileSystemDirectory *p_dir) { } void EditorFileSystem::update_script_classes() { - if (!update_script_classes_queued) { + if (!update_script_classes_queued.is_set()) { return; } - update_script_classes_queued = false; + update_script_classes_queued.clear(); ScriptServer::global_classes_clear(); if (get_filesystem()) { _scan_script_classes(get_filesystem()); @@ -1436,11 +1436,11 @@ void EditorFileSystem::update_script_classes() { } void EditorFileSystem::_queue_update_script_classes() { - if (update_script_classes_queued) { + if (update_script_classes_queued.is_set()) { return; } - update_script_classes_queued = true; + update_script_classes_queued.set(); call_deferred("update_script_classes"); } @@ -2091,7 +2091,7 @@ EditorFileSystem::EditorFileSystem() { memdelete(da); scan_total = 0; - update_script_classes_queued = false; + update_script_classes_queued.clear(); first_scan = true; scan_changes_pending = false; revalidate_import_files = false; diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index fa0b89e667..dec2330256 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -34,6 +34,7 @@ #include "core/os/dir_access.h" #include "core/os/thread.h" #include "core/os/thread_safe.h" +#include "core/templates/safe_refcount.h" #include "core/templates/set.h" #include "scene/main/node.h" class FileAccess; @@ -220,7 +221,7 @@ class EditorFileSystem : public Node { }; void _scan_script_classes(EditorFileSystemDirectory *p_dir); - volatile bool update_script_classes_queued; + SafeFlag update_script_classes_queued; void _queue_update_script_classes(); String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index cdbc4a0de9..ec8430e645 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -5555,7 +5555,8 @@ static void _execute_thread(void *p_ud) { eta->exitcode = err; } - eta->done = true; + eta->done.set(); + ; } int EditorNode::execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok, bool p_close_on_errors) { @@ -5569,13 +5570,12 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p eta.path = p_path; eta.args = p_arguments; eta.exitcode = 255; - eta.done = false; int prev_len = 0; eta.execute_output_thread.start(_execute_thread, &eta); - while (!eta.done) { + while (!eta.done.is_set()) { { MutexLock lock(eta.execute_output_mutex); if (prev_len != eta.output.length()) { @@ -6215,8 +6215,8 @@ EditorNode::EditorNode() { pm_export->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); p->add_separator(); - p->add_shortcut(ED_SHORTCUT("editor/undo", TTR("Undo"), KEY_MASK_CMD + KEY_Z), EDIT_UNDO, true); - p->add_shortcut(ED_SHORTCUT("editor/redo", TTR("Redo"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_Z), EDIT_REDO, true); + p->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO, true); + p->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO, true); p->add_separator(); p->add_shortcut(ED_SHORTCUT("editor/reload_saved_scene", TTR("Reload Saved Scene")), EDIT_RELOAD_SAVED_SCENE); diff --git a/editor/editor_node.h b/editor/editor_node.h index 8740488d18..91d873d16f 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -31,6 +31,7 @@ #ifndef EDITOR_NODE_H #define EDITOR_NODE_H +#include "core/templates/safe_refcount.h" #include "editor/editor_data.h" #include "editor/editor_export.h" #include "editor/editor_folding.h" @@ -111,7 +112,7 @@ public: Thread execute_output_thread; Mutex execute_output_mutex; int exitcode = 0; - volatile bool done = false; + SafeFlag done; }; private: diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index 8056846f52..77288be614 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -206,8 +206,8 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref< } void EditorResourcePreview::_thread() { - exited = false; - while (!exit) { + exited.clear(); + while (!exit.is_set()) { preview_sem.wait(); preview_mutex.lock(); @@ -326,7 +326,7 @@ void EditorResourcePreview::_thread() { preview_mutex.unlock(); } } - exited = true; + exited.set(); } void EditorResourcePreview::queue_edited_resource_preview(const Ref<Resource> &p_res, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata) { @@ -430,9 +430,9 @@ void EditorResourcePreview::start() { void EditorResourcePreview::stop() { if (thread.is_started()) { - exit = true; + exit.set(); preview_sem.post(); - while (!exited) { + while (!exited.is_set()) { OS::get_singleton()->delay_usec(10000); RenderingServer::get_singleton()->sync(); //sync pending stuff, as thread may be blocked on visual server } @@ -443,8 +443,6 @@ void EditorResourcePreview::stop() { EditorResourcePreview::EditorResourcePreview() { singleton = this; order = 0; - exit = false; - exited = false; } EditorResourcePreview::~EditorResourcePreview() { diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h index 99c48967d8..c4e796dcf1 100644 --- a/editor/editor_resource_preview.h +++ b/editor/editor_resource_preview.h @@ -33,6 +33,7 @@ #include "core/os/semaphore.h" #include "core/os/thread.h" +#include "core/templates/safe_refcount.h" #include "scene/main/node.h" #include "scene/resources/texture.h" @@ -71,8 +72,8 @@ class EditorResourcePreview : public Node { Mutex preview_mutex; Semaphore preview_sem; Thread thread; - volatile bool exit; - volatile bool exited; + SafeFlag exit; + SafeFlag exited; struct Item { Ref<Texture2D> preview; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index c530058c80..40647497af 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -31,6 +31,7 @@ #include "editor_settings.h" #include "core/config/project_settings.h" +#include "core/input/input_map.h" #include "core/io/certs_compressed.gen.h" #include "core/io/compression.h" #include "core/io/config_file.h" @@ -70,7 +71,7 @@ bool EditorSettings::_set(const StringName &p_name, const Variant &p_value) { bool EditorSettings::_set_only(const StringName &p_name, const Variant &p_value) { _THREAD_SAFE_METHOD_ - if (p_name.operator String() == "shortcuts") { + if (p_name == "shortcuts") { Array arr = p_value; ERR_FAIL_COND_V(arr.size() && arr.size() & 1, true); for (int i = 0; i < arr.size(); i += 2) { @@ -84,6 +85,24 @@ bool EditorSettings::_set_only(const StringName &p_name, const Variant &p_value) } return false; + } else if (p_name == "builtin_action_overrides") { + Array actions_arr = p_value; + for (int i = 0; i < actions_arr.size(); i++) { + Dictionary action_dict = actions_arr[i]; + + String name = action_dict["name"]; + Array events = action_dict["events"]; + + InputMap *im = InputMap::get_singleton(); + im->action_erase_events(name); + + builtin_action_overrides[name].clear(); + for (int ev_idx = 0; ev_idx < events.size(); ev_idx++) { + im->action_add_event(name, events[ev_idx]); + builtin_action_overrides[name].push_back(events[ev_idx]); + } + } + return false; } bool changed = false; @@ -118,11 +137,16 @@ bool EditorSettings::_set_only(const StringName &p_name, const Variant &p_value) bool EditorSettings::_get(const StringName &p_name, Variant &r_ret) const { _THREAD_SAFE_METHOD_ - if (p_name.operator String() == "shortcuts") { + if (p_name == "shortcuts") { Array arr; for (const Map<String, Ref<Shortcut>>::Element *E = shortcuts.front(); E; E = E->next()) { Ref<Shortcut> sc = E->get(); + if (builtin_action_overrides.has(E->key())) { + // This shortcut was auto-generated from built in actions: don't save. + continue; + } + if (optimize_save) { if (!sc->has_meta("original")) { continue; //this came from settings but is not any longer used @@ -139,6 +163,27 @@ bool EditorSettings::_get(const StringName &p_name, Variant &r_ret) const { } r_ret = arr; return true; + } else if (p_name == "builtin_action_overrides") { + Array actions_arr; + for (Map<String, List<Ref<InputEvent>>>::Element *E = builtin_action_overrides.front(); E; E = E->next()) { + List<Ref<InputEvent>> events = E->get(); + + // TODO: skip actions which are the same as the builtin. + Dictionary action_dict; + action_dict["name"] = E->key(); + + Array events_arr; + for (List<Ref<InputEvent>>::Element *I = events.front(); I; I = I->next()) { + events_arr.push_back(I->get()); + } + + action_dict["events"] = events_arr; + + actions_arr.push_back(action_dict); + } + + r_ret = actions_arr; + return true; } const VariantContainer *v = props.getptr(p_name); @@ -220,6 +265,7 @@ void EditorSettings::_get_property_list(List<PropertyInfo> *p_list) const { } p_list->push_back(PropertyInfo(Variant::ARRAY, "shortcuts", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); //do not edit + p_list->push_back(PropertyInfo(Variant::ARRAY, "builtin_action_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); } void EditorSettings::_add_property_info_bind(const Dictionary &p_info) { @@ -1539,12 +1585,39 @@ bool EditorSettings::is_shortcut(const String &p_name, const Ref<InputEvent> &p_ } Ref<Shortcut> EditorSettings::get_shortcut(const String &p_name) const { - const Map<String, Ref<Shortcut>>::Element *E = shortcuts.find(p_name); - if (!E) { - return Ref<Shortcut>(); + const Map<String, Ref<Shortcut>>::Element *SC = shortcuts.find(p_name); + if (SC) { + return SC->get(); + } + + // If no shortcut with the provided name is found in the list, check the built-in shortcuts. + // Use the first item in the action list for the shortcut event, since a shortcut can only have 1 linked event. + + Ref<Shortcut> sc; + const Map<String, List<Ref<InputEvent>>>::Element *builtin_override = builtin_action_overrides.find(p_name); + if (builtin_override) { + sc.instance(); + sc->set_shortcut(builtin_override->get().front()->get()); + sc->set_name(InputMap::get_singleton()->get_builtin_display_name(p_name)); + } + + // If there was no override, check the default builtins to see if it has an InputEvent for the provided name. + if (sc.is_null()) { + const OrderedHashMap<String, List<Ref<InputEvent>>>::ConstElement builtin_default = InputMap::get_singleton()->get_builtins().find(p_name); + if (builtin_default) { + sc.instance(); + sc->set_shortcut(builtin_default.get().front()->get()); + sc->set_name(InputMap::get_singleton()->get_builtin_display_name(p_name)); + } + } + + if (sc.is_valid()) { + // Add the shortcut to the list. + shortcuts[p_name] = sc; + return sc; } - return E->get(); + return Ref<Shortcut>(); } void EditorSettings::get_shortcut_list(List<String> *r_shortcuts) { @@ -1610,6 +1683,66 @@ Ref<Shortcut> ED_SHORTCUT(const String &p_path, const String &p_name, uint32_t p return sc; } +void EditorSettings::set_builtin_action_override(const String &p_name, const Array &p_events) { + List<Ref<InputEvent>> event_list; + + // Override the whole list, since events may have their order changed or be added, removed or edited. + InputMap::get_singleton()->action_erase_events(p_name); + for (int i = 0; i < p_events.size(); i++) { + event_list.push_back(p_events[i]); + InputMap::get_singleton()->action_add_event(p_name, p_events[i]); + } + + // Check if the provided event array is same as built-in. If it is, it does not need to be added to the overrides. + // Note that event order must also be the same. + bool same_as_builtin = true; + OrderedHashMap<String, List<Ref<InputEvent>>>::ConstElement builtin_default = InputMap::get_singleton()->get_builtins().find(p_name); + if (builtin_default) { + List<Ref<InputEvent>> builtin_events = builtin_default.get(); + + if (p_events.size() == builtin_events.size()) { + int event_idx = 0; + + // Check equality of each event. + for (List<Ref<InputEvent>>::Element *E = builtin_events.front(); E; E = E->next()) { + if (!E->get()->shortcut_match(p_events[event_idx])) { + same_as_builtin = false; + break; + } + event_idx++; + } + } else { + same_as_builtin = false; + } + } + + if (same_as_builtin && builtin_action_overrides.has(p_name)) { + builtin_action_overrides.erase(p_name); + } else { + builtin_action_overrides[p_name] = event_list; + } + + // Update the shortcut (if it is used somewhere in the editor) to be the first event of the new list. + if (shortcuts.has(p_name)) { + shortcuts[p_name]->set_shortcut(event_list.front()->get()); + } +} + +const Array EditorSettings::get_builtin_action_overrides(const String &p_name) const { + const Map<String, List<Ref<InputEvent>>>::Element *AO = builtin_action_overrides.find(p_name); + if (AO) { + Array event_array; + + List<Ref<InputEvent>> events_list = AO->get(); + for (List<Ref<InputEvent>>::Element *E = events_list.front(); E; E = E->next()) { + event_array.push_back(E->get()); + } + return event_array; + } + + return Array(); +} + void EditorSettings::notify_changes() { _THREAD_SAFE_METHOD_ @@ -1648,6 +1781,8 @@ void EditorSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("set_recent_dirs", "dirs"), &EditorSettings::set_recent_dirs); ClassDB::bind_method(D_METHOD("get_recent_dirs"), &EditorSettings::get_recent_dirs); + ClassDB::bind_method(D_METHOD("set_builtin_action_override", "name", "actions_list"), &EditorSettings::set_builtin_action_override); + ADD_SIGNAL(MethodInfo("settings_changed")); BIND_CONSTANT(NOTIFICATION_EDITOR_SETTINGS_CHANGED); diff --git a/editor/editor_settings.h b/editor/editor_settings.h index 616a938a86..e5f8527faf 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -84,7 +84,8 @@ private: int last_order; Ref<Resource> clipboard; - Map<String, Ref<Shortcut>> shortcuts; + mutable Map<String, Ref<Shortcut>> shortcuts; + Map<String, List<Ref<InputEvent>>> builtin_action_overrides; String resource_path; String settings_dir; @@ -186,6 +187,9 @@ public: Ref<Shortcut> get_shortcut(const String &p_name) const; void get_shortcut_list(List<String> *r_shortcuts); + void set_builtin_action_override(const String &p_name, const Array &p_events); + const Array get_builtin_action_overrides(const String &p_name) const; + void notify_changes(); EditorSettings(); diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 63bb785c5e..eb3c06fba1 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -301,7 +301,7 @@ EditorPackedScenePreviewPlugin::EditorPackedScenePreviewPlugin() { ////////////////////////////////////////////////////////////////// void EditorMaterialPreviewPlugin::_preview_done(const Variant &p_udata) { - preview_done = true; + preview_done.set(); } void EditorMaterialPreviewPlugin::_bind_methods() { @@ -325,10 +325,10 @@ Ref<Texture2D> EditorMaterialPreviewPlugin::generate(const RES &p_from, const Si RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture - preview_done = false; + preview_done.clear(); RS::get_singleton()->request_frame_drawn_callback(const_cast<EditorMaterialPreviewPlugin *>(this), "_preview_done", Variant()); - while (!preview_done) { + while (!preview_done.is_set()) { OS::get_singleton()->delay_usec(10); } @@ -677,7 +677,7 @@ EditorAudioStreamPreviewPlugin::EditorAudioStreamPreviewPlugin() { /////////////////////////////////////////////////////////////////////////// void EditorMeshPreviewPlugin::_preview_done(const Variant &p_udata) { - preview_done = true; + preview_done.set(); } void EditorMeshPreviewPlugin::_bind_methods() { @@ -714,10 +714,10 @@ Ref<Texture2D> EditorMeshPreviewPlugin::generate(const RES &p_from, const Size2 RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture - preview_done = false; + preview_done.clear(); RS::get_singleton()->request_frame_drawn_callback(const_cast<EditorMeshPreviewPlugin *>(this), "_preview_done", Variant()); - while (!preview_done) { + while (!preview_done.is_set()) { OS::get_singleton()->delay_usec(10); } @@ -792,7 +792,7 @@ EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() { /////////////////////////////////////////////////////////////////////////// void EditorFontPreviewPlugin::_preview_done(const Variant &p_udata) { - preview_done = true; + preview_done.set(); } void EditorFontPreviewPlugin::_bind_methods() { @@ -883,11 +883,11 @@ Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path, font->draw_string(canvas_item, pos, sample, HALIGN_LEFT, -1.f, 50, Color(1, 1, 1)); - preview_done = false; + preview_done.clear(); RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture RS::get_singleton()->request_frame_drawn_callback(const_cast<EditorFontPreviewPlugin *>(this), "_preview_done", Variant()); - while (!preview_done) { + while (!preview_done.is_set()) { OS::get_singleton()->delay_usec(10); } diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index 57e2911c89..6e8b9a34cf 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -33,6 +33,8 @@ #include "editor/editor_resource_preview.h" +#include "core/templates/safe_refcount.h" + void post_process_preview(Ref<Image> p_image); class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator { @@ -90,7 +92,7 @@ class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator { RID light2; RID light_instance2; RID camera; - mutable volatile bool preview_done = false; + mutable SafeFlag preview_done; void _preview_done(const Variant &p_udata); @@ -134,7 +136,7 @@ class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator { RID light2; RID light_instance2; RID camera; - mutable volatile bool preview_done = false; + mutable SafeFlag preview_done; void _preview_done(const Variant &p_udata); @@ -156,7 +158,7 @@ class EditorFontPreviewPlugin : public EditorResourcePreviewGenerator { RID viewport_texture; RID canvas; RID canvas_item; - mutable volatile bool preview_done = false; + mutable SafeFlag preview_done; void _preview_done(const Variant &p_udata); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 71b18497b0..b6df66b8af 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1066,7 +1066,7 @@ void ScriptTextEditor::_edit_option(int p_op) { return; } - tx->indent_left(); + tx->indent_selected_lines_left(); } break; case EDIT_INDENT_RIGHT: { Ref<Script> scr = script; @@ -1074,7 +1074,7 @@ void ScriptTextEditor::_edit_option(int p_op) { return; } - tx->indent_right(); + tx->indent_selected_lines_right(); } break; case EDIT_DELETE_LINE: { code_editor->delete_lines(); @@ -1632,16 +1632,16 @@ void ScriptTextEditor::_color_changed(const Color &p_color) { void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition, Vector2 p_pos) { context_menu->clear(); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO); context_menu->add_separator(); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); context_menu->add_separator(); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); context_menu->add_separator(); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT); @@ -1743,14 +1743,14 @@ void ScriptTextEditor::_enable_code_editor() { search_menu->get_popup()->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option)); edit_hb->add_child(edit_menu); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); edit_menu->get_popup()->add_separator(); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN); @@ -1763,7 +1763,7 @@ void ScriptTextEditor::_enable_code_editor() { edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unfold_all_lines"), EDIT_UNFOLD_ALL_LINES); edit_menu->get_popup()->add_separator(); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/complete_symbol"), EDIT_COMPLETE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_completion_query"), EDIT_COMPLETE); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/evaluate_selection"), EDIT_EVALUATE); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/trim_trailing_whitespace"), EDIT_TRIM_TRAILING_WHITESAPCE); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_spaces"), EDIT_CONVERT_INDENT_TO_SPACES); @@ -1915,12 +1915,6 @@ static ScriptEditorBase *create_editor(const RES &p_resource) { } void ScriptTextEditor::register_editor() { - ED_SHORTCUT("script_text_editor/undo", TTR("Undo"), KEY_MASK_CMD | KEY_Z); - ED_SHORTCUT("script_text_editor/redo", TTR("Redo"), KEY_MASK_CMD | KEY_Y); - ED_SHORTCUT("script_text_editor/cut", TTR("Cut"), KEY_MASK_CMD | KEY_X); - ED_SHORTCUT("script_text_editor/copy", TTR("Copy"), KEY_MASK_CMD | KEY_C); - ED_SHORTCUT("script_text_editor/paste", TTR("Paste"), KEY_MASK_CMD | KEY_V); - ED_SHORTCUT("script_text_editor/select_all", TTR("Select All"), KEY_MASK_CMD | KEY_A); ED_SHORTCUT("script_text_editor/move_up", TTR("Move Up"), KEY_MASK_ALT | KEY_UP); ED_SHORTCUT("script_text_editor/move_down", TTR("Move Down"), KEY_MASK_ALT | KEY_DOWN); ED_SHORTCUT("script_text_editor/delete_line", TTR("Delete Line"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_K); @@ -1936,10 +1930,8 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), 0); #ifdef OSX_ENABLED ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_C); - ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CTRL | KEY_SPACE); #else ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_D); - ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CMD | KEY_SPACE); #endif ED_SHORTCUT("script_text_editor/evaluate_selection", TTR("Evaluate Selection"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_E); ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_T); diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 8be82628cb..c8a46715ad 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -282,7 +282,7 @@ void ShaderEditor::_menu_option(int p_option) { } CodeEdit *tx = shader_editor->get_text_editor(); - tx->indent_left(); + tx->indent_selected_lines_left(); } break; case EDIT_INDENT_RIGHT: { @@ -291,7 +291,7 @@ void ShaderEditor::_menu_option(int p_option) { } CodeEdit *tx = shader_editor->get_text_editor(); - tx->indent_right(); + tx->indent_selected_lines_right(); } break; case EDIT_DELETE_LINE: { @@ -533,15 +533,15 @@ void ShaderEditor::_bookmark_item_pressed(int p_idx) { void ShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position) { context_menu->clear(); if (p_selection) { - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); } - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); context_menu->add_separator(); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO); context_menu->add_separator(); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT); @@ -585,14 +585,14 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { edit_menu->set_text(TTR("Edit")); edit_menu->set_switch_on_hover(true); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); edit_menu->get_popup()->add_separator(); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN); @@ -602,7 +602,7 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/complete_symbol"), EDIT_COMPLETE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_completion_query"), EDIT_COMPLETE); edit_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option)); search_menu = memnew(MenuButton); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index d45011c8aa..b88f1c91e6 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -363,10 +363,10 @@ void TextEditor::_edit_option(int p_op) { code_editor->move_lines_down(); } break; case EDIT_INDENT_LEFT: { - tx->indent_left(); + tx->indent_selected_lines_left(); } break; case EDIT_INDENT_RIGHT: { - tx->indent_right(); + tx->indent_selected_lines_right(); } break; case EDIT_DELETE_LINE: { code_editor->delete_lines(); @@ -514,15 +514,15 @@ void TextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { void TextEditor::_make_context_menu(bool p_selection, bool p_can_fold, bool p_is_folded, Vector2 p_position) { context_menu->clear(); if (p_selection) { - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); } - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); context_menu->add_separator(); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO); context_menu->add_separator(); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT); @@ -584,14 +584,14 @@ TextEditor::TextEditor() { edit_menu->set_switch_on_hover(true); edit_menu->get_popup()->connect("id_pressed", callable_mp(this, &TextEditor::_edit_option)); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("un_redo"), EDIT_REDO); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); edit_menu->get_popup()->add_separator(); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN); diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index 1dad3c091d..ffd5716364 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -143,7 +143,7 @@ void EditorSettingsDialog::_unhandled_input(const Ref<InputEvent> &p_event) { if (k.is_valid() && k->is_pressed()) { bool handled = false; - if (ED_IS_SHORTCUT("editor/undo", p_event)) { + if (ED_IS_SHORTCUT("ui_undo", p_event)) { String action = undo_redo->get_current_action_name(); if (action != "") { EditorNode::get_log()->add_message("Undo: " + action, EditorLog::MSG_TYPE_EDITOR); @@ -152,7 +152,7 @@ void EditorSettingsDialog::_unhandled_input(const Ref<InputEvent> &p_event) { handled = true; } - if (ED_IS_SHORTCUT("editor/redo", p_event)) { + if (ED_IS_SHORTCUT("ui_redo", p_event)) { undo_redo->redo(); String action = undo_redo->get_current_action_name(); if (action != "") { diff --git a/main/main.cpp b/main/main.cpp index c228323325..41f97b2e3b 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -511,8 +511,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph MAIN_PRINT("Main: Initialize Globals"); - globals = memnew(ProjectSettings); input_map = memnew(InputMap); + globals = memnew(ProjectSettings); register_core_settings(); //here globals is present diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp index dbd6d9e9f9..3beca3d12a 100644 --- a/modules/cvtt/image_compress_cvtt.cpp +++ b/modules/cvtt/image_compress_cvtt.cpp @@ -33,6 +33,7 @@ #include "core/os/os.h" #include "core/os/thread.h" #include "core/string/print_string.h" +#include "core/templates/safe_refcount.h" #include <ConvectionKernels.h> @@ -56,7 +57,7 @@ struct CVTTCompressionJobQueue { CVTTCompressionJobParams job_params; const CVTTCompressionRowTask *job_tasks; uint32_t num_tasks = 0; - uint32_t current_task = 0; + SafeNumeric<uint32_t> current_task; }; static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const CVTTCompressionRowTask &p_row_task) { @@ -131,7 +132,7 @@ static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const static void _digest_job_queue(void *p_job_queue) { CVTTCompressionJobQueue *job_queue = static_cast<CVTTCompressionJobQueue *>(p_job_queue); - for (uint32_t next_task = atomic_increment(&job_queue->current_task); next_task <= job_queue->num_tasks; next_task = atomic_increment(&job_queue->current_task)) { + for (uint32_t next_task = job_queue->current_task.increment(); next_task <= job_queue->num_tasks; next_task = job_queue->current_task.increment()) { _digest_row_task(job_queue->job_params, job_queue->job_tasks[next_task - 1]); } } @@ -263,7 +264,7 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann const CVTTCompressionRowTask *tasks_rb = tasks.ptr(); job_queue.job_tasks = &tasks_rb[0]; - job_queue.current_task = 0; + job_queue.current_task.set(0); job_queue.num_tasks = static_cast<uint32_t>(tasks.size()); for (int i = 0; i < num_job_threads; i++) { diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index 64fb8ee9c7..1bdbb0b03b 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -1670,7 +1670,7 @@ void NativeScriptLanguage::defer_init_library(Ref<GDNativeLibrary> lib, NativeSc MutexLock lock(mutex); libs_to_init.insert(lib); scripts_to_register.insert(script); - has_objects_to_register = true; + has_objects_to_register.set(); } #endif @@ -1759,7 +1759,7 @@ void NativeScriptLanguage::call_libraries_cb(const StringName &name) { void NativeScriptLanguage::frame() { #ifndef NO_THREADS - if (has_objects_to_register) { + if (has_objects_to_register.is_set()) { MutexLock lock(mutex); for (Set<Ref<GDNativeLibrary>>::Element *L = libs_to_init.front(); L; L = L->next()) { init_library(L->get()); @@ -1769,7 +1769,7 @@ void NativeScriptLanguage::frame() { register_script(S->get()); } scripts_to_register.clear(); - has_objects_to_register = false; + has_objects_to_register.clear(); } #endif diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h index 81a158d09b..d6ba2bbec1 100644 --- a/modules/gdnative/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -40,6 +40,7 @@ #include "core/os/thread_safe.h" #include "core/templates/oa_hash_map.h" #include "core/templates/ordered_hash_map.h" +#include "core/templates/safe_refcount.h" #include "core/templates/self_list.h" #include "scene/main/node.h" @@ -262,7 +263,7 @@ private: #ifndef NO_THREADS Set<Ref<GDNativeLibrary>> libs_to_init; Set<NativeScript *> scripts_to_register; - volatile bool has_objects_to_register = false; // so that we don't lock mutex every frame - it's rarely needed + SafeFlag has_objects_to_register; // so that we don't lock mutex every frame - it's rarely needed void defer_init_library(Ref<GDNativeLibrary> lib, NativeScript *script); #endif diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h index f92fe2340e..2685a8a013 100644 --- a/modules/theora/video_stream_theora.h +++ b/modules/theora/video_stream_theora.h @@ -36,6 +36,7 @@ #include "core/os/semaphore.h" #include "core/os/thread.h" #include "core/templates/ring_buffer.h" +#include "core/templates/safe_refcount.h" #include "scene/resources/video_stream.h" #include "servers/audio_server.h" @@ -113,7 +114,7 @@ class VideoStreamPlaybackTheora : public VideoStreamPlayback { bool thread_eof = false; Semaphore *thread_sem; Thread thread; - volatile bool thread_exit = false; + SafeFlag thread_exit; static void _streaming_thread(void *ud); diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 39726a4a58..fcd55b3049 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -4274,13 +4274,13 @@ VisualScriptEditor::VisualScriptEditor() { edit_menu->set_shortcut_context(this); edit_menu->set_text(TTR("Edit")); edit_menu->set_switch_on_hover(true); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/delete_selected"), EDIT_DELETE_NODES); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_graph_delete"), EDIT_DELETE_NODES); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/toggle_breakpoint"), EDIT_TOGGLE_BREAKPOINT); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/find_node_type"), EDIT_FIND_NODE_TYPE); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/copy_nodes"), EDIT_COPY_NODES); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/cut_nodes"), EDIT_CUT_NODES); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/paste_nodes"), EDIT_PASTE_NODES); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY_NODES); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT_NODES); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE_NODES); edit_menu->get_popup()->add_separator(); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/create_function"), EDIT_CREATE_FUNCTION); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/refresh_nodes"), REFRESH_GRAPH); @@ -4520,12 +4520,8 @@ void VisualScriptEditor::free_clipboard() { static void register_editor_callback() { ScriptEditor::register_create_script_editor_function(create_editor); - ED_SHORTCUT("visual_script_editor/delete_selected", TTR("Delete Selected"), KEY_DELETE); ED_SHORTCUT("visual_script_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_F9); ED_SHORTCUT("visual_script_editor/find_node_type", TTR("Find Node Type"), KEY_MASK_CMD + KEY_F); - ED_SHORTCUT("visual_script_editor/copy_nodes", TTR("Copy Nodes"), KEY_MASK_CMD + KEY_C); - ED_SHORTCUT("visual_script_editor/cut_nodes", TTR("Cut Nodes"), KEY_MASK_CMD + KEY_X); - ED_SHORTCUT("visual_script_editor/paste_nodes", TTR("Paste Nodes"), KEY_MASK_CMD + KEY_V); ED_SHORTCUT("visual_script_editor/create_function", TTR("Make Function"), KEY_MASK_CMD + KEY_G); ED_SHORTCUT("visual_script_editor/refresh_nodes", TTR("Refresh Graph"), KEY_MASK_CMD + KEY_R); ED_SHORTCUT("visual_script_editor/edit_member", TTR("Edit Member"), KEY_MASK_CMD + KEY_E); diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index d5eff9823f..888b1546e4 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -37,6 +37,7 @@ #include "core/os/dir_access.h" #include "core/os/file_access.h" #include "core/os/os.h" +#include "core/templates/safe_refcount.h" #include "core/version.h" #include "drivers/png/png_driver_common.h" #include "editor/editor_export.h" @@ -277,38 +278,38 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { Vector<PluginConfigAndroid> plugins; String last_plugin_names; uint64_t last_custom_build_time = 0; - volatile bool plugins_changed; + SafeFlag plugins_changed; Mutex plugins_lock; Vector<Device> devices; - volatile bool devices_changed; + SafeFlag devices_changed; Mutex device_lock; Thread check_for_changes_thread; - volatile bool quit_request; + SafeFlag quit_request; static void _check_for_changes_poll_thread(void *ud) { EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud; - while (!ea->quit_request) { + while (!ea->quit_request.is_set()) { // Check for plugins updates { // Nothing to do if we already know the plugins have changed. - if (!ea->plugins_changed) { + if (!ea->plugins_changed.is_set()) { Vector<PluginConfigAndroid> loaded_plugins = get_plugins(); MutexLock lock(ea->plugins_lock); if (ea->plugins.size() != loaded_plugins.size()) { - ea->plugins_changed = true; + ea->plugins_changed.set(); } else { for (int i = 0; i < ea->plugins.size(); i++) { if (ea->plugins[i].name != loaded_plugins[i].name) { - ea->plugins_changed = true; + ea->plugins_changed.set(); break; } } } - if (ea->plugins_changed) { + if (ea->plugins_changed.is_set()) { ea->plugins = loaded_plugins; } } @@ -422,7 +423,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { } ea->devices = ndevices; - ea->devices_changed = true; + ea->devices_changed.set(); } } @@ -431,7 +432,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { uint64_t time = OS::get_singleton()->get_ticks_usec(); while (OS::get_singleton()->get_ticks_usec() - time < wait) { OS::get_singleton()->delay_usec(1000 * sleep); - if (ea->quit_request) { + if (ea->quit_request.is_set()) { break; } } @@ -1667,7 +1668,7 @@ public: print_verbose("Found Android plugin " + plugins_configs[i].name); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + plugins_configs[i].name), false)); } - plugins_changed = false; + plugins_changed.clear(); Vector<String> abis = get_abis(); for (int i = 0; i < abis.size(); ++i) { @@ -1737,19 +1738,19 @@ public: } virtual bool should_update_export_options() override { - bool export_options_changed = plugins_changed; + bool export_options_changed = plugins_changed.is_set(); if (export_options_changed) { // don't clear unless we're reporting true, to avoid race - plugins_changed = false; + plugins_changed.clear(); } return export_options_changed; } virtual bool poll_export() override { - bool dc = devices_changed; + bool dc = devices_changed.is_set(); if (dc) { // don't clear unless we're reporting true, to avoid race - devices_changed = false; + devices_changed.clear(); } return dc; } @@ -2938,14 +2939,13 @@ public: run_icon.instance(); run_icon->create_from_image(img); - devices_changed = true; - plugins_changed = true; - quit_request = false; + devices_changed.set(); + plugins_changed.set(); check_for_changes_thread.start(_check_for_changes_poll_thread, this); } ~EditorExportPlatformAndroid() { - quit_request = true; + quit_request.set(); check_for_changes_thread.wait_to_finish(); } }; diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp index 4ee4427aa0..41201db32b 100644 --- a/platform/android/java_godot_io_wrapper.cpp +++ b/platform/android/java_godot_io_wrapper.cpp @@ -187,14 +187,14 @@ String GodotIOJavaWrapper::get_system_dir(int p_dir) { } } -// volatile because it can be changed from non-main thread and we need to +// SafeNumeric because it can be changed from non-main thread and we need to // ensure the change is immediately visible to other threads. -static volatile int virtual_keyboard_height; +static SafeNumeric<int> virtual_keyboard_height; int GodotIOJavaWrapper::get_vk_height() { - return virtual_keyboard_height; + return virtual_keyboard_height.get(); } void GodotIOJavaWrapper::set_vk_height(int p_height) { - virtual_keyboard_height = p_height; + virtual_keyboard_height.set(p_height); } diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index fe38482a00..c585c2afbe 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -37,6 +37,7 @@ #include "core/io/zip_io.h" #include "core/os/file_access.h" #include "core/os/os.h" +#include "core/templates/safe_refcount.h" #include "core/version.h" #include "editor/editor_export.h" #include "editor/editor_node.h" @@ -56,9 +57,9 @@ class EditorExportPlatformIOS : public EditorExportPlatform { Ref<ImageTexture> logo; // Plugins - volatile bool plugins_changed; + SafeFlag plugins_changed; Thread check_for_changes_thread; - volatile bool quit_request; + SafeFlag quit_request; Mutex plugins_lock; Vector<PluginConfigIOS> plugins; @@ -141,19 +142,19 @@ class EditorExportPlatformIOS : public EditorExportPlatform { static void _check_for_changes_poll_thread(void *ud) { EditorExportPlatformIOS *ea = (EditorExportPlatformIOS *)ud; - while (!ea->quit_request) { + while (!ea->quit_request.is_set()) { // Nothing to do if we already know the plugins have changed. - if (!ea->plugins_changed) { + if (!ea->plugins_changed.is_set()) { MutexLock lock(ea->plugins_lock); Vector<PluginConfigIOS> loaded_plugins = get_plugins(); if (ea->plugins.size() != loaded_plugins.size()) { - ea->plugins_changed = true; + ea->plugins_changed.set(); } else { for (int i = 0; i < ea->plugins.size(); i++) { if (ea->plugins[i].name != loaded_plugins[i].name || ea->plugins[i].last_updated != loaded_plugins[i].last_updated) { - ea->plugins_changed = true; + ea->plugins_changed.set(); break; } } @@ -165,7 +166,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform { while (OS::get_singleton()->get_ticks_usec() - time < wait) { OS::get_singleton()->delay_usec(300000); - if (ea->quit_request) { + if (ea->quit_request.is_set()) { break; } } @@ -182,10 +183,10 @@ public: virtual Ref<Texture2D> get_logo() const override { return logo; } virtual bool should_update_export_options() override { - bool export_options_changed = plugins_changed; + bool export_options_changed = plugins_changed.is_set(); if (export_options_changed) { // don't clear unless we're reporting true, to avoid race - plugins_changed = false; + plugins_changed.clear(); } return export_options_changed; } @@ -364,7 +365,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) for (int i = 0; i < found_plugins.size(); i++) { r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + found_plugins[i].name), false)); } - plugins_changed = false; + plugins_changed.clear(); plugins = found_plugins; r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/access_wifi"), false)); @@ -1967,14 +1968,13 @@ EditorExportPlatformIOS::EditorExportPlatformIOS() { logo.instance(); logo->create_from_image(img); - plugins_changed = true; - quit_request = false; + plugins_changed.set(); check_for_changes_thread.start(_check_for_changes_poll_thread, this); } EditorExportPlatformIOS::~EditorExportPlatformIOS() { - quit_request = true; + quit_request.set(); check_for_changes_thread.wait_to_finish(); } diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index 53baf17858..fceeb82325 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -2697,7 +2697,7 @@ bool DisplayServerX11::_wait_for_events() const { } void DisplayServerX11::_poll_events() { - while (!events_thread_done) { + while (!events_thread_done.is_set()) { _wait_for_events(); // Process events from the queue. @@ -4279,7 +4279,7 @@ DisplayServerX11::~DisplayServerX11() { _clipboard_transfer_ownership(XA_PRIMARY, x11_main_window); _clipboard_transfer_ownership(XInternAtom(x11_display, "CLIPBOARD", 0), x11_main_window); - events_thread_done = true; + events_thread_done.set(); events_thread.wait_to_finish(); //destroy all windows diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h index 906710f933..10686d8424 100644 --- a/platform/linuxbsd/display_server_x11.h +++ b/platform/linuxbsd/display_server_x11.h @@ -253,7 +253,7 @@ class DisplayServerX11 : public DisplayServer { mutable Mutex events_mutex; Thread events_thread; - bool events_thread_done = false; + SafeFlag events_thread_done; LocalVector<XEvent> polled_events; static void _poll_events_thread(void *ud); bool _wait_for_events() const; diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp index 9d2a768fb3..fc818a478d 100644 --- a/platform/linuxbsd/joypad_linux.cpp +++ b/platform/linuxbsd/joypad_linux.cpp @@ -87,7 +87,7 @@ JoypadLinux::JoypadLinux(Input *in) { } JoypadLinux::~JoypadLinux() { - exit_monitor = true; + exit_monitor.set(); joy_thread.wait_to_finish(); close_joypad(); } @@ -155,7 +155,7 @@ void JoypadLinux::monitor_joypads(udev *p_udev) { udev_monitor_enable_receiving(mon); int fd = udev_monitor_get_fd(mon); - while (!exit_monitor) { + while (!exit_monitor.is_set()) { fd_set fds; struct timeval tv; int ret; @@ -197,7 +197,7 @@ void JoypadLinux::monitor_joypads(udev *p_udev) { #endif void JoypadLinux::monitor_joypads() { - while (!exit_monitor) { + while (!exit_monitor.is_set()) { { MutexLock lock(joy_mutex); diff --git a/platform/linuxbsd/joypad_linux.h b/platform/linuxbsd/joypad_linux.h index 2cca0700d6..b0d0db047b 100644 --- a/platform/linuxbsd/joypad_linux.h +++ b/platform/linuxbsd/joypad_linux.h @@ -73,7 +73,7 @@ private: #ifdef UDEV_ENABLED bool use_udev = false; #endif - bool exit_monitor = false; + SafeFlag exit_monitor; Mutex joy_mutex; Thread joy_thread; Input *input = nullptr; diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 79624bf3bf..6d8d6058eb 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -35,14 +35,14 @@ #include "scene/main/window.h" void AudioStreamPlayer2D::_mix_audio() { - if (!stream_playback.is_valid() || !active || + if (!stream_playback.is_valid() || !active.is_set() || (stream_paused && !stream_paused_fade_out)) { return; } - if (setseek >= 0.0) { - stream_playback->start(setseek); - setseek = -1.0; //reset seek + if (setseek.get() >= 0.0) { + stream_playback->start(setseek.get()); + setseek.set(-1.0); //reset seek } //get data @@ -57,7 +57,8 @@ void AudioStreamPlayer2D::_mix_audio() { stream_playback->mix(buffer, pitch_scale, buffer_size); //write all outputs - for (int i = 0; i < output_count; i++) { + int oc = output_count.get(); + for (int i = 0; i < oc; i++) { Output current = outputs[i]; //see if current output exists, to keep volume ramp @@ -130,14 +131,14 @@ void AudioStreamPlayer2D::_mix_audio() { prev_outputs[i] = current; } - prev_output_count = output_count; + prev_output_count = oc; //stream is no longer active, disable this. if (!stream_playback->is_playing()) { - active = false; + active.clear(); } - output_ready = false; + output_ready.clear(); stream_paused_fade_in = false; stream_paused_fade_out = false; } @@ -168,7 +169,7 @@ void AudioStreamPlayer2D::_notification(int p_what) { if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { //update anything related to position first, if possible of course - if (!output_ready) { + if (!output_ready.is_set()) { List<Viewport *> viewports; Ref<World2D> world_2d = get_world_2d(); ERR_FAIL_COND(world_2d.is_null()); @@ -240,19 +241,19 @@ void AudioStreamPlayer2D::_notification(int p_what) { } } - output_count = new_output_count; - output_ready = true; + output_count.set(new_output_count); + output_ready.set(); } //start playing if requested - if (setplay >= 0.0) { - setseek = setplay; - active = true; - setplay = -1; + if (setplay.get() >= 0.0) { + setseek.set(setplay.get()); + active.set(); + setplay.set(-1); } //stop playing if no longer active - if (!active) { + if (!active.is_set()) { set_physics_process_internal(false); emit_signal("finished"); } @@ -267,8 +268,8 @@ void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) { if (stream_playback.is_valid()) { stream_playback.unref(); stream.unref(); - active = false; - setseek = -1; + active.clear(); + setseek.set(-1); } if (p_stream.is_valid()) { @@ -311,29 +312,29 @@ void AudioStreamPlayer2D::play(float p_from_pos) { } if (stream_playback.is_valid()) { - setplay = p_from_pos; - output_ready = false; + setplay.set(p_from_pos); + output_ready.clear(); set_physics_process_internal(true); } } void AudioStreamPlayer2D::seek(float p_seconds) { if (stream_playback.is_valid()) { - setseek = p_seconds; + setseek.set(p_seconds); } } void AudioStreamPlayer2D::stop() { if (stream_playback.is_valid()) { - active = false; + active.clear(); set_physics_process_internal(false); - setplay = -1; + setplay.set(-1); } } bool AudioStreamPlayer2D::is_playing() const { if (stream_playback.is_valid()) { - return active || setplay >= 0; + return active.is_set() || setplay.get() >= 0; } return false; @@ -341,8 +342,9 @@ bool AudioStreamPlayer2D::is_playing() const { float AudioStreamPlayer2D::get_playback_position() { if (stream_playback.is_valid()) { - if (setseek >= 0.0) { - return setseek; + float ss = setseek.get(); + if (ss >= 0.0) { + return ss; } return stream_playback->get_playback_position(); } @@ -383,7 +385,7 @@ void AudioStreamPlayer2D::_set_playing(bool p_enable) { } bool AudioStreamPlayer2D::_is_active() const { - return active; + return active.is_set(); } void AudioStreamPlayer2D::_validate_property(PropertyInfo &property) const { diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h index 6fb8cc414c..21f524c703 100644 --- a/scene/2d/audio_stream_player_2d.h +++ b/scene/2d/audio_stream_player_2d.h @@ -31,6 +31,7 @@ #ifndef AUDIO_STREAM_PLAYER_2D_H #define AUDIO_STREAM_PLAYER_2D_H +#include "core/templates/safe_refcount.h" #include "scene/2d/node_2d.h" #include "servers/audio/audio_stream.h" #include "servers/audio_server.h" @@ -52,8 +53,8 @@ private: }; Output outputs[MAX_OUTPUTS]; - volatile int output_count = 0; - volatile bool output_ready = false; + SafeNumeric<int> output_count; + SafeFlag output_ready; //these are used by audio thread to have a reference of previous volumes (for ramping volume and avoiding clicks) Output prev_outputs[MAX_OUTPUTS]; @@ -63,9 +64,9 @@ private: Ref<AudioStream> stream; Vector<AudioFrame> mix_buffer; - volatile float setseek = -1.0; - volatile bool active = false; - volatile float setplay = -1.0; + SafeNumeric<float> setseek{ -1.0 }; + SafeFlag active; + SafeNumeric<float> setplay{ -1.0 }; float volume_db = 0.0; float pitch_scale = 1.0; diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index dd77221a3f..72392be5bd 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -138,15 +138,15 @@ void AudioStreamPlayer3D::_calc_output_vol(const Vector3 &source_dir, real_t tig } void AudioStreamPlayer3D::_mix_audio() { - if (!stream_playback.is_valid() || !active || + if (!stream_playback.is_valid() || !active.is_set() || (stream_paused && !stream_paused_fade_out)) { return; } bool started = false; - if (setseek >= 0.0) { - stream_playback->start(setseek); - setseek = -1.0; //reset seek + if (setseek.get() >= 0.0) { + stream_playback->start(setseek.get()); + setseek.set(-1.0); //reset seek started = true; } @@ -160,14 +160,14 @@ void AudioStreamPlayer3D::_mix_audio() { } // Mix if we're not paused or we're fading out - if ((output_count > 0 || out_of_range_mode == OUT_OF_RANGE_MIX)) { + if ((output_count.get() > 0 || out_of_range_mode == OUT_OF_RANGE_MIX)) { float output_pitch_scale = 0.0; - if (output_count) { + if (output_count.get()) { //used for doppler, not realistic but good enough - for (int i = 0; i < output_count; i++) { + for (int i = 0; i < output_count.get(); i++) { output_pitch_scale += outputs[i].pitch_scale; } - output_pitch_scale /= float(output_count); + output_pitch_scale /= float(output_count.get()); } else { output_pitch_scale = 1.0; } @@ -176,7 +176,7 @@ void AudioStreamPlayer3D::_mix_audio() { } //write all outputs - for (int i = 0; i < output_count; i++) { + for (int i = 0; i < output_count.get(); i++) { Output current = outputs[i]; //see if current output exists, to keep volume ramp @@ -285,14 +285,14 @@ void AudioStreamPlayer3D::_mix_audio() { prev_outputs[i] = current; } - prev_output_count = output_count; + prev_output_count = output_count.get(); //stream is no longer active, disable this. if (!stream_playback->is_playing()) { - active = false; + active.clear(); } - output_ready = false; + output_ready.clear(); stream_paused_fade_in = false; stream_paused_fade_out = false; } @@ -360,7 +360,7 @@ void AudioStreamPlayer3D::_notification(int p_what) { if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { //update anything related to position first, if possible of course - if (!output_ready) { + if (!output_ready.is_set()) { Vector3 linear_velocity; //compute linear velocity for doppler @@ -596,19 +596,19 @@ void AudioStreamPlayer3D::_notification(int p_what) { } } - output_count = new_output_count; - output_ready = true; + output_count.set(new_output_count); + output_ready.set(); } //start playing if requested - if (setplay >= 0.0) { - setseek = setplay; - active = true; - setplay = -1; + if (setplay.get() >= 0.0) { + setseek.set(setplay.get()); + active.set(); + setplay.set(-1); } //stop playing if no longer active - if (!active) { + if (!active.is_set()) { set_physics_process_internal(false); emit_signal("finished"); } @@ -623,8 +623,8 @@ void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) { if (stream_playback.is_valid()) { stream_playback.unref(); stream.unref(); - active = false; - setseek = -1; + active.clear(); + setseek.set(-1); } if (p_stream.is_valid()) { @@ -683,29 +683,29 @@ void AudioStreamPlayer3D::play(float p_from_pos) { } if (stream_playback.is_valid()) { - setplay = p_from_pos; - output_ready = false; + setplay.set(p_from_pos); + output_ready.clear(); set_physics_process_internal(true); } } void AudioStreamPlayer3D::seek(float p_seconds) { if (stream_playback.is_valid()) { - setseek = p_seconds; + setseek.set(p_seconds); } } void AudioStreamPlayer3D::stop() { if (stream_playback.is_valid()) { - active = false; + active.clear(); set_physics_process_internal(false); - setplay = -1; + setplay.set(-1); } } bool AudioStreamPlayer3D::is_playing() const { if (stream_playback.is_valid()) { - return active || setplay >= 0; + return active.is_set() || setplay.get() >= 0; } return false; @@ -713,8 +713,9 @@ bool AudioStreamPlayer3D::is_playing() const { float AudioStreamPlayer3D::get_playback_position() { if (stream_playback.is_valid()) { - if (setseek >= 0.0) { - return setseek; + float ss = setseek.get(); + if (ss >= 0.0) { + return ss; } return stream_playback->get_playback_position(); } @@ -755,7 +756,7 @@ void AudioStreamPlayer3D::_set_playing(bool p_enable) { } bool AudioStreamPlayer3D::_is_active() const { - return active; + return active.is_set(); } void AudioStreamPlayer3D::_validate_property(PropertyInfo &property) const { diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index 33ed758749..70c535bd89 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -31,6 +31,7 @@ #ifndef AUDIO_STREAM_PLAYER_3D_H #define AUDIO_STREAM_PLAYER_3D_H +#include "core/templates/safe_refcount.h" #include "scene/3d/node_3d.h" #include "scene/3d/velocity_tracker_3d.h" #include "servers/audio/audio_filter_sw.h" @@ -80,8 +81,8 @@ private: }; Output outputs[MAX_OUTPUTS]; - volatile int output_count = 0; - volatile bool output_ready = false; + SafeNumeric<int> output_count; + SafeFlag output_ready; //these are used by audio thread to have a reference of previous volumes (for ramping volume and avoiding clicks) Output prev_outputs[MAX_OUTPUTS]; @@ -91,9 +92,9 @@ private: Ref<AudioStream> stream; Vector<AudioFrame> mix_buffer; - volatile float setseek = -1.0; - volatile bool active = false; - volatile float setplay = -1.0; + SafeNumeric<float> setseek{ -1.0 }; + SafeFlag active; + SafeNumeric<float> setplay{ -1.0 }; AttenuationModel attenuation_model = ATTENUATION_INVERSE_DISTANCE; float unit_db = 0.0; diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 979c6424b8..d22d7ff3ab 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -1074,7 +1074,7 @@ void CPUParticles3D::_update_particle_data_buffer() { ptr += 20; } - can_update = true; + can_update.set(); } void CPUParticles3D::_set_redraw(bool p_redraw) { @@ -1103,9 +1103,9 @@ void CPUParticles3D::_set_redraw(bool p_redraw) { void CPUParticles3D::_update_render_thread() { MutexLock lock(update_mutex); - if (can_update) { + if (can_update.is_set()) { RS::get_singleton()->multimesh_set_buffer(multimesh, particle_data); - can_update = false; //wait for next time + can_update.clear(); //wait for next time } } @@ -1167,7 +1167,7 @@ void CPUParticles3D::_notification(int p_what) { ptr += 20; } - can_update = true; + can_update.set(); } } } @@ -1468,6 +1468,21 @@ CPUParticles3D::CPUParticles3D() { set_param(PARAM_HUE_VARIATION, 0); set_param(PARAM_ANIM_SPEED, 0); set_param(PARAM_ANIM_OFFSET, 0); + set_emission_shape(EMISSION_SHAPE_POINT); + set_emission_sphere_radius(1); + set_emission_box_extents(Vector3(1, 1, 1)); + + set_gravity(Vector3(0, -9.8, 0)); + + for (int i = 0; i < PARAM_MAX; i++) { + set_param_randomness(Parameter(i), 0); + } + + for (int i = 0; i < PARTICLE_FLAG_MAX; i++) { + particle_flags[i] = false; + } + + set_color(Color(1, 1, 1, 1)); } CPUParticles3D::~CPUParticles3D() { diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index 4af959a7bf..10ac32622d 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -32,6 +32,7 @@ #define CPU_PARTICLES_H #include "core/templates/rid.h" +#include "core/templates/safe_refcount.h" #include "scene/3d/visual_instance_3d.h" class CPUParticles3D : public GeometryInstance3D { @@ -142,7 +143,7 @@ private: Transform inv_emission_transform; - volatile bool can_update = false; + SafeFlag can_update; DrawOrder draw_order = DRAW_ORDER_INDEX; diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index ec0a4b8696..7957571ab7 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -99,7 +99,7 @@ void AudioStreamPlayer::_mix_audio() { use_fadeout = false; } - if (!stream_playback.is_valid() || !active || + if (!stream_playback.is_valid() || !active.is_set() || (stream_paused && !stream_paused_fade)) { return; } @@ -112,24 +112,24 @@ void AudioStreamPlayer::_mix_audio() { return; } - if (setstop) { + if (setstop.is_set()) { _mix_internal(true); stream_playback->stop(); - setstop = false; + setstop.clear(); } - if (setseek >= 0.0 && !stop_has_priority) { + if (setseek.get() >= 0.0 && !stop_has_priority.is_set()) { if (stream_playback->is_playing()) { //fade out to avoid pops _mix_internal(true); } - stream_playback->start(setseek); - setseek = -1.0; //reset seek + stream_playback->start(setseek.get()); + setseek.set(-1.0); //reset seek mix_volume_db = volume_db; //reset ramp } - stop_has_priority = false; + stop_has_priority.clear(); _mix_internal(false); } @@ -143,8 +143,8 @@ void AudioStreamPlayer::_notification(int p_what) { } if (p_what == NOTIFICATION_INTERNAL_PROCESS) { - if (!active || (setseek < 0 && !stream_playback->is_playing())) { - active = false; + if (!active.is_set() || (setseek.get() < 0 && !stream_playback->is_playing())) { + active.clear(); set_process_internal(false); emit_signal("finished"); } @@ -169,7 +169,7 @@ void AudioStreamPlayer::_notification(int p_what) { void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) { AudioServer::get_singleton()->lock(); - if (active && stream_playback.is_valid() && !stream_paused) { + if (active.is_set() && stream_playback.is_valid() && !stream_paused) { //changing streams out of the blue is not a great idea, but at least //lets try to somehow avoid a click @@ -196,9 +196,9 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) { if (stream_playback.is_valid()) { stream_playback.unref(); stream.unref(); - active = false; - setseek = -1; - setstop = false; + active.clear(); + setseek.set(-1); + setstop.clear(); } if (p_stream.is_valid()) { @@ -237,29 +237,29 @@ float AudioStreamPlayer::get_pitch_scale() const { void AudioStreamPlayer::play(float p_from_pos) { if (stream_playback.is_valid()) { //mix_volume_db = volume_db; do not reset volume ramp here, can cause clicks - setseek = p_from_pos; - stop_has_priority = false; - active = true; + setseek.set(p_from_pos); + stop_has_priority.clear(); + active.set(); set_process_internal(true); } } void AudioStreamPlayer::seek(float p_seconds) { if (stream_playback.is_valid()) { - setseek = p_seconds; + setseek.set(p_seconds); } } void AudioStreamPlayer::stop() { - if (stream_playback.is_valid() && active) { - setstop = true; - stop_has_priority = true; + if (stream_playback.is_valid() && active.is_set()) { + setstop.set(); + stop_has_priority.set(); } } bool AudioStreamPlayer::is_playing() const { if (stream_playback.is_valid()) { - return active && !setstop; //&& stream_playback->is_playing(); + return active.is_set() && !setstop.is_set(); //&& stream_playback->is_playing(); } return false; @@ -267,8 +267,9 @@ bool AudioStreamPlayer::is_playing() const { float AudioStreamPlayer::get_playback_position() { if (stream_playback.is_valid()) { - if (setseek >= 0.0) { - return setseek; + float ss = setseek.get(); + if (ss >= 0.0) { + return ss; } return stream_playback->get_playback_position(); } @@ -317,7 +318,7 @@ void AudioStreamPlayer::_set_playing(bool p_enable) { } bool AudioStreamPlayer::_is_active() const { - return active; + return active.is_set(); } void AudioStreamPlayer::set_stream_paused(bool p_pause) { diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h index ab98d41302..d8f83ee38d 100644 --- a/scene/audio/audio_stream_player.h +++ b/scene/audio/audio_stream_player.h @@ -31,6 +31,7 @@ #ifndef AUDIO_STREAM_PLAYER_H #define AUDIO_STREAM_PLAYER_H +#include "core/templates/safe_refcount.h" #include "scene/main/node.h" #include "servers/audio/audio_stream.h" @@ -49,12 +50,12 @@ private: Ref<AudioStream> stream; Vector<AudioFrame> mix_buffer; Vector<AudioFrame> fadeout_buffer; - bool use_fadeout = false; + bool use_fadeout; - volatile float setseek = -1.0; - volatile bool active = false; - volatile bool setstop = false; - volatile bool stop_has_priority = false; + SafeNumeric<float> setseek{ -1.0 }; + SafeFlag active; + SafeFlag setstop; + SafeFlag stop_has_priority; float mix_volume_db = 0.0; float pitch_scale = 1.0; diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 6e61950f10..70015bcf88 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -1330,25 +1330,17 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { } } - Ref<InputEventKey> k = p_ev; - - if (k.is_valid()) { - if (k->get_keycode() == KEY_D && k->is_pressed() && k->get_command()) { + if (p_ev->is_pressed()) { + if (p_ev->is_action("ui_graph_duplicate")) { emit_signal("duplicate_nodes_request"); accept_event(); - } - - if (k->get_keycode() == KEY_C && k->is_pressed() && k->get_command()) { + } else if (p_ev->is_action("ui_copy")) { emit_signal("copy_nodes_request"); accept_event(); - } - - if (k->get_keycode() == KEY_V && k->is_pressed() && k->get_command()) { + } else if (p_ev->is_action("ui_paste")) { emit_signal("paste_nodes_request"); accept_event(); - } - - if (k->get_keycode() == KEY_DELETE && k->is_pressed()) { + } else if (p_ev->is_action("ui_graph_delete")) { emit_signal("delete_nodes_request"); accept_event(); } diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 5bd9259a64..ba08aae8e3 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -30,6 +30,7 @@ #include "line_edit.h" +#include "core/input/input_map.h" #include "core/object/message_queue.h" #include "core/os/keyboard.h" #include "core/os/os.h" @@ -44,6 +45,175 @@ #endif #include "scene/main/window.h" +void LineEdit::_swap_current_input_direction() { + if (input_direction == TEXT_DIRECTION_LTR) { + input_direction = TEXT_DIRECTION_RTL; + } else { + input_direction = TEXT_DIRECTION_LTR; + } + set_cursor_position(get_cursor_position()); + update(); +} + +void LineEdit::_move_cursor_left(bool p_select, bool p_move_by_word) { + if (selection.enabled && !p_select) { + set_cursor_position(selection.begin); + deselect(); + return; + } + + shift_selection_check_pre(p_select); + + if (p_move_by_word) { + int cc = cursor_pos; + + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); + for (int i = words.size() - 1; i >= 0; i--) { + if (words[i].x < cc) { + cc = words[i].x; + break; + } + } + + set_cursor_position(cc); + } else { + if (mid_grapheme_caret_enabled) { + set_cursor_position(get_cursor_position() - 1); + } else { + set_cursor_position(TS->shaped_text_prev_grapheme_pos(text_rid, get_cursor_position())); + } + } + + shift_selection_check_post(p_select); +} + +void LineEdit::_move_cursor_right(bool p_select, bool p_move_by_word) { + if (selection.enabled && !p_select) { + set_cursor_position(selection.end); + deselect(); + return; + } + + shift_selection_check_pre(p_select); + + if (p_move_by_word) { + int cc = cursor_pos; + + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); + for (int i = 0; i < words.size(); i++) { + if (words[i].y > cc) { + cc = words[i].y; + break; + } + } + + set_cursor_position(cc); + } else { + if (mid_grapheme_caret_enabled) { + set_cursor_position(get_cursor_position() + 1); + } else { + set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, get_cursor_position())); + } + } + + shift_selection_check_post(p_select); +} + +void LineEdit::_move_cursor_start(bool p_select) { + shift_selection_check_pre(p_select); + set_cursor_position(0); + shift_selection_check_post(p_select); +} + +void LineEdit::_move_cursor_end(bool p_select) { + shift_selection_check_pre(p_select); + set_cursor_position(text.length()); + shift_selection_check_post(p_select); +} + +void LineEdit::_backspace(bool p_word, bool p_all_to_left) { + if (!editable) { + return; + } + + if (p_all_to_left) { + deselect(); + text = text.substr(0, cursor_pos); + _text_changed(); + return; + } + + if (selection.enabled) { + selection_delete(); + return; + } + + if (p_word) { + int cc = cursor_pos; + + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); + for (int i = words.size() - 1; i >= 0; i--) { + if (words[i].x < cc) { + cc = words[i].x; + } + } + + delete_text(cc, cursor_pos); + + set_cursor_position(cc); + } else { + delete_char(); + } +} + +void LineEdit::_delete(bool p_word, bool p_all_to_right) { + if (!editable) { + return; + } + + if (p_all_to_right) { + deselect(); + text = text.substr(cursor_pos, text.length() - cursor_pos); + _shape(); + set_cursor_position(0); + _text_changed(); + return; + } + + if (selection.enabled) { + selection_delete(); + return; + } + + int text_len = text.length(); + + if (cursor_pos == text_len) { + return; // Nothing to do. + } + + if (p_word) { + int cc = cursor_pos; + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); + for (int i = 0; i < words.size(); i++) { + if (words[i].y > cc) { + cc = words[i].y; + break; + } + } + + delete_text(cursor_pos, cc); + } else { + if (mid_grapheme_caret_enabled) { + set_cursor_position(cursor_pos + 1); + delete_char(); + } else { + int cc = cursor_pos; + set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, cursor_pos)); + delete_text(cc, cursor_pos); + } + } +} + void LineEdit::_gui_input(Ref<InputEvent> p_event) { Ref<InputEventMouseButton> b = p_event; @@ -55,7 +225,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (b->is_pressed() && b->get_button_index() == BUTTON_RIGHT && context_menu_enabled) { menu->set_position(get_screen_transform().xform(get_local_mouse_position())); menu->set_size(Vector2(1, 1)); - //menu->set_scale(get_global_transform().get_scale()); + _generate_context_menu(); menu->popup(); grab_focus(); accept_event(); @@ -153,453 +323,163 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { return; } -#ifdef APPLE_STYLE_KEYS - if (k->get_control() && !k->get_shift() && !k->get_alt() && !k->get_command()) { - uint32_t remap_key = KEY_UNKNOWN; - switch (k->get_keycode()) { - case KEY_F: { - remap_key = KEY_RIGHT; - } break; - case KEY_B: { - remap_key = KEY_LEFT; - } break; - case KEY_P: { - remap_key = KEY_UP; - } break; - case KEY_N: { - remap_key = KEY_DOWN; - } break; - case KEY_D: { - remap_key = KEY_DELETE; - } break; - case KEY_H: { - remap_key = KEY_BACKSPACE; - } break; - case KEY_A: { - remap_key = KEY_HOME; - } break; - case KEY_E: { - remap_key = KEY_END; - } break; + if (context_menu_enabled) { + if (k->is_action("ui_menu", true)) { + Point2 pos = Point2(get_cursor_pixel_pos().x, (get_size().y + get_theme_font("font")->get_height(get_theme_font_size("font_size"))) / 2); + menu->set_position(get_global_transform().xform(pos)); + menu->set_size(Vector2(1, 1)); + _generate_context_menu(); + menu->popup(); + menu->grab_focus(); } + } - if (remap_key != KEY_UNKNOWN) { - k->set_keycode(remap_key); - k->set_control(false); + // Default is ENTER, KP_ENTER. Cannot use ui_accept as default includes SPACE + if (k->is_action("ui_text_newline", true)) { + emit_signal("text_entered", text); + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { + DisplayServer::get_singleton()->virtual_keyboard_hide(); } } -#endif - - unsigned int code = k->get_keycode(); - - if (k->get_command() && is_shortcut_keys_enabled()) { - bool handled = true; - - switch (code) { - case (KEY_QUOTELEFT): { // Swap current input direction (primary cursor) - - if (input_direction == TEXT_DIRECTION_LTR) { - input_direction = TEXT_DIRECTION_RTL; - } else { - input_direction = TEXT_DIRECTION_LTR; - } - set_cursor_position(get_cursor_position()); - update(); - - } break; - - case (KEY_X): { // CUT. - - if (editable) { - cut_text(); - } - - } break; - - case (KEY_C): { // COPY. - - copy_text(); - - } break; - - case (KEY_Y): // PASTE (Yank for unix users). - case (KEY_V): { // PASTE. - - if (editable) { - paste_text(); - } - - } break; - - case (KEY_Z): { // Undo/redo. - if (editable) { - if (k->get_shift()) { - redo(); - } else { - undo(); - } - } - } break; - - case (KEY_U): { // Delete from start to cursor. - - if (editable) { - deselect(); - text = text.substr(cursor_pos, text.length() - cursor_pos); - _shape(); - set_cursor_position(0); - _text_changed(); - } - } break; - - case (KEY_K): { // Delete from cursor_pos to end. - - if (editable) { - deselect(); - text = text.substr(0, cursor_pos); - _text_changed(); - } + if (is_shortcut_keys_enabled()) { + if (k->is_action("ui_copy", true)) { + copy_text(); + accept_event(); + return; + } - } break; - case (KEY_A): { // Select all. - select(); + if (k->is_action("ui_text_select_all", true)) { + select(); + accept_event(); + return; + } - } break; -#ifdef APPLE_STYLE_KEYS - case (KEY_LEFT): { // Go to start of text - like HOME key. - shift_selection_check_pre(k->get_shift()); - set_cursor_position(0); - shift_selection_check_post(k->get_shift()); - } break; - case (KEY_RIGHT): { // Go to end of text - like END key. - shift_selection_check_pre(k->get_shift()); - set_cursor_position(text.length()); - shift_selection_check_post(k->get_shift()); - } break; - case (KEY_BACKSPACE): { - if (!editable) - break; + // Cut / Paste + if (k->is_action("ui_cut", true)) { + cut_text(); + accept_event(); + return; + } - // If selected, delete the selection - if (selection.enabled) { - selection_delete(); - break; - } + if (k->is_action("ui_paste", true)) { + paste_text(); + accept_event(); + return; + } - // Otherwise delete from cursor to beginning of text edit - int current_pos = get_cursor_position(); - if (current_pos != 0) { - delete_text(0, current_pos); - } - } break; -#endif - default: { - handled = false; - } + // Undo / Redo + if (k->is_action("ui_undo", true)) { + undo(); + accept_event(); + return; } - if (handled) { + if (k->is_action("ui_redo", true)) { + redo(); accept_event(); return; } } - _reset_caret_blink_timer(); - if (!k->get_metakey()) { - bool handled = true; - switch (code) { - case KEY_KP_ENTER: - case KEY_ENTER: { - emit_signal("text_entered", text); - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { - DisplayServer::get_singleton()->virtual_keyboard_hide(); - } - - } break; - - case KEY_BACKSPACE: { - if (!editable) { - break; - } - - if (selection.enabled) { - selection_delete(); - break; - } - -#ifdef APPLE_STYLE_KEYS - if (k->get_alt()) { -#else - if (k->get_alt()) { - handled = false; - break; - } else if (k->get_command()) { -#endif - int cc = cursor_pos; - - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); - for (int i = words.size() - 1; i >= 0; i--) { - if (words[i].x < cc) { - cc = words[i].x; - break; - } - } - - delete_text(cc, cursor_pos); - - set_cursor_position(cc); - - } else { - delete_char(); - } - - } break; - case KEY_KP_4: { - if (k->get_unicode() != 0) { - handled = false; - break; - } - [[fallthrough]]; - } - case KEY_LEFT: { -#ifndef APPLE_STYLE_KEYS - if (!k->get_alt()) { -#endif - if (selection.enabled && !k->get_shift()) { - set_cursor_position(selection.begin); - deselect(); - handled = true; - break; - } - - shift_selection_check_pre(k->get_shift()); -#ifndef APPLE_STYLE_KEYS - } -#endif - -#ifdef APPLE_STYLE_KEYS - if (k->get_command()) { - set_cursor_position(0); - } else if (k->get_alt()) { -#else - if (k->get_alt()) { - handled = false; - break; - } else if (k->get_command()) { -#endif - int cc = cursor_pos; - - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); - for (int i = words.size() - 1; i >= 0; i--) { - if (words[i].x < cc) { - cc = words[i].x; - break; - } - } - - set_cursor_position(cc); - - } else { - if (mid_grapheme_caret_enabled) { - set_cursor_position(get_cursor_position() - 1); - } else { - set_cursor_position(TS->shaped_text_prev_grapheme_pos(text_rid, get_cursor_position())); - } - } - - shift_selection_check_post(k->get_shift()); - - } break; - case KEY_KP_6: { - if (k->get_unicode() != 0) { - handled = false; - break; - } - [[fallthrough]]; - } - case KEY_RIGHT: { -#ifndef APPLE_STYLE_KEYS - if (!k->get_alt()) { -#endif - if (selection.enabled && !k->get_shift()) { - set_cursor_position(selection.end); - deselect(); - handled = true; - break; - } - - shift_selection_check_pre(k->get_shift()); -#ifndef APPLE_STYLE_KEYS - } -#endif - -#ifdef APPLE_STYLE_KEYS - if (k->get_command()) { - set_cursor_position(text.length()); - } else if (k->get_alt()) { -#else - if (k->get_alt()) { - handled = false; - break; - } else if (k->get_command()) { -#endif - int cc = cursor_pos; - - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); - for (int i = 0; i < words.size(); i++) { - if (words[i].y > cc) { - cc = words[i].y; - break; - } - } - - set_cursor_position(cc); - - } else { - if (mid_grapheme_caret_enabled) { - set_cursor_position(get_cursor_position() + 1); - } else { - set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, get_cursor_position())); - } - } - - shift_selection_check_post(k->get_shift()); - - } break; - case KEY_UP: { - shift_selection_check_pre(k->get_shift()); - if (get_cursor_position() == 0) { - handled = false; - } - set_cursor_position(0); - shift_selection_check_post(k->get_shift()); - } break; - case KEY_DOWN: { - shift_selection_check_pre(k->get_shift()); - if (get_cursor_position() == text.length()) { - handled = false; - } - set_cursor_position(text.length()); - shift_selection_check_post(k->get_shift()); - } break; - case KEY_DELETE: { - if (!editable) { - break; - } - - if (k->get_shift() && !k->get_command() && !k->get_alt()) { - cut_text(); - break; - } - - if (selection.enabled) { - selection_delete(); - break; - } - - int text_len = text.length(); + // BACKSPACE + if (k->is_action("ui_text_backspace_all_to_left", true)) { + _backspace(false, true); + accept_event(); + return; + } + if (k->is_action("ui_text_backspace_word", true)) { + _backspace(true); + accept_event(); + return; + } + if (k->is_action("ui_text_backspace", true)) { + _backspace(); + accept_event(); + return; + } - if (cursor_pos == text_len) { - break; // Nothing to do. - } + // DELETE + if (k->is_action("ui_text_delete_all_to_right", true)) { + _delete(false, true); + accept_event(); + return; + } + if (k->is_action("ui_text_delete_word", true)) { + _delete(true); + accept_event(); + return; + } + if (k->is_action("ui_text_delete", true)) { + _delete(); + accept_event(); + return; + } -#ifdef APPLE_STYLE_KEYS - if (k->get_alt()) { -#else - if (k->get_alt()) { - handled = false; - break; - } else if (k->get_command()) { -#endif - int cc = cursor_pos; + // Cursor Movement - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); - for (int i = 0; i < words.size(); i++) { - if (words[i].y > cc) { - cc = words[i].y; - break; - } - } + k = k->duplicate(); + bool shift_pressed = k->get_shift(); + // Remove shift or else actions will not match. Use above variable for selection. + k->set_shift(false); - delete_text(cursor_pos, cc); + if (k->is_action("ui_text_caret_word_left", true)) { + _move_cursor_left(shift_pressed, true); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_left", true)) { + _move_cursor_left(shift_pressed); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_word_right", true)) { + _move_cursor_right(shift_pressed, true); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_right", true)) { + _move_cursor_right(shift_pressed, false); + accept_event(); + return; + } - } else { - if (mid_grapheme_caret_enabled) { - set_cursor_position(cursor_pos + 1); - delete_char(); - } else { - int cc = cursor_pos; - set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, cursor_pos)); - delete_text(cc, cursor_pos); - } - } + // Up = Home, Down = End + if (k->is_action("ui_text_caret_up", true) || k->is_action("ui_text_caret_line_start", true) || k->is_action("ui_text_caret_page_up", true)) { + _move_cursor_start(shift_pressed); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_down", true) || k->is_action("ui_text_caret_line_end", true) || k->is_action("ui_text_caret_page_down", true)) { + _move_cursor_end(shift_pressed); + accept_event(); + return; + } - } break; - case KEY_KP_7: { - if (k->get_unicode() != 0) { - handled = false; - break; - } - [[fallthrough]]; - } - case KEY_HOME: { - shift_selection_check_pre(k->get_shift()); - set_cursor_position(0); - shift_selection_check_post(k->get_shift()); - } break; - case KEY_KP_1: { - if (k->get_unicode() != 0) { - handled = false; - break; - } - [[fallthrough]]; - } - case KEY_END: { - shift_selection_check_pre(k->get_shift()); - set_cursor_position(text.length()); - shift_selection_check_post(k->get_shift()); - } break; - case KEY_MENU: { - if (context_menu_enabled) { - Point2 pos = Point2(get_cursor_pixel_pos().x, (get_size().y + get_theme_font("font")->get_height(get_theme_font_size("font_size"))) / 2); - menu->set_position(get_global_transform().xform(pos)); - menu->set_size(Vector2(1, 1)); - //menu->set_scale(get_global_transform().get_scale()); - menu->popup(); - menu->grab_focus(); - } - } break; + // Misc + if (k->is_action("ui_swap_input_direction", true)) { + _swap_current_input_direction(); + accept_event(); + return; + } - default: { - handled = false; - } break; - } + _reset_caret_blink_timer(); - if (handled) { - accept_event(); - } else if (!k->get_command()) { - if (k->get_unicode() >= 32 && k->get_keycode() != KEY_DELETE) { - if (editable) { - selection_delete(); - char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 }; - int prev_len = text.length(); - append_at_cursor(ucodestr); - if (text.length() != prev_len) { - _text_changed(); - } - accept_event(); - } + // Allow unicode handling if: + // * No Modifiers are pressed (except shift) + bool allow_unicode_handling = !(k->get_command() || k->get_control() || k->get_alt() || k->get_metakey()); - } else { - return; - } + if (allow_unicode_handling && editable && k->get_unicode() >= 32) { + // Handle Unicode (if no modifiers active) + selection_delete(); + char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 }; + int prev_len = text.length(); + append_at_cursor(ucodestr); + if (text.length() != prev_len) { + _text_changed(); } - - update(); + accept_event(); } - - return; } } @@ -1013,13 +893,17 @@ void LineEdit::copy_text() { } void LineEdit::cut_text() { - if (selection.enabled && !pass) { + if (editable && selection.enabled && !pass) { DisplayServer::get_singleton()->clipboard_set(text.substr(selection.begin, selection.end - selection.begin)); selection_delete(); } } void LineEdit::paste_text() { + if (!editable) { + return; + } + // Strip escape characters like \n and \t as they can't be displayed on LineEdit. String paste_buffer = DisplayServer::get_singleton()->clipboard_get().strip_escapes(); @@ -1040,6 +924,10 @@ void LineEdit::paste_text() { } void LineEdit::undo() { + if (!editable) { + return; + } + if (undo_stack_pos == nullptr) { if (undo_stack.size() <= 1) { return; @@ -1059,6 +947,10 @@ void LineEdit::undo() { } void LineEdit::redo() { + if (!editable) { + return; + } + if (undo_stack_pos == nullptr) { return; } @@ -2060,25 +1952,50 @@ void LineEdit::_create_undo_state() { undo_stack.push_back(op); } +int LineEdit::_get_menu_action_accelerator(const String &p_action) { + const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(p_action); + if (!events) { + return 0; + } + + // Use first event in the list for the accelerator. + const List<Ref<InputEvent>>::Element *first_event = events->front(); + if (!first_event) { + return 0; + } + + const Ref<InputEventKey> event = first_event->get(); + if (event.is_null()) { + return 0; + } + + // Use physical keycode if non-zero + if (event->get_physical_keycode() != 0) { + return event->get_physical_keycode_with_modifiers(); + } else { + return event->get_keycode_with_modifiers(); + } +} + void LineEdit::_generate_context_menu() { // Reorganize context menu. menu->clear(); if (editable) { - menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_X : 0); + menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_cut") : 0); } - menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_C : 0); + menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : 0); if (editable) { - menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_V : 0); + menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : 0); } menu->add_separator(); if (is_selecting_enabled()) { - menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_A : 0); + menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : 0); } if (editable) { menu->add_item(RTR("Clear"), MENU_CLEAR); menu->add_separator(); - menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_Z : 0); - menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z : 0); + menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_undo") : 0); + menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : 0); } menu->add_separator(); menu->add_submenu_item(RTR("Text writing direction"), "DirMenu"); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 457a709f5b..cbadf818cd 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -163,6 +163,7 @@ private: void _clear_redo(); void _create_undo_state(); + int _get_menu_action_accelerator(const String &p_action); void _generate_context_menu(); void _shape(); @@ -188,6 +189,14 @@ private: void _editor_settings_changed(); + void _swap_current_input_direction(); + void _move_cursor_left(bool p_select, bool p_move_by_word = false); + void _move_cursor_right(bool p_select, bool p_move_by_word = false); + void _move_cursor_start(bool p_select); + void _move_cursor_end(bool p_select); + void _backspace(bool p_word = false, bool p_all_to_left = false); + void _delete(bool p_word = false, bool p_all_to_right = false); + void _gui_input(Ref<InputEvent> p_event); void _notification(int p_what); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 6a1cc291a6..a79c633502 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1575,53 +1575,36 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { Ref<InputEventKey> k = p_event; if (k.is_valid()) { - if (k->is_pressed() && !k->get_alt() && !k->get_shift()) { + if (k->is_pressed()) { bool handled = false; - switch (k->get_keycode()) { - case KEY_PAGEUP: { - if (vscroll->is_visible_in_tree()) { - vscroll->set_value(vscroll->get_value() - vscroll->get_page()); - handled = true; - } - } break; - case KEY_PAGEDOWN: { - if (vscroll->is_visible_in_tree()) { - vscroll->set_value(vscroll->get_value() + vscroll->get_page()); - handled = true; - } - } break; - case KEY_UP: { - if (vscroll->is_visible_in_tree()) { - vscroll->set_value(vscroll->get_value() - get_theme_font("normal_font")->get_height(get_theme_font_size("normal_font_size"))); - handled = true; - } - } break; - case KEY_DOWN: { - if (vscroll->is_visible_in_tree()) { - vscroll->set_value(vscroll->get_value() + get_theme_font("normal_font")->get_height(get_theme_font_size("normal_font_size"))); - handled = true; - } - } break; - case KEY_HOME: { - if (vscroll->is_visible_in_tree()) { - vscroll->set_value(0); - handled = true; - } - } break; - case KEY_END: { - if (vscroll->is_visible_in_tree()) { - vscroll->set_value(vscroll->get_max()); - handled = true; - } - } break; - case KEY_INSERT: - case KEY_C: { - if (k->get_command()) { - selection_copy(); - handled = true; - } - } break; + if (k->is_action("ui_pageup") && vscroll->is_visible_in_tree()) { + vscroll->set_value(vscroll->get_value() - vscroll->get_page()); + handled = true; + } + if (k->is_action("ui_pagedown") && vscroll->is_visible_in_tree()) { + vscroll->set_value(vscroll->get_value() + vscroll->get_page()); + handled = true; + } + if (k->is_action("ui_up") && vscroll->is_visible_in_tree()) { + vscroll->set_value(vscroll->get_value() - get_theme_font("normal_font")->get_height(get_theme_font_size("normal_font_size"))); + handled = true; + } + if (k->is_action("ui_down") && vscroll->is_visible_in_tree()) { + vscroll->set_value(vscroll->get_value() + get_theme_font("normal_font")->get_height(get_theme_font_size("normal_font_size"))); + handled = true; + } + if (k->is_action("ui_home") && vscroll->is_visible_in_tree()) { + vscroll->set_value(0); + handled = true; + } + if (k->is_action("ui_end") && vscroll->is_visible_in_tree()) { + vscroll->set_value(vscroll->get_max()); + handled = true; + } + if (k->is_action("ui_copy")) { + selection_copy(); + handled = true; } if (handled) { diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 880e66eb6a..36aa18417d 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/input/input.h" +#include "core/input/input_map.h" #include "core/object/message_queue.h" #include "core/object/script_language.h" #include "core/os/keyboard.h" @@ -1985,7 +1986,7 @@ void TextEdit::backspace_at_cursor() { cursor_set_column(prev_column); } -void TextEdit::indent_right() { +void TextEdit::indent_selected_lines_right() { int start_line; int end_line; @@ -2037,7 +2038,7 @@ void TextEdit::indent_right() { update(); } -void TextEdit::indent_left() { +void TextEdit::indent_selected_lines_left() { int start_line; int end_line; @@ -2108,6 +2109,618 @@ int TextEdit::_calculate_spaces_till_next_right_indent(int column) { return indent_size - column % indent_size; } +void TextEdit::_swap_current_input_direction() { + if (input_direction == TEXT_DIRECTION_LTR) { + input_direction = TEXT_DIRECTION_RTL; + } else { + input_direction = TEXT_DIRECTION_LTR; + } + cursor_set_column(cursor.column); + update(); +} + +void TextEdit::_new_line(bool p_split_current_line, bool p_above) { + if (readonly) { + return; + } + + String ins = "\n"; + + // Keep indentation. + int space_count = 0; + for (int i = 0; i < cursor.column; i++) { + if (text[cursor.line][i] == '\t') { + if (indent_using_spaces) { + ins += space_indent; + } else { + ins += "\t"; + } + space_count = 0; + } else if (text[cursor.line][i] == ' ') { + space_count++; + + if (space_count == indent_size) { + if (indent_using_spaces) { + ins += space_indent; + } else { + ins += "\t"; + } + space_count = 0; + } + } else { + break; + } + } + + if (is_folded(cursor.line)) { + unfold_line(cursor.line); + } + + bool brace_indent = false; + + // No need to indent if we are going upwards. + if (auto_indent && !p_above) { + // Indent once again if previous line will end with ':','{','[','(' and the line is not a comment + // (i.e. colon/brace precedes current cursor position). + if (cursor.column > 0) { + bool indent_char_found = false; + bool should_indent = false; + char indent_char = ':'; + char c = text[cursor.line][cursor.column]; + + for (int i = 0; i < cursor.column; i++) { + c = text[cursor.line][i]; + switch (c) { + case ':': + case '{': + case '[': + case '(': + indent_char_found = true; + should_indent = true; + indent_char = c; + continue; + } + + if (indent_char_found && is_line_comment(cursor.line)) { + should_indent = true; + break; + } else if (indent_char_found && !_is_whitespace(c)) { + should_indent = false; + indent_char_found = false; + } + } + + if (!is_line_comment(cursor.line) && should_indent) { + if (indent_using_spaces) { + ins += space_indent; + } else { + ins += "\t"; + } + + // No need to move the brace below if we are not taking the text with us. + char32_t closing_char = _get_right_pair_symbol(indent_char); + if ((closing_char != 0) && (closing_char == text[cursor.line][cursor.column]) && !p_split_current_line) { + brace_indent = true; + ins += "\n" + ins.substr(1, ins.length() - 2); + } + } + } + } + begin_complex_operation(); + bool first_line = false; + if (!p_split_current_line) { + if (p_above) { + if (cursor.line > 0) { + cursor_set_line(cursor.line - 1); + cursor_set_column(text[cursor.line].length()); + } else { + cursor_set_column(0); + first_line = true; + } + } else { + cursor_set_column(text[cursor.line].length()); + } + } + + insert_text_at_cursor(ins); + + if (first_line) { + cursor_set_line(0); + } else if (brace_indent) { + cursor_set_line(cursor.line - 1); + cursor_set_column(text[cursor.line].length()); + } + end_complex_operation(); +} + +void TextEdit::_indent_right() { + if (readonly) { + return; + } + + if (is_selection_active()) { + indent_selected_lines_right(); + } else { + // Simple indent. + if (indent_using_spaces) { + // Insert only as much spaces as needed till next indentation level. + int spaces_to_add = _calculate_spaces_till_next_right_indent(cursor.column); + String indent_to_insert = String(); + for (int i = 0; i < spaces_to_add; i++) { + indent_to_insert = ' ' + indent_to_insert; + } + _insert_text_at_cursor(indent_to_insert); + } else { + _insert_text_at_cursor("\t"); + } + } +} + +void TextEdit::_indent_left() { + if (readonly) { + return; + } + + if (is_selection_active()) { + indent_selected_lines_left(); + } else { + // Simple unindent. + int cc = cursor.column; + const String &line = text[cursor.line]; + + int left = _find_first_non_whitespace_column_of_line(line); + cc = MIN(cc, left); + + while (cc < indent_size && cc < left && line[cc] == ' ') { + cc++; + } + + if (cc > 0 && cc <= text[cursor.line].length()) { + if (text[cursor.line][cc - 1] == '\t') { + // Tabs unindentation. + _remove_text(cursor.line, cc - 1, cursor.line, cc); + if (cursor.column >= left) { + cursor_set_column(MAX(0, cursor.column - 1)); + } + update(); + } else { + // Spaces unindentation. + int spaces_to_remove = _calculate_spaces_till_next_left_indent(cc); + if (spaces_to_remove > 0) { + _remove_text(cursor.line, cc - spaces_to_remove, cursor.line, cc); + if (cursor.column > left - spaces_to_remove) { // Inside text? + cursor_set_column(MAX(0, cursor.column - spaces_to_remove)); + } + update(); + } + } + } else if (cc == 0 && line.length() > 0 && line[0] == '\t') { + _remove_text(cursor.line, 0, cursor.line, 1); + update(); + } + } +} + +void TextEdit::_move_cursor_left(bool p_select, bool p_move_by_word) { + // Handle selection + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); + } + + if (p_move_by_word) { + int cc = cursor.column; + + if (cc == 0 && cursor.line > 0) { + cursor_set_line(cursor.line - 1); + cursor_set_column(text[cursor.line].length()); + } else { + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(cursor.line)->get_rid()); + for (int i = words.size() - 1; i >= 0; i--) { + if (words[i].x < cc) { + cc = words[i].x; + break; + } + } + cursor_set_column(cc); + } + } else { + // If the cursor is at the start of the line, and not on the first line, move it up to the end of the previous line. + if (cursor.column == 0) { + if (cursor.line > 0) { + cursor_set_line(cursor.line - num_lines_from(CLAMP(cursor.line - 1, 0, text.size() - 1), -1)); + cursor_set_column(text[cursor.line].length()); + } + } else { + if (mid_grapheme_caret_enabled) { + cursor_set_column(cursor_get_column() - 1); + } else { + cursor_set_column(TS->shaped_text_prev_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), cursor_get_column())); + } + } + } + + if (p_select) { + _post_shift_selection(); + } +} + +void TextEdit::_move_cursor_right(bool p_select, bool p_move_by_word) { + // Handle selection + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); + } + + if (p_move_by_word) { + int cc = cursor.column; + + if (cc == text[cursor.line].length() && cursor.line < text.size() - 1) { + cursor_set_line(cursor.line + 1); + cursor_set_column(0); + } else { + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(cursor.line)->get_rid()); + for (int i = 0; i < words.size(); i++) { + if (words[i].y > cc) { + cc = words[i].y; + break; + } + } + cursor_set_column(cc); + } + } else { + // If we are at the end of the line, move the caret to the next line down. + if (cursor.column == text[cursor.line].length()) { + if (cursor.line < text.size() - 1) { + cursor_set_line(cursor_get_line() + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1), true, false); + cursor_set_column(0); + } + } else { + if (mid_grapheme_caret_enabled) { + cursor_set_column(cursor_get_column() + 1); + } else { + cursor_set_column(TS->shaped_text_next_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), cursor_get_column())); + } + } + } + + if (p_select) { + _post_shift_selection(); + } +} + +void TextEdit::_move_cursor_up(bool p_select) { + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); + } + + int cur_wrap_index = get_cursor_wrap_index(); + if (cur_wrap_index > 0) { + cursor_set_line(cursor.line, true, false, cur_wrap_index - 1); + } else if (cursor.line == 0) { + cursor_set_column(0); + } else { + int new_line = cursor.line - num_lines_from(cursor.line - 1, -1); + if (line_wraps(new_line)) { + cursor_set_line(new_line, true, false, times_line_wraps(new_line)); + } else { + cursor_set_line(new_line, true, false); + } + } + + if (p_select) { + _post_shift_selection(); + } + + _cancel_code_hint(); +} + +void TextEdit::_move_cursor_down(bool p_select) { + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); + } + + int cur_wrap_index = get_cursor_wrap_index(); + if (cur_wrap_index < times_line_wraps(cursor.line)) { + cursor_set_line(cursor.line, true, false, cur_wrap_index + 1); + } else if (cursor.line == get_last_unhidden_line()) { + cursor_set_column(text[cursor.line].length()); + } else { + int new_line = cursor.line + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1); + cursor_set_line(new_line, true, false, 0); + } + + if (p_select) { + _post_shift_selection(); + } + + _cancel_code_hint(); +} + +void TextEdit::_move_cursor_to_line_start(bool p_select) { + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); + } + + // Move cursor column to start of wrapped row and then to start of text. + Vector<String> rows = get_wrap_rows_text(cursor.line); + int wi = get_cursor_wrap_index(); + int row_start_col = 0; + for (int i = 0; i < wi; i++) { + row_start_col += rows[i].length(); + } + if (cursor.column == row_start_col || wi == 0) { + // Compute whitespace symbols seq length. + int current_line_whitespace_len = 0; + while (current_line_whitespace_len < text[cursor.line].length()) { + char32_t c = text[cursor.line][current_line_whitespace_len]; + if (c != '\t' && c != ' ') { + break; + } + current_line_whitespace_len++; + } + + if (cursor_get_column() == current_line_whitespace_len) { + cursor_set_column(0); + } else { + cursor_set_column(current_line_whitespace_len); + } + } else { + cursor_set_column(row_start_col); + } + + if (p_select) { + _post_shift_selection(); + } + + _cancel_completion(); + completion_hint = ""; +} + +void TextEdit::_move_cursor_to_line_end(bool p_select) { + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); + } + + // Move cursor column to end of wrapped row and then to end of text. + Vector<String> rows = get_wrap_rows_text(cursor.line); + int wi = get_cursor_wrap_index(); + int row_end_col = -1; + for (int i = 0; i < wi + 1; i++) { + row_end_col += rows[i].length(); + } + if (wi == rows.size() - 1 || cursor.column == row_end_col) { + cursor_set_column(text[cursor.line].length()); + } else { + cursor_set_column(row_end_col); + } + + if (p_select) { + _post_shift_selection(); + } + _cancel_completion(); + completion_hint = ""; +} + +void TextEdit::_move_cursor_page_up(bool p_select) { + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); + } + + int wi; + int n_line = cursor.line - num_lines_from_rows(cursor.line, get_cursor_wrap_index(), -get_visible_rows(), wi) + 1; + cursor_set_line(n_line, true, false, wi); + + if (p_select) { + _post_shift_selection(); + } + + _cancel_completion(); + completion_hint = ""; +} + +void TextEdit::_move_cursor_page_down(bool p_select) { + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); + } + + int wi; + int n_line = cursor.line + num_lines_from_rows(cursor.line, get_cursor_wrap_index(), get_visible_rows(), wi) - 1; + cursor_set_line(n_line, true, false, wi); + + if (p_select) { + _post_shift_selection(); + } + + _cancel_completion(); + completion_hint = ""; +} + +void TextEdit::_backspace(bool p_word, bool p_all_to_left) { + if (readonly) { + return; + } + + if (is_selection_active()) { + _delete_selection(); + return; + } + if (p_all_to_left) { + int cursor_current_column = cursor.column; + cursor.column = 0; + _remove_text(cursor.line, 0, cursor.line, cursor_current_column); + } else if (p_word) { + int line = cursor.line; + int column = cursor.column; + + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); + for (int i = words.size() - 1; i >= 0; i--) { + if (words[i].x < column) { + column = words[i].x; + break; + } + } + + _remove_text(line, column, cursor.line, cursor.column); + + cursor_set_line(line); + cursor_set_column(column); + } else { + // One character. + if (cursor.line > 0 && is_line_hidden(cursor.line - 1)) { + unfold_line(cursor.line - 1); + } + backspace_at_cursor(); + } +} + +void TextEdit::_delete(bool p_word, bool p_all_to_right) { + if (readonly) { + return; + } + + if (is_selection_active()) { + _delete_selection(); + return; + } + int curline_len = text[cursor.line].length(); + + if (cursor.line == text.size() - 1 && cursor.column == curline_len) { + return; // Last line, last column: Nothing to do. + } + + int next_line = cursor.column < curline_len ? cursor.line : cursor.line + 1; + int next_column; + + if (p_all_to_right) { + // Delete everything to right of cursor + next_column = curline_len; + next_line = cursor.line; + } else if (p_word && cursor.column < curline_len - 1) { + // Delete next word to right of cursor + int line = cursor.line; + int column = cursor.column; + + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); + for (int i = 0; i < words.size(); i++) { + if (words[i].y > column) { + column = words[i].y; + break; + } + } + + next_line = line; + next_column = column; + } else { + // Delete one character + next_column = cursor.column < curline_len ? (cursor.column + 1) : 0; + if (mid_grapheme_caret_enabled) { + next_column = cursor.column < curline_len ? (cursor.column + 1) : 0; + } else { + next_column = cursor.column < curline_len ? TS->shaped_text_next_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), (cursor.column)) : 0; + } + } + + _remove_text(cursor.line, cursor.column, next_line, next_column); + update(); +} + +void TextEdit::_delete_selection() { + if (is_selection_active()) { + selection.active = false; + update(); + _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); + cursor_set_line(selection.from_line, true, false); + cursor_set_column(selection.from_column); + update(); + } +} + +void TextEdit::_move_cursor_document_start(bool p_select) { + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); + } + + cursor_set_line(0); + cursor_set_column(0); + + if (p_select) { + _post_shift_selection(); + } +} + +void TextEdit::_move_cursor_document_end(bool p_select) { + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); + } + + cursor_set_line(get_last_unhidden_line(), true, false, 9999); + cursor_set_column(text[cursor.line].length()); + + if (p_select) { + _post_shift_selection(); + } +} + +void TextEdit::_handle_unicode_character(uint32_t unicode, bool p_had_selection, bool p_update_auto_complete) { + if (p_update_auto_complete) { + _reset_caret_blink_timer(); + } + + if (p_had_selection) { + _delete_selection(); + } + + // Remove the old character if in insert mode and no selection. + if (insert_mode && !p_had_selection) { + begin_complex_operation(); + + // Make sure we don't try and remove empty space. + if (cursor.column < get_line(cursor.line).length()) { + _remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1); + } + } + + const char32_t chr[2] = { (char32_t)unicode, 0 }; + + // Clear completion hint when function closed + if (completion_hint != "" && unicode == ')') { + completion_hint = ""; + } + + if (auto_brace_completion_enabled && _is_pair_symbol(chr[0])) { + _consume_pair_symbol(chr[0]); + } else { + _insert_text_at_cursor(chr); + } + + if ((insert_mode && !p_had_selection) || (selection.active != p_had_selection)) { + end_complex_operation(); + } + + if (p_update_auto_complete) { + _update_completion_candidates(); + } +} + void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) const { float rows = p_mouse.y; rows -= cache.style_normal->get_margin(SIDE_TOP); @@ -2138,6 +2751,18 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co } else { int colx = p_mouse.x - (cache.style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding); colx += cursor.x_ofs; + col = get_char_pos_for_line(colx, row, wrap_index); + if (is_wrap_enabled() && wrap_index < times_line_wraps(row)) { + // Move back one if we are at the end of the row. + Vector<String> rows2 = get_wrap_rows_text(row); + int row_end_col = 0; + for (int i = 0; i < wrap_index + 1; i++) { + row_end_col += rows2[i].length(); + } + if (col >= row_end_col) { + col -= 1; + } + } RID text_rid = text.get_line_data(row)->get_line_rid(wrap_index); if (is_layout_rtl()) { @@ -2390,7 +3015,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { update(); } - } else { selection.active = false; selection.selecting_mode = SelectionMode::SELECTION_MODE_POINTER; @@ -2439,7 +3063,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { menu->set_position(get_screen_transform().xform(mpos)); menu->set_size(Vector2(1, 1)); - // menu->set_scale(get_global_transform().get_scale()); + _generate_context_menu(); menu->popup(); grab_focus(); } @@ -2533,13 +3157,11 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { Ref<InputEventKey> k = p_gui_input; if (k.is_valid()) { - k = k->duplicate(); // It will be modified later on. - + // Ctrl + Hover symbols #ifdef OSX_ENABLED if (k->get_keycode() == KEY_META) { #else if (k->get_keycode() == KEY_CONTROL) { - #endif if (select_identifiers_enabled) { if (k->is_pressed() && !dragging_minimap && !dragging_selection) { @@ -2549,1191 +3171,347 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { set_highlighted_word(String()); } } + return; } if (!k->is_pressed()) { return; } - if (completion_active) { - if (readonly) { - return; - } - - bool valid = true; - if (k->get_command() || k->get_metakey()) { - valid = false; - } - - if (valid) { - if (!k->get_alt()) { - if (k->get_keycode() == KEY_UP) { - if (completion_index > 0) { - completion_index--; - } else { - completion_index = completion_options.size() - 1; - } - completion_current = completion_options[completion_index]; - update(); - - accept_event(); - return; - } - - if (k->get_keycode() == KEY_DOWN) { - if (completion_index < completion_options.size() - 1) { - completion_index++; - } else { - completion_index = 0; - } - completion_current = completion_options[completion_index]; - update(); - - accept_event(); - return; - } - - if (k->get_keycode() == KEY_PAGEUP) { - completion_index -= get_theme_constant("completion_lines"); - if (completion_index < 0) { - completion_index = 0; - } - completion_current = completion_options[completion_index]; - update(); - accept_event(); - return; - } - - if (k->get_keycode() == KEY_PAGEDOWN) { - completion_index += get_theme_constant("completion_lines"); - if (completion_index >= completion_options.size()) { - completion_index = completion_options.size() - 1; - } - completion_current = completion_options[completion_index]; - update(); - accept_event(); - return; - } - - if (k->get_keycode() == KEY_HOME && completion_index > 0) { - completion_index = 0; - completion_current = completion_options[completion_index]; - update(); - accept_event(); - return; - } - - if (k->get_keycode() == KEY_END && completion_index < completion_options.size() - 1) { - completion_index = completion_options.size() - 1; - completion_current = completion_options[completion_index]; - update(); - accept_event(); - return; - } - - if (k->get_keycode() == KEY_KP_ENTER || k->get_keycode() == KEY_ENTER || k->get_keycode() == KEY_TAB) { - _confirm_completion(); - accept_event(); - return; - } - - if (k->get_keycode() == KEY_BACKSPACE) { - _reset_caret_blink_timer(); - - backspace_at_cursor(); - _update_completion_candidates(); - accept_event(); - return; - } - - if (k->get_keycode() == KEY_SHIFT) { - accept_event(); - return; - } - } - - if (k->get_unicode() > 32) { - _reset_caret_blink_timer(); - - const char32_t chr[2] = { (char32_t)k->get_unicode(), 0 }; - if (auto_brace_completion_enabled && _is_pair_symbol(chr[0])) { - _consume_pair_symbol(chr[0]); - } else { - // Remove the old character if in insert mode. - if (insert_mode) { - begin_complex_operation(); - - // Make sure we don't try and remove empty space. - if (cursor.column < get_line(cursor.line).length()) { - _remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1); - } - } - - _insert_text_at_cursor(chr); - - if (insert_mode) { - end_complex_operation(); - } - } - _update_completion_candidates(); - accept_event(); - - return; - } - } - - _cancel_completion(); + // If a modifier has been pressed, and nothing else, return. + if (k->get_keycode() == KEY_CONTROL || k->get_keycode() == KEY_ALT || k->get_keycode() == KEY_SHIFT || k->get_keycode() == KEY_META) { + return; } - /* TEST CONTROL FIRST! */ - - // Some remaps for duplicate functions. - if (k->get_command() && !k->get_shift() && !k->get_alt() && !k->get_metakey() && k->get_keycode() == KEY_INSERT) { - k->set_keycode(KEY_C); - } - if (!k->get_command() && k->get_shift() && !k->get_alt() && !k->get_metakey() && k->get_keycode() == KEY_INSERT) { - k->set_keycode(KEY_V); - k->set_command(true); - k->set_shift(false); - } -#ifdef APPLE_STYLE_KEYS - if (k->get_control() && !k->get_shift() && !k->get_alt() && !k->get_command()) { - uint32_t remap_key = KEY_UNKNOWN; - switch (k->get_keycode()) { - case KEY_F: { - remap_key = KEY_RIGHT; - } break; - case KEY_B: { - remap_key = KEY_LEFT; - } break; - case KEY_P: { - remap_key = KEY_UP; - } break; - case KEY_N: { - remap_key = KEY_DOWN; - } break; - case KEY_D: { - remap_key = KEY_DELETE; - } break; - case KEY_H: { - remap_key = KEY_BACKSPACE; - } break; - } - - if (remap_key != KEY_UNKNOWN) { - k->set_keycode(remap_key); - k->set_control(false); - } - } -#endif - _reset_caret_blink_timer(); + // Allow unicode handling if: + // * No Modifiers are pressed (except shift) + bool allow_unicode_handling = !(k->get_command() || k->get_control() || k->get_alt() || k->get_metakey()); + // Save here for insert mode, just in case it is cleared in the following section. bool had_selection = selection.active; - // Stuff to do when selection is active. - if (!readonly && selection.active) { - bool clear = false; - bool unselect = false; - bool dobreak = false; - - switch (k->get_keycode()) { - case KEY_TAB: { - if (k->get_shift()) { - indent_left(); - } else { - indent_right(); - } - dobreak = true; - accept_event(); - } break; - case KEY_X: - case KEY_C: - // Special keys often used with control, wait. - clear = (!k->get_command() || k->get_shift() || k->get_alt()); - break; - case KEY_DELETE: - if (!k->get_shift()) { - accept_event(); - clear = true; - dobreak = true; - } else if (k->get_command() || k->get_alt()) { - dobreak = true; - } - break; - case KEY_BACKSPACE: - accept_event(); - clear = true; - dobreak = true; - break; - case KEY_LEFT: - case KEY_RIGHT: - case KEY_UP: - case KEY_DOWN: - case KEY_PAGEUP: - case KEY_PAGEDOWN: - case KEY_HOME: - case KEY_END: - // Ignore arrows if any modifiers are held (shift = selecting, others may be used for editor hotkeys). - if (k->get_command() || k->get_shift() || k->get_alt()) { - break; - } - unselect = true; - break; - - default: - if (k->get_unicode() >= 32 && !k->get_command() && !k->get_alt() && !k->get_metakey()) { - clear = true; - } - if (auto_brace_completion_enabled && _is_pair_left_symbol(k->get_unicode())) { - clear = false; - } - } - - if (unselect) { - selection.active = false; - selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE; - update(); - } - if (clear) { - if (!dobreak) { - begin_complex_operation(); - } - selection.active = false; - update(); - _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); - cursor_set_line(selection.from_line, true, false); - cursor_set_column(selection.from_column); - update(); - } - if (dobreak) { - return; - } - } - selection.selecting_text = false; - bool keycode_handled = true; - - // Special keycode test. - - switch (k->get_keycode()) { - case KEY_KP_ENTER: - case KEY_ENTER: { - if (readonly) { - break; - } - - String ins = "\n"; - - // Keep indentation. - int space_count = 0; - for (int i = 0; i < cursor.column; i++) { - if (text[cursor.line][i] == '\t') { - if (indent_using_spaces) { - ins += space_indent; - } else { - ins += "\t"; - } - space_count = 0; - } else if (text[cursor.line][i] == ' ') { - space_count++; - - if (space_count == indent_size) { - if (indent_using_spaces) { - ins += space_indent; - } else { - ins += "\t"; - } - space_count = 0; - } - } else { - break; - } - } - - if (is_folded(cursor.line)) { - unfold_line(cursor.line); - } - - bool brace_indent = false; - - // No need to indent if we are going upwards. - if (auto_indent && !(k->get_command() && k->get_shift())) { - // Indent once again if previous line will end with ':','{','[','(' and the line is not a comment - // (i.e. colon/brace precedes current cursor position). - if (cursor.column > 0) { - bool indent_char_found = false; - bool should_indent = false; - char indent_char = ':'; - char c = text[cursor.line][cursor.column]; - - for (int i = 0; i < cursor.column; i++) { - c = text[cursor.line][i]; - switch (c) { - case ':': - case '{': - case '[': - case '(': - indent_char_found = true; - should_indent = true; - indent_char = c; - continue; - } - - if (indent_char_found && is_line_comment(cursor.line)) { - should_indent = true; - break; - } else if (indent_char_found && !_is_whitespace(c)) { - should_indent = false; - indent_char_found = false; - } - } - - if (!is_line_comment(cursor.line) && should_indent) { - if (indent_using_spaces) { - ins += space_indent; - } else { - ins += "\t"; - } + // Check and handle all built in shortcuts. - // No need to move the brace below if we are not taking the text with us. - char32_t closing_char = _get_right_pair_symbol(indent_char); - if ((closing_char != 0) && (closing_char == text[cursor.line][cursor.column]) && !k->get_command()) { - brace_indent = true; - ins += "\n" + ins.substr(1, ins.length() - 2); - } - } - } - } - begin_complex_operation(); - bool first_line = false; - if (k->get_command()) { - if (k->get_shift()) { - if (cursor.line > 0) { - cursor_set_line(cursor.line - 1); - cursor_set_column(text[cursor.line].length()); - } else { - cursor_set_column(0); - first_line = true; - } - } else { - cursor_set_column(text[cursor.line].length()); - } - } + // AUTO-COMPLETE - insert_text_at_cursor(ins); - - if (first_line) { - cursor_set_line(0); - } else if (brace_indent) { - cursor_set_line(cursor.line - 1); - cursor_set_column(text[cursor.line].length()); - } - end_complex_operation(); - } break; - case KEY_ESCAPE: { - if (completion_hint != "") { - completion_hint = ""; - update(); - } else { - keycode_handled = false; - } - } break; - case KEY_TAB: { - if (k->get_command()) { - break; // Avoid tab when command. - } - - if (readonly) { - break; - } - - if (is_selection_active()) { - if (k->get_shift()) { - indent_left(); - } else { - indent_right(); - } - } else { - if (k->get_shift()) { - // Simple unindent. - int cc = cursor.column; - const String &line = text[cursor.line]; - - int left = _find_first_non_whitespace_column_of_line(line); - cc = MIN(cc, left); - - while (cc < indent_size && cc < left && line[cc] == ' ') { - cc++; - } - - if (cc > 0 && cc <= text[cursor.line].length()) { - if (text[cursor.line][cc - 1] == '\t') { - // Tabs unindentation. - _remove_text(cursor.line, cc - 1, cursor.line, cc); - if (cursor.column >= left) { - cursor_set_column(MAX(0, cursor.column - 1)); - } - update(); - } else { - // Spaces unindentation. - int spaces_to_remove = _calculate_spaces_till_next_left_indent(cc); - if (spaces_to_remove > 0) { - _remove_text(cursor.line, cc - spaces_to_remove, cursor.line, cc); - if (cursor.column > left - spaces_to_remove) { // Inside text? - cursor_set_column(MAX(0, cursor.column - spaces_to_remove)); - } - update(); - } - } - } else if (cc == 0 && line.length() > 0 && line[0] == '\t') { - _remove_text(cursor.line, 0, cursor.line, 1); - update(); - } - } else { - // Simple indent. - if (indent_using_spaces) { - // Insert only as much spaces as needed till next indentation level. - int spaces_to_add = _calculate_spaces_till_next_right_indent(cursor.column); - String indent_to_insert = String(); - for (int i = 0; i < spaces_to_add; i++) { - indent_to_insert = ' ' + indent_to_insert; - } - _insert_text_at_cursor(indent_to_insert); - } else { - _insert_text_at_cursor("\t"); - } - } - } - - } break; - case KEY_BACKSPACE: { - if (readonly) { - break; - } - -#ifdef APPLE_STYLE_KEYS - if (k->get_alt() && cursor.column > 1) { -#else - if (k->get_alt()) { - keycode_handled = false; - break; - } else if (k->get_command() && cursor.column > 1) { -#endif - int line = cursor.line; - int column = cursor.column; - - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); - for (int i = words.size() - 1; i >= 0; i--) { - if (words[i].x < column) { - column = words[i].x; - break; - } - } - - _remove_text(line, column, cursor.line, cursor.column); - - cursor_set_line(line); - cursor_set_column(column); - -#ifdef APPLE_STYLE_KEYS - } else if (k->get_command()) { - int cursor_current_column = cursor.column; - cursor.column = 0; - _remove_text(cursor.line, 0, cursor.line, cursor_current_column); -#endif - } else { - if (cursor.line > 0 && is_line_hidden(cursor.line - 1)) { - unfold_line(cursor.line - 1); - } - backspace_at_cursor(); - } - - } break; - case KEY_KP_4: { - if (k->get_unicode() != 0) { - keycode_handled = false; - break; - } - [[fallthrough]]; - } - case KEY_LEFT: { - if (k->get_shift()) { - _pre_shift_selection(); -#ifdef APPLE_STYLE_KEYS - } else { -#else - } else if (!k->get_alt()) { -#endif - deselect(); - } - -#ifdef APPLE_STYLE_KEYS - if (k->get_command()) { - // Start at first column (it's slightly faster that way) and look for the first non-whitespace character. - int new_cursor_pos = 0; - for (int i = 0; i < text[cursor.line].length(); ++i) { - if (!_is_whitespace(text[cursor.line][i])) { - new_cursor_pos = i; - break; - } - } - if (new_cursor_pos == cursor.column) { - // We're already at the first text character, so move to the very beginning of the line. - cursor_set_column(0); - } else { - // We're somewhere to the right of the first text character; move to the first one. - cursor_set_column(new_cursor_pos); - } - } else if (k->get_alt()) { -#else - if (k->get_alt()) { - keycode_handled = false; - break; - } else if (k->get_command()) { -#endif - int cc = cursor.column; - - if (cc == 0 && cursor.line > 0) { - cursor_set_line(cursor.line - 1); - cursor_set_column(text[cursor.line].length()); - } else { - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(cursor.line)->get_rid()); - for (int i = words.size() - 1; i >= 0; i--) { - if (words[i].x < cc) { - cc = words[i].x; - break; - } - } - cursor_set_column(cc); - } + if (k->is_action("ui_text_completion_query", true)) { + query_code_comple(); + accept_event(); + return; + } - } else if (cursor.column == 0) { - if (cursor.line > 0) { - cursor_set_line(cursor.line - num_lines_from(CLAMP(cursor.line - 1, 0, text.size() - 1), -1)); - cursor_set_column(text[cursor.line].length()); - } + if (completion_active) { + if (k->is_action("ui_up", true)) { + if (completion_index > 0) { + completion_index--; } else { - if (mid_grapheme_caret_enabled) { - cursor_set_column(cursor_get_column() - 1); - } else { - cursor_set_column(TS->shaped_text_prev_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), cursor_get_column())); - } - } - - if (k->get_shift()) { - _post_shift_selection(); - } - - } break; - case KEY_KP_6: { - if (k->get_unicode() != 0) { - keycode_handled = false; - break; + completion_index = completion_options.size() - 1; } - [[fallthrough]]; + completion_current = completion_options[completion_index]; + update(); + accept_event(); + return; } - case KEY_RIGHT: { - if (k->get_shift()) { - _pre_shift_selection(); -#ifdef APPLE_STYLE_KEYS - } else { -#else - } else if (!k->get_alt()) { -#endif - deselect(); - } - -#ifdef APPLE_STYLE_KEYS - if (k->get_command()) { - cursor_set_column(text[cursor.line].length()); - } else if (k->get_alt()) { -#else - if (k->get_alt()) { - keycode_handled = false; - break; - } else if (k->get_command()) { -#endif - int cc = cursor.column; - - if (cc == text[cursor.line].length() && cursor.line < text.size() - 1) { - cursor_set_line(cursor.line + 1); - cursor_set_column(0); - } else { - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(cursor.line)->get_rid()); - for (int i = 0; i < words.size(); i++) { - if (words[i].y > cc) { - cc = words[i].y; - break; - } - } - cursor_set_column(cc); - } - - } else if (cursor.column == text[cursor.line].length()) { - if (cursor.line < text.size() - 1) { - cursor_set_line(cursor_get_line() + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1), true, false); - cursor_set_column(0); - } + if (k->is_action("ui_down", true)) { + if (completion_index < completion_options.size() - 1) { + completion_index++; } else { - if (mid_grapheme_caret_enabled) { - cursor_set_column(cursor_get_column() + 1); - } else { - cursor_set_column(TS->shaped_text_next_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), cursor_get_column())); - } - } - - if (k->get_shift()) { - _post_shift_selection(); - } - - } break; - case KEY_KP_8: { - if (k->get_unicode() != 0) { - keycode_handled = false; - break; + completion_index = 0; } - [[fallthrough]]; + completion_current = completion_options[completion_index]; + update(); + accept_event(); + return; } - case KEY_UP: { - if (k->get_alt()) { - keycode_handled = false; - break; + if (k->is_action("ui_page_up", true)) { + completion_index -= get_theme_constant("completion_lines"); + if (completion_index < 0) { + completion_index = 0; } -#ifndef APPLE_STYLE_KEYS - if (k->get_command()) { -#else - if (k->get_command() && k->get_alt()) { -#endif - _scroll_lines_up(); - break; - } - - if (k->get_shift()) { - _pre_shift_selection(); - } - -#ifdef APPLE_STYLE_KEYS - if (k->get_command()) { - cursor_set_line(0); - } else -#endif - { - int cur_wrap_index = get_cursor_wrap_index(); - if (cur_wrap_index > 0) { - cursor_set_line(cursor.line, true, false, cur_wrap_index - 1); - } else if (cursor.line == 0) { - cursor_set_column(0); - } else { - int new_line = cursor.line - num_lines_from(cursor.line - 1, -1); - if (line_wraps(new_line)) { - cursor_set_line(new_line, true, false, times_line_wraps(new_line)); - } else { - cursor_set_line(new_line, true, false); - } - } - } - - if (k->get_shift()) { - _post_shift_selection(); - } - _cancel_code_hint(); - - } break; - case KEY_KP_2: { - if (k->get_unicode() != 0) { - keycode_handled = false; - break; - } - [[fallthrough]]; + completion_current = completion_options[completion_index]; + update(); + accept_event(); + return; } - case KEY_DOWN: { - if (k->get_alt()) { - keycode_handled = false; - break; + if (k->is_action("ui_page_down", true)) { + completion_index += get_theme_constant("completion_lines"); + if (completion_index >= completion_options.size()) { + completion_index = completion_options.size() - 1; } -#ifndef APPLE_STYLE_KEYS - if (k->get_command()) { -#else - if (k->get_command() && k->get_alt()) { -#endif - _scroll_lines_down(); - break; - } - - if (k->get_shift()) { - _pre_shift_selection(); - } - -#ifdef APPLE_STYLE_KEYS - if (k->get_command()) { - cursor_set_line(get_last_unhidden_line(), true, false, 9999); - } else -#endif - { - int cur_wrap_index = get_cursor_wrap_index(); - if (cur_wrap_index < times_line_wraps(cursor.line)) { - cursor_set_line(cursor.line, true, false, cur_wrap_index + 1); - } else if (cursor.line == get_last_unhidden_line()) { - cursor_set_column(text[cursor.line].length()); - } else { - int new_line = cursor.line + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1); - cursor_set_line(new_line, true, false, 0); - } - } - - if (k->get_shift()) { - _post_shift_selection(); - } - _cancel_code_hint(); - - } break; - case KEY_DELETE: { - if (readonly) { - break; - } - - if (k->get_shift() && !k->get_command() && !k->get_alt() && is_shortcut_keys_enabled()) { - cut(); - break; - } - - int curline_len = text[cursor.line].length(); - - if (cursor.line == text.size() - 1 && cursor.column == curline_len) { - break; // Nothing to do. - } - - int next_line = cursor.column < curline_len ? cursor.line : cursor.line + 1; - int next_column; - -#ifdef APPLE_STYLE_KEYS - if (k->get_alt() && cursor.column < curline_len - 1) { -#else - if (k->get_alt()) { - keycode_handled = false; - break; - } else if (k->get_command() && cursor.column < curline_len - 1) { -#endif - - int line = cursor.line; - int column = cursor.column; - - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); - for (int i = 0; i < words.size(); i++) { - if (words[i].y > column) { - column = words[i].y; - break; - } - } - - next_line = line; - next_column = column; -#ifdef APPLE_STYLE_KEYS - } else if (k->get_command()) { - next_column = curline_len; - next_line = cursor.line; -#endif - } else { - if (mid_grapheme_caret_enabled) { - next_column = cursor.column < curline_len ? (cursor.column + 1) : 0; - } else { - next_column = cursor.column < curline_len ? TS->shaped_text_next_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), (cursor.column)) : 0; - } - } - - _remove_text(cursor.line, cursor.column, next_line, next_column); + completion_current = completion_options[completion_index]; update(); - - } break; - case KEY_KP_7: { - if (k->get_unicode() != 0) { - keycode_handled = false; - break; - } - [[fallthrough]]; + accept_event(); + return; } - case KEY_HOME: { -#ifdef APPLE_STYLE_KEYS - if (k->get_shift()) - _pre_shift_selection(); - - cursor_set_line(0); - - if (k->get_shift()) - _post_shift_selection(); - else if (k->get_command() || k->get_control()) - deselect(); -#else - if (k->get_shift()) { - _pre_shift_selection(); - } - - if (k->get_command()) { - cursor_set_line(0); - cursor_set_column(0); - } else { - // Move cursor column to start of wrapped row and then to start of text. - Vector<String> rows = get_wrap_rows_text(cursor.line); - int wi = get_cursor_wrap_index(); - int row_start_col = 0; - for (int i = 0; i < wi; i++) { - row_start_col += rows[i].length(); - } - if (cursor.column == row_start_col || wi == 0) { - // Compute whitespace symbols seq length. - int current_line_whitespace_len = 0; - while (current_line_whitespace_len < text[cursor.line].length()) { - char32_t c = text[cursor.line][current_line_whitespace_len]; - if (c != '\t' && c != ' ') { - break; - } - current_line_whitespace_len++; - } - - if (cursor_get_column() == current_line_whitespace_len) { - cursor_set_column(0); - } else { - cursor_set_column(current_line_whitespace_len); - } - } else { - cursor_set_column(row_start_col); - } - } - - if (k->get_shift()) { - _post_shift_selection(); - } else if (k->get_command() || k->get_control()) { - deselect(); - } - _cancel_completion(); - completion_hint = ""; -#endif - } break; - case KEY_KP_1: { - if (k->get_unicode() != 0) { - keycode_handled = false; - break; + if (k->is_action("ui_home", true)) { + if (completion_index > 0) { + completion_index = 0; + completion_current = completion_options[completion_index]; + update(); } - [[fallthrough]]; + accept_event(); + return; } - case KEY_END: { -#ifdef APPLE_STYLE_KEYS - if (k->get_shift()) - _pre_shift_selection(); - - cursor_set_line(get_last_unhidden_line(), true, false, 9999); - - if (k->get_shift()) - _post_shift_selection(); - else if (k->get_command() || k->get_control()) - deselect(); -#else - if (k->get_shift()) { - _pre_shift_selection(); - } - - if (k->get_command()) { - cursor_set_line(get_last_unhidden_line(), true, false, 9999); - } - - // Move cursor column to end of wrapped row and then to end of text. - Vector<String> rows = get_wrap_rows_text(cursor.line); - int wi = get_cursor_wrap_index(); - int row_end_col = -1; - for (int i = 0; i < wi + 1; i++) { - row_end_col += rows[i].length(); - } - if (wi == rows.size() - 1 || cursor.column == row_end_col) { - cursor_set_column(text[cursor.line].length()); - } else { - cursor_set_column(row_end_col); - } - - if (k->get_shift()) { - _post_shift_selection(); - } else if (k->get_command() || k->get_control()) { - deselect(); - } - - _cancel_completion(); - completion_hint = ""; -#endif - } break; - case KEY_KP_9: { - if (k->get_unicode() != 0) { - keycode_handled = false; - break; + if (k->is_action("ui_end", true)) { + if (completion_index < completion_options.size() - 1) { + completion_index = completion_options.size() - 1; + completion_current = completion_options[completion_index]; + update(); } - [[fallthrough]]; + accept_event(); + return; } - case KEY_PAGEUP: { - if (k->get_shift()) { - _pre_shift_selection(); - } - - int wi; - int n_line = cursor.line - num_lines_from_rows(cursor.line, get_cursor_wrap_index(), -get_visible_rows(), wi) + 1; - cursor_set_line(n_line, true, false, wi); - - if (k->get_shift()) { - _post_shift_selection(); - } - + if (k->is_action("ui_accept", true) || k->is_action("ui_text_completion_accept", true)) { + _confirm_completion(); + accept_event(); + return; + } + if (k->is_action("ui_cancel", true)) { _cancel_completion(); - completion_hint = ""; + accept_event(); + return; + } - } break; - case KEY_KP_3: { - if (k->get_unicode() != 0) { - keycode_handled = false; - break; + // Handle Unicode here (if no modifiers active) and update autocomplete. + if (k->get_unicode() >= 32) { + if (allow_unicode_handling && !readonly) { + _handle_unicode_character(k->get_unicode(), had_selection, true); + accept_event(); + return; } - [[fallthrough]]; } - case KEY_PAGEDOWN: { - if (k->get_shift()) { - _pre_shift_selection(); - } - - int wi; - int n_line = cursor.line + num_lines_from_rows(cursor.line, get_cursor_wrap_index(), get_visible_rows(), wi) - 1; - cursor_set_line(n_line, true, false, wi); + } - if (k->get_shift()) { - _post_shift_selection(); - } + // NEWLINES. + if (k->is_action("ui_text_newline_above", true)) { + _new_line(false, true); + accept_event(); + return; + } + if (k->is_action("ui_text_newline_blank", true)) { + _new_line(false); + accept_event(); + return; + } + if (k->is_action("ui_text_newline", true)) { + _new_line(); + accept_event(); + return; + } - _cancel_completion(); - completion_hint = ""; + // INDENTATION. + if (k->is_action("ui_text_dedent", true)) { + _indent_left(); + accept_event(); + return; + } + if (k->is_action("ui_text_indent", true)) { + _indent_right(); + accept_event(); + return; + } - } break; - case KEY_A: { -#ifndef APPLE_STYLE_KEYS - if (!k->get_control() || k->get_shift() || k->get_alt()) { - keycode_handled = false; - break; - } - if (is_shortcut_keys_enabled()) { - select_all(); - } -#else - if ((!k->get_command() && !k->get_control())) { - keycode_handled = false; - break; - } - if (!k->get_shift() && k->get_command() && is_shortcut_keys_enabled()) - select_all(); - else if (k->get_control()) { - if (k->get_shift()) - _pre_shift_selection(); - - int current_line_whitespace_len = 0; - while (current_line_whitespace_len < text[cursor.line].length()) { - char32_t c = text[cursor.line][current_line_whitespace_len]; - if (c != '\t' && c != ' ') - break; - current_line_whitespace_len++; - } + // BACKSPACE AND DELETE. + if (k->is_action("ui_text_backspace_all_to_left", true)) { + _backspace(false, true); + accept_event(); + return; + } + if (k->is_action("ui_text_backspace_word", true)) { + _backspace(true); + accept_event(); + return; + } + if (k->is_action("ui_text_backspace", true)) { + _backspace(); + if (completion_active) { + _update_completion_candidates(); + } + accept_event(); + return; + } + if (k->is_action("ui_text_delete_all_to_right", true)) { + _delete(false, true); + accept_event(); + return; + } + if (k->is_action("ui_text_delete_word", true)) { + _delete(true); + accept_event(); + return; + } + if (k->is_action("ui_text_delete", true)) { + _delete(); + accept_event(); + return; + } - if (cursor_get_column() == current_line_whitespace_len) - cursor_set_column(0); - else - cursor_set_column(current_line_whitespace_len); + // SCROLLING. + if (k->is_action("ui_text_scroll_up", true)) { + _scroll_lines_up(); + accept_event(); + return; + } + if (k->is_action("ui_text_scroll_down", true)) { + _scroll_lines_down(); + accept_event(); + return; + } - if (k->get_shift()) - _post_shift_selection(); - else if (k->get_command() || k->get_control()) - deselect(); - } - } break; - case KEY_E: { - if (!k->get_control() || k->get_command() || k->get_alt()) { - keycode_handled = false; - break; - } + // SELECT ALL, CUT, COPY, PASTE. - if (k->get_shift()) - _pre_shift_selection(); + if (k->is_action("ui_text_select_all", true)) { + select_all(); + accept_event(); + return; + } + if (k->is_action("ui_cut", true)) { + cut(); + accept_event(); + return; + } + if (k->is_action("ui_copy", true)) { + copy(); + accept_event(); + return; + } + if (k->is_action("ui_paste", true)) { + paste(); + accept_event(); + return; + } - if (k->get_command()) - cursor_set_line(text.size() - 1, true, false); - cursor_set_column(text[cursor.line].length()); + // UNDO/REDO. + if (k->is_action("ui_undo", true)) { + undo(); + accept_event(); + return; + } + if (k->is_action("ui_redo", true)) { + redo(); + accept_event(); + return; + } - if (k->get_shift()) - _post_shift_selection(); - else if (k->get_command() || k->get_control()) - deselect(); + // MISC. - _cancel_completion(); + if (k->is_action("ui_menu", true)) { + if (context_menu_enabled) { + menu->set_position(get_screen_transform().xform(_get_cursor_pixel_pos())); + menu->set_size(Vector2(1, 1)); + _generate_context_menu(); + menu->popup(); + menu->grab_focus(); + } + accept_event(); + return; + } + if (k->is_action("ui_text_toggle_insert_mode", true)) { + set_insert_mode(!insert_mode); + accept_event(); + return; + } + if (k->is_action("ui_cancel", true)) { + if (completion_hint != "") { completion_hint = ""; -#endif - } break; - case (KEY_QUOTELEFT): { // Swap current input direction (primary cursor) - if (!k->get_command()) { - keycode_handled = false; - break; - } - - if (input_direction == TEXT_DIRECTION_LTR) { - input_direction = TEXT_DIRECTION_RTL; - } else { - input_direction = TEXT_DIRECTION_LTR; - } - cursor_set_column(cursor.column); update(); - } break; - case KEY_X: { - if (readonly) { - break; - } - if (!k->get_command() || k->get_shift() || k->get_alt()) { - keycode_handled = false; - break; - } - if (is_shortcut_keys_enabled()) { - cut(); - } - - } break; - case KEY_C: { - if (!k->get_command() || k->get_shift() || k->get_alt()) { - keycode_handled = false; - break; - } - - if (is_shortcut_keys_enabled()) { - copy(); - } - - } break; - case KEY_Z: { - if (readonly) { - break; - } - - if (!k->get_command()) { - keycode_handled = false; - break; - } - - if (is_shortcut_keys_enabled()) { - if (k->get_shift()) { - redo(); - } else { - undo(); - } - } - } break; - case KEY_Y: { - if (readonly) { - break; - } - - if (!k->get_command()) { - keycode_handled = false; - break; - } - - if (is_shortcut_keys_enabled()) { - redo(); - } - } break; - case KEY_V: { - if (readonly) { - break; - } - if (!k->get_command() || k->get_shift() || k->get_alt()) { - keycode_handled = false; - break; - } - - if (is_shortcut_keys_enabled()) { - paste(); - } - - } break; - case KEY_SPACE: { -#ifdef OSX_ENABLED - if (completion_enabled && k->get_metakey()) { // cmd-space is spotlight shortcut in OSX -#else - if (completion_enabled && k->get_command()) { -#endif - - query_code_comple(); - keycode_handled = true; - } else { - keycode_handled = false; - } + } + accept_event(); + return; + } + if (k->is_action("ui_swap_input_direction", true)) { + _swap_current_input_direction(); + accept_event(); + return; + } - } break; + // CURSOR MOVEMENT - case KEY_MENU: { - if (context_menu_enabled) { - menu->set_position(get_screen_transform().xform(_get_cursor_pixel_pos())); - menu->set_size(Vector2(1, 1)); - menu->popup(); - menu->grab_focus(); - } - } break; + k = k->duplicate(); + bool shift_pressed = k->get_shift(); + // Remove shift or else actions will not match. Use above variable for selection. + k->set_shift(false); - default: { - keycode_handled = false; - } break; + // CURSOR MOVEMENT - LEFT, RIGHT. + if (k->is_action("ui_text_caret_word_left", true)) { + _move_cursor_left(shift_pressed, true); + accept_event(); + return; } - - if (keycode_handled) { + if (k->is_action("ui_text_caret_left", true)) { + _move_cursor_left(shift_pressed, false); accept_event(); + return; } - - if (k->get_keycode() == KEY_INSERT) { - set_insert_mode(!insert_mode); + if (k->is_action("ui_text_caret_word_right", true)) { + _move_cursor_right(shift_pressed, true); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_right", true)) { + _move_cursor_right(shift_pressed, false); accept_event(); return; } - if (!keycode_handled && !k->get_command()) { // For German keyboards. - - if (k->get_unicode() >= 32) { - if (readonly) { - return; - } - - // Remove the old character if in insert mode and no selection. - if (insert_mode && !had_selection) { - begin_complex_operation(); - - // Make sure we don't try and remove empty space. - if (cursor.column < get_line(cursor.line).length()) { - _remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1); - } - } - - const char32_t chr[2] = { (char32_t)k->get_unicode(), 0 }; + // CURSOR MOVEMENT - UP, DOWN. + if (k->is_action("ui_text_caret_up", true)) { + _move_cursor_up(shift_pressed); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_down", true)) { + _move_cursor_down(shift_pressed); + accept_event(); + return; + } - if (completion_hint != "" && k->get_unicode() == ')') { - completion_hint = ""; - } - if (auto_brace_completion_enabled && _is_pair_symbol(chr[0])) { - _consume_pair_symbol(chr[0]); - } else { - _insert_text_at_cursor(chr); - } + // CURSOR MOVEMENT - DOCUMENT START/END. + if (k->is_action("ui_text_caret_document_start", true)) { // && shift_pressed) { + _move_cursor_document_start(shift_pressed); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_document_end", true)) { // && shift_pressed) { + _move_cursor_document_end(shift_pressed); + accept_event(); + return; + } - if (insert_mode && !had_selection) { - end_complex_operation(); - } + // CURSOR MOVEMENT - LINE START/END. + if (k->is_action("ui_text_caret_line_start", true)) { + _move_cursor_to_line_start(shift_pressed); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_line_end", true)) { + _move_cursor_to_line_end(shift_pressed); + accept_event(); + return; + } - if (selection.active != had_selection) { - end_complex_operation(); - } - accept_event(); - } + // CURSOR MOVEMENT - PAGE UP/DOWN. + if (k->is_action("ui_text_caret_page_up", true)) { + _move_cursor_page_up(shift_pressed); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_page_down", true)) { + _move_cursor_page_down(shift_pressed); + accept_event(); + return; } - return; + if (allow_unicode_handling && !readonly && k->get_unicode() >= 32) { + // Handle Unicode (if no modifiers active). + _handle_unicode_character(k->get_unicode(), had_selection, false); + accept_event(); + return; + } } } @@ -4109,25 +3887,50 @@ int TextEdit::_get_control_height() const { return control_height; } +int TextEdit::_get_menu_action_accelerator(const String &p_action) { + const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(p_action); + if (!events) { + return 0; + } + + // Use first event in the list for the accelerator. + const List<Ref<InputEvent>>::Element *first_event = events->front(); + if (!first_event) { + return 0; + } + + const Ref<InputEventKey> event = first_event->get(); + if (event.is_null()) { + return 0; + } + + // Use physical keycode if non-zero + if (event->get_physical_keycode() != 0) { + return event->get_physical_keycode_with_modifiers(); + } else { + return event->get_keycode_with_modifiers(); + } +} + void TextEdit::_generate_context_menu() { // Reorganize context menu. menu->clear(); if (!readonly) { - menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_X : 0); + menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_cut") : 0); } - menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_C : 0); + menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : 0); if (!readonly) { - menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_V : 0); + menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : 0); } menu->add_separator(); if (is_selecting_enabled()) { - menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_A : 0); + menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : 0); } if (!readonly) { menu->add_item(RTR("Clear"), MENU_CLEAR); menu->add_separator(); - menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_Z : 0); - menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z : 0); + menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_undo") : 0); + menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : 0); } menu->add_separator(); menu->add_submenu_item(RTR("Text writing direction"), "DirMenu"); @@ -5217,6 +5020,10 @@ void TextEdit::set_auto_indent(bool p_auto_indent) { } void TextEdit::cut() { + if (readonly) { + return; + } + if (!selection.active) { String clipboard = text[cursor.line]; DisplayServer::get_singleton()->clipboard_set(clipboard); @@ -5264,6 +5071,10 @@ void TextEdit::copy() { } void TextEdit::paste() { + if (readonly) { + return; + } + String clipboard = DisplayServer::get_singleton()->clipboard_get(); begin_complex_operation(); @@ -5967,6 +5778,10 @@ void TextEdit::_clear_redo() { } void TextEdit::undo() { + if (readonly) { + return; + } + _push_current_op(); if (undo_stack_pos == nullptr) { @@ -6017,6 +5832,9 @@ void TextEdit::undo() { } void TextEdit::redo() { + if (readonly) { + return; + } _push_current_op(); if (undo_stack_pos == nullptr) { diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index f50585d9e9..b0c7314c65 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -408,6 +408,7 @@ private: int _get_control_height() const; Point2 _get_local_mouse_pos() const; + int _get_menu_action_accelerator(const String &p_action); void _reset_caret_blink_timer(); void _toggle_draw_caret(); @@ -441,6 +442,26 @@ private: int _calculate_spaces_till_next_left_indent(int column); int _calculate_spaces_till_next_right_indent(int column); + // Methods used in shortcuts + void _swap_current_input_direction(); + void _new_line(bool p_split_current = true, bool p_above = false); + void _indent_right(); + void _indent_left(); + void _move_cursor_left(bool p_select, bool p_move_by_word = false); + void _move_cursor_right(bool p_select, bool p_move_by_word = false); + void _move_cursor_up(bool p_select); + void _move_cursor_down(bool p_select); + void _move_cursor_to_line_start(bool p_select); + void _move_cursor_to_line_end(bool p_select); + void _move_cursor_page_up(bool p_select); + void _move_cursor_page_down(bool p_select); + void _backspace(bool p_word = false, bool p_all_to_left = false); + void _delete(bool p_word = false, bool p_all_to_right = false); + void _delete_selection(); + void _move_cursor_document_start(bool p_select); + void _move_cursor_document_end(bool p_select); + void _handle_unicode_character(uint32_t unicode, bool p_had_selection, bool p_update_auto_complete); + protected: struct Cache { Ref<Texture2D> tab_icon; @@ -639,8 +660,8 @@ public: int get_row_height() const; void backspace_at_cursor(); - void indent_left(); - void indent_right(); + void indent_selected_lines_left(); + void indent_selected_lines_right(); int get_indent_level(int p_line) const; bool is_line_comment(int p_line) const; diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index 384e7d2652..ce7d6ef13c 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -49,7 +49,7 @@ Error HTTPRequest::_parse_url(const String &p_url) { got_response = false; body_len = -1; body.resize(0); - downloaded = 0; + downloaded.set(0); redirections = 0; String url_lower = url.to_lower(); @@ -159,9 +159,9 @@ Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_cust requesting = true; - if (use_threads) { - thread_done = false; - thread_request_quit = false; + if (use_threads.is_set()) { + thread_done.clear(); + thread_request_quit.clear(); client->set_blocking_mode(true); thread.start(_thread_func, this); } else { @@ -186,7 +186,7 @@ void HTTPRequest::_thread_func(void *p_userdata) { if (err != OK) { hr->call_deferred("_request_done", RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); } else { - while (!hr->thread_request_quit) { + while (!hr->thread_request_quit.is_set()) { bool exit = hr->_update_connection(); if (exit) { break; @@ -195,7 +195,7 @@ void HTTPRequest::_thread_func(void *p_userdata) { } } - hr->thread_done = true; + hr->thread_done.set(); } void HTTPRequest::cancel_request() { @@ -205,10 +205,10 @@ void HTTPRequest::cancel_request() { return; } - if (!use_threads) { + if (!use_threads.is_set()) { set_process_internal(false); } else { - thread_request_quit = true; + thread_request_quit.set(); thread.wait_to_finish(); } @@ -236,7 +236,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) { List<String> rheaders; client->get_response_headers(&rheaders); response_headers.resize(0); - downloaded = 0; + downloaded.set(0); for (List<String>::Element *E = rheaders.front(); E; E = E->next()) { response_headers.push_back(E->get()); } @@ -276,7 +276,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) { got_response = false; body_len = -1; body.resize(0); - downloaded = 0; + downloaded.set(0); redirections = new_redirs; *ret_value = false; return true; @@ -389,7 +389,7 @@ bool HTTPRequest::_update_connection() { client->poll(); PackedByteArray chunk = client->read_response_body_chunk(); - downloaded += chunk.size(); + downloaded.add(chunk.size()); if (file) { const uint8_t *r = chunk.ptr(); @@ -402,13 +402,13 @@ bool HTTPRequest::_update_connection() { body.append_array(chunk); } - if (body_size_limit >= 0 && downloaded > body_size_limit) { + if (body_size_limit >= 0 && downloaded.get() > body_size_limit) { call_deferred("_request_done", RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray()); return true; } if (body_len >= 0) { - if (downloaded == body_len) { + if (downloaded.get() == body_len) { call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, body); return true; } @@ -478,7 +478,7 @@ void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArra void HTTPRequest::_notification(int p_what) { if (p_what == NOTIFICATION_INTERNAL_PROCESS) { - if (use_threads) { + if (use_threads.is_set()) { return; } bool done = _update_connection(); @@ -497,11 +497,11 @@ void HTTPRequest::_notification(int p_what) { void HTTPRequest::set_use_threads(bool p_use) { ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED); - use_threads = p_use; + use_threads.set_to(p_use); } bool HTTPRequest::is_using_threads() const { - return use_threads; + return use_threads.is_set(); } void HTTPRequest::set_accept_gzip(bool p_gzip) { @@ -555,7 +555,7 @@ int HTTPRequest::get_max_redirects() const { } int HTTPRequest::get_downloaded_bytes() const { - return downloaded; + return downloaded.get(); } int HTTPRequest::get_body_size() const { diff --git a/scene/main/http_request.h b/scene/main/http_request.h index 5525ea7912..92b0ff28e9 100644 --- a/scene/main/http_request.h +++ b/scene/main/http_request.h @@ -34,6 +34,7 @@ #include "core/io/http_client.h" #include "core/os/file_access.h" #include "core/os/thread.h" +#include "core/templates/safe_refcount.h" #include "node.h" #include "scene/main/timer.h" @@ -74,7 +75,7 @@ private: bool request_sent = false; Ref<HTTPClient> client; PackedByteArray body; - volatile bool use_threads = false; + SafeFlag use_threads; bool accept_gzip = true; bool got_response = false; @@ -86,7 +87,7 @@ private: FileAccess *file = nullptr; int body_len = -1; - volatile int downloaded = 0; + SafeNumeric<int> downloaded; int body_size_limit = -1; int redirections = 0; @@ -107,8 +108,8 @@ private: bool has_header(const PackedStringArray &p_headers, const String &p_header_name); String get_header_value(const PackedStringArray &p_headers, const String &header_name); - volatile bool thread_done = false; - volatile bool thread_request_quit = false; + SafeFlag thread_done; + SafeFlag thread_request_quit; Thread thread; diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index a296156bf5..d8e63e47be 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -1400,11 +1400,11 @@ bool VisualShader::has_func_name(RenderingServer::ShaderMode p_mode, const Strin } void VisualShader::_update_shader() const { - if (!dirty) { + if (!dirty.is_set()) { return; } - dirty = false; + dirty.clear(); StringBuilder global_code; StringBuilder global_code_per_node; @@ -1590,11 +1590,11 @@ void VisualShader::_update_shader() const { } void VisualShader::_queue_update() { - if (dirty) { + if (dirty.is_set()) { return; } - dirty = true; + dirty.set(); call_deferred("_update_shader"); } @@ -1614,7 +1614,7 @@ void VisualShader::_input_type_changed(Type p_type, int p_id) { } void VisualShader::rebuild() { - dirty = true; + dirty.set(); _update_shader(); } @@ -1668,6 +1668,7 @@ void VisualShader::_bind_methods() { } VisualShader::VisualShader() { + dirty.set(); for (int i = 0; i < TYPE_MAX; i++) { Ref<VisualShaderNodeOutput> output; output.instance(); diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index f3f3caf15e..ef724c7650 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -32,6 +32,7 @@ #define VISUAL_SHADER_H #include "core/string/string_builder.h" +#include "core/templates/safe_refcount.h" #include "scene/gui/control.h" #include "scene/resources/shader.h" @@ -99,7 +100,7 @@ private: static RenderModeEnums render_mode_enums[]; - volatile mutable bool dirty = true; + mutable SafeFlag dirty; void _queue_update(); union ConnectionKey { diff --git a/servers/audio/audio_rb_resampler.cpp b/servers/audio/audio_rb_resampler.cpp index efdcb916ed..3c8a1469cd 100644 --- a/servers/audio/audio_rb_resampler.cpp +++ b/servers/audio/audio_rb_resampler.cpp @@ -131,7 +131,7 @@ bool AudioRBResampler::mix(AudioFrame *p_dest, int p_frames) { src_read = read_space; } - rb_read_pos = (rb_read_pos + src_read) & rb_mask; + rb_read_pos.set((rb_read_pos.get() + src_read) & rb_mask); // Create fadeout effect for the end of stream (note that it can be because of slow writer) if (p_frames - target_todo > 0) { @@ -183,8 +183,8 @@ Error AudioRBResampler::setup(int p_channels, int p_src_mix_rate, int p_target_m src_mix_rate = p_src_mix_rate; target_mix_rate = p_target_mix_rate; offset = 0; - rb_read_pos = 0; - rb_write_pos = 0; + rb_read_pos.set(0); + rb_write_pos.set(0); //avoid maybe strange noises upon load for (unsigned int i = 0; i < (rb_len * channels); i++) { @@ -205,8 +205,8 @@ void AudioRBResampler::clear() { memdelete_arr(read_buf); rb = nullptr; offset = 0; - rb_read_pos = 0; - rb_write_pos = 0; + rb_read_pos.set(0); + rb_write_pos.set(0); read_buf = nullptr; } @@ -214,8 +214,8 @@ AudioRBResampler::AudioRBResampler() { rb = nullptr; offset = 0; read_buf = nullptr; - rb_read_pos = 0; - rb_write_pos = 0; + rb_read_pos.set(0); + rb_write_pos.set(0); rb_bits = 0; rb_len = 0; diff --git a/servers/audio/audio_rb_resampler.h b/servers/audio/audio_rb_resampler.h index 7b74e3a2a1..c0f981704b 100644 --- a/servers/audio/audio_rb_resampler.h +++ b/servers/audio/audio_rb_resampler.h @@ -32,6 +32,7 @@ #define AUDIO_RB_RESAMPLER_H #include "core/os/memory.h" +#include "core/templates/safe_refcount.h" #include "core/typedefs.h" #include "servers/audio_server.h" @@ -44,8 +45,8 @@ struct AudioRBResampler { uint32_t src_mix_rate; uint32_t target_mix_rate; - volatile int rb_read_pos; - volatile int rb_write_pos; + SafeNumeric<int> rb_read_pos; + SafeNumeric<int> rb_write_pos; int32_t offset; //contains the fractional remainder of the resampler enum { @@ -62,8 +63,8 @@ struct AudioRBResampler { public: _FORCE_INLINE_ void flush() { - rb_read_pos = 0; - rb_write_pos = 0; + rb_read_pos.set(0); + rb_write_pos.set(0); offset = 0; } @@ -78,8 +79,8 @@ public: _FORCE_INLINE_ int get_writer_space() const { int space, r, w; - r = rb_read_pos; - w = rb_write_pos; + r = rb_read_pos.get(); + w = rb_write_pos.get(); if (r == w) { space = rb_len - 1; @@ -95,8 +96,8 @@ public: _FORCE_INLINE_ int get_reader_space() const { int space, r, w; - r = rb_read_pos; - w = rb_write_pos; + r = rb_read_pos.get(); + w = rb_write_pos.get(); if (r == w) { space = 0; @@ -110,48 +111,52 @@ public: } _FORCE_INLINE_ bool has_data() const { - return rb && rb_read_pos != rb_write_pos; + return rb && rb_read_pos.get() != rb_write_pos.get(); } _FORCE_INLINE_ float *get_write_buffer() { return read_buf; } _FORCE_INLINE_ void write(uint32_t p_frames) { ERR_FAIL_COND(p_frames >= rb_len); + int wp = rb_write_pos.get(); + switch (channels) { case 1: { for (uint32_t i = 0; i < p_frames; i++) { - rb[rb_write_pos] = read_buf[i]; - rb_write_pos = (rb_write_pos + 1) & rb_mask; + rb[wp] = read_buf[i]; + wp = (wp + 1) & rb_mask; } } break; case 2: { for (uint32_t i = 0; i < p_frames; i++) { - rb[(rb_write_pos << 1) + 0] = read_buf[(i << 1) + 0]; - rb[(rb_write_pos << 1) + 1] = read_buf[(i << 1) + 1]; - rb_write_pos = (rb_write_pos + 1) & rb_mask; + rb[(wp << 1) + 0] = read_buf[(i << 1) + 0]; + rb[(wp << 1) + 1] = read_buf[(i << 1) + 1]; + wp = (wp + 1) & rb_mask; } } break; case 4: { for (uint32_t i = 0; i < p_frames; i++) { - rb[(rb_write_pos << 2) + 0] = read_buf[(i << 2) + 0]; - rb[(rb_write_pos << 2) + 1] = read_buf[(i << 2) + 1]; - rb[(rb_write_pos << 2) + 2] = read_buf[(i << 2) + 2]; - rb[(rb_write_pos << 2) + 3] = read_buf[(i << 2) + 3]; - rb_write_pos = (rb_write_pos + 1) & rb_mask; + rb[(wp << 2) + 0] = read_buf[(i << 2) + 0]; + rb[(wp << 2) + 1] = read_buf[(i << 2) + 1]; + rb[(wp << 2) + 2] = read_buf[(i << 2) + 2]; + rb[(wp << 2) + 3] = read_buf[(i << 2) + 3]; + wp = (wp + 1) & rb_mask; } } break; case 6: { for (uint32_t i = 0; i < p_frames; i++) { - rb[(rb_write_pos * 6) + 0] = read_buf[(i * 6) + 0]; - rb[(rb_write_pos * 6) + 1] = read_buf[(i * 6) + 1]; - rb[(rb_write_pos * 6) + 2] = read_buf[(i * 6) + 2]; - rb[(rb_write_pos * 6) + 3] = read_buf[(i * 6) + 3]; - rb[(rb_write_pos * 6) + 4] = read_buf[(i * 6) + 4]; - rb[(rb_write_pos * 6) + 5] = read_buf[(i * 6) + 5]; - rb_write_pos = (rb_write_pos + 1) & rb_mask; + rb[(wp * 6) + 0] = read_buf[(i * 6) + 0]; + rb[(wp * 6) + 1] = read_buf[(i * 6) + 1]; + rb[(wp * 6) + 2] = read_buf[(i * 6) + 2]; + rb[(wp * 6) + 3] = read_buf[(i * 6) + 3]; + rb[(wp * 6) + 4] = read_buf[(i * 6) + 4]; + rb[(wp * 6) + 5] = read_buf[(i * 6) + 5]; + wp = (wp + 1) & rb_mask; } } break; } + + rb_write_pos.set(wp); } int get_channel_count() const; diff --git a/servers/audio/effects/audio_effect_capture.cpp b/servers/audio/effects/audio_effect_capture.cpp index f37938eec8..37e4122e50 100644 --- a/servers/audio/effects/audio_effect_capture.cpp +++ b/servers/audio/effects/audio_effect_capture.cpp @@ -106,7 +106,7 @@ int AudioEffectCapture::get_frames_available() const { } int64_t AudioEffectCapture::get_discarded_frames() const { - return discarded_frames; + return discarded_frames.get(); } int AudioEffectCapture::get_buffer_length_frames() const { @@ -115,7 +115,7 @@ int AudioEffectCapture::get_buffer_length_frames() const { } int64_t AudioEffectCapture::get_pushed_frames() const { - return pushed_frames; + return pushed_frames.get(); } void AudioEffectCaptureInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) { @@ -129,9 +129,9 @@ void AudioEffectCaptureInstance::process(const AudioFrame *p_src_frames, AudioFr // Add incoming audio frames to the IO ring buffer int32_t ret = buffer.write(p_src_frames, p_frame_count); ERR_FAIL_COND_MSG(ret != p_frame_count, "Failed to add data to effect capture ring buffer despite sufficient space."); - atomic_add(&base->pushed_frames, p_frame_count); + base->pushed_frames.add(p_frame_count); } else { - atomic_add(&base->discarded_frames, p_frame_count); + base->discarded_frames.add(p_frame_count); } } diff --git a/servers/audio/effects/audio_effect_capture.h b/servers/audio/effects/audio_effect_capture.h index b154be85de..81d4ed6b0f 100644 --- a/servers/audio/effects/audio_effect_capture.h +++ b/servers/audio/effects/audio_effect_capture.h @@ -55,8 +55,8 @@ class AudioEffectCapture : public AudioEffect { friend class AudioEffectCaptureInstance; RingBuffer<AudioFrame> buffer; - uint64_t discarded_frames = 0; - uint64_t pushed_frames = 0; + SafeNumeric<uint64_t> discarded_frames; + SafeNumeric<uint64_t> pushed_frames; float buffer_length_seconds = 0.1f; bool buffer_initialized = false; diff --git a/servers/physics_2d/physics_server_2d_wrap_mt.cpp b/servers/physics_2d/physics_server_2d_wrap_mt.cpp index c2557d8f7f..790c87cc44 100644 --- a/servers/physics_2d/physics_server_2d_wrap_mt.cpp +++ b/servers/physics_2d/physics_server_2d_wrap_mt.cpp @@ -33,7 +33,7 @@ #include "core/os/os.h" void PhysicsServer2DWrapMT::thread_exit() { - exit = true; + exit.set(); } void PhysicsServer2DWrapMT::thread_step(real_t p_delta) { @@ -52,9 +52,9 @@ void PhysicsServer2DWrapMT::thread_loop() { physics_2d_server->init(); - exit = false; - step_thread_up = true; - while (!exit) { + exit.clear(); + step_thread_up.set(); + while (!exit.is_set()) { // flush commands one by one, until exit is requested command_queue.wait_and_flush_one(); } @@ -98,7 +98,7 @@ void PhysicsServer2DWrapMT::init() { if (create_thread) { //OS::get_singleton()->release_rendering_thread(); thread.start(_thread_callback, this); - while (!step_thread_up) { + while (!step_thread_up.is_set()) { OS::get_singleton()->delay_usec(1000); } } else { @@ -120,7 +120,6 @@ PhysicsServer2DWrapMT::PhysicsServer2DWrapMT(PhysicsServer2D *p_contained, bool physics_2d_server = p_contained; create_thread = p_create_thread; step_pending = 0; - step_thread_up = false; pool_max_size = GLOBAL_GET("memory/limits/multithreaded_server/rid_pool_prealloc"); diff --git a/servers/physics_2d/physics_server_2d_wrap_mt.h b/servers/physics_2d/physics_server_2d_wrap_mt.h index a6f0b1d4f1..3577f706de 100644 --- a/servers/physics_2d/physics_server_2d_wrap_mt.h +++ b/servers/physics_2d/physics_server_2d_wrap_mt.h @@ -34,6 +34,7 @@ #include "core/config/project_settings.h" #include "core/os/thread.h" #include "core/templates/command_queue_mt.h" +#include "core/templates/safe_refcount.h" #include "servers/physics_server_2d.h" #ifdef DEBUG_SYNC @@ -52,9 +53,9 @@ class PhysicsServer2DWrapMT : public PhysicsServer2D { Thread::ID server_thread; Thread::ID main_thread; - volatile bool exit; + SafeFlag exit; Thread thread; - volatile bool step_thread_up; + SafeFlag step_thread_up; bool create_thread; Semaphore step_sem; diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index 2e8f60d879..61d1efaf22 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -237,7 +237,7 @@ void RenderingServerDefault::init() { thread.start(_thread_callback, this); print_verbose("RenderingServerWrapMT: Starting render thread"); } - while (!draw_thread_up) { + while (!draw_thread_up.is_set()) { OS::get_singleton()->delay_usec(1000); } print_verbose("RenderingServerWrapMT: Finished render thread"); @@ -329,17 +329,17 @@ bool RenderingServerDefault::is_low_end() const { } void RenderingServerDefault::_thread_exit() { - exit = true; + exit.set(); } void RenderingServerDefault::_thread_draw(bool p_swap_buffers, double frame_step) { - if (!atomic_decrement(&draw_pending)) { + if (!draw_pending.decrement()) { _draw(p_swap_buffers, frame_step); } } void RenderingServerDefault::_thread_flush() { - atomic_decrement(&draw_pending); + draw_pending.decrement(); } void RenderingServerDefault::_thread_callback(void *_instance) { @@ -355,9 +355,8 @@ void RenderingServerDefault::_thread_loop() { _init(); - exit = false; - draw_thread_up = true; - while (!exit) { + draw_thread_up.set(); + while (!exit.is_set()) { // flush commands one by one, until exit is requested command_queue.wait_and_flush_one(); } @@ -371,7 +370,7 @@ void RenderingServerDefault::_thread_loop() { void RenderingServerDefault::sync() { if (create_thread) { - atomic_increment(&draw_pending); + draw_pending.increment(); command_queue.push_and_sync(this, &RenderingServerDefault::_thread_flush); } else { command_queue.flush_all(); //flush all pending from other threads @@ -380,7 +379,7 @@ void RenderingServerDefault::sync() { void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) { if (create_thread) { - atomic_increment(&draw_pending); + draw_pending.increment(); command_queue.push(this, &RenderingServerDefault::_thread_draw, p_swap_buffers, frame_step); } else { _draw(p_swap_buffers, frame_step); @@ -390,8 +389,6 @@ void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) { RenderingServerDefault::RenderingServerDefault(bool p_create_thread) : command_queue(p_create_thread) { create_thread = p_create_thread; - draw_pending = 0; - draw_thread_up = false; if (!p_create_thread) { server_thread = Thread::get_caller_id(); diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index effa555ec4..73b463f6e7 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -89,12 +89,12 @@ class RenderingServerDefault : public RenderingServer { void _thread_loop(); Thread::ID server_thread; - volatile bool exit; + SafeFlag exit; Thread thread; - volatile bool draw_thread_up; + SafeFlag draw_thread_up; bool create_thread; - uint64_t draw_pending; + SafeNumeric<uint64_t> draw_pending; void _thread_draw(bool p_swap_buffers, double frame_step); void _thread_flush(); |