Merge pull request #101774 from Riteo/wayland-multiwindow-for-real-this-time

Wayland: Implement native sub-windows
This commit is contained in:
Thaddeus Crews 2025-04-04 17:40:58 -05:00
commit f4c7a5ae1d
No known key found for this signature in database
GPG key ID: 8C6E5FEB5FC03CCC
12 changed files with 1263 additions and 475 deletions

View file

@ -1958,6 +1958,9 @@
<constant name="FEATURE_NATIVE_COLOR_PICKER" value="32" enum="Feature">
Display server supports native color picker. [b]Linux (X11/Wayland)[/b]
</constant>
<constant name="FEATURE_SELF_FITTING_WINDOWS" value="33" enum="Feature">
Display server automatically fits popups according to the screen boundaries. Window nodes should not attempt to do that themselves.
</constant>
<constant name="MOUSE_MODE_VISIBLE" value="0" enum="MouseMode">
Makes the mouse cursor visible if it is hidden.
</constant>
@ -2181,7 +2184,10 @@
[b]Note:[/b] This flag is implemented on macOS and Windows.
[b]Note:[/b] Setting this flag will [b]NOT[/b] prevent other apps from capturing an image, it should not be used as a security measure.
</constant>
<constant name="WINDOW_FLAG_MAX" value="10" enum="WindowFlags">
<constant name="WINDOW_FLAG_POPUP_WM_HINT" value="10" enum="WindowFlags">
Signals the window manager that this window is supposed to be an implementation-defined "popup" (usually a floating, borderless, untileable and immovable child window).
</constant>
<constant name="WINDOW_FLAG_MAX" value="11" enum="WindowFlags">
Max value of the [enum WindowFlags].
</constant>
<constant name="WINDOW_EVENT_MOUSE_ENTER" value="0" enum="WindowEvent">
@ -2211,6 +2217,10 @@
Sent when the window title bar decoration is changed (e.g. [constant WINDOW_FLAG_EXTEND_TO_TITLE] is set or window entered/exited full screen mode).
[b]Note:[/b] This flag is implemented only on macOS.
</constant>
<constant name="WINDOW_EVENT_FORCE_CLOSE" value="8" enum="WindowEvent">
Sent when the window has been forcibly closed by the Display Server. The window shall immediately hide and clean any internal rendering references.
[b]Note:[/b] This flag is implemented only on Linux (Wayland).
</constant>
<constant name="WINDOW_EDGE_TOP_LEFT" value="0" enum="WindowResizeEdge">
Top-left edge of a window.
</constant>

View file

@ -11,6 +11,7 @@
<members>
<member name="borderless" type="bool" setter="set_flag" getter="get_flag" overrides="Window" default="true" />
<member name="popup_window" type="bool" setter="set_flag" getter="get_flag" overrides="Window" default="true" />
<member name="popup_wm_hint" type="bool" setter="set_flag" getter="get_flag" overrides="Window" default="true" />
<member name="transient" type="bool" setter="set_transient" getter="is_transient" overrides="Window" default="true" />
<member name="unresizable" type="bool" setter="set_flag" getter="get_flag" overrides="Window" default="true" />
<member name="visible" type="bool" setter="set_visible" getter="is_visible" overrides="Window" default="false" />

View file

