Merge pull request #99010 from Hilderin/embedding-game-process
Embed game process in editor
This commit is contained in:
@ -568,8 +568,8 @@ Vector<String> 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(
|
||||
@ -636,7 +636,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");
|
||||
|
||||
@ -224,7 +224,7 @@ public:
|
||||
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<String> get_rendering_drivers_func();
|
||||
static void register_android_driver();
|
||||
|
||||
@ -241,7 +241,7 @@ public:
|
||||
virtual void set_native_icon(const String &p_filename) override;
|
||||
virtual void set_icon(const Ref<Image> &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();
|
||||
};
|
||||
|
||||
|
||||
@ -84,7 +84,7 @@ class DisplayServerIOS : public DisplayServer {
|
||||
|
||||
void perform_event(const Ref<InputEvent> &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:
|
||||
@ -93,7 +93,7 @@ public:
|
||||
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<String> get_rendering_drivers_func();
|
||||
|
||||
// MARK: - Events
|
||||
|
||||
@ -53,7 +53,7 @@ DisplayServerIOS *DisplayServerIOS::get_singleton() {
|
||||
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;
|
||||
@ -196,8 +196,8 @@ DisplayServerIOS::~DisplayServerIOS() {
|
||||
#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<String> DisplayServerIOS::get_rendering_drivers_func() {
|
||||
|
||||
@ -1309,8 +1309,8 @@ Vector<String> 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);
|
||||
@ -1320,7 +1320,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
|
||||
|
||||
@ -294,12 +294,12 @@ public:
|
||||
|
||||
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<String> 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();
|
||||
};
|
||||
|
||||
|
||||
@ -142,6 +142,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;
|
||||
@ -1461,6 +1462,36 @@ Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
|
||||
return rect;
|
||||
}
|
||||
|
||||
Rect2i DisplayServerX11::_screens_get_full_rect() const {
|
||||
Rect2i full_rect;
|
||||
|
||||
int count = get_screen_count();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (i == 0) {
|
||||
full_rect = _screen_get_rect(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
Rect2i screen_rect = _screen_get_rect(i);
|
||||
if (full_rect.position.x > screen_rect.position.x) {
|
||||
full_rect.size.x += full_rect.position.x - screen_rect.position.x;
|
||||
full_rect.position.x = screen_rect.position.x;
|
||||
}
|
||||
if (full_rect.position.y > screen_rect.position.y) {
|
||||
full_rect.size.y += full_rect.position.y - screen_rect.position.y;
|
||||
full_rect.position.y = screen_rect.position.y;
|
||||
}
|
||||
if (full_rect.position.x + full_rect.size.x < screen_rect.position.x + screen_rect.size.x) {
|
||||
full_rect.size.x = screen_rect.position.x + screen_rect.size.x - full_rect.position.x;
|
||||
}
|
||||
if (full_rect.position.y + full_rect.size.y < screen_rect.position.y + screen_rect.size.y) {
|
||||
full_rect.size.y = screen_rect.position.y + screen_rect.size.y - full_rect.position.y;
|
||||
}
|
||||
}
|
||||
|
||||
return full_rect;
|
||||
}
|
||||
|
||||
int DisplayServerX11::screen_get_dpi(int p_screen) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
@ -1745,7 +1776,7 @@ Vector<DisplayServer::WindowID> 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);
|
||||
@ -2074,6 +2105,8 @@ void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window
|
||||
return;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_MSG(wd.embed_parent, "Embedded window can't be moved to another screen.");
|
||||
|
||||
if (window_get_mode(p_window) == WINDOW_MODE_FULLSCREEN || window_get_mode(p_window) == WINDOW_MODE_MAXIMIZED) {
|
||||
Point2i position = screen_get_position(p_screen);
|
||||
Size2i size = screen_get_size(p_screen);
|
||||
@ -2231,6 +2264,8 @@ void DisplayServerX11::window_set_position(const Point2i &p_position, WindowID p
|
||||
ERR_FAIL_COND(!windows.has(p_window));
|
||||
WindowData &wd = windows[p_window];
|
||||
|
||||
ERR_FAIL_COND_MSG(wd.embed_parent, "Embedded window can't be moved.");
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
if (!window_get_flag(WINDOW_FLAG_BORDERLESS, p_window)) {
|
||||
@ -2263,6 +2298,8 @@ void DisplayServerX11::window_set_max_size(const Size2i p_size, WindowID p_windo
|
||||
ERR_FAIL_COND(!windows.has(p_window));
|
||||
WindowData &wd = windows[p_window];
|
||||
|
||||
ERR_FAIL_COND_MSG(wd.embed_parent, "Embedded windows can't have a maximum size.");
|
||||
|
||||
if ((p_size != Size2i()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {
|
||||
ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
|
||||
return;
|
||||
@ -2288,6 +2325,8 @@ void DisplayServerX11::window_set_min_size(const Size2i p_size, WindowID p_windo
|
||||
ERR_FAIL_COND(!windows.has(p_window));
|
||||
WindowData &wd = windows[p_window];
|
||||
|
||||
ERR_FAIL_COND_MSG(wd.embed_parent, "Embedded windows can't have a minimum size.");
|
||||
|
||||
if ((p_size != Size2i()) && (wd.max_size != Size2i()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {
|
||||
ERR_PRINT("Minimum window size can't be larger than maximum window size!");
|
||||
return;
|
||||
@ -2317,6 +2356,8 @@ void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) {
|
||||
|
||||
WindowData &wd = windows[p_window];
|
||||
|
||||
ERR_FAIL_COND_MSG(wd.embed_parent, "Embedded window can't be resized.");
|
||||
|
||||
if (wd.size.width == size.width && wd.size.height == size.height) {
|
||||
return;
|
||||
}
|
||||
@ -2734,8 +2775,10 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
|
||||
if (old_mode == p_mode) {
|
||||
return; // do nothing
|
||||
}
|
||||
//remove all "extra" modes
|
||||
|
||||
ERR_FAIL_COND_MSG(p_mode != WINDOW_MODE_WINDOWED && wd.embed_parent, "Embedded window only supports Windowed mode.");
|
||||
|
||||
// Remove all "extra" modes.
|
||||
switch (old_mode) {
|
||||
case WINDOW_MODE_WINDOWED: {
|
||||
//do nothing
|
||||
@ -2835,8 +2878,9 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
|
||||
|
||||
switch (p_flag) {
|
||||
case WINDOW_FLAG_RESIZE_DISABLED: {
|
||||
wd.resize_disabled = p_enabled;
|
||||
ERR_FAIL_COND_MSG(p_enabled && wd.embed_parent, "Embedded window resize can't be disabled.");
|
||||
|
||||
wd.resize_disabled = p_enabled;
|
||||
_update_size_hints(p_window);
|
||||
|
||||
XFlush(x11_display);
|
||||
@ -2852,13 +2896,16 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
|
||||
}
|
||||
|
||||
// Preserve window size
|
||||
window_set_size(window_get_size(p_window), p_window);
|
||||
if (!wd.embed_parent) {
|
||||
window_set_size(window_get_size(p_window), p_window);
|
||||
}
|
||||
|
||||
wd.borderless = p_enabled;
|
||||
_update_window_mouse_passthrough(p_window);
|
||||
} break;
|
||||
case WINDOW_FLAG_ALWAYS_ON_TOP: {
|
||||
ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID, "Can't make a window transient if the 'on top' flag is active.");
|
||||
ERR_FAIL_COND_MSG(p_enabled && wd.embed_parent, "Embedded window can't become on top.");
|
||||
if (p_enabled && wd.fullscreen) {
|
||||
_set_wm_maximized(p_window, true);
|
||||
}
|
||||
@ -2900,6 +2947,7 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
|
||||
|
||||
ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup.");
|
||||
ERR_FAIL_COND_MSG((xwa.map_state == IsViewable) && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened.");
|
||||
ERR_FAIL_COND_MSG(p_enabled && wd.embed_parent, "Embedded window can't be popup.");
|
||||
wd.is_popup = p_enabled;
|
||||
} break;
|
||||
default: {
|
||||
@ -3354,6 +3402,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;
|
||||
@ -4003,7 +4052,9 @@ void DisplayServerX11::_window_changed(XEvent *event) {
|
||||
unsigned int nchildren;
|
||||
if (XQueryTree(x11_display, wd.x11_window, &root, &parent, &children, &nchildren) && wd.parent != parent) {
|
||||
wd.parent = parent;
|
||||
window_set_position(wd.position, window_id);
|
||||
if (!wd.embed_parent) {
|
||||
window_set_position(wd.position, window_id);
|
||||
}
|
||||
}
|
||||
XFree(children);
|
||||
|
||||
@ -5027,9 +5078,9 @@ void DisplayServerX11::process_events() {
|
||||
// Don't propagate the motion event unless we have focus
|
||||
// this is so that the relative motion doesn't get messed up
|
||||
// after we regain focus.
|
||||
if (focused) {
|
||||
Input::get_singleton()->parse_input_event(mm);
|
||||
} else {
|
||||
// Adjusted to parse the input event if the window is not focused allowing mouse hovering on the editor
|
||||
// the embedding process has focus.
|
||||
if (!focused) {
|
||||
// Propagate the event to the focused window,
|
||||
// because it's received only on the topmost window.
|
||||
// Note: This is needed for drag & drop to work between windows,
|
||||
@ -5048,13 +5099,14 @@ void DisplayServerX11::process_events() {
|
||||
mm->set_position(pos_focused);
|
||||
mm->set_global_position(pos_focused);
|
||||
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
|
||||
Input::get_singleton()->parse_input_event(mm);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Input::get_singleton()->parse_input_event(mm);
|
||||
|
||||
} break;
|
||||
case KeyPress:
|
||||
case KeyRelease: {
|
||||
@ -5458,6 +5510,277 @@ void DisplayServerX11::window_start_drag(WindowID p_window) {
|
||||
XSync(x11_display, 0);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Error DisplayServerX11::embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible, bool p_grab_focus) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
ERR_FAIL_COND_V(!windows.has(p_window), FAILED);
|
||||
|
||||
const WindowData &wd = windows[p_window];
|
||||
|
||||
DEBUG_LOG_X11("Starting embedding %ld to window %lu \n", p_pid, wd.x11_window);
|
||||
|
||||
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;
|
||||
XSetTransientForHint(x11_display, process_window, wd.x11_window);
|
||||
_set_window_taskbar_pager_enabled(process_window, false);
|
||||
embedded_processes.insert(p_pid, ep);
|
||||
}
|
||||
|
||||
// Handle bad window errors silently because just in case the embedded window was closed.
|
||||
int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&bad_window_error_handler);
|
||||
|
||||
if (p_visible) {
|
||||
// Resize and move the window to match the desired rectangle.
|
||||
// X11 does not allow moving the window entirely outside the screen boundaries.
|
||||
// To ensure the window remains visible, we will resize it to fit within both the screen and the specified rectangle.
|
||||
Rect2i desired_rect = p_rect;
|
||||
|
||||
// First resize the desired rect to fit inside all the screens without considering the
|
||||
// working area.
|
||||
Rect2i screens_full_rect = _screens_get_full_rect();
|
||||
Vector2i screens_full_end = screens_full_rect.get_end();
|
||||
if (desired_rect.position.x < screens_full_rect.position.x) {
|
||||
desired_rect.size.x = MAX(desired_rect.size.x - (screens_full_rect.position.x - desired_rect.position.x), 0);
|
||||
desired_rect.position.x = screens_full_rect.position.x;
|
||||
}
|
||||
if (desired_rect.position.x + desired_rect.size.x > screens_full_end.x) {
|
||||
desired_rect.size.x = MAX(screens_full_end.x - desired_rect.position.x, 0);
|
||||
}
|
||||
if (desired_rect.position.y < screens_full_rect.position.y) {
|
||||
desired_rect.size.y = MAX(desired_rect.size.y - (screens_full_rect.position.y - desired_rect.position.y), 0);
|
||||
desired_rect.position.y = screens_full_rect.position.y;
|
||||
}
|
||||
if (desired_rect.position.y + desired_rect.size.y > screens_full_end.y) {
|
||||
desired_rect.size.y = MAX(screens_full_end.y - desired_rect.position.y, 0);
|
||||
}
|
||||
|
||||
// Second, for each screen, check if the desired rectangle is within a portion of the screen
|
||||
// that is outside the working area. Each screen can have a different working area
|
||||
// depending on top, bottom, or side panels.
|
||||
int desired_area = desired_rect.get_area();
|
||||
int count = get_screen_count();
|
||||
for (int i = 0; i < count; i++) {
|
||||
Rect2i screen_rect = _screen_get_rect(i);
|
||||
if (screen_rect.intersection(desired_rect).get_area() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The desired rect is inside this screen.
|
||||
Rect2i screen_usable_rect = screen_get_usable_rect(i);
|
||||
int screen_usable_area = screen_usable_rect.intersection(desired_rect).get_area();
|
||||
if (screen_usable_area == desired_area) {
|
||||
// The desired rect is fulling inside the usable rect of the screen. No need to resize.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (desired_rect.position.x >= screen_rect.position.x && desired_rect.position.x < screen_usable_rect.position.x) {
|
||||
int offset = screen_usable_rect.position.x - desired_rect.position.x;
|
||||
desired_rect.size.x = MAX(desired_rect.size.x - offset, 0);
|
||||
desired_rect.position.x += offset;
|
||||
}
|
||||
if (desired_rect.position.y >= screen_rect.position.y && desired_rect.position.y < screen_usable_rect.position.y) {
|
||||
int offset = screen_usable_rect.position.y - desired_rect.position.y;
|
||||
desired_rect.size.y = MAX(desired_rect.size.y - offset, 0);
|
||||
desired_rect.position.y += offset;
|
||||
}
|
||||
|
||||
Vector2i desired_end = desired_rect.get_end();
|
||||
Vector2i screen_end = screen_rect.get_end();
|
||||
Vector2i screen_usable_end = screen_usable_rect.get_end();
|
||||
if (desired_end.x > screen_usable_end.x && desired_end.x <= screen_end.x) {
|
||||
desired_rect.size.x = MAX(desired_rect.size.x - (desired_end.x - screen_usable_end.x), 0);
|
||||
}
|
||||
if (desired_end.y > screen_usable_end.y && desired_end.y <= screen_end.y) {
|
||||
desired_rect.size.y = MAX(desired_rect.size.y - (desired_end.y - screen_usable_end.y), 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (desired_rect.size.x < 100 || desired_rect.size.y < 100) {
|
||||
p_visible = false;
|
||||
}
|
||||
|
||||
if (p_visible) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (p_grab_focus && p_visible) {
|
||||
Window focused_window = 0;
|
||||
int revert_to = 0;
|
||||
XGetInputFocus(x11_display, &focused_window, &revert_to);
|
||||
if (focused_window != ep->process_window) {
|
||||
// Be sure that the window is visible to prevent BadMatch error when calling XSetInputFocus on a not viewable window.
|
||||
XWindowAttributes attr;
|
||||
if (XGetWindowAttributes(x11_display, ep->process_window, &attr) && attr.map_state == IsViewable) {
|
||||
XSetInputFocus(x11_display, ep->process_window, RevertToParent, CurrentTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore default error handler.
|
||||
XSetErrorHandler(oldHandler);
|
||||
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;
|
||||
}
|
||||
|
||||
OS::ProcessID DisplayServerX11::get_focused_process_id() {
|
||||
Window focused_window = 0;
|
||||
int revert_to = 0;
|
||||
|
||||
XGetInputFocus(x11_display, &focused_window, &revert_to);
|
||||
|
||||
if (focused_window == None) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return get_window_pid(x11_display, focused_window);
|
||||
}
|
||||
|
||||
Vector<String> DisplayServerX11::get_rendering_drivers_func() {
|
||||
Vector<String> drivers;
|
||||
|
||||
@ -5472,12 +5795,12 @@ Vector<String> 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;
|
||||
@ -5576,16 +5899,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
|
||||
@ -5597,6 +5923,14 @@ 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) {
|
||||
wd.embed_parent = 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;
|
||||
|
||||
@ -5748,7 +6082,7 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
|
||||
}
|
||||
}
|
||||
|
||||
if (wd.is_popup || wd.no_focus) {
|
||||
if (wd.is_popup || wd.no_focus || wd.embed_parent) {
|
||||
// Set Utility type to disable fade animations.
|
||||
Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
|
||||
Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
|
||||
@ -5764,6 +6098,11 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
|
||||
}
|
||||
}
|
||||
|
||||
if (p_parent_window) {
|
||||
// Disable the window in the taskbar and alt-tab.
|
||||
_set_window_taskbar_pager_enabled(wd.x11_window, false);
|
||||
}
|
||||
|
||||
_update_size_hints(id);
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
@ -5885,7 +6224,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";
|
||||
@ -6342,7 +6681,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;
|
||||
|
||||
@ -208,6 +208,8 @@ class DisplayServerX11 : public DisplayServer {
|
||||
bool layered_window = false;
|
||||
bool mpass = false;
|
||||
|
||||
Window embed_parent = 0;
|
||||
|
||||
Rect2i parent_safe_rect;
|
||||
|
||||
unsigned int focus_order = 0;
|
||||
@ -234,7 +236,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 +377,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<OS::ProcessID, EmbeddedProcessData *> 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);
|
||||
Rect2i _screens_get_full_rect() const;
|
||||
|
||||
protected:
|
||||
void _window_changed(XEvent *event);
|
||||
|
||||
@ -514,6 +528,10 @@ public:
|
||||
|
||||
virtual void window_start_drag(WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual Error embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible, bool p_grab_focus) override;
|
||||
virtual Error remove_embedded_process(OS::ProcessID p_pid) override;
|
||||
virtual OS::ProcessID get_focused_process_id() override;
|
||||
|
||||
virtual void cursor_set_shape(CursorShape p_shape) override;
|
||||
virtual CursorShape cursor_get_shape() const override;
|
||||
virtual void cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) override;
|
||||
@ -538,12 +556,12 @@ public:
|
||||
virtual void set_native_icon(const String &p_filename) override;
|
||||
virtual void set_icon(const Ref<Image> &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<String> 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();
|
||||
};
|
||||
|
||||
|
||||
@ -450,12 +450,12 @@ public:
|
||||
|
||||
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<String> 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();
|
||||
};
|
||||
|
||||
|
||||
@ -3399,8 +3399,8 @@ bool DisplayServerMacOS::is_window_transparency_available() const {
|
||||
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;
|
||||
@ -3594,7 +3594,7 @@ bool DisplayServerMacOS::mouse_process_popups(bool p_close) {
|
||||
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);
|
||||
|
||||
@ -1025,11 +1025,11 @@ void DisplayServerWeb::_dispatch_input_event(const Ref<InputEvent> &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");
|
||||
|
||||
@ -148,7 +148,7 @@ private:
|
||||
void process_keys();
|
||||
|
||||
static Vector<String> 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<InputEvent> &p_event);
|
||||
|
||||
@ -280,7 +280,7 @@ public:
|
||||
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();
|
||||
};
|
||||
|
||||
|
||||
@ -136,6 +136,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;
|
||||
@ -1539,7 +1540,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];
|
||||
@ -1889,7 +1890,6 @@ void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p
|
||||
|
||||
void DisplayServerWindows::_update_window_mouse_passthrough(WindowID p_window) {
|
||||
ERR_FAIL_COND(!windows.has(p_window));
|
||||
|
||||
if (windows[p_window].mpass || windows[p_window].mpath.size() == 0) {
|
||||
SetWindowRgn(windows[p_window].hWnd, nullptr, FALSE);
|
||||
} else {
|
||||
@ -1930,6 +1930,7 @@ void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_wi
|
||||
return;
|
||||
}
|
||||
const WindowData &wd = windows[p_window];
|
||||
ERR_FAIL_COND_MSG(wd.parent_hwnd, "Embedded window can't be moved to another screen.");
|
||||
if (wd.fullscreen) {
|
||||
Point2 pos = screen_get_position(p_screen) + _get_screens_origin();
|
||||
Size2 size = screen_get_size(p_screen);
|
||||
@ -2010,6 +2011,8 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window
|
||||
ERR_FAIL_COND(!windows.has(p_window));
|
||||
WindowData &wd = windows[p_window];
|
||||
|
||||
ERR_FAIL_COND_MSG(wd.parent_hwnd, "Embedded window can't be moved.");
|
||||
|
||||
if (wd.fullscreen || wd.maximized) {
|
||||
return;
|
||||
}
|
||||
@ -2094,6 +2097,8 @@ void DisplayServerWindows::window_set_max_size(const Size2i p_size, WindowID p_w
|
||||
ERR_FAIL_COND(!windows.has(p_window));
|
||||
WindowData &wd = windows[p_window];
|
||||
|
||||
ERR_FAIL_COND_MSG(wd.parent_hwnd, "Embedded windows can't have a maximum size.");
|
||||
|
||||
if ((p_size != Size2()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {
|
||||
ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
|
||||
return;
|
||||
@ -2115,6 +2120,8 @@ void DisplayServerWindows::window_set_min_size(const Size2i p_size, WindowID p_w
|
||||
ERR_FAIL_COND(!windows.has(p_window));
|
||||
WindowData &wd = windows[p_window];
|
||||
|
||||
ERR_FAIL_COND_MSG(wd.parent_hwnd, "Embedded windows can't have a minimum size.");
|
||||
|
||||
if ((p_size != Size2()) && (wd.max_size != Size2()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {
|
||||
ERR_PRINT("Minimum window size can't be larger than maximum window size!");
|
||||
return;
|
||||
@ -2136,6 +2143,8 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo
|
||||
ERR_FAIL_COND(!windows.has(p_window));
|
||||
WindowData &wd = windows[p_window];
|
||||
|
||||
ERR_FAIL_COND_MSG(wd.parent_hwnd, "Embedded window can't be resized.");
|
||||
|
||||
if (wd.fullscreen || wd.maximized) {
|
||||
return;
|
||||
}
|
||||
@ -2187,7 +2196,7 @@ Size2i DisplayServerWindows::window_get_size_with_decorations(WindowID p_window)
|
||||
return Size2();
|
||||
}
|
||||
|
||||
void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_initialized, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_minimized, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) {
|
||||
void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_initialized, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_minimized, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, bool p_embed_child, DWORD &r_style, DWORD &r_style_ex) {
|
||||
// Windows docs for window styles:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
|
||||
// https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
|
||||
@ -2195,13 +2204,19 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_initiali
|
||||
r_style = 0;
|
||||
r_style_ex = WS_EX_WINDOWEDGE;
|
||||
if (p_main_window) {
|
||||
r_style_ex |= WS_EX_APPWINDOW;
|
||||
// When embedded, we don't want the window to have WS_EX_APPWINDOW because it will
|
||||
// show the embedded process in the taskbar and Alt-Tab.
|
||||
if (!p_embed_child) {
|
||||
r_style_ex |= WS_EX_APPWINDOW;
|
||||
}
|
||||
if (p_initialized) {
|
||||
r_style |= WS_VISIBLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (p_fullscreen || p_borderless) {
|
||||
if (p_embed_child) {
|
||||
r_style |= WS_POPUP;
|
||||
} else if (p_fullscreen || p_borderless) {
|
||||
r_style |= WS_POPUP; // p_borderless was WS_EX_TOOLWINDOW in the past.
|
||||
if (p_minimized) {
|
||||
r_style |= WS_MINIMIZE;
|
||||
@ -2236,7 +2251,7 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_initiali
|
||||
}
|
||||
}
|
||||
|
||||
if (p_no_activate_focus) {
|
||||
if (p_no_activate_focus && !p_embed_child) {
|
||||
r_style_ex |= WS_EX_TOPMOST | WS_EX_NOACTIVATE;
|
||||
}
|
||||
|
||||
@ -2261,7 +2276,7 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain
|
||||
DWORD style = 0;
|
||||
DWORD style_ex = 0;
|
||||
|
||||
_get_window_style(p_window == MAIN_WINDOW_ID, wd.initialized, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.minimized, wd.maximized, wd.maximized_fs, wd.no_focus || wd.is_popup, style, style_ex);
|
||||
_get_window_style(p_window == MAIN_WINDOW_ID, wd.initialized, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.minimized, wd.maximized, wd.maximized_fs, wd.no_focus || wd.is_popup, wd.parent_hwnd, style, style_ex);
|
||||
|
||||
SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);
|
||||
SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex);
|
||||
@ -2275,6 +2290,7 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain
|
||||
if (p_repaint) {
|
||||
RECT rect;
|
||||
GetWindowRect(wd.hWnd, &rect);
|
||||
|
||||
MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
|
||||
}
|
||||
}
|
||||
@ -2285,6 +2301,8 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
|
||||
ERR_FAIL_COND(!windows.has(p_window));
|
||||
WindowData &wd = windows[p_window];
|
||||
|
||||
ERR_FAIL_COND_MSG(p_mode != WINDOW_MODE_WINDOWED && wd.parent_hwnd, "Embedded window only supports Windowed mode.");
|
||||
|
||||
bool was_fullscreen = wd.fullscreen;
|
||||
wd.was_fullscreen_pre_min = false;
|
||||
|
||||
@ -2418,6 +2436,7 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
|
||||
WindowData &wd = windows[p_window];
|
||||
switch (p_flag) {
|
||||
case WINDOW_FLAG_RESIZE_DISABLED: {
|
||||
ERR_FAIL_COND_MSG(p_enabled && wd.parent_hwnd, "Embedded window resize can't be disabled.");
|
||||
wd.resizable = !p_enabled;
|
||||
_update_window_style(p_window);
|
||||
} break;
|
||||
@ -2428,7 +2447,8 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
|
||||
ShowWindow(wd.hWnd, (wd.no_focus || wd.is_popup) ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window.
|
||||
} break;
|
||||
case WINDOW_FLAG_ALWAYS_ON_TOP: {
|
||||
ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID && p_enabled, "Transient windows can't become on top");
|
||||
ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID && p_enabled, "Transient windows can't become on top.");
|
||||
ERR_FAIL_COND_MSG(p_enabled && wd.parent_hwnd, "Embedded window can't become on top.");
|
||||
wd.always_on_top = p_enabled;
|
||||
_update_window_style(p_window);
|
||||
} break;
|
||||
@ -2488,6 +2508,7 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
|
||||
case WINDOW_FLAG_POPUP: {
|
||||
ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup.");
|
||||
ERR_FAIL_COND_MSG(IsWindowVisible(wd.hWnd) && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened.");
|
||||
ERR_FAIL_COND_MSG(p_enabled && wd.parent_hwnd, "Embedded window can't be popup.");
|
||||
wd.is_popup = p_enabled;
|
||||
} break;
|
||||
default:
|
||||
@ -2842,6 +2863,183 @@ void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) {
|
||||
AllowSetForegroundWindow(pid);
|
||||
}
|
||||
|
||||
struct WindowEnumData {
|
||||
DWORD process_id;
|
||||
HWND parent_hWnd;
|
||||
HWND hWnd;
|
||||
};
|
||||
|
||||
static BOOL CALLBACK _enum_proc_find_window_from_process_id_callback(HWND hWnd, LPARAM lParam) {
|
||||
WindowEnumData &ed = *(WindowEnumData *)lParam;
|
||||
DWORD process_id = 0x0;
|
||||
|
||||
GetWindowThreadProcessId(hWnd, &process_id);
|
||||
if (ed.process_id == process_id) {
|
||||
if (GetParent(hWnd) != ed.parent_hWnd) {
|
||||
const DWORD style = GetWindowLongPtr(hWnd, GWL_STYLE);
|
||||
if ((style & WS_VISIBLE) != WS_VISIBLE) {
|
||||
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, HWND p_current_hwnd) {
|
||||
DWORD pid = p_pid;
|
||||
WindowEnumData ed = { pid, p_current_hwnd, NULL };
|
||||
|
||||
// First, check our own child, maybe it's already embedded.
|
||||
if (!EnumChildWindows(p_current_hwnd, _enum_proc_find_window_from_process_id_callback, (LPARAM)&ed) && (GetLastError() == ERROR_SUCCESS)) {
|
||||
if (ed.hWnd) {
|
||||
return ed.hWnd;
|
||||
}
|
||||
}
|
||||
|
||||
// Then check all the opened windows on the computer.
|
||||
if (!EnumWindows(_enum_proc_find_window_from_process_id_callback, (LPARAM)&ed) && (GetLastError() == ERROR_SUCCESS)) {
|
||||
return ed.hWnd;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Error DisplayServerWindows::embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible, bool p_grab_focus) {
|
||||
_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);
|
||||
} else {
|
||||
// New process, trying to find the window.
|
||||
HWND handle_to_embed = _find_window_from_process_id(p_pid, wd.hWnd);
|
||||
if (!handle_to_embed) {
|
||||
return ERR_DOES_NOT_EXIST;
|
||||
}
|
||||
|
||||
const DWORD style = GetWindowLongPtr(handle_to_embed, GWL_STYLE);
|
||||
|
||||
ep = memnew(EmbeddedProcessData);
|
||||
ep->window_handle = handle_to_embed;
|
||||
ep->parent_window_handle = wd.hWnd;
|
||||
ep->is_visible = (style & WS_VISIBLE) == WS_VISIBLE;
|
||||
|
||||
embedded_processes.insert(p_pid, ep);
|
||||
|
||||
HWND old_parent = GetParent(ep->window_handle);
|
||||
if (old_parent != wd.hWnd) {
|
||||
// It's important that the window does not have the WS_CHILD flag
|
||||
// to prevent the current process from interfering with the embedded process.
|
||||
// I observed lags and issues with mouse capture when WS_CHILD is set.
|
||||
// Additionally, WS_POPUP must be set to ensure that the coordinates of the embedded
|
||||
// window remain screen coordinates and not local coordinates of the parent window.
|
||||
if ((style & WS_CHILD) == WS_CHILD || (style & WS_POPUP) != WS_POPUP) {
|
||||
const DWORD new_style = (style & ~WS_CHILD) | WS_POPUP;
|
||||
SetWindowLong(ep->window_handle, GWL_STYLE, new_style);
|
||||
}
|
||||
// Set the parent to current window.
|
||||
SetParent(ep->window_handle, wd.hWnd);
|
||||
}
|
||||
}
|
||||
|
||||
if (p_rect.size.x < 100 || p_rect.size.y < 100) {
|
||||
p_visible = false;
|
||||
}
|
||||
|
||||
// In Godot, the window position is offset by the screen's origin coordinates.
|
||||
// We need to adjust for this when a screen is positioned in the negative space
|
||||
// (e.g., a screen to the left of the main screen).
|
||||
const Rect2i adjusted_rect = Rect2i(p_rect.position + _get_screens_origin(), p_rect.size);
|
||||
|
||||
SetWindowPos(ep->window_handle, HWND_BOTTOM, adjusted_rect.position.x, adjusted_rect.position.y, adjusted_rect.size.x, adjusted_rect.size.y, SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
|
||||
if (ep->is_visible != p_visible) {
|
||||
if (p_visible) {
|
||||
ShowWindow(ep->window_handle, SW_SHOWNA);
|
||||
} else {
|
||||
ShowWindow(ep->window_handle, SW_HIDE);
|
||||
}
|
||||
ep->is_visible = p_visible;
|
||||
}
|
||||
|
||||
if (p_grab_focus) {
|
||||
SetFocus(ep->window_handle);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// This is a workaround to ensure the parent window correctly regains focus after the
|
||||
// embedded window is closed. When the embedded window is closed while it has focus,
|
||||
// the parent window (the editor) does not become active. It appears focused but is not truly activated.
|
||||
// Opening a new window and closing it forces Windows to set the focus and activation correctly.
|
||||
DWORD style = WS_POPUP | WS_VISIBLE;
|
||||
DWORD style_ex = WS_EX_TOPMOST;
|
||||
|
||||
WNDCLASSW wcTemp = {};
|
||||
wcTemp.lpfnWndProc = DefWindowProcW;
|
||||
wcTemp.hInstance = GetModuleHandle(nullptr);
|
||||
wcTemp.lpszClassName = L"Engine temp window";
|
||||
RegisterClassW(&wcTemp);
|
||||
|
||||
HWND hWnd = CreateWindowExW(
|
||||
style_ex,
|
||||
L"Engine temp window", L"",
|
||||
style,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
ep->parent_window_handle,
|
||||
nullptr,
|
||||
GetModuleHandle(nullptr),
|
||||
nullptr);
|
||||
|
||||
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
|
||||
|
||||
DestroyWindow(hWnd);
|
||||
UnregisterClassW(L"Engine temp window", GetModuleHandle(nullptr));
|
||||
|
||||
SetForegroundWindow(ep->parent_window_handle);
|
||||
|
||||
embedded_processes.erase(p_pid);
|
||||
memdelete(ep);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
OS::ProcessID DisplayServerWindows::get_focused_process_id() {
|
||||
HWND hwnd = GetForegroundWindow();
|
||||
if (!hwnd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get the process ID of the window.
|
||||
DWORD processID;
|
||||
GetWindowThreadProcessId(hwnd, &processID);
|
||||
|
||||
return processID;
|
||||
}
|
||||
|
||||
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.
|
||||
@ -4216,6 +4414,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
|
||||
if (windows[window_id].no_focus || windows[window_id].is_popup) {
|
||||
return MA_NOACTIVATE; // Do not activate, but process mouse messages.
|
||||
}
|
||||
// When embedded, the window is a child of the parent and is not activated
|
||||
// by default because it lacks native controls.
|
||||
if (windows[window_id].parent_hwnd) {
|
||||
SetFocus(windows[window_id].hWnd);
|
||||
return MA_ACTIVATE;
|
||||
}
|
||||
} break;
|
||||
case WM_ACTIVATEAPP: {
|
||||
bool new_app_focused = (bool)wParam;
|
||||
@ -4232,8 +4436,15 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
|
||||
// Therefore, it's safer to defer the delivery of the event.
|
||||
// It's important to set an nIDEvent different from the SetTimer for move_timer_id because
|
||||
// if the same nIDEvent is passed, the timer is replaced and the same timer_id is returned.
|
||||
windows[window_id].activate_timer_id = SetTimer(windows[window_id].hWnd, DisplayServerWindows::TIMER_ID_WINDOW_ACTIVATION, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
|
||||
// The problem with the timer is that the window cannot be resized or the buttons cannot be used correctly
|
||||
// if the window is not activated first. This happens because the code in the activation process runs
|
||||
// after the mouse click is handled. To address this, the timer is now used only when the window is created.
|
||||
windows[window_id].activate_state = GET_WM_ACTIVATE_STATE(wParam, lParam);
|
||||
if (windows[window_id].first_activation_done) {
|
||||
_process_activate_event(window_id);
|
||||
} else {
|
||||
windows[window_id].activate_timer_id = SetTimer(windows[window_id].hWnd, DisplayServerWindows::TIMER_ID_WINDOW_ACTIVATION, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
|
||||
}
|
||||
return 0;
|
||||
} break;
|
||||
case WM_GETMINMAXINFO: {
|
||||
@ -5303,6 +5514,16 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
|
||||
|
||||
MoveWindow(window.hWnd, pos.x, pos.y, size.width, size.height, TRUE);
|
||||
}
|
||||
} else {
|
||||
if (window.parent_hwnd) {
|
||||
// WM_WINDOWPOSCHANGED is sent when the parent changes.
|
||||
// If we are supposed to have a parent and now we don't, it's likely
|
||||
// because the parent was closed. We will close our window as well.
|
||||
// This prevents an embedded game from staying alive when the editor is closed or crashes.
|
||||
if (!GetParent(window.hWnd)) {
|
||||
SendMessage(window.hWnd, WM_CLOSE, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return here to prevent WM_MOVE and WM_SIZE from being sent
|
||||
@ -5330,6 +5551,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
|
||||
_process_activate_event(window_id);
|
||||
KillTimer(windows[window_id].hWnd, windows[window_id].activate_timer_id);
|
||||
windows[window_id].activate_timer_id = 0;
|
||||
windows[window_id].first_activation_done = true;
|
||||
}
|
||||
} break;
|
||||
case WM_SYSKEYUP:
|
||||
@ -5718,11 +5940,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_hwnd) {
|
||||
DWORD dwExStyle;
|
||||
DWORD dwStyle;
|
||||
|
||||
_get_window_style(window_id_counter == MAIN_WINDOW_ID, false, (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_MINIMIZED, p_mode == WINDOW_MODE_MAXIMIZED, false, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle);
|
||||
_get_window_style(window_id_counter == MAIN_WINDOW_ID, false, (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_MINIMIZED, p_mode == WINDOW_MODE_MAXIMIZED, false, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), p_parent_hwnd, dwStyle, dwExStyle);
|
||||
|
||||
RECT WindowRect;
|
||||
|
||||
@ -5736,41 +5958,46 @@ 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));
|
||||
Point2i offset = _get_screens_origin();
|
||||
|
||||
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);
|
||||
if (!p_parent_hwnd) {
|
||||
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 = 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;
|
||||
}
|
||||
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_hwnd) {
|
||||
owner_hwnd = p_parent_hwnd;
|
||||
} 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;
|
||||
@ -5804,6 +6031,9 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
|
||||
windows.erase(id);
|
||||
ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Windows OS window.");
|
||||
}
|
||||
|
||||
wd.parent_hwnd = p_parent_hwnd;
|
||||
|
||||
if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
|
||||
wd.fullscreen = true;
|
||||
if (p_mode == WINDOW_MODE_FULLSCREEN) {
|
||||
@ -6164,7 +6394,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();
|
||||
@ -6564,7 +6794,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_hwnd = NULL;
|
||||
if (p_parent_window) {
|
||||
// Parented window.
|
||||
parent_hwnd = (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_hwnd);
|
||||
if (main_window == INVALID_WINDOW_ID) {
|
||||
r_error = ERR_UNAVAILABLE;
|
||||
ERR_FAIL_MSG("Failed to create main window.");
|
||||
@ -6642,8 +6878,8 @@ Vector<String> 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");
|
||||
|
||||
@ -483,6 +483,8 @@ class DisplayServerWindows : public DisplayServer {
|
||||
int activate_state = 0;
|
||||
bool was_maximized_pre_fs = false;
|
||||
bool was_fullscreen_pre_min = false;
|
||||
bool first_activation_done = false;
|
||||
bool was_maximized = false;
|
||||
bool always_on_top = false;
|
||||
bool no_focus = false;
|
||||
bool exclusive = false;
|
||||
@ -546,6 +548,8 @@ class DisplayServerWindows : public DisplayServer {
|
||||
Rect2i parent_safe_rect;
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
HWND parent_hwnd = 0;
|
||||
};
|
||||
|
||||
JoypadWindows *joypad = nullptr;
|
||||
@ -554,7 +558,7 @@ class DisplayServerWindows : public DisplayServer {
|
||||
uint64_t time_since_popup = 0;
|
||||
Ref<Image> 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_hwnd);
|
||||
WindowID window_id_counter = MAIN_WINDOW_ID;
|
||||
RBMap<WindowID, WindowData> windows;
|
||||
|
||||
@ -613,7 +617,7 @@ class DisplayServerWindows : public DisplayServer {
|
||||
HashMap<int64_t, Vector2> pointer_last_pos;
|
||||
|
||||
void _send_window_event(const WindowData &wd, WindowEvent p_event);
|
||||
void _get_window_style(bool p_main_window, bool p_initialized, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_minimized, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex);
|
||||
void _get_window_style(bool p_main_window, bool p_initialized, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_minimized, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, bool p_embed_child, DWORD &r_style, DWORD &r_style_ex);
|
||||
|
||||
MouseMode mouse_mode;
|
||||
int restore_mouse_trails = 0;
|
||||
@ -669,6 +673,15 @@ 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;
|
||||
HWND parent_window_handle = 0;
|
||||
bool is_visible = false;
|
||||
};
|
||||
HashMap<OS::ProcessID, EmbeddedProcessData *> embedded_processes;
|
||||
|
||||
HWND _find_window_from_process_id(OS::ProcessID p_pid, HWND p_current_hwnd);
|
||||
|
||||
public:
|
||||
LRESULT WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
@ -814,6 +827,9 @@ public:
|
||||
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, bool p_grab_focus) override;
|
||||
virtual Error remove_embedded_process(OS::ProcessID p_pid) override;
|
||||
virtual OS::ProcessID get_focused_process_id() override;
|
||||
|
||||
virtual Error dialog_show(String p_title, String p_description, Vector<String> 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;
|
||||
@ -853,11 +869,11 @@ public:
|
||||
|
||||
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<String> 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();
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user