From 5e5734dbd4704c9217d2f87e34f81aec7fcd18b0 Mon Sep 17 00:00:00 2001
From: Hilderin <81109165+Hilderin@users.noreply.github.com>
Date: Wed, 25 Sep 2024 16:57:23 -0400
Subject: [PATCH] Embedding game process in editor
---
doc/classes/DisplayServer.xml | 3 +
editor/editor_run.cpp | 170 +++++++-----
editor/editor_run.h | 3 +
editor/gui/editor_run_bar.cpp | 36 +++
editor/gui/editor_run_bar.h | 4 +
editor/icons/AutoFocus.svg | 1 +
editor/icons/EmbeddedProcess.svg | 1 +
editor/icons/KeepAspect.svg | 1 +
editor/plugins/game_editor_plugin.cpp | 178 +++++++++++-
editor/plugins/game_editor_plugin.h | 24 ++
main/main.cpp | 27 +-
platform/android/display_server_android.cpp | 6 +-
platform/android/display_server_android.h | 4 +-
platform/ios/display_server_ios.h | 4 +-
platform/ios/display_server_ios.mm | 6 +-
.../wayland/display_server_wayland.cpp | 6 +-
.../linuxbsd/wayland/display_server_wayland.h | 4 +-
platform/linuxbsd/x11/display_server_x11.cpp | 258 +++++++++++++++++-
platform/linuxbsd/x11/display_server_x11.h | 22 +-
platform/macos/display_server_macos.h | 4 +-
platform/macos/display_server_macos.mm | 6 +-
platform/web/display_server_web.cpp | 6 +-
platform/web/display_server_web.h | 4 +-
platform/windows/display_server_windows.cpp | 194 ++++++++++---
platform/windows/display_server_windows.h | 19 +-
scene/gui/embedded_process.cpp | 155 +++++++++++
scene/gui/embedded_process.h | 71 +++++
scene/main/node.h | 1 +
scene/main/window.cpp | 12 +-
scene/scene_string_names.h | 1 +
servers/display_server.cpp | 15 +-
servers/display_server.h | 11 +-
servers/display_server_headless.h | 2 +-
tests/display_server_mock.h | 2 +-
tests/test_main.cpp | 2 +-
35 files changed, 1101 insertions(+), 162 deletions(-)
create mode 100644 editor/icons/AutoFocus.svg
create mode 100644 editor/icons/EmbeddedProcess.svg
create mode 100644 editor/icons/KeepAspect.svg
create mode 100644 scene/gui/embedded_process.cpp
create mode 100644 scene/gui/embedded_process.h
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 79064a88ba6c..89f2c1c4196a 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -1883,6 +1883,9 @@
Display server supports spawning dialogs for selecting files or directories using the operating system's native look-and-feel. See [method file_dialog_show] and [method file_dialog_with_options_show]. [b]Windows, macOS, Linux (X11/Wayland)[/b]
+
+ Display server supports embedding a window from another process. [b]Windows, Linux (X11)[/b]
+
Makes the mouse cursor visible if it is hidden.
diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp
index d5135f419848..0af03fe087a7 100644
--- a/editor/editor_run.cpp
+++ b/editor/editor_run.cpp
@@ -34,6 +34,7 @@
#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "editor/plugins/game_editor_plugin.h"
#include "editor/run_instances_dialog.h"
#include "main/main.h"
#include "servers/display_server.h"
@@ -46,63 +47,10 @@ String EditorRun::get_running_scene() const {
return running_scene;
}
-Error EditorRun::run(const String &p_scene, const String &p_write_movie) {
- List args;
-
- for (const String &a : Main::get_forwardable_cli_arguments(Main::CLI_SCOPE_PROJECT)) {
- args.push_back(a);
- }
-
- String resource_path = ProjectSettings::get_singleton()->get_resource_path();
- if (!resource_path.is_empty()) {
- args.push_back("--path");
- args.push_back(resource_path.replace(" ", "%20"));
- }
-
- const String debug_uri = EditorDebuggerNode::get_singleton()->get_server_uri();
- if (debug_uri.size()) {
- args.push_back("--remote-debug");
- args.push_back(debug_uri);
- }
-
- args.push_back("--editor-pid");
- args.push_back(itos(OS::get_singleton()->get_process_id()));
-
- bool debug_collisions = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_collisions", false);
- bool debug_paths = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_paths", false);
- bool debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false);
- bool debug_avoidance = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_avoidance", false);
- bool debug_canvas_redraw = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_canvas_redraw", false);
-
- if (debug_collisions) {
- args.push_back("--debug-collisions");
- }
-
- if (debug_paths) {
- args.push_back("--debug-paths");
- }
-
- if (debug_navigation) {
- args.push_back("--debug-navigation");
- }
-
- if (debug_avoidance) {
- args.push_back("--debug-avoidance");
- }
-
- if (debug_canvas_redraw) {
- args.push_back("--debug-canvas-item-redraw");
- }
-
- if (p_write_movie != "") {
- args.push_back("--write-movie");
- args.push_back(p_write_movie);
- args.push_back("--fixed-fps");
- args.push_back(itos(GLOBAL_GET("editor/movie_writer/fps")));
- if (bool(GLOBAL_GET("editor/movie_writer/disable_vsync"))) {
- args.push_back("--disable-vsync");
- }
- }
+void EditorRun::get_default_instance_setting(DisplayServer::WindowMode &r_window_mode, Rect2i &r_screen_rect) {
+ // Default = Windowed.
+ r_window_mode = DisplayServer::WINDOW_MODE_WINDOWED;
+ r_screen_rect = Rect2i();
int screen = EDITOR_GET("run/window_placement/screen");
if (screen == -5) {
@@ -160,44 +108,112 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) {
switch (window_placement) {
case 0: { // top left
- args.push_back("--position");
- args.push_back(itos(screen_rect.position.x) + "," + itos(screen_rect.position.y));
+ r_screen_rect = Rect2i(screen_rect.position, window_size);
} break;
case 1: { // centered
Vector2 pos = (screen_rect.position) + ((screen_rect.size - window_size) / 2).floor();
- args.push_back("--position");
- args.push_back(itos(pos.x) + "," + itos(pos.y));
+ r_screen_rect = Rect2i(pos, window_size);
} break;
case 2: { // custom pos
Vector2 pos = EDITOR_GET("run/window_placement/rect_custom_position");
- pos += screen_rect.position;
- args.push_back("--position");
- args.push_back(itos(pos.x) + "," + itos(pos.y));
+ r_screen_rect = Rect2i(pos, window_size);
} break;
case 3: { // force maximized
+ r_window_mode = DisplayServer::WINDOW_MODE_MAXIMIZED;
Vector2 pos = screen_rect.position + screen_rect.size / 2;
- args.push_back("--position");
- args.push_back(itos(pos.x) + "," + itos(pos.y));
- args.push_back("--maximized");
+ r_screen_rect = Rect2i(pos, window_size);
} break;
case 4: { // force fullscreen
+ r_window_mode = DisplayServer::WINDOW_MODE_FULLSCREEN;
Vector2 pos = screen_rect.position + screen_rect.size / 2;
- args.push_back("--position");
- args.push_back(itos(pos.x) + "," + itos(pos.y));
- args.push_back("--fullscreen");
+ r_screen_rect = Rect2i(pos, window_size);
} break;
}
} else {
// Unable to get screen info, skip setting position.
switch (window_placement) {
case 3: { // force maximized
- args.push_back("--maximized");
+ r_window_mode = DisplayServer::WINDOW_MODE_MAXIMIZED;
} break;
case 4: { // force fullscreen
- args.push_back("--fullscreen");
+ r_window_mode = DisplayServer::WINDOW_MODE_FULLSCREEN;
} break;
}
}
+}
+
+Error EditorRun::run(const String &p_scene, const String &p_write_movie) {
+ List args;
+
+ for (const String &a : Main::get_forwardable_cli_arguments(Main::CLI_SCOPE_PROJECT)) {
+ args.push_back(a);
+ }
+
+ String resource_path = ProjectSettings::get_singleton()->get_resource_path();
+ if (!resource_path.is_empty()) {
+ args.push_back("--path");
+ args.push_back(resource_path.replace(" ", "%20"));
+ }
+
+ const String debug_uri = EditorDebuggerNode::get_singleton()->get_server_uri();
+ if (debug_uri.size()) {
+ args.push_back("--remote-debug");
+ args.push_back(debug_uri);
+ }
+
+ args.push_back("--editor-pid");
+ args.push_back(itos(OS::get_singleton()->get_process_id()));
+
+ bool debug_collisions = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_collisions", false);
+ bool debug_paths = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_paths", false);
+ bool debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false);
+ bool debug_avoidance = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_avoidance", false);
+ bool debug_canvas_redraw = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_canvas_redraw", false);
+
+ if (debug_collisions) {
+ args.push_back("--debug-collisions");
+ }
+
+ if (debug_paths) {
+ args.push_back("--debug-paths");
+ }
+
+ if (debug_navigation) {
+ args.push_back("--debug-navigation");
+ }
+
+ if (debug_avoidance) {
+ args.push_back("--debug-avoidance");
+ }
+
+ if (debug_canvas_redraw) {
+ args.push_back("--debug-canvas-item-redraw");
+ }
+
+ if (p_write_movie != "") {
+ args.push_back("--write-movie");
+ args.push_back(p_write_movie);
+ args.push_back("--fixed-fps");
+ args.push_back(itos(GLOBAL_GET("editor/movie_writer/fps")));
+ if (bool(GLOBAL_GET("editor/movie_writer/disable_vsync"))) {
+ args.push_back("--disable-vsync");
+ }
+ }
+
+ DisplayServer::WindowMode window_mode;
+ Rect2i screen_rect;
+ get_default_instance_setting(window_mode, screen_rect);
+
+ if (window_mode == DisplayServer::WINDOW_MODE_FULLSCREEN) {
+ args.push_back("--fullscreen");
+ } else if (window_mode == DisplayServer::WINDOW_MODE_MAXIMIZED) {
+ args.push_back("--maximized");
+ }
+
+ if (screen_rect != Rect2i()) {
+ args.push_back("--position");
+ args.push_back(itos(screen_rect.position.x) + "," + itos(screen_rect.position.y));
+ }
List breakpoints;
EditorNode::get_editor_data().get_editor_breakpoints(&breakpoints);
@@ -234,6 +250,9 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) {
List instance_args(args);
RunInstancesDialog::get_singleton()->get_argument_list_for_instance(i, instance_args);
RunInstancesDialog::get_singleton()->apply_custom_features(i);
+ if (GameEditor::get_singleton()) {
+ GameEditor::get_singleton()->get_argument_list_for_instance(i, instance_args);
+ }
if (OS::get_singleton()->is_stdout_verbose()) {
print_line(vformat("Running: %s", exec));
@@ -286,6 +305,13 @@ void EditorRun::stop() {
running_scene = "";
}
+OS::ProcessID EditorRun::get_current_process() const {
+ if (pids.is_empty()) {
+ return 0;
+ }
+ return pids.get(0);
+}
+
EditorRun::EditorRun() {
status = STATUS_STOP;
running_scene = "";
diff --git a/editor/editor_run.h b/editor/editor_run.h
index bd6770ae3dbb..f43f4c15f4cc 100644
--- a/editor/editor_run.h
+++ b/editor/editor_run.h
@@ -32,6 +32,7 @@
#define EDITOR_RUN_H
#include "core/os/os.h"
+#include "servers/display_server.h"
class EditorRun {
public:
@@ -58,6 +59,8 @@ class EditorRun {
void stop_child_process(OS::ProcessID p_pid);
bool has_child_process(OS::ProcessID p_pid) const;
int get_child_process_count() const { return pids.size(); }
+ OS::ProcessID get_current_process() const;
+ void get_default_instance_setting(DisplayServer::WindowMode &r_window_mode, Rect2i &r_screen_rect);
EditorRun();
};
diff --git a/editor/gui/editor_run_bar.cpp b/editor/gui/editor_run_bar.cpp
index 9050ee0cd4b9..e3be474bcc88 100644
--- a/editor/gui/editor_run_bar.cpp
+++ b/editor/gui/editor_run_bar.cpp
@@ -293,6 +293,34 @@ void EditorRunBar::play_custom_scene(const String &p_custom) {
current_mode = RunMode::RUN_CUSTOM;
_run_scene(p_custom);
}
+void EditorRunBar::restart() {
+ if (!is_playing()) {
+ return;
+ }
+
+ RunMode last_current_mode = current_mode;
+ String last_run_custom_filename = run_custom_filename;
+ String last_run_current_filename = run_current_filename;
+
+ stop_playing();
+
+ switch (last_current_mode) {
+ case RunMode::RUN_MAIN: {
+ _run_scene();
+ } break;
+ case RunMode::RUN_CUSTOM: {
+ play_custom_scene(last_run_custom_filename);
+ } break;
+ case RunMode::RUN_CURRENT: {
+ _run_scene(last_run_current_filename);
+ } break;
+ case RunMode::STOPPED: {
+ // Nothing to do.
+ } break;
+ }
+
+ current_mode = last_current_mode;
+}
void EditorRunBar::stop_playing() {
if (editor_run.get_status() == EditorRun::STATUS_STOP) {
@@ -345,6 +373,10 @@ void EditorRunBar::stop_child_process(OS::ProcessID p_pid) {
}
}
+OS::ProcessID EditorRunBar::get_current_process() const {
+ return editor_run.get_current_process();
+}
+
void EditorRunBar::set_movie_maker_enabled(bool p_enabled) {
write_movie_button->set_pressed(p_enabled);
}
@@ -353,6 +385,10 @@ bool EditorRunBar::is_movie_maker_enabled() const {
return write_movie_button->is_pressed();
}
+void EditorRunBar::get_default_instance_setting(DisplayServer::WindowMode &r_window_mode, Rect2i &r_screen_rect) {
+ editor_run.get_default_instance_setting(r_window_mode, r_screen_rect);
+}
+
HBoxContainer *EditorRunBar::get_buttons_container() {
return main_hbox;
}
diff --git a/editor/gui/editor_run_bar.h b/editor/gui/editor_run_bar.h
index 1cb999612a0c..aa8d48a2a7ff 100644
--- a/editor/gui/editor_run_bar.h
+++ b/editor/gui/editor_run_bar.h
@@ -96,6 +96,7 @@ class EditorRunBar : public MarginContainer {
void play_main_scene(bool p_from_native = false);
void play_current_scene(bool p_reload = false);
void play_custom_scene(const String &p_custom);
+ void restart();
void stop_playing();
bool is_playing() const;
@@ -105,10 +106,13 @@ class EditorRunBar : public MarginContainer {
OS::ProcessID has_child_process(OS::ProcessID p_pid) const;
void stop_child_process(OS::ProcessID p_pid);
+ OS::ProcessID get_current_process() const;
void set_movie_maker_enabled(bool p_enabled);
bool is_movie_maker_enabled() const;
+ void get_default_instance_setting(DisplayServer::WindowMode &r_window_mode, Rect2i &r_screen_rect);
+
Button *get_pause_button() { return pause_button; }
HBoxContainer *get_buttons_container();
diff --git a/editor/icons/AutoFocus.svg b/editor/icons/AutoFocus.svg
new file mode 100644
index 000000000000..e7673dc88fb3
--- /dev/null
+++ b/editor/icons/AutoFocus.svg
@@ -0,0 +1 @@
+
diff --git a/editor/icons/EmbeddedProcess.svg b/editor/icons/EmbeddedProcess.svg
new file mode 100644
index 000000000000..5c8a1b3d4742
--- /dev/null
+++ b/editor/icons/EmbeddedProcess.svg
@@ -0,0 +1 @@
+
diff --git a/editor/icons/KeepAspect.svg b/editor/icons/KeepAspect.svg
new file mode 100644
index 000000000000..884eefc576fa
--- /dev/null
+++ b/editor/icons/KeepAspect.svg
@@ -0,0 +1 @@
+
diff --git a/editor/plugins/game_editor_plugin.cpp b/editor/plugins/game_editor_plugin.cpp
index 062d572714f4..406221784ae6 100644
--- a/editor/plugins/game_editor_plugin.cpp
+++ b/editor/plugins/game_editor_plugin.cpp
@@ -30,14 +30,15 @@
#include "game_editor_plugin.h"
+#include "core/config/project_settings.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_main_screen.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "editor/gui/editor_run_bar.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/button.h"
#include "scene/gui/panel.h"
-#include "scene/gui/separator.h"
void GameEditorDebugger::_session_started(Ref p_session) {
p_session->send_message("scene:runtime_node_select_setup", Array());
@@ -118,6 +119,12 @@ void GameEditorDebugger::_bind_methods() {
///////
+GameEditor *GameEditor::singleton = nullptr;
+
+GameEditor *GameEditor::get_singleton() {
+ return singleton;
+}
+
void GameEditor::_sessions_changed() {
// The debugger session's `session_started/stopped` signal can be unreliable, so count it manually.
active_sessions = 0;
@@ -131,6 +138,35 @@ void GameEditor::_sessions_changed() {
_update_debugger_buttons();
}
+void GameEditor::_play_pressed() {
+ OS::ProcessID current_process_id = EditorRunBar::get_singleton()->get_current_process();
+ if (current_process_id == 0) {
+ return;
+ }
+
+ if (embedded_button->is_pressed()) {
+ embedded_process->embed_process(current_process_id);
+ _update_ui();
+
+ if (auto_focus_button->is_pressed()) {
+ EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_GAME);
+ }
+ }
+}
+
+void GameEditor::_stop_pressed() {
+ embedded_process->reset();
+ _update_ui();
+}
+
+void GameEditor::_embedding_completed() {
+ _update_ui();
+}
+
+void GameEditor::_embedding_failed() {
+ state_label->set_text(TTR("Connection impossible to the game process."));
+}
+
void GameEditor::_update_debugger_buttons() {
bool empty = active_sessions == 0;
suspend_button->set_disabled(empty);
@@ -167,6 +203,40 @@ void GameEditor::_select_mode_pressed(int p_option) {
debugger->set_select_mode(mode);
}
+void GameEditor::_embedded_button_pressed() {
+ EditorSettings::get_singleton()->set_project_metadata("game_editor", "embedded", embedded_button->is_pressed());
+
+ if (EditorRunBar::get_singleton()->is_playing()) {
+ EditorRunBar::get_singleton()->restart();
+ }
+
+ _update_ui();
+}
+
+void GameEditor::_auto_focus_button_pressed() {
+ EditorSettings::get_singleton()->set_project_metadata("game_editor", "auto_focus", auto_focus_button->is_pressed());
+}
+
+void GameEditor::_keep_aspect_button_pressed() {
+ EditorSettings::get_singleton()->set_project_metadata("game_editor", "keep_aspect", keep_aspect_button->is_pressed());
+}
+
+void GameEditor::_update_ui() {
+ if (!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_WINDOW_EMBEDDING)) {
+ state_label->set_text(TTR("Game embedding not available on your OS."));
+ } else if (embedded_process->is_embedding_completed()) {
+ state_label->set_text("");
+ } else if (embedded_process->is_embedding_in_progress()) {
+ state_label->set_text(TTR("Game starting..."));
+ } else if (EditorRunBar::get_singleton()->is_playing()) {
+ state_label->set_text(TTR("Game running not embedded."));
+ } else if (embedded_button->is_pressed()) {
+ state_label->set_text(TTR("Press play to start the game."));
+ } else {
+ state_label->set_text(TTR("Embedding is disabled."));
+ }
+}
+
void GameEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
@@ -180,12 +250,77 @@ void GameEditor::_notification(int p_what) {
select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_icon(get_editor_theme_icon(SNAME("ToolSelect")));
select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_icon(get_editor_theme_icon(SNAME("ListSelect")));
+ embedded_button->set_icon(get_editor_theme_icon(SNAME("EmbeddedProcess")));
+ auto_focus_button->set_icon(get_editor_theme_icon(SNAME("AutoFocus")));
+ keep_aspect_button->set_icon(get_editor_theme_icon(SNAME("KeepAspect")));
+
panel->set_theme_type_variation("GamePanel");
} break;
+
+ case NOTIFICATION_READY: {
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_WINDOW_EMBEDDING)) {
+ // Embedding available.
+ embedded_button->set_pressed(EditorSettings::get_singleton()->get_project_metadata("game_editor", "embedded", true));
+ auto_focus_button->set_pressed(EditorSettings::get_singleton()->get_project_metadata("game_editor", "auto_focus", true));
+ keep_aspect_button->set_pressed(EditorSettings::get_singleton()->get_project_metadata("game_editor", "keep_aspect", true));
+
+ EditorRunBar::get_singleton()->connect("play_pressed", callable_mp(this, &GameEditor::_play_pressed));
+ EditorRunBar::get_singleton()->connect("stop_pressed", callable_mp(this, &GameEditor::_stop_pressed));
+ } else {
+ // Embedding not available.
+ embedding_separator->hide();
+ embedded_button->hide();
+ auto_focus_button->hide();
+ keep_aspect_button->hide();
+ keep_aspect_button->hide();
+ }
+
+ _update_ui();
+ } break;
}
}
+void GameEditor::get_argument_list_for_instance(int p_idx, List &r_list) {
+ if (p_idx != 0 || !embedded_button->is_pressed() || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_WINDOW_EMBEDDING)) {
+ return;
+ }
+
+ // Remove duplicates/unwanted parameters.
+ List::Element *position_item = r_list.find("--position");
+ if (position_item) {
+ r_list.erase(position_item->next());
+ r_list.erase(position_item);
+ }
+ List::Element *resolution_item = r_list.find("--resolution");
+ if (resolution_item) {
+ r_list.erase(resolution_item->next());
+ r_list.erase(resolution_item);
+ }
+ List::Element *screen_item = r_list.find("--screen");
+ if (screen_item) {
+ r_list.erase(screen_item->next());
+ r_list.erase(screen_item);
+ }
+ r_list.erase("-f");
+ r_list.erase("--fullscreen");
+ r_list.erase("-m");
+ r_list.erase("--maximized");
+ r_list.erase("-t");
+ r_list.erase("--always-on-top");
+
+ // Add editor window native id so the started game can directly set it's parent to it.
+ r_list.push_back("--wid");
+ r_list.push_back(itos(DisplayServer::get_singleton()->window_get_native_handle(DisplayServer::WINDOW_HANDLE, get_window()->get_window_id())));
+
+ Rect2i rect = embedded_process->get_screen_rect();
+ r_list.push_back("--position");
+ r_list.push_back(itos(rect.position.x) + "," + itos(rect.position.y));
+ r_list.push_back("--resolution");
+ r_list.push_back(itos(rect.size.x) + "x" + itos(rect.size.y));
+}
+
GameEditor::GameEditor(Ref p_debugger) {
+ singleton = this;
debugger = p_debugger;
// Add some margin to the sides for better aesthetics.
@@ -252,14 +387,55 @@ GameEditor::GameEditor(Ref p_debugger) {
select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->connect(SceneStringName(pressed), callable_mp(this, &GameEditor::_select_mode_pressed).bind(RuntimeNodeSelect::SELECT_MODE_LIST));
select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_tooltip_text(TTR("Show list of selectable nodes at position clicked."));
+ embedding_separator = memnew(VSeparator);
+ main_menu_hbox->add_child(embedding_separator);
+
+ embedded_button = memnew(Button);
+ main_menu_hbox->add_child(embedded_button);
+ embedded_button->set_toggle_mode(true);
+ embedded_button->set_theme_type_variation("FlatButton");
+ embedded_button->set_tooltip_text(TTR("Activate the game embedding mode."));
+ embedded_button->connect(SceneStringName(pressed), callable_mp(this, &GameEditor::_embedded_button_pressed));
+
+ auto_focus_button = memnew(Button);
+ main_menu_hbox->add_child(auto_focus_button);
+ auto_focus_button->set_toggle_mode(true);
+ auto_focus_button->set_theme_type_variation("FlatButton");
+ auto_focus_button->set_tooltip_text(TTR("Focus the game editor on project run."));
+ auto_focus_button->connect(SceneStringName(pressed), callable_mp(this, &GameEditor::_auto_focus_button_pressed));
+
+ keep_aspect_button = memnew(Button);
+ main_menu_hbox->add_child(keep_aspect_button);
+ keep_aspect_button->set_toggle_mode(true);
+ keep_aspect_button->set_theme_type_variation("FlatButton");
+ keep_aspect_button->set_tooltip_text(TTR("Keep aspect ratio of the embedded game."));
+ keep_aspect_button->connect(SceneStringName(pressed), callable_mp(this, &GameEditor::_keep_aspect_button_pressed));
+
panel = memnew(Panel);
add_child(panel);
panel->set_v_size_flags(SIZE_EXPAND_FILL);
+ embedded_process = memnew(EmbeddedProcess);
+ panel->add_child(embedded_process);
+ embedded_process->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
+ embedded_process->connect(SNAME("embedding_failed"), callable_mp(this, &GameEditor::_embedding_failed));
+ embedded_process->connect(SNAME("embedding_completed"), callable_mp(this, &GameEditor::_embedding_completed));
+
+ state_label = memnew(Label());
+ panel->add_child(state_label);
+ state_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ state_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
+ state_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD);
+ state_label->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
+
p_debugger->connect("session_started", callable_mp(this, &GameEditor::_sessions_changed));
p_debugger->connect("session_stopped", callable_mp(this, &GameEditor::_sessions_changed));
}
+GameEditor::~GameEditor() {
+ singleton = nullptr;
+}
+
///////
void GameEditorPlugin::make_visible(bool p_visible) {
diff --git a/editor/plugins/game_editor_plugin.h b/editor/plugins/game_editor_plugin.h
index 9f600902765e..b8c31fb7ab07 100644
--- a/editor/plugins/game_editor_plugin.h
+++ b/editor/plugins/game_editor_plugin.h
@@ -35,6 +35,9 @@
#include "editor/plugins/editor_plugin.h"
#include "scene/debugger/scene_debugger.h"
#include "scene/gui/box_container.h"
+#include "scene/gui/embedded_process.h"
+#include "scene/gui/label.h"
+#include "scene/gui/separator.h"
class GameEditorDebugger : public EditorDebuggerPlugin {
GDCLASS(GameEditorDebugger, EditorDebuggerPlugin);
@@ -66,6 +69,7 @@ class GameEditor : public VBoxContainer {
GDCLASS(GameEditor, VBoxContainer);
private:
+ static GameEditor *singleton;
Ref debugger;
int active_sessions = 0;
@@ -75,8 +79,14 @@ class GameEditor : public VBoxContainer {
Button *node_type_button[RuntimeNodeSelect::NODE_TYPE_MAX];
Button *select_mode_button[RuntimeNodeSelect::SELECT_MODE_MAX];
+ VSeparator *embedding_separator;
+ Button *embedded_button;
+ Button *auto_focus_button;
+ Button *keep_aspect_button;
Panel *panel = nullptr;
+ EmbeddedProcess *embedded_process = nullptr;
+ Label *state_label = nullptr;
void _sessions_changed();
@@ -86,12 +96,26 @@ class GameEditor : public VBoxContainer {
void _node_type_pressed(int p_option);
void _select_mode_pressed(int p_option);
+ void _embedded_button_pressed();
+ void _auto_focus_button_pressed();
+ void _keep_aspect_button_pressed();
+
+ void _play_pressed();
+ void _stop_pressed();
+ void _embedding_completed();
+ void _embedding_failed();
+ void _update_ui();
protected:
void _notification(int p_what);
public:
+ static GameEditor *get_singleton();
+
+ void get_argument_list_for_instance(int p_idx, List &r_list);
+
GameEditor(Ref p_debugger);
+ ~GameEditor();
};
class GameEditorPlugin : public EditorPlugin {
diff --git a/main/main.cpp b/main/main.cpp
index 439cd385c028..08b12cb4edef 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -224,6 +224,7 @@ static bool init_always_on_top = false;
static bool init_use_custom_pos = false;
static bool init_use_custom_screen = false;
static Vector2 init_custom_pos;
+static int64_t init_embed_parent_window_id = 0;
// Debug
@@ -604,6 +605,7 @@ void Main::print_help(const char *p_binary) {
print_help_option("--screen ", "Request window screen.\n");
print_help_option("--single-window", "Use a single window (no separate subwindows).\n");
print_help_option("--xr-mode ", "Select XR (Extended Reality) mode [\"default\", \"off\", \"on\"].\n");
+ print_help_option("--wid ", "Request window be parented.\n");
print_help_title("Debug options");
print_help_option("-d, --debug", "Debug (local stdout debugger).\n");
@@ -1760,6 +1762,20 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
goto error;
}
#endif // TOOLS_ENABLED
+ } else if (arg == "--wid") {
+ if (N) {
+ init_embed_parent_window_id = N->get().to_int();
+ if (init_embed_parent_window_id == 0) {
+ OS::get_singleton()->print(" argument for --wid must be different then 0.\n");
+ goto error;
+ }
+
+ N = N->next();
+ } else {
+ OS::get_singleton()->print("Missing argumentfor --wid .\n");
+ goto error;
+ }
+
} else if (arg == "--" || arg == "++") {
adding_user_args = true;
} else {
@@ -2910,9 +2926,16 @@ Error Main::setup2(bool p_show_boot_logo) {
context = DisplayServer::CONTEXT_ENGINE;
}
+ if (init_embed_parent_window_id) {
+ // Reset flags and other settings to be sure it's borderless and windowed. The position and size should have been initalized correctly
+ // from --position and --resolution parameters.
+ window_mode = DisplayServer::WINDOW_MODE_WINDOWED;
+ window_flags = DisplayServer::WINDOW_FLAG_BORDERLESS_BIT;
+ }
+
// rendering_driver now held in static global String in main and initialized in setup()
Error err;
- display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, init_screen, context, err);
+ display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, init_screen, context, init_embed_parent_window_id, err);
if (err != OK || display_server == nullptr) {
String last_name = DisplayServer::get_create_function_name(display_driver_idx);
@@ -2926,7 +2949,7 @@ Error Main::setup2(bool p_show_boot_logo) {
String name = DisplayServer::get_create_function_name(i);
WARN_PRINT(vformat("Display driver %s failed, falling back to %s.", last_name, name));
- display_server = DisplayServer::create(i, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, init_screen, context, err);
+ display_server = DisplayServer::create(i, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, init_screen, context, init_embed_parent_window_id, err);
if (err == OK && display_server != nullptr) {
break;
}
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index c1053215c694..d6ba2086880c 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -514,8 +514,8 @@ Vector DisplayServerAndroid::get_rendering_drivers_func() {
return drivers;
}
-DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error));
+DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, p_parent_window, r_error));
if (r_error != OK) {
if (p_rendering_driver == "vulkan") {
OS::get_singleton()->alert(
@@ -582,7 +582,7 @@ void DisplayServerAndroid::notify_surface_changed(int p_width, int p_height) {
}
}
-DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
+DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
rendering_driver = p_rendering_driver;
keep_screen_on = GLOBAL_GET("display/window/energy_saving/keep_screen_on");
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
index 90bda18cfa2c..32735bb57c2e 100644
--- a/platform/android/display_server_android.h
+++ b/platform/android/display_server_android.h
@@ -211,7 +211,7 @@ class DisplayServerAndroid : public DisplayServer {
virtual void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() const override;
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
static Vector get_rendering_drivers_func();
static void register_android_driver();
@@ -228,7 +228,7 @@ class DisplayServerAndroid : public DisplayServer {
virtual void set_native_icon(const String &p_filename) override;
virtual void set_icon(const Ref &p_icon) override;
- DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
+ DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
~DisplayServerAndroid();
};
diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h
index bbb758074d9b..e50457c1fb9c 100644
--- a/platform/ios/display_server_ios.h
+++ b/platform/ios/display_server_ios.h
@@ -88,7 +88,7 @@ class DisplayServerIOS : public DisplayServer {
void perform_event(const Ref &p_event);
- DisplayServerIOS(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
+ DisplayServerIOS(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
~DisplayServerIOS();
public:
@@ -97,7 +97,7 @@ class DisplayServerIOS : public DisplayServer {
static DisplayServerIOS *get_singleton();
static void register_ios_driver();
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
static Vector get_rendering_drivers_func();
// MARK: - Events
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm
index e51d43bd8999..c643dca0597e 100644
--- a/platform/ios/display_server_ios.mm
+++ b/platform/ios/display_server_ios.mm
@@ -51,7 +51,7 @@
return (DisplayServerIOS *)DisplayServer::get_singleton();
}
-DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
+DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
KeyMappingIOS::initialize();
rendering_driver = p_rendering_driver;
@@ -189,8 +189,8 @@
#endif
}
-DisplayServer *DisplayServerIOS::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
- return memnew(DisplayServerIOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error));
+DisplayServer *DisplayServerIOS::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
+ return memnew(DisplayServerIOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, p_parent_window, r_error);
}
Vector DisplayServerIOS::get_rendering_drivers_func() {
diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp
index 3fbbc263a01d..26a50eb38b78 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.cpp
+++ b/platform/linuxbsd/wayland/display_server_wayland.cpp
@@ -1286,8 +1286,8 @@ Vector DisplayServerWayland::get_rendering_drivers_func() {
return drivers;
}
-DisplayServer *DisplayServerWayland::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerWayland(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, p_context, r_error));
+DisplayServer *DisplayServerWayland::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerWayland(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, p_context, p_parent_window, r_error));
if (r_error != OK) {
ERR_PRINT("Can't create the Wayland display server.");
memdelete(ds);
@@ -1297,7 +1297,7 @@ DisplayServer *DisplayServerWayland::create_func(const String &p_rendering_drive
return ds;
}
-DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Context p_context, Error &r_error) {
+DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Context p_context, int64_t p_parent_window, Error &r_error) {
#ifdef GLES3_ENABLED
#ifdef SOWRAP_ENABLED
#ifdef DEBUG_ENABLED
diff --git a/platform/linuxbsd/wayland/display_server_wayland.h b/platform/linuxbsd/wayland/display_server_wayland.h
index e61153366421..bd5143f279e1 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.h
+++ b/platform/linuxbsd/wayland/display_server_wayland.h
@@ -290,12 +290,12 @@ class DisplayServerWayland : public DisplayServer {
virtual bool is_window_transparency_available() const override;
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
static Vector get_rendering_drivers_func();
static void register_wayland_driver();
- DisplayServerWayland(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Context p_context, Error &r_error);
+ DisplayServerWayland(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Context p_context, int64_t p_parent_window, Error &r_error);
~DisplayServerWayland();
};
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 7949f80f2486..10ac5584dafd 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -139,6 +139,7 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
#endif
case FEATURE_CLIPBOARD_PRIMARY:
case FEATURE_TEXT_TO_SPEECH:
+ case FEATURE_WINDOW_EMBEDDING:
return true;
case FEATURE_SCREEN_CAPTURE:
return !xwayland;
@@ -1738,7 +1739,7 @@ Vector DisplayServerX11::get_window_list() const {
DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, bool p_exclusive, WindowID p_transient_parent) {
_THREAD_SAFE_METHOD_
- WindowID id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect);
+ WindowID id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect, 0);
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
if (p_flags & (1 << i)) {
window_set_flag(WindowFlags(i), true, id);
@@ -2328,6 +2329,10 @@ void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) {
OS::get_singleton()->delay_usec(10'000);
}
+ if (wd.is_external_window) {
+ return;
+ }
+
// Keep rendering context window size in sync
#if defined(RD_ENABLED)
if (rendering_context) {
@@ -3339,6 +3344,7 @@ Key DisplayServerX11::keyboard_get_label_from_physical(Key p_keycode) const {
}
return (Key)(key | modifiers);
}
+
DisplayServerX11::Property DisplayServerX11::_read_property(Display *p_display, Window p_window, Atom p_property) {
Atom actual_type = None;
int actual_format = 0;
@@ -5416,6 +5422,218 @@ DisplayServer::VSyncMode DisplayServerX11::window_get_vsync_mode(WindowID p_wind
return DisplayServer::VSYNC_ENABLED;
}
+pid_t get_window_pid(Display *p_display, Window p_window) {
+ Atom atom = XInternAtom(p_display, "_NET_WM_PID", False);
+ Atom actualType;
+ int actualFormat;
+ unsigned long nItems, bytesAfter;
+ unsigned char *prop = nullptr;
+ if (XGetWindowProperty(p_display, p_window, atom, 0, sizeof(pid_t), False, AnyPropertyType,
+ &actualType, &actualFormat, &nItems, &bytesAfter, &prop) == Success) {
+ if (nItems > 0) {
+ pid_t pid = *(pid_t *)prop;
+ XFree(prop);
+ return pid;
+ }
+ }
+
+ return 0; // PID not found.
+}
+
+Window find_window_from_process_id_internal(Display *p_display, pid_t p_process_id, Window p_window) {
+ Window dummy;
+ Window *children;
+ unsigned int num_children;
+
+ if (!XQueryTree(p_display, p_window, &dummy, &dummy, &children, &num_children)) {
+ return 0;
+ }
+
+ for (unsigned int i = 0; i < num_children; i++) {
+ pid_t pid = get_window_pid(p_display, children[i]);
+ if (pid == p_process_id) {
+ return children[i];
+ }
+ }
+
+ // Then check children of children.
+ for (unsigned int i = 0; i < num_children; i++) {
+ Window wnd = find_window_from_process_id_internal(p_display, p_process_id, children[i]);
+ if (wnd != 0) {
+ return wnd;
+ }
+ }
+
+ if (children) {
+ XFree(children);
+ }
+
+ return 0;
+}
+
+Window find_window_from_process_id(Display *p_display, pid_t p_process_id) {
+ // Handle bad window errors silently because while looping
+ // windows can be destroyed, resulting in BadWindow errors.
+ int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&bad_window_error_handler);
+
+ const int screencount = XScreenCount(p_display);
+ Window process_window = 0;
+
+ for (int screen_index = 0; screen_index < screencount; screen_index++) {
+ Window root = RootWindow(p_display, screen_index);
+
+ Window wnd = find_window_from_process_id_internal(p_display, p_process_id, root);
+
+ if (wnd != 0) {
+ process_window = wnd;
+ break;
+ }
+ }
+
+ // Restore default error handler.
+ XSetErrorHandler(oldHandler);
+
+ return process_window;
+}
+
+Point2i DisplayServerX11::_get_window_position(Window p_window) const {
+ int x = 0, y = 0;
+ Window child;
+ XTranslateCoordinates(x11_display, p_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child);
+ return Point2i(x, y);
+}
+
+Rect2i DisplayServerX11::_get_window_rect(Window p_window) const {
+ XWindowAttributes xwa;
+ XGetWindowAttributes(x11_display, p_window, &xwa);
+ return Rect2i(xwa.x, xwa.y, xwa.width, xwa.height);
+}
+
+void DisplayServerX11::_set_window_taskbar_pager_enabled(Window p_window, bool p_enabled) {
+ Atom wmState = XInternAtom(x11_display, "_NET_WM_STATE", False);
+ Atom skipTaskbar = XInternAtom(x11_display, "_NET_WM_STATE_SKIP_TASKBAR", False);
+ Atom skipPager = XInternAtom(x11_display, "_NET_WM_STATE_SKIP_PAGER", False);
+
+ XClientMessageEvent xev;
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.window = p_window;
+ xev.message_type = wmState;
+ xev.format = 32;
+ xev.data.l[0] = p_enabled ? _NET_WM_STATE_REMOVE : _NET_WM_STATE_ADD; // When enabled, we must remove the skip.
+ xev.data.l[1] = skipTaskbar;
+ xev.data.l[2] = skipPager;
+ xev.data.l[3] = 0;
+ xev.data.l[4] = 0;
+
+ // Send the client message to the root window
+ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xev);
+}
+
+void DisplayServerX11::_set_external_window_settings(Window p_window, Window p_parent_transient, WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+ XSetTransientForHint(x11_display, p_window, p_parent_transient);
+
+ _set_window_taskbar_pager_enabled(p_window, !p_parent_transient);
+
+ WindowID window_id_process = window_id_counter++;
+ WindowData &wd_process = windows[window_id_process];
+ wd_process.x11_window = p_window;
+ wd_process.is_external_window = true;
+ wd_process.position = p_rect.position;
+ wd_process.size = p_rect.size;
+
+ // Reset flags...
+ for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
+ window_set_flag(WindowFlags(i), p_flags & (1 << i), window_id_process);
+ }
+
+ window_set_mode(p_mode, window_id_process);
+ XFlush(x11_display);
+
+ windows.erase(window_id_process);
+
+ // Same kind of hack that is also done in window_set_size to be sure the size is actually want we want.
+ // The Window Manager does not like changing the type of window and repositioning it at the same time,
+ // the first couple of times, the window position is resetted to the original.
+ for (int timeout = 0; timeout < 20; ++timeout) {
+ XSync(x11_display, False);
+
+ Rect2i rect = _get_window_rect(p_window);
+ if (rect != p_rect) {
+ XMoveResizeWindow(x11_display, p_window, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y);
+ XFlush(x11_display);
+ }
+
+ OS::get_singleton()->delay_usec(10'000);
+ }
+}
+
+Error DisplayServerX11::embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), FAILED);
+
+ const WindowData &wd = windows[p_window];
+
+ DEBUG_LOG_X11("Starting embeding %ld to window %lu \n", p_pid, wd.x11_window);
+
+ Point2i window_position = _get_window_position(wd.x11_window);
+ Rect2i desired_rect = Rect2i(p_rect.position + window_position, p_rect.size);
+
+ EmbeddedProcessData *ep = nullptr;
+ if (embedded_processes.has(p_pid)) {
+ ep = embedded_processes.get(p_pid);
+ } else {
+ // New process, trying to find the window.
+ Window process_window = find_window_from_process_id(x11_display, p_pid);
+ if (!process_window) {
+ return ERR_DOES_NOT_EXIST;
+ }
+
+ DEBUG_LOG_X11("Process %ld window found: %lu \n", p_pid, process_window);
+
+ ep = memnew(EmbeddedProcessData);
+ ep->process_window = process_window;
+ ep->visible = true;
+
+ _set_external_window_settings(process_window, wd.x11_window, WindowMode::WINDOW_MODE_WINDOWED, WINDOW_FLAG_BORDERLESS_BIT, desired_rect);
+
+ embedded_processes.insert(p_pid, ep);
+ }
+
+ // Resize and move window to match desired rect
+ Rect2i current_process_window_rect = _get_window_rect(ep->process_window);
+ if (current_process_window_rect != desired_rect) {
+ DEBUG_LOG_X11("Embedding XMoveResizeWindow process %ld, window %lu to %d, %d, %d, %d \n", p_pid, wd.x11_window, desired_rect.position.x, desired_rect.position.y, desired_rect.size.x, desired_rect.size.y);
+ XMoveResizeWindow(x11_display, ep->process_window, desired_rect.position.x, desired_rect.position.y, desired_rect.size.x, desired_rect.size.y);
+ }
+
+ if (ep->visible != p_visible) {
+ if (p_visible) {
+ XMapWindow(x11_display, ep->process_window);
+ } else {
+ XUnmapWindow(x11_display, ep->process_window);
+ }
+ ep->visible = p_visible;
+ }
+
+ return OK;
+}
+
+Error DisplayServerX11::remove_embedded_process(OS::ProcessID p_pid) {
+ _THREAD_SAFE_METHOD_
+
+ if (!embedded_processes.has(p_pid)) {
+ return ERR_DOES_NOT_EXIST;
+ }
+
+ EmbeddedProcessData ep = embedded_processes.get(p_pid);
+ embedded_processes.erase(p_pid);
+ memdelete(ep);
+
+ return OK;
+}
+
Vector DisplayServerX11::get_rendering_drivers_func() {
Vector drivers;
@@ -5430,12 +5648,12 @@ Vector DisplayServerX11::get_rendering_drivers_func() {
return drivers;
}
-DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error));
+DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, p_parent_window, r_error));
return ds;
}
-DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) {
+DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, Window p_parent_window) {
//Create window
XVisualInfo visualInfo;
@@ -5534,16 +5752,19 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
}
Rect2i win_rect = p_rect;
- if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
- Rect2i screen_rect = Rect2i(screen_get_position(rq_screen), screen_get_size(rq_screen));
+ if (!p_parent_window) {
+ // No parent.
+ if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
+ Rect2i screen_rect = Rect2i(screen_get_position(rq_screen), screen_get_size(rq_screen));
- win_rect = screen_rect;
- } else {
- Rect2i srect = screen_get_usable_rect(rq_screen);
- Point2i wpos = p_rect.position;
- wpos = wpos.clamp(srect.position, srect.position + srect.size - p_rect.size / 3);
+ win_rect = screen_rect;
+ } else {
+ Rect2i srect = screen_get_usable_rect(rq_screen);
+ Point2i wpos = p_rect.position;
+ wpos = wpos.clamp(srect.position, srect.position + srect.size - p_rect.size / 3);
- win_rect.position = wpos;
+ win_rect.position = wpos;
+ }
}
// Position and size hints are set from these values before they are updated to the actual
@@ -5555,6 +5776,12 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo.screen), win_rect.position.x, win_rect.position.y, win_rect.size.width > 0 ? win_rect.size.width : 1, win_rect.size.height > 0 ? win_rect.size.height : 1, 0, visualInfo.depth, InputOutput, visualInfo.visual, valuemask, &windowAttributes);
wd.parent = RootWindow(x11_display, visualInfo.screen);
+ DEBUG_LOG_X11("CreateWindow window=%lu, parent: %lu \n", wd.x11_window, wd.parent);
+
+ if (p_parent_window) {
+ XSetTransientForHint(x11_display, wd.x11_window, p_parent_window);
+ }
+
XSetWindowAttributes window_attributes_ime = {};
window_attributes_ime.event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask;
@@ -5843,7 +6070,7 @@ static ::XIMStyle _get_best_xim_style(const ::XIMStyle &p_style_a, const ::XIMSt
return p_style_a;
}
-DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
+DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
KeyMappingX11::initialize();
xwayland = OS::get_singleton()->get_environment("XDG_SESSION_TYPE").to_lower() == "wayland";
@@ -6297,7 +6524,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
window_position = scr_rect.position + (scr_rect.size - p_resolution) / 2;
}
- WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution));
+ WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution), p_parent_window);
if (main_window == INVALID_WINDOW_ID) {
r_error = ERR_CANT_CREATE;
return;
@@ -6500,6 +6727,9 @@ DisplayServerX11::~DisplayServerX11() {
//destroy all windows
for (KeyValue &E : windows) {
+ if (E.value.is_external_window) {
+ continue;
+ }
#if defined(RD_ENABLED)
if (rendering_device) {
rendering_device->screen_free(E.key);
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 0cbfbe51ef1f..d0ba55a768cf 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -207,6 +207,7 @@ class DisplayServerX11 : public DisplayServer {
bool is_popup = false;
bool layered_window = false;
bool mpass = false;
+ bool is_external_window = false;
Rect2i parent_safe_rect;
@@ -234,7 +235,7 @@ class DisplayServerX11 : public DisplayServer {
WindowID last_focused_window = INVALID_WINDOW_ID;
WindowID window_id_counter = MAIN_WINDOW_ID;
- WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect);
+ WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, Window p_parent_window);
String internal_clipboard;
String internal_clipboard_primary;
@@ -375,6 +376,18 @@ class DisplayServerX11 : public DisplayServer {
static Bool _predicate_clipboard_incr(Display *display, XEvent *event, XPointer arg);
static Bool _predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg);
+ struct EmbeddedProcessData {
+ Window process_window = 0;
+ bool visible = true;
+ };
+ HashMap embedded_processes;
+
+ Point2i _get_window_position(Window p_window) const;
+ Rect2i _get_window_rect(Window p_window) const;
+ void _set_external_window_settings(Window p_window, Window p_parent_transient, WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect);
+ void _set_window_taskbar_pager_enabled(Window p_window, bool p_enabled);
+ void _set_embedded_mode(Window p_window, bool p_enabled);
+
protected:
void _window_changed(XEvent *event);
@@ -510,6 +523,9 @@ class DisplayServerX11 : public DisplayServer {
virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
+ virtual Error embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible);
+ virtual Error remove_embedded_process(OS::ProcessID p_pid);
+
virtual void cursor_set_shape(CursorShape p_shape) override;
virtual CursorShape cursor_get_shape() const override;
virtual void cursor_set_custom_image(const Ref &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) override;
@@ -534,12 +550,12 @@ class DisplayServerX11 : public DisplayServer {
virtual void set_native_icon(const String &p_filename) override;
virtual void set_icon(const Ref &p_icon) override;
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
static Vector get_rendering_drivers_func();
static void register_x11_driver();
- DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
+ DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
~DisplayServerX11();
};
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 97af6d0a5a25..28641bd1317a 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -442,12 +442,12 @@ class DisplayServerMacOS : public DisplayServer {
virtual bool is_window_transparency_available() const override;
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
static Vector get_rendering_drivers_func();
static void register_macos_driver();
- DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
+ DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
~DisplayServerMacOS();
};
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index f6c1d11028db..58bdbccb6aa3 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -3276,8 +3276,8 @@
return OS::get_singleton()->is_layered_allowed();
}
-DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerMacOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error));
+DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerMacOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, p_parent_window, r_error);
if (r_error != OK) {
if (p_rendering_driver == "vulkan") {
String executable_command;
@@ -3471,7 +3471,7 @@
return closed;
}
-DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
+DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
KeyMappingMacOS::initialize();
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index 4e55cc137a32..613434f6140b 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -1025,11 +1025,11 @@ void DisplayServerWeb::_dispatch_input_event(const Ref &p_event) {
}
}
-DisplayServer *DisplayServerWeb::create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
- return memnew(DisplayServerWeb(p_rendering_driver, p_window_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error));
+DisplayServer *DisplayServerWeb::create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
+ return memnew(DisplayServerWeb(p_rendering_driver, p_window_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, p_parent_window, r_error));
}
-DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
+DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
r_error = OK; // Always succeeds for now.
tts = GLOBAL_GET("audio/general/text_to_speech");
diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h
index 352b3fe523de..eec771e2a0c6 100644
--- a/platform/web/display_server_web.h
+++ b/platform/web/display_server_web.h
@@ -148,7 +148,7 @@ class DisplayServerWeb : public DisplayServer {
void process_keys();
static Vector get_rendering_drivers_func();
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
static void _dispatch_input_event(const Ref &p_event);
@@ -278,7 +278,7 @@ class DisplayServerWeb : public DisplayServer {
virtual void swap_buffers() override;
static void register_web_driver();
- DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, Error &r_error);
+ DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
~DisplayServerWeb();
};
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index ed9d5244a37a..08b59c2a117f 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -122,6 +122,7 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const {
case FEATURE_TEXT_TO_SPEECH:
case FEATURE_SCREEN_CAPTURE:
case FEATURE_STATUS_INDICATOR:
+ case FEATURE_WINDOW_EMBEDDING:
return true;
default:
return false;
@@ -1469,7 +1470,7 @@ DisplayServer::WindowID DisplayServerWindows::get_window_at_screen_position(cons
DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, bool p_exclusive, WindowID p_transient_parent) {
_THREAD_SAFE_METHOD_
- WindowID window_id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect, p_exclusive, p_transient_parent);
+ WindowID window_id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect, p_exclusive, p_transient_parent, NULL);
ERR_FAIL_COND_V_MSG(window_id == INVALID_WINDOW_ID, INVALID_WINDOW_ID, "Failed to create sub window.");
WindowData &wd = windows[window_id];
@@ -2109,6 +2110,10 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre
r_style_ex |= WS_EX_ACCEPTFILES;
}
+void DisplayServerWindows::_get_window_style_from_mode_and_flags(bool p_main_window, WindowMode p_mode, uint32_t p_flags, DWORD &r_style, DWORD &r_style_ex) {
+ _get_window_style(p_main_window, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, false, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), r_style, r_style_ex);
+}
+
void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repaint) {
_THREAD_SAFE_METHOD_
@@ -2665,6 +2670,120 @@ void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) {
AllowSetForegroundWindow(pid);
}
+struct WindowEnumData {
+ DWORD dwProcessId;
+ HWND hWnd;
+};
+
+static BOOL CALLBACK _enum_proc_find_window_from_process_id_callback(HWND hWnd, LPARAM lParam) {
+ WindowEnumData &ed = *(WindowEnumData *)lParam;
+ DWORD dwProcessId = 0x0;
+
+ GetWindowThreadProcessId(hWnd, &dwProcessId);
+ if (ed.dwProcessId == dwProcessId) {
+ const DWORD exStyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE);
+ if ((exStyle & WS_EX_APPWINDOW) != WS_EX_APPWINDOW) {
+ return TRUE;
+ }
+
+ // Found it.
+ ed.hWnd = hWnd;
+ SetLastError(ERROR_SUCCESS);
+ return FALSE;
+ }
+ // Continue enumeration
+ return TRUE;
+}
+
+HWND DisplayServerWindows::_find_window_from_process_id(OS::ProcessID p_pid) {
+ DWORD pid = p_pid;
+ WindowEnumData ed = { pid };
+ if (!EnumWindows(_enum_proc_find_window_from_process_id_callback, (LPARAM)&ed) && (GetLastError() == ERROR_SUCCESS)) {
+ return ed.hWnd;
+ }
+ return NULL;
+}
+
+static int cpt_test = 0;
+Error DisplayServerWindows::embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), FAILED);
+
+ const WindowData &wd = windows[p_window];
+
+ EmbeddedProcessData *ep = nullptr;
+ if (embedded_processes.has(p_pid)) {
+ ep = embedded_processes.get(p_pid);
+ print_line("already in memory.");
+ } else {
+ // New process, trying to find the window.
+ HWND handle_to_embed = _find_window_from_process_id(p_pid);
+ if (!handle_to_embed) {
+ return ERR_DOES_NOT_EXIST;
+ }
+
+ ep = memnew(EmbeddedProcessData);
+ ep->window_handle = handle_to_embed;
+ if (p_visible) {
+ print_line(vformat("init not visible %d", cpt_test));
+ } else {
+ print_line(vformat("init visible %d", cpt_test));
+ }
+ ep->is_visible = false;
+ ep->visible = "INIT";
+
+ embedded_processes.insert(p_pid, ep);
+
+ // Hide the window to prevent a flicker when we change the style
+ // and reposition the window.
+ ShowWindow(ep->window_handle, SW_HIDE);
+ SetWindowLong(ep->window_handle, GWL_STYLE, WS_POPUP);
+ SetParent(ep->window_handle, wd.hWnd);
+
+ // Update styles to activate borderless mode and "standard" window.
+ SetWindowLong(ep->window_handle, GWL_STYLE, WS_POPUP);
+ DWORD exStyle = GetWindowLongPtr(ep->window_handle, GWL_EXSTYLE);
+ exStyle &= ~(WS_EX_TOPMOST | WS_EX_NOACTIVATE);
+ SetWindowLong(ep->window_handle, GWL_EXSTYLE, exStyle);
+ }
+
+ SetWindowPos(ep->window_handle, HWND_BOTTOM, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, SWP_NOZORDER | SWP_NOACTIVATE);
+ print_line(ep->visible);
+
+ if (ep->is_visible != p_visible) {
+ DWORD style = WS_POPUP;
+ if (p_visible) {
+ style |= WS_VISIBLE;
+ }
+ SetWindowLong(ep->window_handle, GWL_STYLE, style);
+ ep->is_visible = p_visible;
+ if (p_visible) {
+ ep->visible = "visible";
+ } else {
+ ep->visible = "not visible";
+ }
+ }
+
+ print_line("EXIT: " + ep->visible);
+
+ return OK;
+}
+
+Error DisplayServerWindows::remove_embedded_process(OS::ProcessID p_pid) {
+ _THREAD_SAFE_METHOD_
+
+ if (!embedded_processes.has(p_pid)) {
+ return ERR_DOES_NOT_EXIST;
+ }
+
+ EmbeddedProcessData *ep = embedded_processes.get(p_pid);
+ embedded_processes.erase(p_pid);
+ memdelete(ep);
+
+ return OK;
+}
+
static HRESULT CALLBACK win32_task_dialog_callback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData) {
if (msg == TDN_CREATED) {
// To match the input text dialog.
@@ -5525,11 +5644,11 @@ void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const
}
}
-DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, bool p_exclusive, WindowID p_transient_parent) {
+DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, bool p_exclusive, WindowID p_transient_parent, HWND p_parent_window_handle) {
DWORD dwExStyle;
DWORD dwStyle;
- _get_window_style(window_id_counter == MAIN_WINDOW_ID, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, false, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle);
+ _get_window_style_from_mode_and_flags(window_id_counter == MAIN_WINDOW_ID, p_mode, p_flags, dwStyle, dwExStyle);
RECT WindowRect;
@@ -5543,41 +5662,45 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
rq_screen = get_primary_screen(); // Requested window rect is outside any screen bounds.
}
- if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
- Rect2i screen_rect = Rect2i(screen_get_position(rq_screen), screen_get_size(rq_screen));
+ if (!p_parent_window_handle) {
+ if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
+ Rect2i screen_rect = Rect2i(screen_get_position(rq_screen), screen_get_size(rq_screen));
- WindowRect.left = screen_rect.position.x;
- WindowRect.right = screen_rect.position.x + screen_rect.size.x;
- WindowRect.top = screen_rect.position.y;
- WindowRect.bottom = screen_rect.position.y + screen_rect.size.y;
- } else {
- Rect2i srect = screen_get_usable_rect(rq_screen);
- Point2i wpos = p_rect.position;
- if (srect != Rect2i()) {
- wpos = wpos.clamp(srect.position, srect.position + srect.size - p_rect.size / 3);
- }
+ WindowRect.left = screen_rect.position.x;
+ WindowRect.right = screen_rect.position.x + screen_rect.size.x;
+ WindowRect.top = screen_rect.position.y;
+ WindowRect.bottom = screen_rect.position.y + screen_rect.size.y;
+ } else {
+ Rect2i srect = screen_get_usable_rect(rq_screen);
+ Point2i wpos = p_rect.position;
+ if (srect != Rect2i()) {
+ wpos = wpos.clamp(srect.position, srect.position + srect.size - p_rect.size / 3);
+ }
- WindowRect.left = wpos.x;
- WindowRect.right = wpos.x + p_rect.size.x;
- WindowRect.top = wpos.y;
- WindowRect.bottom = wpos.y + p_rect.size.y;
- }
+ WindowRect.left = wpos.x;
+ WindowRect.right = wpos.x + p_rect.size.x;
+ WindowRect.top = wpos.y;
+ WindowRect.bottom = wpos.y + p_rect.size.y;
+ }
- Point2i offset = _get_screens_origin();
- WindowRect.left += offset.x;
- WindowRect.right += offset.x;
- WindowRect.top += offset.y;
- WindowRect.bottom += offset.y;
+ Point2i offset = _get_screens_origin();
+ WindowRect.left += offset.x;
+ WindowRect.right += offset.x;
+ WindowRect.top += offset.y;
+ WindowRect.bottom += offset.y;
- if (p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
- AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
+ if (p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
+ AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
+ }
}
WindowID id = window_id_counter;
{
WindowData *wd_transient_parent = nullptr;
HWND owner_hwnd = nullptr;
- if (p_transient_parent != INVALID_WINDOW_ID) {
+ if (p_parent_window_handle) {
+ owner_hwnd = p_parent_window_handle;
+ } else if (p_transient_parent != INVALID_WINDOW_ID) {
if (!windows.has(p_transient_parent)) {
ERR_PRINT("Condition \"!windows.has(p_transient_parent)\" is true.");
p_transient_parent = INVALID_WINDOW_ID;
@@ -5611,6 +5734,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
windows.erase(id);
ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Windows OS window.");
}
+
if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
wd.fullscreen = true;
if (p_mode == WINDOW_MODE_FULLSCREEN) {
@@ -5953,7 +6077,7 @@ void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) {
}
}
-DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
+DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
KeyMappingWindows::initialize();
tested_drivers.clear();
@@ -6350,7 +6474,13 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
window_position = scr_rect.position + (scr_rect.size - p_resolution) / 2;
}
- WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution), false, INVALID_WINDOW_ID);
+ HWND parent_window_handle = NULL;
+ if (p_parent_window) {
+ // Parented window.
+ parent_window_handle = (HWND)p_parent_window;
+ }
+
+ WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution), false, INVALID_WINDOW_ID, parent_window_handle);
ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window.");
joypad = new JoypadWindows(&windows[MAIN_WINDOW_ID].hWnd);
@@ -6424,8 +6554,8 @@ Vector DisplayServerWindows::get_rendering_drivers_func() {
return drivers;
}
-DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error));
+DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, p_parent_window, r_error));
if (r_error != OK) {
if (tested_drivers == 0) {
OS::get_singleton()->alert("Failed to register the window class.", "Unable to initialize DisplayServer");
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 54e1c9681ddc..da76d156e066 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -532,7 +532,7 @@ class DisplayServerWindows : public DisplayServer {
uint64_t time_since_popup = 0;
Ref icon;
- WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, bool p_exclusive, WindowID p_transient_parent);
+ WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, bool p_exclusive, WindowID p_transient_parent, HWND p_parent_window_handle);
WindowID window_id_counter = MAIN_WINDOW_ID;
RBMap windows;
@@ -592,6 +592,7 @@ class DisplayServerWindows : public DisplayServer {
void _send_window_event(const WindowData &wd, WindowEvent p_event);
void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex);
+ void _get_window_style_from_mode_and_flags(bool p_main_window, WindowMode p_mode, uint32_t p_flags, DWORD &r_style, DWORD &r_style_ex);
MouseMode mouse_mode;
int restore_mouse_trails = 0;
@@ -645,6 +646,16 @@ class DisplayServerWindows : public DisplayServer {
String _get_keyboard_layout_display_name(const String &p_klid) const;
String _get_klid(HKL p_hkl) const;
+ struct EmbeddedProcessData {
+ HWND window_handle = 0;
+ bool is_visible = false;
+ String visible = "HIDDEN";
+ int cpt = 0;
+ };
+ HashMap embedded_processes;
+
+ HWND _find_window_from_process_id(OS::ProcessID p_pid);
+
public:
LRESULT WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
@@ -785,6 +796,8 @@ class DisplayServerWindows : public DisplayServer {
virtual bool get_swap_cancel_ok() override;
virtual void enable_for_stealing_focus(OS::ProcessID pid) override;
+ virtual Error embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible);
+ virtual Error remove_embedded_process(OS::ProcessID p_pid);
virtual Error dialog_show(String p_title, String p_description, Vector p_buttons, const Callable &p_callback) override;
virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) override;
@@ -824,11 +837,11 @@ class DisplayServerWindows : public DisplayServer {
virtual bool is_window_transparency_available() const override;
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
static Vector get_rendering_drivers_func();
static void register_windows_driver();
- DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
+ DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
~DisplayServerWindows();
};
diff --git a/scene/gui/embedded_process.cpp b/scene/gui/embedded_process.cpp
new file mode 100644
index 000000000000..e6917611114c
--- /dev/null
+++ b/scene/gui/embedded_process.cpp
@@ -0,0 +1,155 @@
+/**************************************************************************/
+/* embedded_process.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "embedded_process.h"
+#include "scene/main/window.h"
+#include "scene/theme/theme_db.h"
+
+void EmbeddedProcess::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_READY: {
+ _window = get_window();
+ } break;
+ case NOTIFICATION_RESIZED:
+ case NOTIFICATION_VISIBILITY_CHANGED:
+ case NOTIFICATION_WM_POSITION_CHANGED: {
+ _update_embedded_process();
+ } break;
+ }
+}
+
+void EmbeddedProcess::set_embedding_timeout(int timeout) {
+ _embedding_timeout = timeout;
+}
+
+int EmbeddedProcess::get_embedding_timeout() {
+ return _embedding_timeout;
+}
+
+bool EmbeddedProcess::is_embedding_in_progress() {
+ return !timer_embedding->is_stopped();
+}
+
+bool EmbeddedProcess::is_embedding_completed() {
+ return _embedding_completed;
+}
+
+void EmbeddedProcess::embed_process(OS::ProcessID p_pid) {
+ if (!_window) {
+ return;
+ }
+
+ ERR_FAIL_COND_MSG(!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_WINDOW_EMBEDDING), "Embedded process not supported by this display server.");
+
+ if (_current_process_id != 0) {
+ // Stop embedding the last process.
+ OS::get_singleton()->kill(_current_process_id);
+ }
+
+ reset();
+
+ _current_process_id = p_pid;
+ _start_embedding_time = OS::get_singleton()->get_ticks_msec();
+
+ // Try to embed, but the process maybe just started and the window is not yet ready
+ // we will retry in this case.
+ _try_embed_process();
+}
+
+void EmbeddedProcess::reset() {
+ if (_current_process_id != 0 && _embedding_completed) {
+ DisplayServer::get_singleton()->remove_embedded_process(_current_process_id);
+ }
+ _current_process_id = 0;
+ _embedding_completed = false;
+ _start_embedding_time = 0;
+ timer_embedding->stop();
+}
+
+void EmbeddedProcess::_try_embed_process() {
+ Error err = DisplayServer::get_singleton()->embed_process(_window->get_window_id(), _current_process_id, this->get_global_rect(), is_visible_in_tree());
+ if (err == OK) {
+ _embedding_completed = true;
+ emit_signal(SNAME("embedding_completed"));
+ } else if (err == ERR_DOES_NOT_EXIST) {
+ if (OS::get_singleton()->get_ticks_msec() - _start_embedding_time >= (uint64_t)_embedding_timeout) {
+ // Embedding failed.
+ reset();
+ emit_signal(SNAME("embedding_failed"));
+ } else {
+ // Tries another shot.
+ timer_embedding->start();
+ }
+ } else {
+ // Another error.
+ reset();
+ emit_signal(SNAME("embedding_failed"));
+ }
+}
+
+void EmbeddedProcess::_update_embedded_process() {
+ if (!_window || _current_process_id == 0 || !_embedding_completed) {
+ return;
+ }
+
+ DisplayServer::get_singleton()->embed_process(_window->get_window_id(), _current_process_id, this->get_global_rect(), is_visible_in_tree());
+}
+
+void EmbeddedProcess::_timer_embedding_timeout() {
+ _try_embed_process();
+}
+
+void EmbeddedProcess::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("embed_process", "process_id"), &EmbeddedProcess::embed_process);
+ ClassDB::bind_method(D_METHOD("reset"), &EmbeddedProcess::reset);
+ ClassDB::bind_method(D_METHOD("set_embedding_timeout", "timeout"), &EmbeddedProcess::set_embedding_timeout);
+ ClassDB::bind_method(D_METHOD("get_embedding_timeout"), &EmbeddedProcess::get_embedding_timeout);
+ ClassDB::bind_method(D_METHOD("is_embedding_completed"), &EmbeddedProcess::is_embedding_completed);
+ ClassDB::bind_method(D_METHOD("is_embedding_in_progress"), &EmbeddedProcess::is_embedding_in_progress);
+
+ ADD_SIGNAL(MethodInfo("embedding_completed"));
+ ADD_SIGNAL(MethodInfo("embedding_failed"));
+}
+
+EmbeddedProcess::EmbeddedProcess() {
+ timer_embedding = memnew(Timer);
+ timer_embedding->set_wait_time(0.1);
+ timer_embedding->set_one_shot(true);
+ add_child(timer_embedding);
+ timer_embedding->connect("timeout", callable_mp(this, &EmbeddedProcess::_timer_embedding_timeout));
+}
+
+EmbeddedProcess::~EmbeddedProcess() {
+ if (_current_process_id != 0) {
+ // Stop embedding the last process.
+ OS::get_singleton()->kill(_current_process_id);
+ reset();
+ }
+}
diff --git a/scene/gui/embedded_process.h b/scene/gui/embedded_process.h
new file mode 100644
index 000000000000..a7feca0c56e0
--- /dev/null
+++ b/scene/gui/embedded_process.h
@@ -0,0 +1,71 @@
+/**************************************************************************/
+/* embedded_process.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#ifndef EMBEDDED_PROCESS_H
+#define EMBEDDED_PROCESS_H
+
+#include "scene/gui/control.h"
+
+class EmbeddedProcess : public Control {
+ GDCLASS(EmbeddedProcess, Control);
+
+ OS::ProcessID _current_process_id = 0;
+ bool _embedding_completed = false;
+ uint64_t _start_embedding_time = 0;
+
+ Window *_window = nullptr;
+ Timer *timer_embedding = nullptr;
+
+ int _embedding_timeout = 15000;
+
+ bool _keep_aspect = false;
+
+ void _try_embed_process();
+ void _update_embedded_process();
+ void _timer_embedding_timeout();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void embed_process(OS::ProcessID p_pid);
+ void reset();
+
+ void set_embedding_timeout(int timeout);
+ int get_embedding_timeout();
+ bool is_embedding_in_progress();
+ bool is_embedding_completed();
+
+ EmbeddedProcess();
+ ~EmbeddedProcess();
+};
+
+#endif // EMBEDDED_PROCESS_H
diff --git a/scene/main/node.h b/scene/main/node.h
index cda657dfbbef..d31325e0e3d4 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -423,6 +423,7 @@ class Node : public Object {
NOTIFICATION_WM_GO_BACK_REQUEST = 1007,
NOTIFICATION_WM_SIZE_CHANGED = 1008,
NOTIFICATION_WM_DPI_CHANGE = 1009,
+ NOTIFICATION_WM_POSITION_CHANGED = 1012,
NOTIFICATION_VP_MOUSE_ENTER = 1010,
NOTIFICATION_VP_MOUSE_EXIT = 1011,
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 803ce89bc9e1..17cbbe35036e 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -700,7 +700,12 @@ void Window::_rect_changed_callback(const Rect2i &p_callback) {
if (size == p_callback.size && position == p_callback.position) {
return;
}
- position = p_callback.position;
+
+ if (position != p_callback.position) {
+ position = p_callback.position;
+ _propagate_window_notification(this, NOTIFICATION_WM_POSITION_CHANGED);
+ emit_signal(SceneStringName(position_changed));
+ }
if (size != p_callback.size) {
size = p_callback.size;
@@ -766,7 +771,6 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) {
focused = true;
_propagate_window_notification(this, NOTIFICATION_WM_WINDOW_FOCUS_IN);
emit_signal(SceneStringName(focus_entered));
-
} break;
case DisplayServer::WINDOW_EVENT_FOCUS_OUT: {
focused = false;
@@ -792,6 +796,9 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) {
case DisplayServer::WINDOW_EVENT_TITLEBAR_CHANGE: {
emit_signal(SNAME("titlebar_changed"));
} break;
+ case DisplayServer::WINDOW_EVENT_POSITION_CHANGED: {
+ emit_signal(SNAME("position_changed"));
+ } break;
}
}
@@ -3036,6 +3043,7 @@ void Window::_bind_methods() {
ADD_SIGNAL(MethodInfo("theme_changed"));
ADD_SIGNAL(MethodInfo("dpi_changed"));
ADD_SIGNAL(MethodInfo("titlebar_changed"));
+ ADD_SIGNAL(MethodInfo("position_changed"));
BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED);
BIND_CONSTANT(NOTIFICATION_THEME_CHANGED);
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index 381a161ad57e..bc1ed617b7bf 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -80,6 +80,7 @@ class SceneStringNames {
StringName mouse_shape_exited;
StringName focus_entered;
StringName focus_exited;
+ StringName position_changed;
StringName pre_sort_children;
StringName sort_children;
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 86b4016da8bf..31ca6b2c0cea 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -653,6 +653,16 @@ bool DisplayServer::get_swap_cancel_ok() {
void DisplayServer::enable_for_stealing_focus(OS::ProcessID pid) {
}
+Error DisplayServer::embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible) {
+ WARN_PRINT("Embedded process not supported by this display server.");
+ return ERR_UNAVAILABLE;
+}
+
+Error DisplayServer::remove_embedded_process(OS::ProcessID p_pid) {
+ WARN_PRINT("Embedded process not supported by this display server.");
+ return ERR_UNAVAILABLE;
+}
+
Error DisplayServer::dialog_show(String p_title, String p_description, Vector p_buttons, const Callable &p_callback) {
WARN_PRINT("Native dialogs not supported by this display server.");
return ERR_UNAVAILABLE;
@@ -1050,6 +1060,7 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(FEATURE_NATIVE_HELP);
BIND_ENUM_CONSTANT(FEATURE_NATIVE_DIALOG_INPUT);
BIND_ENUM_CONSTANT(FEATURE_NATIVE_DIALOG_FILE);
+ BIND_ENUM_CONSTANT(FEATURE_WINDOW_EMBEDDING);
BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE);
BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN);
@@ -1197,9 +1208,9 @@ Vector DisplayServer::get_create_function_rendering_drivers(int p_index)
return server_create_functions[p_index].get_rendering_drivers_function();
}
-DisplayServer *DisplayServer::create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
+DisplayServer *DisplayServer::create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
ERR_FAIL_INDEX_V(p_index, server_create_count, nullptr);
- return server_create_functions[p_index].create_function(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error);
+ return server_create_functions[p_index].create_function(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, p_parent_window, r_error);
}
void DisplayServer::_input_set_mouse_mode(Input::MouseMode p_mode) {
diff --git a/servers/display_server.h b/servers/display_server.h
index 04f4b0c03d25..4ebcee516426 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -90,7 +90,7 @@ class DisplayServer : public Object {
CONTEXT_ENGINE,
};
- typedef DisplayServer *(*CreateFunction)(const String &, WindowMode, VSyncMode, uint32_t, const Point2i *, const Size2i &, int p_screen, Context, Error &r_error);
+ typedef DisplayServer *(*CreateFunction)(const String &, WindowMode, VSyncMode, uint32_t, const Point2i *, const Size2i &, int p_screen, Context, int64_t p_parent_window, Error &r_error);
typedef Vector (*GetRenderingDriversFunction)();
private:
@@ -150,6 +150,7 @@ class DisplayServer : public Object {
FEATURE_NATIVE_HELP,
FEATURE_NATIVE_DIALOG_INPUT,
FEATURE_NATIVE_DIALOG_FILE,
+ FEATURE_WINDOW_EMBEDDING
};
virtual bool has_feature(Feature p_feature) const = 0;
@@ -393,7 +394,7 @@ class DisplayServer : public Object {
WINDOW_FLAG_NO_FOCUS_BIT = (1 << WINDOW_FLAG_NO_FOCUS),
WINDOW_FLAG_POPUP_BIT = (1 << WINDOW_FLAG_POPUP),
WINDOW_FLAG_EXTEND_TO_TITLE_BIT = (1 << WINDOW_FLAG_EXTEND_TO_TITLE),
- WINDOW_FLAG_MOUSE_PASSTHROUGH_BIT = (1 << WINDOW_FLAG_MOUSE_PASSTHROUGH),
+ WINDOW_FLAG_MOUSE_PASSTHROUGH_BIT = (1 << WINDOW_FLAG_MOUSE_PASSTHROUGH)
};
virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i(), bool p_exclusive = false, WindowID p_transient_parent = INVALID_WINDOW_ID);
@@ -422,6 +423,7 @@ class DisplayServer : public Object {
WINDOW_EVENT_GO_BACK_REQUEST,
WINDOW_EVENT_DPI_CHANGE,
WINDOW_EVENT_TITLEBAR_CHANGE,
+ WINDOW_EVENT_POSITION_CHANGED,
};
virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) = 0;
@@ -535,6 +537,9 @@ class DisplayServer : public Object {
virtual void enable_for_stealing_focus(OS::ProcessID pid);
+ virtual Error embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible);
+ virtual Error remove_embedded_process(OS::ProcessID p_pid);
+
virtual Error dialog_show(String p_title, String p_description, Vector p_buttons, const Callable &p_callback);
virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback);
@@ -592,7 +597,7 @@ class DisplayServer : public Object {
static int get_create_function_count();
static const char *get_create_function_name(int p_index);
static Vector get_create_function_rendering_drivers(int p_index);
- static DisplayServer *create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
+ static DisplayServer *create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
static bool can_create_rendering_device();
diff --git a/servers/display_server_headless.h b/servers/display_server_headless.h
index a5277479caa1..c7efcb20c4ad 100644
--- a/servers/display_server_headless.h
+++ b/servers/display_server_headless.h
@@ -45,7 +45,7 @@ class DisplayServerHeadless : public DisplayServer {
return drivers;
}
- static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
+ static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
r_error = OK;
RasterizerDummy::make_current();
return memnew(DisplayServerHeadless());
diff --git a/tests/display_server_mock.h b/tests/display_server_mock.h
index b44ff06b3546..d2bdb5183bee 100644
--- a/tests/display_server_mock.h
+++ b/tests/display_server_mock.h
@@ -55,7 +55,7 @@ class DisplayServerMock : public DisplayServerHeadless {
return drivers;
}
- static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
+ static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
r_error = OK;
RasterizerDummy::make_current();
return memnew(DisplayServerMock());
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 3c184ccc5d61..c1d9eaa4d5f4 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -275,7 +275,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
OS::get_singleton()->set_has_server_feature_callback(nullptr);
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
if (String("mock") == DisplayServer::get_create_function_name(i)) {
- DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), DisplayServer::SCREEN_PRIMARY, DisplayServer::CONTEXT_EDITOR, err);
+ DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), DisplayServer::SCREEN_PRIMARY, DisplayServer::CONTEXT_EDITOR, 0, err);
break;
}
}