Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions core/debugger/remote_debugger_peer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ bool RemoteDebuggerPeerTCP::is_peer_connected() {
return connected;
}

String RemoteDebuggerPeerTCP::get_peer_host() {
if (tcp_client.is_valid() && tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) {
return String(tcp_client->get_connected_host());
}
return "";
}

bool RemoteDebuggerPeerTCP::has_message() {
return in_queue.size() > 0;
}
Expand Down
2 changes: 2 additions & 0 deletions core/debugger/remote_debugger_peer.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class RemoteDebuggerPeer : public RefCounted {

public:
virtual bool is_peer_connected() = 0;
virtual String get_peer_host() = 0;
virtual int get_max_message_size() const = 0;
virtual bool has_message() = 0;
virtual Error put_message(const Array &p_arr) = 0;
Expand Down Expand Up @@ -85,6 +86,7 @@ class RemoteDebuggerPeerTCP : public RemoteDebuggerPeer {
Error connect_to_host(const String &p_host, uint16_t p_port);

bool is_peer_connected() override;
String get_peer_host() override;
int get_max_message_size() const override;
bool has_message() override;
Error put_message(const Array &p_arr) override;
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/EditorSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,9 @@
If enabled, displays internal engine errors in toast notifications (toggleable by clicking the "bell" icon at the bottom of the editor). No matter the value of this setting, non-internal engine errors will always be visible in toast notifications.
The default [b]Auto[/b] value will only enable this if the editor was compiled with the [code]dev_build=yes[/code] SCons option (the default is [code]dev_build=no[/code]).
</member>
<member name="interface/editor/show_remote_debug_connection_status" type="bool" setter="" getter="">
If enabled, displays a new area in the top bar of the editor UI that shows the status of a remote debug session. Hovering over this area will show a tooltip of the ip address.
</member>
<member name="interface/editor/show_update_spinner" type="int" setter="" getter="">
If enabled, displays an icon in the top-right corner of the editor that spins when the editor redraws a frame. This can be used to diagnose situations where the engine is constantly redrawing, which should be avoided as this increases CPU and GPU utilization for no good reason. To further troubleshoot these situations, start the editor with the [code]--debug-canvas-item-redraw[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url].
Consider enabling this if you are developing editor plugins to ensure they only make the editor redraw when required.
Expand Down
15 changes: 15 additions & 0 deletions editor/debugger/script_editor_debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1379,6 +1379,21 @@ void ScriptEditorDebugger::_resources_reimported(const PackedStringArray &p_reso
_put_msg("scene:reload_cached_files", msg);
}

String ScriptEditorDebugger::get_connected_host_ip() {
if (!peer.is_valid() || !peer->is_peer_connected()) {
return "";
}

// Try to cast to TCP peer to get the IP address
Ref<RemoteDebuggerPeerTCP> tcp_peer = peer;
if (tcp_peer.is_valid()) {
return tcp_peer->get_peer_host();
}

// For non-TCP peers (e.g., WebSocket), return a generic message
return "Connected";
}

int ScriptEditorDebugger::_get_node_path_cache(const NodePath &p_path) {
const int *r = node_path_cache.getptr(p_path);
if (r) {
Expand Down
1 change: 1 addition & 0 deletions editor/debugger/script_editor_debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ class ScriptEditorDebugger : public MarginContainer {
void debug_continue();
bool is_breaked() const { return threads_debugged.size() > 0; }
bool is_debuggable() const { return threads_debugged.size() > 0 && threads_debugged[debugging_thread_id].can_debug; }
String get_connected_host_ip();
bool is_session_active() { return peer.is_valid() && peer->is_peer_connected(); }
int get_remote_pid() const { return remote_pid; }

Expand Down
132 changes: 132 additions & 0 deletions editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,16 @@ void EditorNode::_update_theme(bool p_skip_creation) {
help_menu->set_item_icon(help_menu->get_item_index(HELP_ABOUT), _get_editor_theme_native_menu_icon(SNAME("Godot"), global_menu, dark_mode));
help_menu->set_item_icon(help_menu->get_item_index(HELP_SUPPORT_GODOT_DEVELOPMENT), _get_editor_theme_native_menu_icon(SNAME("Heart"), global_menu, dark_mode));

// Initialize debug target status icon
if (debug_target_status && (bool)EDITOR_GET("interface/editor/show_remote_debug_connection_status") == true) {
Ref<Texture2D> icon = theme->get_icon(SNAME("GuiSliderGrabber"), EditorStringName(EditorIcons));
debug_target_status->set_button_icon(icon);
debug_target_status->add_theme_color_override("icon_normal_color", Color(0.94, 0.44, 0.56, 1.0)); // Red is for disconnected
debug_target_status->add_theme_color_override("icon_pressed_color", Color(0.94, 0.44, 0.56, 1.0));
debug_target_status->add_theme_color_override("icon_hover_color", Color(0.94, 0.44, 0.56, 1.0));
debug_target_status->set_self_modulate(Color(1, 1, 1, 0.95)); // 95% opacity for icon and text
}

_update_renderer_color();
}

Expand Down Expand Up @@ -802,6 +812,9 @@ void EditorNode::_notification(int p_what) {
scene_tabs->update_scene_tabs();
}

// Update debug target status
_update_debug_target_status();

// Update the animation frame of the update spinner.
uint64_t frame = Engine::get_singleton()->get_frames_drawn();
uint64_t tick = OS::get_singleton()->get_ticks_msec();
Expand Down Expand Up @@ -7805,6 +7818,73 @@ HashMap<String, Variant> EditorNode::get_initial_settings() {
return settings;
}

void EditorNode::_update_debug_target_status() {
if (!debug_target_status) {
return;
}

// Get the current debugger
EditorDebuggerNode *debugger_node = EditorDebuggerNode::get_singleton();
if (!debugger_node) {
return;
}

ScriptEditorDebugger *debugger = debugger_node->get_default_debugger();
if (!debugger) {
return;
}

// Check if session is active
bool is_connected = debugger->is_session_active();

// Only update the ui if state has changed
if (is_connected != debug_target_last_connected_state) {
// Connection state changed, update everything
debug_target_last_connected_state = is_connected;

if (is_connected) {
// Connected state: green icon + stringified IP address
String ip_address = debugger->get_connected_host_ip();
if (ip_address.is_empty()) {
ip_address = "Connected";
}
debug_target_last_ip = ip_address;

Ref<Texture2D> icon = theme->get_icon(SNAME("GuiSliderGrabber"), EditorStringName(EditorIcons));
debug_target_status->set_button_icon(icon);
debug_target_status->add_theme_color_override("icon_normal_color", Color(0.46, 0.85, 0.69, 1.0));
debug_target_status->add_theme_color_override("icon_pressed_color", Color(0.46, 0.85, 0.69, 1.0));
debug_target_status->add_theme_color_override("icon_hover_color", Color(0.46, 0.85, 0.69, 1.0));
debug_target_status->add_theme_color_override("font_color", Color(1, 1, 1, 0.95));
debug_target_status->set_tooltip_text(vformat("Connected to: %s", ip_address));
debug_target_label->set_tooltip_text(vformat("Connected to: %s", ip_address));
} else {
// Disconnected state: red icon + "No Connection"
debug_target_last_ip = "";

Ref<Texture2D> icon = theme->get_icon(SNAME("GuiSliderGrabber"), EditorStringName(EditorIcons));
debug_target_status->set_button_icon(icon);
debug_target_status->add_theme_color_override("icon_normal_color", Color(0.94, 0.44, 0.56, 1.0));
debug_target_status->add_theme_color_override("icon_pressed_color", Color(0.94, 0.44, 0.56, 1.0));
debug_target_status->add_theme_color_override("icon_hover_color", Color(0.94, 0.44, 0.56, 1.0));
debug_target_status->add_theme_color_override("font_color", Color(1, 1, 1, 0.95));
debug_target_status->set_tooltip_text("No Connection");
debug_target_label->set_tooltip_text("No Connection");
}
} else if (is_connected) {
// Connected state hasn't changed, but IP might have
String ip_address = debugger->get_connected_host_ip();
if (ip_address.is_empty()) {
ip_address = "Connected";
}
if (ip_address != debug_target_last_ip) {
debug_target_last_ip = ip_address;
debug_target_status->set_tooltip_text(vformat("Connected to: %s", ip_address));
debug_target_label->set_tooltip_text(vformat("Connected to: %s", ip_address));
}
}
}

EditorNode::EditorNode() {
DEV_ASSERT(!singleton);
singleton = this;
Expand Down Expand Up @@ -8585,6 +8665,58 @@ EditorNode::EditorNode() {
project_run_bar->connect("play_pressed", callable_mp(this, &EditorNode::_project_run_started));
project_run_bar->connect("stop_pressed", callable_mp(this, &EditorNode::_project_run_stopped));

if ((bool)EDITOR_GET("interface/editor/show_remote_debug_connection_status") == true) {
// Transparent non-interactive label spacer for debug target section (left side)
Label *debug_target_spacer_left = memnew(Label);
debug_target_spacer_left->set_text(" | ");
debug_target_spacer_left->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
debug_target_spacer_left->add_theme_color_override("font_color", Color(1, 1, 1, .2));
title_bar->add_child(debug_target_spacer_left);

// Debug target section
debug_target_hb = memnew(HBoxContainer);
title_bar->add_child(debug_target_hb);

// "Debug Client:" label
debug_target_label = memnew(Button);
debug_target_label->set_text("Debug Client:");
debug_target_label->set_flat(true);
debug_target_label->set_focus_mode(Control::FOCUS_NONE);
debug_target_label->set_mouse_filter(Control::MOUSE_FILTER_PASS);
debug_target_label->add_theme_color_override("font_color", Color(1, 1, 1, 0.5));
//debug_target_label->add_theme_font_size_override(SceneStringName(font_size), 11);
Ref<StyleBoxEmpty> label_empty_style;
label_empty_style.instantiate();
debug_target_label->set_tooltip_text("No Connection");
debug_target_label->add_theme_style_override("normal", label_empty_style);
debug_target_label->add_theme_style_override("hover", label_empty_style);
debug_target_label->add_theme_style_override("pressed", label_empty_style);
debug_target_label->add_theme_style_override("focus", label_empty_style);
debug_target_hb->add_child(debug_target_label);
// Connection status button
debug_target_status = memnew(Button);
debug_target_status->set_flat(true);
debug_target_status->set_focus_mode(Control::FOCUS_NONE);
debug_target_status->set_mouse_filter(Control::MOUSE_FILTER_PASS);
Ref<StyleBoxEmpty> status_empty_style;
status_empty_style.instantiate();
debug_target_status->add_theme_style_override("normal", status_empty_style);
debug_target_status->add_theme_style_override("hover", status_empty_style);
debug_target_status->add_theme_style_override("pressed", status_empty_style);
debug_target_status->add_theme_style_override("focus", status_empty_style);
// Set initial disconnected state
debug_target_status->set_tooltip_text("No Connection");
debug_target_status->add_theme_color_override("font_color", Color(1, 1, 1, 0.85));
debug_target_hb->add_child(debug_target_status);

// Transparent non-interactive label spacer for debug target section (right side)
Label *debug_target_spacer_right = memnew(Label);
debug_target_spacer_right->set_text(" | ");
debug_target_spacer_right->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
debug_target_spacer_right->add_theme_color_override("font_color", Color(1, 1, 1, .2));
title_bar->add_child(debug_target_spacer_right);
}

right_menu_hb = memnew(HBoxContainer);
right_menu_hb->set_mouse_filter(Control::MOUSE_FILTER_STOP);
title_bar->add_child(right_menu_hb);
Expand Down
7 changes: 7 additions & 0 deletions editor/editor_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,11 @@ class EditorNode : public Node {
Control *right_menu_spacer = nullptr;
EditorTitleBar *title_bar = nullptr;
EditorRunBar *project_run_bar = nullptr;
HBoxContainer *debug_target_hb = nullptr;
Button *debug_target_label = nullptr;
Button *debug_target_status = nullptr;
bool debug_target_last_connected_state = false;
String debug_target_last_ip = "";
HBoxContainer *right_menu_hb = nullptr;

// Spacers to center 2D / 3D / Script buttons.
Expand Down Expand Up @@ -720,6 +725,8 @@ class EditorNode : public Node {
void _update_main_menu_type();
void _add_to_main_menu(const String &p_name, PopupMenu *p_menu);

void _update_debug_target_status();

protected:
friend class FileSystemDock;

Expand Down
2 changes: 1 addition & 1 deletion editor/run/editor_run_native.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ void EditorRunNative::_notification(int p_what) {
const int device_count = MIN(eep->get_options_count(), 9000);
String error;
if (device_count > 0 && preset->is_runnable()) {
popup->add_icon_item(eep->get_run_icon(), eep->get_name(), -1);
popup->add_icon_item(eep->get_run_icon(), preset->get_name(), -1);
popup->set_item_disabled(-1, true);
for (int j = 0; j < device_count; j++) {
popup->add_icon_item(eep->get_option_icon(j), eep->get_option_label(j), EditorExport::encode_platform_device_id(platform_idx, j));
Expand Down
1 change: 1 addition & 0 deletions editor/settings/editor_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/accept_dialog_cancel_ok_buttons", 0,
vformat("Auto (%s),Cancel First,OK First", DisplayServer::get_singleton()->get_swap_cancel_ok() ? "OK First" : "Cancel First"),
PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/show_remote_debug_connection_status", false, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
#ifdef DEV_ENABLED
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/show_internal_errors_in_toast_notifications", 0, "Auto (Enabled),Enabled,Disabled")
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/show_update_spinner", 0, "Auto (Enabled),Enabled,Disabled")
Expand Down
7 changes: 7 additions & 0 deletions modules/websocket/remote_debugger_peer_websocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ bool RemoteDebuggerPeerWebSocket::is_peer_connected() {
return ws_peer.is_valid() && (ws_peer->get_ready_state() == WebSocketPeer::STATE_OPEN || ws_peer->get_ready_state() == WebSocketPeer::STATE_CONNECTING);
}

String RemoteDebuggerPeerWebSocket::get_peer_host() {
if (ws_peer.is_valid() && ws_peer->get_ready_state() == WebSocketPeer::STATE_OPEN) {
return String(ws_peer->get_connected_host());
}
return "";
}

void RemoteDebuggerPeerWebSocket::poll() {
ERR_FAIL_COND(ws_peer.is_null());
ws_peer->poll();
Expand Down
1 change: 1 addition & 0 deletions modules/websocket/remote_debugger_peer_websocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class RemoteDebuggerPeerWebSocket : public RemoteDebuggerPeer {
Error connect_to_host(const String &p_uri);

bool is_peer_connected() override;
String get_peer_host() override;
int get_max_message_size() const override;
bool has_message() override;
Error put_message(const Array &p_arr) override;
Expand Down
Loading