Skip to content

Commit ee5934d

Browse files
committed
Add support for taking embedded window screenshots.
1 parent 64b0990 commit ee5934d

File tree

10 files changed

+191
-13
lines changed

10 files changed

+191
-13
lines changed

editor/editor_node.cpp

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
#include "editor/editor_property_name_processor.h"
9797
#include "editor/editor_resource_picker.h"
9898
#include "editor/editor_resource_preview.h"
99+
#include "editor/editor_run.h"
99100
#include "editor/editor_script.h"
100101
#include "editor/editor_settings.h"
101102
#include "editor/editor_settings_dialog.h"
@@ -3484,14 +3485,37 @@ void EditorNode::_request_screenshot() {
34843485

34853486
void EditorNode::_screenshot(bool p_use_utc) {
34863487
String name = "editor_screenshot_" + Time::get_singleton()->get_datetime_string_from_system(p_use_utc).remove_char(':') + ".png";
3487-
NodePath path = String("user://") + name;
3488-
_save_screenshot(path);
3488+
String path = String("user://") + name;
3489+
3490+
if (!EditorRun::request_screenshot(callable_mp(this, &EditorNode::_save_screenshot_with_embedded_process).bind(path))) {
3491+
_save_screenshot(path);
3492+
}
3493+
}
3494+
3495+
void EditorNode::_save_screenshot_with_embedded_process(int64_t p_w, int64_t p_h, const String &p_emb_path, const Rect2i &p_rect, const String &p_path) {
3496+
Control *main_screen_control = editor_main_screen->get_control();
3497+
ERR_FAIL_NULL_MSG(main_screen_control, "Cannot get the editor main screen control.");
3498+
Viewport *viewport = main_screen_control->get_viewport();
3499+
ERR_FAIL_NULL_MSG(viewport, "Cannot get a viewport from the editor main screen.");
3500+
Ref<ViewportTexture> texture = viewport->get_texture();
3501+
ERR_FAIL_COND_MSG(texture.is_null(), "Cannot get a viewport texture from the editor main screen.");
3502+
Ref<Image> img = texture->get_image();
3503+
ERR_FAIL_COND_MSG(img.is_null(), "Cannot get an image from a viewport texture of the editor main screen.");
3504+
ERR_FAIL_COND(p_emb_path.is_empty());
3505+
Ref<Image> overlay = Image::load_from_file(p_emb_path);
3506+
DirAccess::remove_absolute(p_emb_path);
3507+
ERR_FAIL_COND_MSG(overlay.is_null(), "Cannot get an image from a embedded process.");
3508+
overlay->resize(p_rect.size.x, p_rect.size.y);
3509+
img->blend_rect(overlay, Rect2i(0, 0, p_w, p_h), p_rect.position);
3510+
Error error = img->save_png(p_path);
3511+
ERR_FAIL_COND_MSG(error != OK, "Cannot save screenshot to file '" + p_path + "'.");
3512+
34893513
if (EDITOR_GET("interface/editor/automatically_open_screenshots")) {
3490-
OS::get_singleton()->shell_show_in_file_manager(ProjectSettings::get_singleton()->globalize_path(path), true);
3514+
OS::get_singleton()->shell_show_in_file_manager(ProjectSettings::get_singleton()->globalize_path(p_path), true);
34913515
}
34923516
}
34933517

3494-
void EditorNode::_save_screenshot(NodePath p_path) {
3518+
void EditorNode::_save_screenshot(const String &p_path) {
34953519
Control *main_screen_control = editor_main_screen->get_control();
34963520
ERR_FAIL_NULL_MSG(main_screen_control, "Cannot get the editor main screen control.");
34973521
Viewport *viewport = main_screen_control->get_viewport();
@@ -3502,6 +3526,10 @@ void EditorNode::_save_screenshot(NodePath p_path) {
35023526
ERR_FAIL_COND_MSG(img.is_null(), "Cannot get an image from a viewport texture of the editor main screen.");
35033527
Error error = img->save_png(p_path);
35043528
ERR_FAIL_COND_MSG(error != OK, "Cannot save screenshot to file '" + p_path + "'.");
3529+
3530+
if (EDITOR_GET("interface/editor/automatically_open_screenshots")) {
3531+
OS::get_singleton()->shell_show_in_file_manager(ProjectSettings::get_singleton()->globalize_path(p_path), true);
3532+
}
35053533
}
35063534

35073535
void EditorNode::_check_system_theme_changed() {

editor/editor_node.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,8 @@ class EditorNode : public Node {
547547

548548
void _request_screenshot();
549549
void _screenshot(bool p_use_utc = false);
550-
void _save_screenshot(NodePath p_path);
550+
void _save_screenshot(const String &p_path);
551+
void _save_screenshot_with_embedded_process(int64_t p_w, int64_t p_h, const String &p_emb_path, const Rect2i &p_rect, const String &p_path);
551552

552553
void _check_system_theme_changed();
553554

editor/editor_run.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,14 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie, const V
189189
return OK;
190190
}
191191

192+
bool EditorRun::request_screenshot(const Callable &p_callback) {
193+
if (instance_rq_screenshot_callback) {
194+
return instance_rq_screenshot_callback(p_callback);
195+
} else {
196+
return false;
197+
}
198+
}
199+
192200
bool EditorRun::has_child_process(OS::ProcessID p_pid) const {
193201
for (const OS::ProcessID &E : pids) {
194202
if (E == p_pid) {

editor/editor_run.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "core/os/os.h"
3434

3535
typedef void (*EditorRunInstanceStarting)(int p_index, List<String> &r_arguments);
36+
typedef bool (*EditorRunInstanceRequestScreenshot)(const Callable &p_callback);
3637

3738
class EditorRun {
3839
public:
@@ -58,6 +59,7 @@ class EditorRun {
5859

5960
public:
6061
inline static EditorRunInstanceStarting instance_starting_callback = nullptr;
62+
inline static EditorRunInstanceRequestScreenshot instance_rq_screenshot_callback = nullptr;
6163

6264
Status get_status() const;
6365
String get_running_scene() const;
@@ -71,6 +73,8 @@ class EditorRun {
7173
int get_child_process_count() const { return pids.size(); }
7274
OS::ProcessID get_current_process() const;
7375

76+
static bool request_screenshot(const Callable &p_callback);
77+
7478
static WindowPlacement get_window_placement();
7579

7680
EditorRun();

editor/plugins/game_view_plugin.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
#include "core/config/project_settings.h"
3434
#include "core/debugger/debugger_marshalls.h"
35+
#include "core/debugger/engine_debugger.h"
3536
#include "core/string/translation_server.h"
3637
#include "editor/debugger/editor_debugger_node.h"
3738
#include "editor/debugger/script_editor_debugger.h"
@@ -52,6 +53,8 @@
5253
#include "scene/gui/panel.h"
5354
#include "scene/gui/separator.h"
5455

56+
HashMap<String, GameViewDebugger::ParseMessageFunc> GameViewDebugger::parse_message_handlers_base;
57+
5558
void GameViewDebugger::_session_started(Ref<EditorDebuggerSession> p_session) {
5659
if (!is_feature_enabled) {
5760
return;
@@ -225,8 +228,67 @@ void GameViewDebugger::_bind_methods() {
225228
ADD_SIGNAL(MethodInfo("session_stopped"));
226229
}
227230

231+
bool GameViewDebugger::add_screenshot_callback(const Callable &p_callaback, const Rect2i &p_rect) {
232+
bool found = false;
233+
for (Ref<EditorDebuggerSession> &I : sessions) {
234+
if (I->is_active()) {
235+
ScreenshotCB sd;
236+
sd.cb = p_callaback;
237+
sd.rect = p_rect;
238+
screenshot_callbacks[scr_rq_id] = sd;
239+
240+
Array arr;
241+
arr.append(scr_rq_id);
242+
I->send_message("scene:rq_screenshot", arr);
243+
scr_rq_id++;
244+
found = true;
245+
}
246+
}
247+
return found;
248+
}
249+
250+
bool GameViewDebugger::_msg_get_screenshot(const Array &p_args) {
251+
ERR_FAIL_COND_V_MSG(p_args.size() != 4, false, "get_screenshot: invalid number of arguments");
252+
253+
int64_t id = p_args[0];
254+
int64_t w = p_args[1];
255+
int64_t h = p_args[2];
256+
const String &path = p_args[3];
257+
258+
if (screenshot_callbacks.has(id)) {
259+
if (screenshot_callbacks[id].cb.is_valid()) {
260+
screenshot_callbacks[id].cb.call(w, h, path, screenshot_callbacks[id].rect);
261+
}
262+
screenshot_callbacks.erase(id);
263+
}
264+
return true;
265+
}
266+
267+
bool GameViewDebugger::capture(const String &p_message, const Array &p_data, int p_session) {
268+
Ref<EditorDebuggerSession> session = get_session(p_session);
269+
ERR_FAIL_COND_V(session.is_null(), true);
270+
271+
ParseMessageFunc *fn_ptr = parse_message_handlers_base.getptr(p_message);
272+
if (fn_ptr) {
273+
return (this->**fn_ptr)(p_data);
274+
} else {
275+
// Any other messages with this prefix should be ignored.
276+
WARN_PRINT("GameViewDebugger unknown message: " + p_message);
277+
return false;
278+
}
279+
280+
return true;
281+
}
282+
283+
bool GameViewDebugger::has_capture(const String &p_capture) const {
284+
return p_capture == "game_view";
285+
}
286+
228287
GameViewDebugger::GameViewDebugger() {
229288
EditorFeatureProfileManager::get_singleton()->connect("current_feature_profile_changed", callable_mp(this, &GameViewDebugger::_feature_profile_changed));
289+
if (parse_message_handlers_base.is_empty()) {
290+
parse_message_handlers_base["game_view:get_screenshot"] = &GameViewDebugger::_msg_get_screenshot;
291+
}
230292
}
231293

232294
///////
@@ -280,6 +342,23 @@ void GameView::_instance_starting(int p_idx, List<String> &r_arguments) {
280342
_update_arguments_for_instance(p_idx, r_arguments);
281343
}
282344

345+
bool GameView::_instance_rq_screenshot_static(const Callable &p_callback) {
346+
ERR_FAIL_NULL_V(singleton, false);
347+
return singleton->_instance_rq_screenshot(p_callback);
348+
}
349+
350+
bool GameView::_instance_rq_screenshot(const Callable &p_callback) {
351+
if (debugger.is_null() || window_wrapper->get_window_enabled() || !embedded_process || !embedded_process->is_embedding_completed()) {
352+
return false;
353+
}
354+
Rect2 r = embedded_process->get_adjusted_embedded_window_rect(embedded_process->get_rect());
355+
r.position += embedded_process->get_global_position();
356+
#ifndef MACOS_ENABLED
357+
r.position -= embedded_process->get_window()->get_position();
358+
#endif
359+
return debugger->add_screenshot_callback(p_callback, r);
360+
}
361+
283362
void GameView::_show_update_window_wrapper() {
284363
EditorRun::WindowPlacement placement = EditorRun::get_window_placement();
285364
Point2 position = floating_window_rect.position;
@@ -744,6 +823,7 @@ void GameView::_notification(int p_what) {
744823
EditorRunBar::get_singleton()->connect("play_pressed", callable_mp(this, &GameView::_play_pressed));
745824
EditorRunBar::get_singleton()->connect("stop_pressed", callable_mp(this, &GameView::_stop_pressed));
746825
EditorRun::instance_starting_callback = _instance_starting_static;
826+
EditorRun::instance_rq_screenshot_callback = _instance_rq_screenshot_static;
747827

748828
// Listen for project settings changes to update the window size and aspect ratio.
749829
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &GameView::_editor_or_project_settings_changed));

editor/plugins/game_view_plugin.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,28 @@ class GameViewDebugger : public EditorDebuggerPlugin {
6060

6161
void _feature_profile_changed();
6262

63+
typedef bool (GameViewDebugger::*ParseMessageFunc)(const Array &p_args);
64+
static HashMap<String, ParseMessageFunc> parse_message_handlers_base;
65+
66+
struct ScreenshotCB {
67+
Callable cb;
68+
Rect2i rect;
69+
};
70+
71+
int64_t scr_rq_id = 0;
72+
HashMap<uint64_t, ScreenshotCB> screenshot_callbacks;
73+
74+
bool _msg_get_screenshot(const Array &p_args);
75+
6376
protected:
6477
static void _bind_methods();
6578

6679
public:
80+
virtual bool capture(const String &p_message, const Array &p_data, int p_session) override;
81+
virtual bool has_capture(const String &p_capture) const override;
82+
83+
bool add_screenshot_callback(const Callable &p_callaback, const Rect2i &p_rect);
84+
6785
void set_suspend(bool p_enabled);
6886
void next_frame();
6987

@@ -173,6 +191,8 @@ class GameView : public VBoxContainer {
173191
void _play_pressed();
174192
static void _instance_starting_static(int p_idx, List<String> &r_arguments);
175193
void _instance_starting(int p_idx, List<String> &r_arguments);
194+
static bool _instance_rq_screenshot_static(const Callable &p_callback);
195+
bool _instance_rq_screenshot(const Callable &p_callback);
176196
void _stop_pressed();
177197
void _embedding_completed();
178198
void _embedding_failed();

platform/macos/editor/embedded_game_view_plugin.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ class GameViewDebuggerMacOS : public GameViewDebugger {
6060

6161
public:
6262
virtual bool capture(const String &p_message, const Array &p_data, int p_session) override;
63-
virtual bool has_capture(const String &p_capture) const override;
6463

6564
GameViewDebuggerMacOS(EmbeddedProcessMacOS *p_embedded_process);
6665
};

platform/macos/editor/embedded_game_view_plugin.mm

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,6 @@
109109
parse_message_handlers["game_view:joy_stop"] = &GameViewDebuggerMacOS::_msg_joy_stop;
110110
}
111111

112-
bool GameViewDebuggerMacOS::has_capture(const String &p_capture) const {
113-
return p_capture == "game_view";
114-
}
115-
116112
bool GameViewDebuggerMacOS::capture(const String &p_message, const Array &p_data, int p_session) {
117113
Ref<EditorDebuggerSession> session = get_session(p_session);
118114
ERR_FAIL_COND_V(session.is_null(), true);
@@ -121,9 +117,7 @@
121117
if (fn_ptr) {
122118
return (this->**fn_ptr)(p_data);
123119
} else {
124-
// Any other messages with this prefix should be ignored.
125-
WARN_PRINT("GameViewDebuggerMacOS unknown message: " + p_message);
126-
return ERR_SKIP;
120+
return GameViewDebugger::capture(p_message, p_data, p_session);
127121
}
128122

129123
return true;

scene/debugger/scene_debugger.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@
3232

3333
#include "core/debugger/debugger_marshalls.h"
3434
#include "core/debugger/engine_debugger.h"
35+
#include "core/io/dir_access.h"
3536
#include "core/io/marshalls.h"
3637
#include "core/math/math_fieldwise.h"
3738
#include "core/object/script_language.h"
39+
#include "core/os/time.h"
3840
#include "core/templates/local_vector.h"
3941
#include "scene/gui/popup_menu.h"
4042
#include "scene/main/canvas_layer.h"
@@ -424,6 +426,46 @@ Error SceneDebugger::_msg_runtime_node_select_reset_camera_3d(const Array &p_arg
424426

425427
// endregion
426428

429+
// region Embedded process screenshot.
430+
431+
Error SceneDebugger::_msg_rq_screenshot(const Array &p_args, SceneTree *p_scene_tree, LiveEditor *p_live_editor, RuntimeNodeSelect *p_runtime_node_select) {
432+
ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA);
433+
434+
Viewport *viewport = SceneTree::get_singleton()->get_root();
435+
ERR_FAIL_NULL_V_MSG(viewport, ERR_UNCONFIGURED, "Cannot get a viewport from the main screen.");
436+
Ref<ViewportTexture> texture = viewport->get_texture();
437+
ERR_FAIL_COND_V_MSG(texture.is_null(), ERR_UNCONFIGURED, "Cannot get a viewport texture from the main screen.");
438+
Ref<Image> img = texture->get_image();
439+
ERR_FAIL_COND_V_MSG(img.is_null(), ERR_UNCONFIGURED, "Cannot get an image from a viewport texture of the main screen.");
440+
img->clear_mipmaps();
441+
442+
const String TEMP_DIR = OS::get_singleton()->get_temp_path();
443+
uint32_t suffix_i = 0;
444+
String path;
445+
while (true) {
446+
String datetime = Time::get_singleton()->get_datetime_string_from_system().remove_chars("-T:");
447+
datetime += itos(Time::get_singleton()->get_ticks_usec());
448+
String suffix = datetime + (suffix_i > 0 ? itos(suffix_i) : "");
449+
path = TEMP_DIR.path_join("scr-" + suffix + ".png");
450+
if (!DirAccess::exists(path)) {
451+
break;
452+
}
453+
suffix_i += 1;
454+
}
455+
img->save_png(path);
456+
457+
Array arr;
458+
arr.append(p_args[0]);
459+
arr.append(img->get_width());
460+
arr.append(img->get_height());
461+
arr.append(path);
462+
EngineDebugger::get_singleton()->send_message("game_view:get_screenshot", arr);
463+
464+
return OK;
465+
}
466+
467+
// endregion
468+
427469
Error SceneDebugger::parse_message(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured) {
428470
SceneTree *scene_tree = SceneTree::get_singleton();
429471
if (!scene_tree) {
@@ -503,6 +545,7 @@ void SceneDebugger::_init_parse_message_handlers() {
503545
#ifndef _3D_DISABLED
504546
HANDLER(runtime_node_select_reset_camera_3d);
505547
#endif
548+
HANDLER(rq_screenshot);
506549

507550
#undef HANDLER
508551
}

scene/debugger/scene_debugger.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ class SceneDebugger {
121121
#ifndef _3D_DISABLED
122122
HANDLER(runtime_node_select_reset_camera_3d);
123123
#endif
124+
HANDLER(rq_screenshot);
124125

125126
#undef HANDLER
126127

0 commit comments

Comments
 (0)