| 1 | /**************************************************************************/ |
| 2 | /* editor_debugger_node.h */ |
| 3 | /**************************************************************************/ |
| 4 | /* This file is part of: */ |
| 5 | /* GODOT ENGINE */ |
| 6 | /* https://godotengine.org */ |
| 7 | /**************************************************************************/ |
| 8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
| 9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
| 10 | /* */ |
| 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
| 12 | /* a copy of this software and associated documentation files (the */ |
| 13 | /* "Software"), to deal in the Software without restriction, including */ |
| 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
| 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
| 16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
| 17 | /* the following conditions: */ |
| 18 | /* */ |
| 19 | /* The above copyright notice and this permission notice shall be */ |
| 20 | /* included in all copies or substantial portions of the Software. */ |
| 21 | /* */ |
| 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
| 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
| 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
| 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
| 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
| 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
| 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
| 29 | /**************************************************************************/ |
| 30 | |
| 31 | #ifndef EDITOR_DEBUGGER_NODE_H |
| 32 | #define EDITOR_DEBUGGER_NODE_H |
| 33 | |
| 34 | #include "core/object/script_language.h" |
| 35 | #include "editor/debugger/editor_debugger_server.h" |
| 36 | #include "scene/gui/margin_container.h" |
| 37 | |
| 38 | class Button; |
| 39 | class DebugAdapterParser; |
| 40 | class EditorDebuggerPlugin; |
| 41 | class EditorDebuggerTree; |
| 42 | class EditorDebuggerRemoteObject; |
| 43 | class ; |
| 44 | class ScriptEditorDebugger; |
| 45 | class TabContainer; |
| 46 | class UndoRedo; |
| 47 | |
| 48 | class EditorDebuggerNode : public MarginContainer { |
| 49 | GDCLASS(EditorDebuggerNode, MarginContainer); |
| 50 | |
| 51 | public: |
| 52 | enum CameraOverride { |
| 53 | OVERRIDE_NONE, |
| 54 | OVERRIDE_2D, |
| 55 | OVERRIDE_3D_1, // 3D Viewport 1 |
| 56 | OVERRIDE_3D_2, // 3D Viewport 2 |
| 57 | OVERRIDE_3D_3, // 3D Viewport 3 |
| 58 | OVERRIDE_3D_4 // 3D Viewport 4 |
| 59 | }; |
| 60 | |
| 61 | private: |
| 62 | enum Options { |
| 63 | DEBUG_NEXT, |
| 64 | DEBUG_STEP, |
| 65 | DEBUG_BREAK, |
| 66 | DEBUG_CONTINUE, |
| 67 | DEBUG_WITH_EXTERNAL_EDITOR, |
| 68 | }; |
| 69 | |
| 70 | class Breakpoint { |
| 71 | public: |
| 72 | String source; |
| 73 | int line = 0; |
| 74 | |
| 75 | static uint32_t hash(const Breakpoint &p_val) { |
| 76 | uint32_t h = HashMapHasherDefault::hash(p_val.source); |
| 77 | return hash_murmur3_one_32(p_val.line, h); |
| 78 | } |
| 79 | bool operator==(const Breakpoint &p_b) const { |
| 80 | return (line == p_b.line && source == p_b.source); |
| 81 | } |
| 82 | |
| 83 | bool operator<(const Breakpoint &p_b) const { |
| 84 | if (line == p_b.line) { |
| 85 | return source < p_b.source; |
| 86 | } |
| 87 | return line < p_b.line; |
| 88 | } |
| 89 | |
| 90 | Breakpoint() {} |
| 91 | |
| 92 | Breakpoint(const String &p_source, int p_line) { |
| 93 | line = p_line; |
| 94 | source = p_source; |
| 95 | } |
| 96 | }; |
| 97 | |
| 98 | Ref<EditorDebuggerServer> server; |
| 99 | TabContainer *tabs = nullptr; |
| 100 | Button *debugger_button = nullptr; |
| 101 | MenuButton * = nullptr; |
| 102 | |
| 103 | Ref<Script> stack_script; // Why?!? |
| 104 | |
| 105 | int last_error_count = 0; |
| 106 | int last_warning_count = 0; |
| 107 | |
| 108 | float inspect_edited_object_timeout = 0; |
| 109 | EditorDebuggerTree *remote_scene_tree = nullptr; |
| 110 | float remote_scene_tree_timeout = 0.0; |
| 111 | bool auto_switch_remote_scene_tree = false; |
| 112 | bool debug_with_external_editor = false; |
| 113 | bool keep_open = false; |
| 114 | String current_uri; |
| 115 | |
| 116 | CameraOverride camera_override = OVERRIDE_NONE; |
| 117 | HashMap<Breakpoint, bool, Breakpoint> breakpoints; |
| 118 | |
| 119 | HashSet<Ref<EditorDebuggerPlugin>> debugger_plugins; |
| 120 | |
| 121 | ScriptEditorDebugger *_add_debugger(); |
| 122 | EditorDebuggerRemoteObject *get_inspected_remote_object(); |
| 123 | void _update_errors(); |
| 124 | |
| 125 | friend class DebuggerEditorPlugin; |
| 126 | friend class DebugAdapterParser; |
| 127 | static EditorDebuggerNode *singleton; |
| 128 | EditorDebuggerNode(); |
| 129 | |
| 130 | protected: |
| 131 | void _debugger_stopped(int p_id); |
| 132 | void _debugger_wants_stop(int p_id); |
| 133 | void _debugger_changed(int p_tab); |
| 134 | void _remote_tree_updated(int p_debugger); |
| 135 | void _remote_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); |
| 136 | void _remote_object_updated(ObjectID p_id, int p_debugger); |
| 137 | void _remote_object_property_updated(ObjectID p_id, const String &p_property, int p_debugger); |
| 138 | void _remote_object_requested(ObjectID p_id, int p_debugger); |
| 139 | void _save_node_requested(ObjectID p_id, const String &p_file, int p_debugger); |
| 140 | |
| 141 | void _clear_execution(Ref<RefCounted> p_script) { |
| 142 | emit_signal(SNAME("clear_execution" ), p_script); |
| 143 | } |
| 144 | |
| 145 | void _text_editor_stack_goto(const ScriptEditorDebugger *p_debugger); |
| 146 | void _stack_frame_selected(int p_debugger); |
| 147 | void _error_selected(const String &p_file, int p_line, int p_debugger); |
| 148 | void _breaked(bool p_breaked, bool p_can_debug, String p_message, bool p_has_stackdump, int p_debugger); |
| 149 | void _paused(); |
| 150 | void _break_state_changed(); |
| 151 | void (int p_id); |
| 152 | void _update_debug_options(); |
| 153 | |
| 154 | protected: |
| 155 | void _notification(int p_what); |
| 156 | static void _bind_methods(); |
| 157 | |
| 158 | public: |
| 159 | static EditorDebuggerNode *get_singleton() { return singleton; } |
| 160 | void register_undo_redo(UndoRedo *p_undo_redo); |
| 161 | |
| 162 | ScriptEditorDebugger *get_current_debugger() const; |
| 163 | ScriptEditorDebugger *get_default_debugger() const; |
| 164 | ScriptEditorDebugger *get_debugger(int p_debugger) const; |
| 165 | |
| 166 | void debug_next(); |
| 167 | void debug_step(); |
| 168 | void debug_break(); |
| 169 | void debug_continue(); |
| 170 | |
| 171 | void (MenuButton *p_button); |
| 172 | |
| 173 | void set_tool_button(Button *p_button) { |
| 174 | debugger_button = p_button; |
| 175 | } |
| 176 | |
| 177 | String get_var_value(const String &p_var) const; |
| 178 | Ref<Script> get_dump_stack_script() const { return stack_script; } // Why do we need this? |
| 179 | |
| 180 | bool get_debug_with_external_editor() { return debug_with_external_editor; } |
| 181 | |
| 182 | bool is_skip_breakpoints() const; |
| 183 | void set_breakpoint(const String &p_path, int p_line, bool p_enabled); |
| 184 | void set_breakpoints(const String &p_path, Array p_lines); |
| 185 | void reload_scripts(); |
| 186 | |
| 187 | // Remote inspector/edit. |
| 188 | void request_remote_tree(); |
| 189 | static void _method_changeds(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount); |
| 190 | static void _property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value); |
| 191 | |
| 192 | // LiveDebug |
| 193 | void set_live_debugging(bool p_enabled); |
| 194 | void update_live_edit_root(); |
| 195 | void live_debug_create_node(const NodePath &p_parent, const String &p_type, const String &p_name); |
| 196 | void live_debug_instantiate_node(const NodePath &p_parent, const String &p_path, const String &p_name); |
| 197 | void live_debug_remove_node(const NodePath &p_at); |
| 198 | void live_debug_remove_and_keep_node(const NodePath &p_at, ObjectID p_keep_id); |
| 199 | void live_debug_restore_node(ObjectID p_id, const NodePath &p_at, int p_at_pos); |
| 200 | void live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name); |
| 201 | void live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos); |
| 202 | |
| 203 | void set_camera_override(CameraOverride p_override); |
| 204 | CameraOverride get_camera_override(); |
| 205 | |
| 206 | String get_server_uri() const; |
| 207 | |
| 208 | void set_keep_open(bool p_keep_open); |
| 209 | Error start(const String &p_uri = "tcp://" ); |
| 210 | void stop(bool p_force = false); |
| 211 | |
| 212 | bool plugins_capture(ScriptEditorDebugger *p_debugger, const String &p_message, const Array &p_data); |
| 213 | void add_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin); |
| 214 | void remove_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin); |
| 215 | }; |
| 216 | |
| 217 | #endif // EDITOR_DEBUGGER_NODE_H |
| 218 | |