@ -670,6 +670,9 @@
<member name="popup_window" type="bool" setter="set_flag" getter="get_flag" default="false">
If [code]true[/code], the [Window] will be considered a popup. Popups are sub-windows that don't show as separate windows in system's window manager's window list and will send close request when anything is clicked outside of them (unless [member exclusive] is enabled).
</member>
<member name="popup_wm_hint" type="bool" setter="set_flag" getter="get_flag" default="false">
If [code]true[/code], the [Window] will signal to the window manager that it is supposed to be an implementation-defined "popup" (usually a floating, borderless, untileable and immovable child window).
</member>
<member name="position" type="Vector2i" setter="set_position" getter="get_position" default="Vector2i(0, 0)">
The window's position in pixels.
If [member ProjectSettings.display/window/subwindows/embed_subwindows] is [code]false[/code], the position is in absolute screen coordinates. This typically applies to editor plugins. If the setting is [code]true[/code], the window's position is in the coordinates of its parent [Viewport].
@ -879,7 +882,10 @@
[b]Note:[/b] This flag is implemented on macOS and Windows.
[b]Note:[/b] Setting this flag will [b]NOT[/b] prevent other apps from capturing an image, it should not be used as a security measure.
</constant>
<constant name="FLAG_MAX" value="10" enum="Flags">
<constant name="FLAG_POPUP_WM_HINT" value="10" enum="Flags">
Signals the window manager that this window is supposed to be an implementation-defined "popup" (usually a floating, borderless, untileable and immovable child window).
</constant>
<constant name="FLAG_MAX" value="11" enum="Flags">
Max value of the [enum Flags].
</constant>
<constant name="CONTENT_SCALE_MODE_DISABLED" value="0" enum="ContentScaleMode">

File diff suppressed because it is too large Load diff

View file

