summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/variant/variant.cpp2
-rw-r--r--core/variant/variant_op.h32
-rw-r--r--doc/classes/AnimationMixer.xml7
-rw-r--r--doc/classes/Basis.xml2
-rw-r--r--doc/classes/CanvasItem.xml4
-rw-r--r--doc/classes/Transform3D.xml2
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.cpp7
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp41
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h4
-rw-r--r--platform/web/js/libs/library_godot_audio.js10
-rw-r--r--servers/display_server_headless.h23
-rw-r--r--tests/display_server_mock.h22
12 files changed, 94 insertions, 62 deletions
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 30a8facd67..c1ef31c784 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -951,7 +951,7 @@ bool Variant::is_zero() const {
return *reinterpret_cast<const ::RID *>(_data._mem) == ::RID();
}
case OBJECT: {
- return get_validated_object() == nullptr;
+ return _get_obj().obj == nullptr;
}
case CALLABLE: {
return reinterpret_cast<const Callable *>(_data._mem)->is_null();
diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h
index 0b94d79a97..ac39a4135f 100644
--- a/core/variant/variant_op.h
+++ b/core/variant/variant_op.h
@@ -548,14 +548,14 @@ public:
class OperatorEvaluatorEqualObject {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const ObjectID &a = VariantInternal::get_object_id(&p_left);
- const ObjectID &b = VariantInternal::get_object_id(&p_right);
+ const Object *a = p_left.get_validated_object();
+ const Object *b = p_right.get_validated_object();
*r_ret = a == b;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const ObjectID &a = VariantInternal::get_object_id(left);
- const ObjectID &b = VariantInternal::get_object_id(right);
+ const Object *a = left->get_validated_object();
+ const Object *b = right->get_validated_object();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == b;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@@ -567,12 +567,12 @@ public:
class OperatorEvaluatorEqualObjectNil {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Object *a = p_left.operator Object *();
+ const Object *a = p_left.get_validated_object();
*r_ret = a == nullptr;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const Object *a = left->operator Object *();
+ const Object *a = left->get_validated_object();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == nullptr;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@@ -584,12 +584,12 @@ public:
class OperatorEvaluatorEqualNilObject {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Object *b = p_right.operator Object *();
+ const Object *b = p_right.get_validated_object();
*r_ret = nullptr == b;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const Object *b = right->operator Object *();
+ const Object *b = right->get_validated_object();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr == b;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@@ -619,14 +619,14 @@ public:
class OperatorEvaluatorNotEqualObject {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const ObjectID &a = VariantInternal::get_object_id(&p_left);
- const ObjectID &b = VariantInternal::get_object_id(&p_right);
+ Object *a = p_left.get_validated_object();
+ Object *b = p_right.get_validated_object();
*r_ret = a != b;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const ObjectID &a = VariantInternal::get_object_id(left);
- const ObjectID &b = VariantInternal::get_object_id(right);
+ Object *a = left->get_validated_object();
+ Object *b = right->get_validated_object();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != b;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@@ -638,12 +638,12 @@ public:
class OperatorEvaluatorNotEqualObjectNil {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- Object *a = p_left.operator Object *();
+ Object *a = p_left.get_validated_object();
*r_ret = a != nullptr;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- Object *a = left->operator Object *();
+ Object *a = left->get_validated_object();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != nullptr;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@@ -655,12 +655,12 @@ public:
class OperatorEvaluatorNotEqualNilObject {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- Object *b = p_right.operator Object *();
+ Object *b = p_right.get_validated_object();
*r_ret = nullptr != b;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- Object *b = right->operator Object *();
+ Object *b = right->get_validated_object();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr != b;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
diff --git a/doc/classes/AnimationMixer.xml b/doc/classes/AnimationMixer.xml
index c635eba2ab..25fcecda4b 100644
--- a/doc/classes/AnimationMixer.xml
+++ b/doc/classes/AnimationMixer.xml
@@ -27,6 +27,13 @@
<param index="1" name="library" type="AnimationLibrary" />
<description>
Adds [param library] to the animation player, under the key [param name].
+ AnimationMixer has a global library by default with an empty string as key. For adding an animation to the global library:
+ [codeblocks]
+ [gdscript]
+ var global_library = mixer.get_animation_library("")
+ global_library.add_animation("animation_name", animation_resource)
+ [/gdscript]
+ [/codeblocks]
</description>
</method>
<method name="advance">
diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml
index 41bda1033d..322d2ab9d4 100644
--- a/doc/classes/Basis.xml
+++ b/doc/classes/Basis.xml
@@ -8,7 +8,7 @@
A [Basis] is composed by 3 axis vectors, each representing a column of the matrix: [member x], [member y], and [member z]. The length of each axis ([method Vector3.length]) influences the basis's scale, while the direction of all axes influence the rotation. Usually, these axes are perpendicular to one another. However, when you rotate any axis individually, the basis becomes sheared. Applying a sheared basis to a 3D model will make the model appear distorted.
A [Basis] is [b]orthogonal[/b] if its axes are perpendicular to each other. A basis is [b]normalized[/b] if the length of every axis is [code]1[/code]. A basis is [b]uniform[/b] if all axes share the same length (see [method get_scale]). A basis is [b]orthonormal[/b] if it is both orthogonal and normalized, which allows it to only represent rotations. A basis is [b]conformal[/b] if it is both orthogonal and uniform, which ensures it is not distorted.
For a general introduction, see the [url=$DOCS_URL/tutorials/math/matrices_and_transforms.html]Matrices and transforms[/url] tutorial.
- [b]Note:[/b] Godot uses a [url=https://en.wikipedia.org/wiki/Right-hand_rule]right-handed coordinate system[/url], which is a common standard. For directions, the convention for built-in types like [Camera3D] is for -Z to point forward (+X is right, +Y is up, and +Z is back). Other objects may use different direction conventions. For more information, see the [url=$DOCS_URL/tutorials/assets_pipeline/importing_scenes.html#d-asset-direction-conventions]Importing 3D Scenes[/url] tutorial.
+ [b]Note:[/b] Godot uses a [url=https://en.wikipedia.org/wiki/Right-hand_rule]right-handed coordinate system[/url], which is a common standard. For directions, the convention for built-in types like [Camera3D] is for -Z to point forward (+X is right, +Y is up, and +Z is back). Other objects may use different direction conventions. For more information, see the [url=$DOCS_URL/tutorials/assets_pipeline/importing_3d_scenes/model_export_considerations.html#d-asset-direction-conventions]3D asset direction conventions[/url] tutorial.
[b]Note:[/b] The basis matrices are exposed as [url=https://www.mindcontrol.org/~hplus/graphics/matrix-layout.html]column-major[/url] order, which is the same as OpenGL. However, they are stored internally in row-major order, which is the same as DirectX.
</description>
<tutorials>
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 207045b065..8bee6c3470 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -623,8 +623,8 @@
[b]Note:[/b] For controls that inherit [Popup], the correct way to make them visible is to call one of the multiple [code]popup*()[/code] functions instead.
</member>
<member name="y_sort_enabled" type="bool" setter="set_y_sort_enabled" getter="is_y_sort_enabled" default="false">
- If [code]true[/code], this and child [CanvasItem] nodes with a lower Y position are rendered in front of nodes with a higher Y position. If [code]false[/code], this and child [CanvasItem] nodes are rendered normally in scene tree order.
- With Y-sorting enabled on a parent node ('A') but disabled on a child node ('B'), the child node ('B') is sorted but its children ('C1', 'C2', etc) render together on the same Y position as the child node 'B'. This allows you to organize the render order of a scene without changing the scene tree.
+ If [code]true[/code], this and child [CanvasItem] nodes with a higher Y position are rendered in front of nodes with a lower Y position. If [code]false[/code], this and child [CanvasItem] nodes are rendered normally in scene tree order.
+ With Y-sorting enabled on a parent node ('A') but disabled on a child node ('B'), the child node ('B') is sorted but its children ('C1', 'C2', etc) render together on the same Y position as the child node ('B'). This allows you to organize the render order of a scene without changing the scene tree.
Nodes sort relative to each other only if they are on the same [member z_index].
</member>
<member name="z_as_relative" type="bool" setter="set_z_as_relative" getter="is_z_relative" default="true">
diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml
index 2a0bbc46af..30c141659a 100644
--- a/doc/classes/Transform3D.xml
+++ b/doc/classes/Transform3D.xml
@@ -6,7 +6,7 @@
<description>
The [Transform3D] built-in [Variant] type is a 3×4 matrix representing a transformation in 3D space. It contains a [Basis], which on its own can represent rotation, scale, and shear. Additionally, combined with its own [member origin], the transform can also represent a translation.
For a general introduction, see the [url=$DOCS_URL/tutorials/math/matrices_and_transforms.html]Matrices and transforms[/url] tutorial.
- [b]Note:[/b] Godot uses a [url=https://en.wikipedia.org/wiki/Right-hand_rule]right-handed coordinate system[/url], which is a common standard. For directions, the convention for built-in types like [Camera3D] is for -Z to point forward (+X is right, +Y is up, and +Z is back). Other objects may use different direction conventions. For more information, see the [url=$DOCS_URL/tutorials/assets_pipeline/importing_scenes.html#d-asset-direction-conventions]Importing 3D Scenes[/url] tutorial.
+ [b]Note:[/b] Godot uses a [url=https://en.wikipedia.org/wiki/Right-hand_rule]right-handed coordinate system[/url], which is a common standard. For directions, the convention for built-in types like [Camera3D] is for -Z to point forward (+X is right, +Y is up, and +Z is back). Other objects may use different direction conventions. For more information, see the [url=$DOCS_URL/tutorials/assets_pipeline/importing_3d_scenes/model_export_considerations.html#d-asset-direction-conventions]3D asset direction conventions[/url] tutorial.
</description>
<tutorials>
<link title="Math documentation index">$DOCS_URL/tutorials/math/index.html</link>
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index ca2b392e66..a6bce1d79a 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp
@@ -872,6 +872,7 @@ RDD::BufferID RenderingDeviceDriverD3D12::buffer_create(uint64_t p_size, BitFiel
D3D12MA::ALLOCATION_DESC allocation_desc = {};
allocation_desc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
+ D3D12_RESOURCE_STATES initial_state = D3D12_RESOURCE_STATE_COMMON;
switch (p_allocation_type) {
case MEMORY_ALLOCATION_TYPE_CPU: {
bool is_src = p_usage.has_flag(BUFFER_USAGE_TRANSFER_FROM_BIT);
@@ -879,10 +880,12 @@ RDD::BufferID RenderingDeviceDriverD3D12::buffer_create(uint64_t p_size, BitFiel
if (is_src && !is_dst) {
// Looks like a staging buffer: CPU maps, writes sequentially, then GPU copies to VRAM.
allocation_desc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
+ initial_state = D3D12_RESOURCE_STATE_GENERIC_READ;
}
if (is_dst && !is_src) {
// Looks like a readback buffer: GPU copies from VRAM, then CPU maps and reads.
allocation_desc.HeapType = D3D12_HEAP_TYPE_READBACK;
+ initial_state = D3D12_RESOURCE_STATE_COPY_DEST;
}
} break;
case MEMORY_ALLOCATION_TYPE_GPU: {
@@ -911,7 +914,7 @@ RDD::BufferID RenderingDeviceDriverD3D12::buffer_create(uint64_t p_size, BitFiel
res = allocator->CreateResource(
&allocation_desc,
reinterpret_cast<const D3D12_RESOURCE_DESC *>(&resource_desc),
- D3D12_RESOURCE_STATE_COMMON,
+ initial_state,
nullptr,
allocation.GetAddressOf(),
IID_PPV_ARGS(buffer.GetAddressOf()));
@@ -925,7 +928,7 @@ RDD::BufferID RenderingDeviceDriverD3D12::buffer_create(uint64_t p_size, BitFiel
buf_info->resource = buffer.Get();
buf_info->owner_info.resource = buffer;
buf_info->owner_info.allocation = allocation;
- buf_info->owner_info.states.subresource_states.push_back(D3D12_RESOURCE_STATE_COMMON);
+ buf_info->owner_info.states.subresource_states.push_back(initial_state);
buf_info->states_ptr = &buf_info->owner_info.states;
buf_info->size = p_size;
buf_info->flags.usable_as_uav = (resource_desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 5b4341bbe1..4e2940a8cb 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -780,19 +780,24 @@ bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_po
return still_selected;
}
-List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retrieve_locked, bool remove_canvas_item_if_parent_in_selection) const {
+List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool p_retrieve_locked, bool p_remove_canvas_item_if_parent_in_selection, bool *r_has_locked_items) const {
List<CanvasItem *> selection;
for (const KeyValue<Node *, Object *> &E : editor_selection->get_selection()) {
CanvasItem *ci = Object::cast_to<CanvasItem>(E.key);
- if (ci && ci->is_visible_in_tree() && ci->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (retrieve_locked || !_is_node_locked(ci))) {
- CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(ci);
- if (se) {
- selection.push_back(ci);
+ if (ci) {
+ if (ci->is_visible_in_tree() && ci->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (p_retrieve_locked || !_is_node_locked(ci))) {
+ CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(ci);
+ if (se) {
+ selection.push_back(ci);
+ }
+ } else if (r_has_locked_items) {
+ // CanvasItem is selected, but can't be interacted with.
+ *r_has_locked_items = true;
}
}
}
- if (remove_canvas_item_if_parent_in_selection) {
+ if (p_remove_canvas_item_if_parent_in_selection) {
List<CanvasItem *> filtered_selection;
for (CanvasItem *E : selection) {
if (!selection.find(E->get_parent())) {
@@ -1454,7 +1459,8 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) {
if (drag_type == DRAG_NONE) {
if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed()) {
if ((b->is_command_or_control_pressed() && !b->is_alt_pressed() && tool == TOOL_SELECT) || tool == TOOL_ROTATE) {
- List<CanvasItem *> selection = _get_edited_canvas_items();
+ bool has_locked_items = false;
+ List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items);
// Remove not movable nodes
for (CanvasItem *E : selection) {
@@ -1477,6 +1483,11 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) {
}
_save_canvas_item_state(drag_selection);
return true;
+ } else {
+ if (has_locked_items) {
+ EditorToaster::get_singleton()->popup_str(TTR(locked_transform_warning), EditorToaster::SEVERITY_WARNING);
+ }
+ return has_locked_items;
}
}
}
@@ -1917,7 +1928,8 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
// Drag resize handles
if (drag_type == DRAG_NONE) {
if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && ((b->is_alt_pressed() && b->is_command_or_control_pressed()) || tool == TOOL_SCALE)) {
- List<CanvasItem *> selection = _get_edited_canvas_items();
+ bool has_locked_items = false;
+ List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items);
if (selection.size() == 1) {
CanvasItem *ci = selection.front()->get();
@@ -1946,6 +1958,11 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
_save_canvas_item_state(drag_selection);
return true;
}
+ } else {
+ if (has_locked_items) {
+ EditorToaster::get_singleton()->popup_str(TTR(locked_transform_warning), EditorToaster::SEVERITY_WARNING);
+ }
+ return has_locked_items;
}
}
}
@@ -2056,7 +2073,8 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
//Start moving the nodes
if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed()) {
if ((b->is_alt_pressed() && !b->is_command_or_control_pressed()) || tool == TOOL_MOVE) {
- List<CanvasItem *> selection = _get_edited_canvas_items();
+ bool has_locked_items = false;
+ List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items);
if (selection.size() > 0) {
drag_selection.clear();
@@ -2089,6 +2107,11 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
_save_canvas_item_state(drag_selection);
return true;
+ } else {
+ if (has_locked_items) {
+ EditorToaster::get_singleton()->popup_str(TTR(locked_transform_warning), EditorToaster::SEVERITY_WARNING);
+ }
+ return has_locked_items;
}
}
}
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index a9de5e9a0b..bae9efebc9 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -188,6 +188,8 @@ private:
GRID_VISIBILITY_HIDE,
};
+ const String locked_transform_warning = TTRC("All selected CanvasItems are either invisible or locked in some way and can't be transformed.");
+
bool selection_menu_additive_selection = false;
Tool tool = TOOL_SELECT;
@@ -430,7 +432,7 @@ private:
ThemePreviewMode theme_preview = THEME_PREVIEW_PROJECT;
void _switch_theme_preview(int p_mode);
- List<CanvasItem *> _get_edited_canvas_items(bool retrieve_locked = false, bool remove_canvas_item_if_parent_in_selection = true) const;
+ List<CanvasItem *> _get_edited_canvas_items(bool p_retrieve_locked = false, bool p_remove_canvas_item_if_parent_in_selection = true, bool *r_has_locked_items = nullptr) const;
Rect2 _get_encompassing_rect_from_list(const List<CanvasItem *> &p_list);
void _expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D(), bool include_locked_nodes = true);
Rect2 _get_encompassing_rect(const Node *p_node);
diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js
index 4bca13d2d6..531dbdaeab 100644
--- a/platform/web/js/libs/library_godot_audio.js
+++ b/platform/web/js/libs/library_godot_audio.js
@@ -142,7 +142,7 @@ class Sample {
* @returns {void}
*/
clear() {
- this.audioBuffer = null;
+ this.setAudioBuffer(null);
GodotAudio.Sample.delete(this.id);
}
@@ -432,7 +432,7 @@ class SampleNode {
/** @type {number} */
this._playbackRate = 44100;
/** @type {LoopMode} */
- this.loopMode = 'disabled';
+ this.loopMode = options.loopMode ?? this.getSample().loopMode ?? 'disabled';
/** @type {number} */
this._pitchScale = 1;
/** @type {number} */
@@ -445,7 +445,6 @@ class SampleNode {
this._onended = null;
this.setPlaybackRate(options.playbackRate ?? 44100);
- this.loopMode = options.loopMode ?? this.getSample().loopMode ?? 'disabled';
this._source.buffer = this.getSample().getAudioBuffer();
this._addEndedListener();
@@ -777,8 +776,7 @@ class Bus {
*/
static move(fromIndex, toIndex) {
const movedBus = GodotAudio.Bus.getBus(fromIndex);
- let buses = GodotAudio.buses;
- buses = buses.filter((_, i) => i !== fromIndex);
+ const buses = GodotAudio.buses.filter((_, i) => i !== fromIndex);
// Inserts at index.
buses.splice(toIndex - 1, 0, movedBus);
GodotAudio.buses = buses;
@@ -1369,7 +1367,7 @@ const _GodotAudio = {
*/
set_sample_bus_volume_db: function (busIndex, volumeDb) {
const bus = GodotAudio.Bus.getBus(busIndex);
- bus.volumeDb = volumeDb;
+ bus.setVolumeDb(volumeDb);
},
/**
diff --git a/servers/display_server_headless.h b/servers/display_server_headless.h
index 7e4a517c1d..60422c16cc 100644
--- a/servers/display_server_headless.h
+++ b/servers/display_server_headless.h
@@ -51,7 +51,18 @@ private:
return memnew(DisplayServerHeadless());
}
+ static void _dispatch_input_events(const Ref<InputEvent> &p_event) {
+ static_cast<DisplayServerHeadless *>(get_singleton())->_dispatch_input_event(p_event);
+ }
+
+ void _dispatch_input_event(const Ref<InputEvent> &p_event) {
+ if (input_event_callback.is_valid()) {
+ input_event_callback.call(p_event);
+ }
+ }
+
NativeMenu *native_menu = nullptr;
+ Callable input_event_callback;
public:
bool has_feature(Feature p_feature) const override { return false; }
@@ -86,7 +97,11 @@ public:
void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {}
void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {}
- void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {}
+
+ void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {
+ input_event_callback = p_callable;
+ }
+
void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {}
void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {}
@@ -137,7 +152,9 @@ public:
int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override { return 0; }
- void process_events() override {}
+ void process_events() override {
+ Input::get_singleton()->flush_buffered_events();
+ }
void set_native_icon(const String &p_filename) override {}
void set_icon(const Ref<Image> &p_icon) override {}
@@ -179,7 +196,9 @@ public:
DisplayServerHeadless() {
native_menu = memnew(NativeMenu);
+ Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
}
+
~DisplayServerHeadless() {
if (native_menu) {
memdelete(native_menu);
diff --git a/tests/display_server_mock.h b/tests/display_server_mock.h
index e4946995a7..b44ff06b35 100644
--- a/tests/display_server_mock.h
+++ b/tests/display_server_mock.h
@@ -36,7 +36,7 @@
#include "servers/rendering/dummy/rasterizer_dummy.h"
// Specialized DisplayServer for unittests based on DisplayServerHeadless, that
-// additionally supports rudimentary InputEvent handling and mouse position.
+// additionally supports things like mouse enter/exit events and clipboard.
class DisplayServerMock : public DisplayServerHeadless {
private:
friend class DisplayServer;
@@ -45,7 +45,6 @@ private:
CursorShape cursor_shape = CursorShape::CURSOR_ARROW;
bool window_over = false;
Callable event_callback;
- Callable input_event_callback;
String clipboard_text;
String primary_clipboard_text;
@@ -62,16 +61,6 @@ private:
return memnew(DisplayServerMock());
}
- static void _dispatch_input_events(const Ref<InputEvent> &p_event) {
- static_cast<DisplayServerMock *>(get_singleton())->_dispatch_input_event(p_event);
- }
-
- void _dispatch_input_event(const Ref<InputEvent> &p_event) {
- if (input_event_callback.is_valid()) {
- input_event_callback.call(p_event);
- }
- }
-
void _set_mouse_position(const Point2i &p_position) {
if (mouse_position == p_position) {
return;
@@ -153,18 +142,9 @@ public:
event_callback = p_callable;
}
- virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {
- input_event_callback = p_callable;
- }
-
static void register_mock_driver() {
register_create_function("mock", create_func, get_rendering_drivers_func);
}
-
- DisplayServerMock() {
- Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
- }
- ~DisplayServerMock() {}
};
#endif // DISPLAY_SERVER_MOCK_H