1/**************************************************************************/
2/* debugger_editor_plugin.cpp */
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#include "debugger_editor_plugin.h"
32
33#include "core/os/keyboard.h"
34#include "editor/debugger/editor_debugger_node.h"
35#include "editor/debugger/editor_debugger_server.h"
36#include "editor/debugger/editor_file_server.h"
37#include "editor/editor_node.h"
38#include "editor/editor_scale.h"
39#include "editor/editor_settings.h"
40#include "editor/plugins/script_editor_plugin.h"
41#include "scene/gui/menu_button.h"
42
43DebuggerEditorPlugin::DebuggerEditorPlugin(PopupMenu *p_debug_menu) {
44 EditorDebuggerServer::initialize();
45
46 ED_SHORTCUT("debugger/step_into", TTR("Step Into"), Key::F11);
47 ED_SHORTCUT("debugger/step_over", TTR("Step Over"), Key::F10);
48 ED_SHORTCUT("debugger/break", TTR("Break"));
49 ED_SHORTCUT("debugger/continue", TTR("Continue"), Key::F12);
50 ED_SHORTCUT("debugger/debug_with_external_editor", TTR("Debug with External Editor"));
51
52 // File Server for deploy with remote filesystem.
53 file_server = memnew(EditorFileServer);
54
55 EditorDebuggerNode *debugger = memnew(EditorDebuggerNode);
56 Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger);
57 debugger->set_tool_button(db);
58
59 // Main editor debug menu.
60 debug_menu = p_debug_menu;
61 debug_menu->set_hide_on_checkable_item_selection(false);
62 debug_menu->add_check_shortcut(ED_SHORTCUT("editor/deploy_with_remote_debug", TTR("Deploy with Remote Debug")), RUN_DEPLOY_REMOTE_DEBUG);
63 debug_menu->set_item_tooltip(-1,
64 TTR("When this option is enabled, using one-click deploy will make the executable attempt to connect to this computer's IP so the running project can be debugged.\nThis option is intended to be used for remote debugging (typically with a mobile device).\nYou don't need to enable it to use the GDScript debugger locally."));
65 debug_menu->add_check_shortcut(ED_SHORTCUT("editor/small_deploy_with_network_fs", TTR("Small Deploy with Network Filesystem")), RUN_FILE_SERVER);
66 debug_menu->set_item_tooltip(-1,
67 TTR("When this option is enabled, using one-click deploy for Android will only export an executable without the project data.\nThe filesystem will be provided from the project by the editor over the network.\nOn Android, deploying will use the USB cable for faster performance. This option speeds up testing for projects with large assets."));
68 debug_menu->add_separator();
69 debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_collision_shapes", TTR("Visible Collision Shapes")), RUN_DEBUG_COLLISIONS);
70 debug_menu->set_item_tooltip(-1,
71 TTR("When this option is enabled, collision shapes and raycast nodes (for 2D and 3D) will be visible in the running project."));
72 debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_paths", TTR("Visible Paths")), RUN_DEBUG_PATHS);
73 debug_menu->set_item_tooltip(-1,
74 TTR("When this option is enabled, curve resources used by path nodes will be visible in the running project."));
75 debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_navigation", TTR("Visible Navigation")), RUN_DEBUG_NAVIGATION);
76 debug_menu->set_item_tooltip(-1,
77 TTR("When this option is enabled, navigation meshes and polygons will be visible in the running project."));
78 debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_avoidance", TTR("Visible Avoidance")), RUN_DEBUG_AVOIDANCE);
79 debug_menu->set_item_tooltip(-1,
80 TTR("When this option is enabled, avoidance objects shapes, radius and velocities will be visible in the running project."));
81 debug_menu->add_separator();
82 debug_menu->add_check_shortcut(ED_SHORTCUT("editor/sync_scene_changes", TTR("Synchronize Scene Changes")), RUN_LIVE_DEBUG);
83 debug_menu->set_item_tooltip(-1,
84 TTR("When this option is enabled, any changes made to the scene in the editor will be replicated in the running project.\nWhen used remotely on a device, this is more efficient when the network filesystem option is enabled."));
85 debug_menu->add_check_shortcut(ED_SHORTCUT("editor/sync_script_changes", TTR("Synchronize Script Changes")), RUN_RELOAD_SCRIPTS);
86 debug_menu->set_item_tooltip(-1,
87 TTR("When this option is enabled, any script that is saved will be reloaded in the running project.\nWhen used remotely on a device, this is more efficient when the network filesystem option is enabled."));
88 debug_menu->add_check_shortcut(ED_SHORTCUT("editor/keep_server_open", TTR("Keep Debug Server Open")), SERVER_KEEP_OPEN);
89 debug_menu->set_item_tooltip(-1,
90 TTR("When this option is enabled, the editor debug server will stay open and listen for new sessions started outside of the editor itself."));
91
92 // Multi-instance, start/stop
93 instances_menu = memnew(PopupMenu);
94 instances_menu->set_name("run_instances");
95 instances_menu->set_hide_on_checkable_item_selection(false);
96
97 debug_menu->add_child(instances_menu);
98 debug_menu->add_separator();
99 debug_menu->add_submenu_item(TTR("Run Multiple Instances"), "run_instances");
100
101 for (int i = 1; i <= 4; i++) {
102 instances_menu->add_radio_check_item(vformat(TTRN("Run %d Instance", "Run %d Instances", i), i));
103 instances_menu->set_item_metadata(i - 1, i);
104 }
105 instances_menu->set_item_checked(0, true);
106 instances_menu->connect("index_pressed", callable_mp(this, &DebuggerEditorPlugin::_select_run_count));
107 debug_menu->connect("id_pressed", callable_mp(this, &DebuggerEditorPlugin::_menu_option));
108}
109
110DebuggerEditorPlugin::~DebuggerEditorPlugin() {
111 EditorDebuggerServer::deinitialize();
112 memdelete(file_server);
113}
114
115void DebuggerEditorPlugin::_select_run_count(int p_index) {
116 int len = instances_menu->get_item_count();
117 for (int idx = 0; idx < len; idx++) {
118 instances_menu->set_item_checked(idx, idx == p_index);
119 }
120 EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_instances", instances_menu->get_item_metadata(p_index));
121}
122
123void DebuggerEditorPlugin::_menu_option(int p_option) {
124 switch (p_option) {
125 case RUN_FILE_SERVER: {
126 bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_FILE_SERVER));
127
128 if (ischecked) {
129 file_server->stop();
130 set_process(false);
131 } else {
132 file_server->start();
133 set_process(true);
134 }
135
136 debug_menu->set_item_checked(debug_menu->get_item_index(RUN_FILE_SERVER), !ischecked);
137 EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_file_server", !ischecked);
138
139 } break;
140 case RUN_LIVE_DEBUG: {
141 bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_LIVE_DEBUG));
142
143 debug_menu->set_item_checked(debug_menu->get_item_index(RUN_LIVE_DEBUG), !ischecked);
144 EditorDebuggerNode::get_singleton()->set_live_debugging(!ischecked);
145 EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_live_debug", !ischecked);
146
147 } break;
148 case RUN_DEPLOY_REMOTE_DEBUG: {
149 bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEPLOY_REMOTE_DEBUG));
150 debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEPLOY_REMOTE_DEBUG), !ischecked);
151 EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_deploy_remote_debug", !ischecked);
152
153 } break;
154 case RUN_DEBUG_COLLISIONS: {
155 bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEBUG_COLLISIONS));
156 debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_COLLISIONS), !ischecked);
157 EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_collisions", !ischecked);
158
159 } break;
160 case RUN_DEBUG_PATHS: {
161 bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEBUG_PATHS));
162 debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_PATHS), !ischecked);
163 EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_paths", !ischecked);
164
165 } break;
166 case RUN_DEBUG_NAVIGATION: {
167 bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEBUG_NAVIGATION));
168 debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_NAVIGATION), !ischecked);
169 EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_navigation", !ischecked);
170
171 } break;
172 case RUN_DEBUG_AVOIDANCE: {
173 bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEBUG_AVOIDANCE));
174 debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_AVOIDANCE), !ischecked);
175 EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_avoidance", !ischecked);
176
177 } break;
178 case RUN_RELOAD_SCRIPTS: {
179 bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_RELOAD_SCRIPTS));
180 debug_menu->set_item_checked(debug_menu->get_item_index(RUN_RELOAD_SCRIPTS), !ischecked);
181
182 ScriptEditor::get_singleton()->set_live_auto_reload_running_scripts(!ischecked);
183 EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_reload_scripts", !ischecked);
184
185 } break;
186 case SERVER_KEEP_OPEN: {
187 bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(SERVER_KEEP_OPEN));
188 debug_menu->set_item_checked(debug_menu->get_item_index(SERVER_KEEP_OPEN), !ischecked);
189
190 EditorDebuggerNode::get_singleton()->set_keep_open(!ischecked);
191 EditorSettings::get_singleton()->set_project_metadata("debug_options", "server_keep_open", !ischecked);
192
193 } break;
194 }
195}
196
197void DebuggerEditorPlugin::_notification(int p_what) {
198 switch (p_what) {
199 case NOTIFICATION_READY: {
200 _update_debug_options();
201 } break;
202
203 case NOTIFICATION_PROCESS: {
204 file_server->poll();
205 } break;
206 }
207}
208
209void DebuggerEditorPlugin::_update_debug_options() {
210 bool check_deploy_remote = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_deploy_remote_debug", false);
211 bool check_file_server = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_file_server", false);
212 bool check_debug_collisions = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_collisions", false);
213 bool check_debug_paths = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_paths", false);
214 bool check_debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false);
215 bool check_debug_avoidance = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_avoidance", false);
216 bool check_live_debug = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_live_debug", true);
217 bool check_reload_scripts = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_reload_scripts", true);
218 bool check_server_keep_open = EditorSettings::get_singleton()->get_project_metadata("debug_options", "server_keep_open", false);
219 int instances = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_instances", 1);
220
221 if (check_deploy_remote) {
222 _menu_option(RUN_DEPLOY_REMOTE_DEBUG);
223 }
224 if (check_file_server) {
225 _menu_option(RUN_FILE_SERVER);
226 }
227 if (check_debug_collisions) {
228 _menu_option(RUN_DEBUG_COLLISIONS);
229 }
230 if (check_debug_paths) {
231 _menu_option(RUN_DEBUG_PATHS);
232 }
233 if (check_debug_navigation) {
234 _menu_option(RUN_DEBUG_NAVIGATION);
235 }
236 if (check_debug_avoidance) {
237 _menu_option(RUN_DEBUG_AVOIDANCE);
238 }
239 if (check_live_debug) {
240 _menu_option(RUN_LIVE_DEBUG);
241 }
242 if (check_reload_scripts) {
243 _menu_option(RUN_RELOAD_SCRIPTS);
244 }
245 if (check_server_keep_open) {
246 _menu_option(SERVER_KEEP_OPEN);
247 }
248
249 int len = instances_menu->get_item_count();
250 for (int idx = 0; idx < len; idx++) {
251 bool checked = (int)instances_menu->get_item_metadata(idx) == instances;
252 instances_menu->set_item_checked(idx, checked);
253 }
254}
255