@ -68,7 +68,15 @@
class DisplayServerWayland : public DisplayServer {
// No need to register with GDCLASS, it's platform-specific and nothing is added.
struct WindowData {
WindowID id;
WindowID id = INVALID_WINDOW_ID;
WindowID parent_id = INVALID_WINDOW_ID;
// For popups.
WindowID root_id = INVALID_WINDOW_ID;
// For toplevels.
List<WindowID> popup_stack;
Rect2i rect;
Size2i max_size;
@ -76,6 +84,8 @@ class DisplayServerWayland : public DisplayServer {
Rect2i safe_rect;
bool emulate_vsync = false;
#ifdef GLES3_ENABLED
struct wl_egl_window *wl_egl_window = nullptr;
#endif
@ -119,17 +129,25 @@ class DisplayServerWayland : public DisplayServer {
HashMap<CursorShape, CustomCursor> custom_cursors;
WindowData main_window;
HashMap<WindowID, WindowData> windows;
WindowID window_id_counter = MAIN_WINDOW_ID;
WaylandThread wayland_thread;
Context context;
bool swap_cancel_ok = false;
// NOTE: These are the based on WINDOW_FLAG_POPUP, which does NOT imply what it
// seems. It's particularly confusing for our usecase, but just know that these
// are the "take all input thx" windows while the `popup_stack` variable keeps
// track of all the generic floating window concept.
List<WindowID> popup_menu_list;
BitField<MouseButtonMask> last_mouse_monitor_mask;
String ime_text;
Vector2i ime_selection;
SuspendState suspend_state = SuspendState::NONE;
bool emulate_vsync = false;
String rendering_driver;
@ -155,14 +173,12 @@ class DisplayServerWayland : public DisplayServer {
#endif
static String _get_app_id_from_context(Context p_context);
void _send_window_event(WindowEvent p_event);
void _send_window_event(WindowEvent p_event, WindowID p_window_id = MAIN_WINDOW_ID);
static void dispatch_input_events(const Ref<InputEvent> &p_event);
void _dispatch_input_event(const Ref<InputEvent> &p_event);
void _resize_window(const Size2i &p_size);
virtual void _show_window();
void _update_window_rect(const Rect2i &p_rect, WindowID p_window_id = MAIN_WINDOW_ID);
void try_suspend();
@ -227,6 +243,14 @@ public:
virtual Vector<DisplayServer::WindowID> get_window_list() const override;
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) override;
virtual void show_window(WindowID p_id) override;
virtual void delete_sub_window(WindowID p_id) override;
virtual WindowID window_get_active_popup() const override;
virtual void window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) override;
virtual Rect2i window_get_popup_safe_rect(WindowID p_window) const override;
virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override;
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;

File diff suppressed because it is too large Load diff

View file

@ -95,8 +95,15 @@ public:
virtual ~Message() = default;
};
class WindowMessage : public Message {
GDSOFTCLASS(WindowMessage, Message);
public:
DisplayServer::WindowID id = DisplayServer::INVALID_WINDOW_ID;
};
// Message data for window rect changes.
class WindowRectMessage : public Message {
class WindowRectMessage : public WindowMessage {
GDSOFTCLASS(WindowRectMessage, Message);
public:
@ -105,7 +112,7 @@ public:
Rect2i rect;
};
class WindowEventMessage : public Message {
class WindowEventMessage : public WindowMessage {
GDSOFTCLASS(WindowEventMessage, Message);
public:
@ -119,14 +126,14 @@ public:
Ref<InputEvent> event;
};
class DropFilesEventMessage : public Message {
class DropFilesEventMessage : public WindowMessage {
GDSOFTCLASS(DropFilesEventMessage, Message);
public:
Vector<String> files;
};
class IMEUpdateEventMessage : public Message {
class IMEUpdateEventMessage : public WindowMessage {
GDSOFTCLASS(IMEUpdateEventMessage, Message);
public:
@ -134,7 +141,7 @@ public:
Vector2i selection;
};
class IMECommitEventMessage : public Message {
class IMECommitEventMessage : public WindowMessage {
GDSOFTCLASS(IMECommitEventMessage, Message);
public:
@ -215,7 +222,8 @@ public:
// TODO: Make private?
struct WindowState {
DisplayServer::WindowID id;
DisplayServer::WindowID id = DisplayServer::INVALID_WINDOW_ID;
DisplayServer::WindowID parent_id = DisplayServer::INVALID_WINDOW_ID;
Rect2i rect;
DisplayServer::WindowMode mode = DisplayServer::WINDOW_MODE_WINDOWED;
@ -237,6 +245,7 @@ public:
// be called even after being destroyed, pointing to probably invalid window
// data by then and segfaulting hard.
struct wl_callback *frame_callback = nullptr;
uint64_t last_frame_time = 0;
struct wl_surface *wl_surface = nullptr;
struct xdg_surface *xdg_surface = nullptr;
@ -250,6 +259,8 @@ public:
struct zxdg_exported_v2 *xdg_exported_v2 = nullptr;
struct xdg_popup *xdg_popup = nullptr;
String exported_handle;
// Currently applied buffer scale.
@ -335,6 +346,9 @@ public:
MouseButton last_button_pressed = MouseButton::NONE;
Point2 last_pressed_position;
DisplayServer::WindowID pointed_id = DisplayServer::INVALID_WINDOW_ID;
DisplayServer::WindowID last_pointed_id = DisplayServer::INVALID_WINDOW_ID;
// This is needed to check for a new double click every time.
bool double_click_begun = false;
@ -364,20 +378,17 @@ public:
bool double_click_begun = false;
// Note: the protocol doesn't have it (I guess that this isn't really meant to
// be used as a mouse...), but we'll hack one in with the current ticks.
uint64_t button_time = 0;
uint64_t motion_time = 0;
DisplayServer::WindowID proximal_id = DisplayServer::INVALID_WINDOW_ID;
DisplayServer::WindowID last_proximal_id = DisplayServer::INVALID_WINDOW_ID;
uint32_t proximity_serial = 0;
struct wl_surface *proximal_surface = nullptr;
};
struct TabletToolState {
struct wl_seat *wl_seat = nullptr;
struct wl_surface *last_surface = nullptr;
bool is_eraser = false;
TabletToolData data_pending;
@ -401,9 +412,6 @@ public:
uint32_t pointer_enter_serial = 0;
struct wl_surface *pointed_surface = nullptr;
struct wl_surface *last_pointed_surface = nullptr;
struct zwp_relative_pointer_v1 *wp_relative_pointer = nullptr;
struct zwp_locked_pointer_v1 *wp_locked_pointer = nullptr;
struct zwp_confined_pointer_v1 *wp_confined_pointer = nullptr;
@ -434,6 +442,9 @@ public:
// Keyboard.
struct wl_keyboard *wl_keyboard = nullptr;
// For key events.
DisplayServer::WindowID focused_id = DisplayServer::INVALID_WINDOW_ID;
struct xkb_context *xkb_context = nullptr;
struct xkb_keymap *xkb_keymap = nullptr;
struct xkb_state *xkb_state = nullptr;
@ -462,6 +473,7 @@ public:
struct wl_data_device *wl_data_device = nullptr;
// Drag and drop.
DisplayServer::WindowID dnd_id = DisplayServer::INVALID_WINDOW_ID;
struct wl_data_offer *wl_data_offer_dnd = nullptr;
uint32_t dnd_enter_serial = 0;
@ -486,6 +498,7 @@ public:
// IME.
struct zwp_text_input_v3 *wp_text_input = nullptr;
DisplayServer::WindowID ime_window_id = DisplayServer::INVALID_WINDOW_ID;
bool ime_enabled = false;
bool ime_active = false;
String ime_text;
@ -516,7 +529,7 @@ private:
Thread events_thread;
ThreadData thread_data;
WindowState main_window;
HashMap<DisplayServer::WindowID, WindowState> windows;
List<Ref<Message>> messages;
@ -628,6 +641,10 @@ private:
static void _xdg_toplevel_on_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height);
static void _xdg_toplevel_on_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, struct wl_array *capabilities);
static void _xdg_popup_on_configure(void *data, struct xdg_popup *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height);
static void _xdg_popup_on_popup_done(void *data, struct xdg_popup *xdg_popup);
static void _xdg_popup_on_repositioned(void *data, struct xdg_popup *xdg_popup, uint32_t token);
// wayland-protocols event handlers.
static void _wp_fractional_scale_on_preferred_scale(void *data, struct wp_fractional_scale_v1 *wp_fractional_scale_v1, uint32_t scale);
@ -783,6 +800,12 @@ private:
.wm_capabilities = _xdg_toplevel_on_wm_capabilities,
};
static constexpr struct xdg_popup_listener xdg_popup_listener = {
.configure = _xdg_popup_on_configure,
.popup_done = _xdg_popup_on_popup_done,
.repositioned = _xdg_popup_on_repositioned,
};
// wayland-protocols event listeners.
static constexpr struct wp_fractional_scale_v1_listener wp_fractional_scale_listener = {
.preferred_scale = _wp_fractional_scale_on_preferred_scale,
@ -968,8 +991,13 @@ public:
void beep() const;
void window_create(DisplayServer::WindowID p_window_id, int p_width, int p_height);
void window_create_popup(DisplayServer::WindowID p_window_id, DisplayServer::WindowID p_parent_id, Rect2i p_rect);
void window_destroy(DisplayServer::WindowID p_window_Id);
void window_set_parent(DisplayServer::WindowID p_window_id, DisplayServer::WindowID p_parent_id);
struct wl_surface *window_get_wl_surface(DisplayServer::WindowID p_window_id) const;
WindowState *window_get_state(DisplayServer::WindowID p_window_id);
void window_start_resize(DisplayServer::WindowResizeEdge p_edge, DisplayServer::WindowID p_window);
@ -1002,6 +1030,7 @@ public:
void pointer_set_hint(const Point2i &p_hint);
PointerConstraint pointer_get_constraint() const;
DisplayServer::WindowID pointer_get_pointed_window_id() const;
DisplayServer::WindowID pointer_get_last_pointed_window_id() const;
BitField<MouseButtonMask> pointer_get_button_mask() const;
void cursor_set_visible(bool p_visible);
@ -1040,6 +1069,8 @@ public:
bool get_reset_frame();
bool wait_frame_suspend_ms(int p_timeout);
uint64_t window_get_last_frame_time(DisplayServer::WindowID p_window_id) const;
bool window_is_suspended(DisplayServer::WindowID p_window_id) const;
bool is_suspended() const;
Error init();

View file

@ -167,6 +167,11 @@ Rect2i Popup::_popup_adjust_rect() const {
Rect2i current(get_position(), get_size());
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_SELF_FITTING_WINDOWS)) {
// We're fine as is, the Display Server will take care of that for us.
return current;
}
if (current.position.x + current.size.x > parent_rect.position.x + parent_rect.size.x) {
current.position.x = parent_rect.position.x + parent_rect.size.x - current.size.x;
}
@ -219,6 +224,7 @@ Popup::Popup() {
set_flag(FLAG_BORDERLESS, true);
set_flag(FLAG_RESIZE_DISABLED, true);
set_flag(FLAG_POPUP, true);
set_flag(FLAG_POPUP_WM_HINT, true);
}
Popup::~Popup() {

View file

@ -835,6 +835,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_FORCE_CLOSE: {
hide();
} break;
}
}
@ -1859,12 +1862,19 @@ void Window::popup(const Rect2i &p_screen_rect) {
// Update window size to calculate the actual window size based on contents minimum size and minimum size.
_update_window_size();
bool should_fit = !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_SELF_FITTING_WINDOWS);
if (p_screen_rect != Rect2i()) {
set_position(p_screen_rect.position);
int screen_id = DisplayServer::get_singleton()->get_screen_from_rect(p_screen_rect);
Size2i screen_size = DisplayServer::get_singleton()->screen_get_usable_rect(screen_id).size;
Size2i new_size = p_screen_rect.size.min(screen_size);
set_size(new_size);
if (should_fit) {
int screen_id = DisplayServer::get_singleton()->get_screen_from_rect(p_screen_rect);
Size2i screen_size = DisplayServer::get_singleton()->screen_get_usable_rect(screen_id).size;
Size2i new_size = p_screen_rect.size.min(screen_size);
set_size(new_size);
} else {
set_size(p_screen_rect.size);
}
}
Rect2i adjust = _popup_adjust_rect();
@ -1892,7 +1902,7 @@ void Window::popup(const Rect2i &p_screen_rect) {
int screen_id = DisplayServer::get_singleton()->window_get_current_screen(get_window_id());
parent_rect = DisplayServer::get_singleton()->screen_get_usable_rect(screen_id);
}
if (parent_rect != Rect2i() && !parent_rect.intersects(Rect2i(position, size))) {
if (should_fit && parent_rect != Rect2i() && !parent_rect.intersects(Rect2i(position, size))) {
ERR_PRINT(vformat("Window %d spawned at invalid position: %s.", get_window_id(), position));
set_position((parent_rect.size - size) / 2);
}
@ -3095,6 +3105,7 @@ void Window::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "mouse_passthrough"), "set_flag", "get_flag", FLAG_MOUSE_PASSTHROUGH);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "sharp_corners"), "set_flag", "get_flag", FLAG_SHARP_CORNERS);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "exclude_from_capture"), "set_flag", "get_flag", FLAG_EXCLUDE_FROM_CAPTURE);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "popup_wm_hint"), "set_flag", "get_flag", FLAG_POPUP_WM_HINT);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_native"), "set_force_native", "get_force_native");
ADD_GROUP("Limits", "");
@ -3151,6 +3162,7 @@ void Window::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_MOUSE_PASSTHROUGH);
BIND_ENUM_CONSTANT(FLAG_SHARP_CORNERS);
BIND_ENUM_CONSTANT(FLAG_EXCLUDE_FROM_CAPTURE);
BIND_ENUM_CONSTANT(FLAG_POPUP_WM_HINT);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(CONTENT_SCALE_MODE_DISABLED);

View file

@ -64,6 +64,7 @@ public:
FLAG_MOUSE_PASSTHROUGH = DisplayServer::WINDOW_FLAG_MOUSE_PASSTHROUGH,
FLAG_SHARP_CORNERS = DisplayServer::WINDOW_FLAG_SHARP_CORNERS,
FLAG_EXCLUDE_FROM_CAPTURE = DisplayServer::WINDOW_FLAG_EXCLUDE_FROM_CAPTURE,
FLAG_POPUP_WM_HINT = DisplayServer::WINDOW_FLAG_POPUP_WM_HINT,
FLAG_MAX = DisplayServer::WINDOW_FLAG_MAX,
};

View file

@ -1120,6 +1120,7 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(FEATURE_NATIVE_DIALOG_FILE_MIME);
BIND_ENUM_CONSTANT(FEATURE_EMOJI_AND_SYMBOL_PICKER);
BIND_ENUM_CONSTANT(FEATURE_NATIVE_COLOR_PICKER);
BIND_ENUM_CONSTANT(FEATURE_SELF_FITTING_WINDOWS);
BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE);
BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN);
@ -1195,6 +1196,7 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(WINDOW_FLAG_MOUSE_PASSTHROUGH);
BIND_ENUM_CONSTANT(WINDOW_FLAG_SHARP_CORNERS);
BIND_ENUM_CONSTANT(WINDOW_FLAG_EXCLUDE_FROM_CAPTURE);
BIND_ENUM_CONSTANT(WINDOW_FLAG_POPUP_WM_HINT);
BIND_ENUM_CONSTANT(WINDOW_FLAG_MAX);
BIND_ENUM_CONSTANT(WINDOW_EVENT_MOUSE_ENTER);
@ -1205,6 +1207,7 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(WINDOW_EVENT_GO_BACK_REQUEST);
BIND_ENUM_CONSTANT(WINDOW_EVENT_DPI_CHANGE);
BIND_ENUM_CONSTANT(WINDOW_EVENT_TITLEBAR_CHANGE);
BIND_ENUM_CONSTANT(WINDOW_EVENT_FORCE_CLOSE);
BIND_ENUM_CONSTANT(WINDOW_EDGE_TOP_LEFT);
BIND_ENUM_CONSTANT(WINDOW_EDGE_TOP);

View file

@ -166,6 +166,7 @@ public:
FEATURE_NATIVE_DIALOG_FILE_MIME,
FEATURE_EMOJI_AND_SYMBOL_PICKER,
FEATURE_NATIVE_COLOR_PICKER,
FEATURE_SELF_FITTING_WINDOWS,
};
virtual bool has_feature(Feature p_feature) const = 0;
@ -406,6 +407,7 @@ public:
WINDOW_FLAG_MOUSE_PASSTHROUGH,
WINDOW_FLAG_SHARP_CORNERS,
WINDOW_FLAG_EXCLUDE_FROM_CAPTURE,
WINDOW_FLAG_POPUP_WM_HINT,
WINDOW_FLAG_MAX,
};
@ -421,6 +423,7 @@ public:
WINDOW_FLAG_MOUSE_PASSTHROUGH_BIT = (1 << WINDOW_FLAG_MOUSE_PASSTHROUGH),
WINDOW_FLAG_SHARP_CORNERS_BIT = (1 << WINDOW_FLAG_SHARP_CORNERS),
WINDOW_FLAG_EXCLUDE_FROM_CAPTURE_BIT = (1 << WINDOW_FLAG_EXCLUDE_FROM_CAPTURE),
WINDOW_FLAG_POPUP_WM_HINT_BIT = (1 << WINDOW_FLAG_POPUP_WM_HINT),
};
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);
@ -449,6 +452,7 @@ public:
WINDOW_EVENT_GO_BACK_REQUEST,
WINDOW_EVENT_DPI_CHANGE,
WINDOW_EVENT_TITLEBAR_CHANGE,
WINDOW_EVENT_FORCE_CLOSE,
};
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;