mirror of
https://github.com/godotengine/godot
synced 2024-09-30 04:53:46 +00:00
Compare commits
29 commits
e1648b3327
...
0810ecaafd
Author | SHA1 | Date | |
---|---|---|---|
0810ecaafd | |||
935a6ef46b | |||
ea3566f2ed | |||
14a4408e02 | |||
026549db2e | |||
5300daaff2 | |||
2b710bc336 | |||
8612c12be6 | |||
925784df03 | |||
1c42e141d0 | |||
cf2bf08b41 | |||
534369d267 | |||
b8ef55a427 | |||
92a6586fb6 | |||
4cfdd25384 | |||
6d9c9d30b9 | |||
361f3f1721 | |||
83cb968965 | |||
a47d4d57ca | |||
38a806e13f | |||
a2af839a59 | |||
bcecb4fe37 | |||
a0c4f849e0 | |||
7de1b1a40d | |||
bb9a00889a | |||
951349c481 | |||
7e2a8afb57 | |||
13b87c13c7 | |||
2bf2217b1a |
|
@ -1138,7 +1138,7 @@ Array ProjectSettings::get_global_class_list() {
|
|||
Ref<ConfigFile> cf;
|
||||
cf.instantiate();
|
||||
if (cf->load(get_global_class_list_path()) == OK) {
|
||||
script_classes = cf->get_value("", "list");
|
||||
script_classes = cf->get_value("", "list", Array());
|
||||
} else {
|
||||
#ifndef TOOLS_ENABLED
|
||||
// Script classes can't be recreated in exported project, so print an error.
|
||||
|
|
|
@ -891,6 +891,31 @@ void Input::parse_input_event(const Ref<InputEvent> &p_event) {
|
|||
|
||||
ERR_FAIL_COND(p_event.is_null());
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
uint64_t curr_frame = Engine::get_singleton()->get_process_frames();
|
||||
if (curr_frame != last_parsed_frame) {
|
||||
frame_parsed_events.clear();
|
||||
last_parsed_frame = curr_frame;
|
||||
frame_parsed_events.insert(p_event);
|
||||
} else if (frame_parsed_events.has(p_event)) {
|
||||
// It would be technically safe to send the same event in cases such as:
|
||||
// - After an explicit flush.
|
||||
// - In platforms using buffering when agile flushing is enabled, after one of the mid-frame flushes.
|
||||
// - If platform doesn't use buffering and event accumulation is disabled.
|
||||
// - If platform doesn't use buffering and the event type is not accumulable.
|
||||
// However, it wouldn't be reasonable to ask users to remember the full ruleset and be aware at all times
|
||||
// of the possibilites of the target platform, project settings and engine internals, which may change
|
||||
// without prior notice.
|
||||
// Therefore, the guideline is, "don't send the same event object more than once per frame".
|
||||
WARN_PRINT_ONCE(
|
||||
"An input event object is being parsed more than once in the same frame, which is unsafe.\n"
|
||||
"If you are generating events in a script, you have to instantiate a new event instead of sending the same one more than once, unless the original one was sent on an earlier frame.\n"
|
||||
"You can call duplicate() on the event to get a new instance with identical values.");
|
||||
} else {
|
||||
frame_parsed_events.insert(p_event);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (use_accumulated_input) {
|
||||
if (buffered_events.is_empty() || !buffered_events.back()->get()->accumulate(p_event)) {
|
||||
buffered_events.push_back(p_event);
|
||||
|
|
|
@ -223,6 +223,10 @@ private:
|
|||
void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated);
|
||||
|
||||
List<Ref<InputEvent>> buffered_events;
|
||||
#ifdef DEBUG_ENABLED
|
||||
HashSet<Ref<InputEvent>> frame_parsed_events;
|
||||
uint64_t last_parsed_frame = UINT64_MAX;
|
||||
#endif
|
||||
|
||||
friend class DisplayServer;
|
||||
|
||||
|
|
|
@ -67,6 +67,9 @@
|
|||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="blend_mode" type="int" setter="set_blend_mode" getter="get_blend_mode" enum="AnimationNodeBlendSpace1D.BlendMode" default="0">
|
||||
Controls the interpolation between animations. See [enum BlendMode] constants.
|
||||
</member>
|
||||
<member name="max_space" type="float" setter="set_max_space" getter="get_max_space" default="1.0">
|
||||
The blend space's axis's upper limit for the points' position. See [method add_blend_point].
|
||||
</member>
|
||||
|
@ -84,4 +87,15 @@
|
|||
Label of the virtual axis of the blend space.
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
<constant name="BLEND_MODE_INTERPOLATED" value="0" enum="BlendMode">
|
||||
The interpolation between animations is linear.
|
||||
</constant>
|
||||
<constant name="BLEND_MODE_DISCRETE" value="1" enum="BlendMode">
|
||||
The blend space plays the animation of the node the blending position is closest to. Useful for frame-by-frame 2D animations.
|
||||
</constant>
|
||||
<constant name="BLEND_MODE_DISCRETE_CARRY" value="2" enum="BlendMode">
|
||||
Similar to [constant BLEND_MODE_DISCRETE], but starts the new animation at the last animation's playback position.
|
||||
</constant>
|
||||
</constants>
|
||||
</class>
|
||||
|
|
|
@ -29,13 +29,6 @@
|
|||
Adds an [AudioEffect] effect to the bus [param bus_idx] at [param at_position].
|
||||
</description>
|
||||
</method>
|
||||
<method name="capture_get_device_list">
|
||||
<return type="PackedStringArray" />
|
||||
<description>
|
||||
Returns the names of all audio input devices detected on the system.
|
||||
[b]Note:[/b] [member ProjectSettings.audio/driver/enable_input] must be [code]true[/code] for audio input to work. See also that setting's description for caveats related to permissions and operating system privacy settings.
|
||||
</description>
|
||||
</method>
|
||||
<method name="generate_bus_layout" qualifiers="const">
|
||||
<return type="AudioBusLayout" />
|
||||
<description>
|
||||
|
@ -117,10 +110,11 @@
|
|||
Returns the volume of the bus at index [param bus_idx] in dB.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_device_list">
|
||||
<method name="get_input_device_list">
|
||||
<return type="PackedStringArray" />
|
||||
<description>
|
||||
Returns the names of all audio devices detected on the system.
|
||||
Returns the names of all audio input devices detected on the system.
|
||||
[b]Note:[/b] [member ProjectSettings.audio/driver/enable_input] must be [code]true[/code] for audio input to work. See also that setting's description for caveats related to permissions and operating system privacy settings.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_mix_rate" qualifiers="const">
|
||||
|
@ -129,6 +123,12 @@
|
|||
Returns the sample rate at the output of the [AudioServer].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_output_device_list">
|
||||
<return type="PackedStringArray" />
|
||||
<description>
|
||||
Returns the names of all audio output devices detected on the system.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_output_latency" qualifiers="const">
|
||||
<return type="float" />
|
||||
<description>
|
||||
|
@ -302,12 +302,12 @@
|
|||
<member name="bus_count" type="int" setter="set_bus_count" getter="get_bus_count" default="1">
|
||||
Number of available audio buses.
|
||||
</member>
|
||||
<member name="capture_device" type="String" setter="capture_set_device" getter="capture_get_device" default=""Default"">
|
||||
Name of the current device for audio input (see [method capture_get_device_list]). On systems with multiple audio inputs (such as analog, USB and HDMI audio), this can be used to select the audio input device. The value [code]"Default"[/code] will record audio on the system-wide default audio input. If an invalid device name is set, the value will be reverted back to [code]"Default"[/code].
|
||||
<member name="input_device" type="String" setter="set_input_device" getter="get_input_device" default=""Default"">
|
||||
Name of the current device for audio input (see [method get_input_device_list]). On systems with multiple audio inputs (such as analog, USB and HDMI audio), this can be used to select the audio input device. The value [code]"Default"[/code] will record audio on the system-wide default audio input. If an invalid device name is set, the value will be reverted back to [code]"Default"[/code].
|
||||
[b]Note:[/b] [member ProjectSettings.audio/driver/enable_input] must be [code]true[/code] for audio input to work. See also that setting's description for caveats related to permissions and operating system privacy settings.
|
||||
</member>
|
||||
<member name="device" type="String" setter="set_device" getter="get_device" default=""Default"">
|
||||
Name of the current device for audio output (see [method get_device_list]). On systems with multiple audio outputs (such as analog, USB and HDMI audio), this can be used to select the audio output device. The value [code]"Default"[/code] will play audio on the system-wide default audio output. If an invalid device name is set, the value will be reverted back to [code]"Default"[/code].
|
||||
<member name="output_device" type="String" setter="set_output_device" getter="get_output_device" default=""Default"">
|
||||
Name of the current device for audio output (see [method get_output_device_list]). On systems with multiple audio outputs (such as analog, USB and HDMI audio), this can be used to select the audio output device. The value [code]"Default"[/code] will play audio on the system-wide default audio output. If an invalid device name is set, the value will be reverted back to [code]"Default"[/code].
|
||||
</member>
|
||||
<member name="playback_speed_scale" type="float" setter="set_playback_speed_scale" getter="get_playback_speed_scale" default="1.0">
|
||||
Scales the rate at which audio is played (i.e. setting it to [code]0.5[/code] will make the audio be played at half its speed).
|
||||
|
|
|
@ -55,6 +55,18 @@
|
|||
[b]Note:[/b] The returned value is not the same as [member Node2D.global_position], as it is affected by the drag properties. It is also not the same as the current position if [member position_smoothing_enabled] is [code]true[/code] (see [method get_screen_center_position]).
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_current" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if this [Camera2D] is the active camera (see [method Viewport.get_camera_2d]).
|
||||
</description>
|
||||
</method>
|
||||
<method name="make_current">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Forces this [Camera2D] to become the current active one. [member enabled] must be [code]true[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="reset_smoothing">
|
||||
<return type="void" />
|
||||
<description>
|
||||
|
@ -83,9 +95,6 @@
|
|||
<member name="anchor_mode" type="int" setter="set_anchor_mode" getter="get_anchor_mode" enum="Camera2D.AnchorMode" default="1">
|
||||
The Camera2D's anchor point. See [enum AnchorMode] constants.
|
||||
</member>
|
||||
<member name="current" type="bool" setter="set_current" getter="is_current" default="false">
|
||||
If [code]true[/code], the camera acts as the active camera for its [Viewport] ancestor. Only one camera can be current in a given viewport, so setting a different camera in the same viewport [code]current[/code] will disable whatever camera was already active in that viewport.
|
||||
</member>
|
||||
<member name="custom_viewport" type="Node" setter="set_custom_viewport" getter="get_custom_viewport">
|
||||
The custom [Viewport] node attached to the [Camera2D]. If [code]null[/code] or not a [Viewport], uses the default viewport instead.
|
||||
</member>
|
||||
|
@ -124,6 +133,10 @@
|
|||
<member name="editor_draw_screen" type="bool" setter="set_screen_drawing_enabled" getter="is_screen_drawing_enabled" default="true">
|
||||
If [code]true[/code], draws the camera's screen rectangle in the editor.
|
||||
</member>
|
||||
<member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true">
|
||||
Controls whether the camera can be active or not. If [code]true[/code], the [Camera2D] will become the main camera when it enters the scene tree and there is no active camera currently (see [method Viewport.get_camera_2d]).
|
||||
When the camera is currently active and [member enabled] is set to [code]false[/code], the next enabled [Camera2D] in the scene tree will become active.
|
||||
</member>
|
||||
<member name="ignore_rotation" type="bool" setter="set_ignore_rotation" getter="is_ignoring_rotation" default="true">
|
||||
If [code]true[/code], the camera's rendered view is not affected by its [member Node2D.rotation] and [member Node2D.global_rotation].
|
||||
</member>
|
||||
|
|
|
@ -426,6 +426,9 @@
|
|||
<member name="debug/gdscript/warnings/redundant_await" type="int" setter="" getter="" default="1">
|
||||
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a function that is not a coroutine is called with await.
|
||||
</member>
|
||||
<member name="debug/gdscript/warnings/renamed_in_godot_4_hint" type="bool" setter="" getter="" default="1">
|
||||
When enabled, using a property, enum, or function that was renamed since Godot 3 will produce a hint if an error occurs.
|
||||
</member>
|
||||
<member name="debug/gdscript/warnings/return_value_discarded" type="int" setter="" getter="" default="0">
|
||||
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a function without using its return value (by assigning it to a variable or using it as a function argument). Such return values are sometimes used to denote possible errors using the [enum Error] enum.
|
||||
</member>
|
||||
|
@ -474,6 +477,9 @@
|
|||
<member name="debug/gdscript/warnings/unsafe_property_access" type="int" setter="" getter="" default="0">
|
||||
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when accessing a property whose presence is not guaranteed at compile-time in the class.
|
||||
</member>
|
||||
<member name="debug/gdscript/warnings/unsafe_void_return" type="int" setter="" getter="" default="0">
|
||||
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when returning a call from a [code]void[/code] function when such call cannot be guaranteed to be also [code]void[/code].
|
||||
</member>
|
||||
<member name="debug/gdscript/warnings/unused_local_constant" type="int" setter="" getter="" default="1">
|
||||
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local constant is never used.
|
||||
</member>
|
||||
|
|
|
@ -44,10 +44,10 @@ OSStatus AudioDriverCoreAudio::input_device_address_cb(AudioObjectID inObjectID,
|
|||
void *inClientData) {
|
||||
AudioDriverCoreAudio *driver = static_cast<AudioDriverCoreAudio *>(inClientData);
|
||||
|
||||
// If our selected device is the Default call set_device to update the
|
||||
// If our selected input device is the Default, call set_input_device to update the
|
||||
// kAudioOutputUnitProperty_CurrentDevice property
|
||||
if (driver->capture_device_name == "Default") {
|
||||
driver->capture_set_device("Default");
|
||||
if (driver->input_device_name == "Default") {
|
||||
driver->set_input_device("Default");
|
||||
}
|
||||
|
||||
return noErr;
|
||||
|
@ -58,10 +58,10 @@ OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID
|
|||
void *inClientData) {
|
||||
AudioDriverCoreAudio *driver = static_cast<AudioDriverCoreAudio *>(inClientData);
|
||||
|
||||
// If our selected device is the Default call set_device to update the
|
||||
// If our selected output device is the Default call set_output_device to update the
|
||||
// kAudioOutputUnitProperty_CurrentDevice property
|
||||
if (driver->device_name == "Default") {
|
||||
driver->set_device("Default");
|
||||
if (driver->output_device_name == "Default") {
|
||||
driver->set_output_device("Default");
|
||||
}
|
||||
|
||||
return noErr;
|
||||
|
@ -495,7 +495,7 @@ Error AudioDriverCoreAudio::capture_stop() {
|
|||
|
||||
#ifdef MACOS_ENABLED
|
||||
|
||||
PackedStringArray AudioDriverCoreAudio::_get_device_list(bool capture) {
|
||||
PackedStringArray AudioDriverCoreAudio::_get_device_list(bool input) {
|
||||
PackedStringArray list;
|
||||
|
||||
list.push_back("Default");
|
||||
|
@ -514,7 +514,7 @@ PackedStringArray AudioDriverCoreAudio::_get_device_list(bool capture) {
|
|||
|
||||
UInt32 deviceCount = size / sizeof(AudioDeviceID);
|
||||
for (UInt32 i = 0; i < deviceCount; i++) {
|
||||
prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
|
||||
prop.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
|
||||
prop.mSelector = kAudioDevicePropertyStreamConfiguration;
|
||||
|
||||
AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, nullptr, &size);
|
||||
|
@ -555,10 +555,10 @@ PackedStringArray AudioDriverCoreAudio::_get_device_list(bool capture) {
|
|||
return list;
|
||||
}
|
||||
|
||||
void AudioDriverCoreAudio::_set_device(const String &device, bool capture) {
|
||||
void AudioDriverCoreAudio::_set_device(const String &output_device, bool input) {
|
||||
AudioDeviceID deviceId;
|
||||
bool found = false;
|
||||
if (device != "Default") {
|
||||
if (output_device != "Default") {
|
||||
AudioObjectPropertyAddress prop;
|
||||
|
||||
prop.mSelector = kAudioHardwarePropertyDevices;
|
||||
|
@ -573,7 +573,7 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) {
|
|||
|
||||
UInt32 deviceCount = size / sizeof(AudioDeviceID);
|
||||
for (UInt32 i = 0; i < deviceCount && !found; i++) {
|
||||
prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
|
||||
prop.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
|
||||
prop.mSelector = kAudioDevicePropertyStreamConfiguration;
|
||||
|
||||
AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, nullptr, &size);
|
||||
|
@ -602,7 +602,7 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) {
|
|||
ERR_FAIL_NULL_MSG(buffer, "Out of memory.");
|
||||
if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) {
|
||||
String name = String::utf8(buffer) + " (" + itos(audioDevices[i]) + ")";
|
||||
if (name == device) {
|
||||
if (name == output_device) {
|
||||
deviceId = audioDevices[i];
|
||||
found = true;
|
||||
}
|
||||
|
@ -618,7 +618,7 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) {
|
|||
if (!found) {
|
||||
// If we haven't found the desired device get the system default one
|
||||
UInt32 size = sizeof(AudioDeviceID);
|
||||
UInt32 elem = capture ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice;
|
||||
UInt32 elem = input ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice;
|
||||
AudioObjectPropertyAddress property = { elem, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
|
||||
|
||||
OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &deviceId);
|
||||
|
@ -628,10 +628,10 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) {
|
|||
}
|
||||
|
||||
if (found) {
|
||||
OSStatus result = AudioUnitSetProperty(capture ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID));
|
||||
OSStatus result = AudioUnitSetProperty(input ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID));
|
||||
ERR_FAIL_COND(result != noErr);
|
||||
|
||||
if (capture) {
|
||||
if (input) {
|
||||
// Reset audio input to keep synchronization.
|
||||
input_position = 0;
|
||||
input_size = 0;
|
||||
|
@ -639,34 +639,34 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) {
|
|||
}
|
||||
}
|
||||
|
||||
PackedStringArray AudioDriverCoreAudio::get_device_list() {
|
||||
PackedStringArray AudioDriverCoreAudio::get_output_device_list() {
|
||||
return _get_device_list();
|
||||
}
|
||||
|
||||
String AudioDriverCoreAudio::get_device() {
|
||||
return device_name;
|
||||
String AudioDriverCoreAudio::get_output_device() {
|
||||
return output_device_name;
|
||||
}
|
||||
|
||||
void AudioDriverCoreAudio::set_device(String device) {
|
||||
device_name = device;
|
||||
void AudioDriverCoreAudio::set_output_device(String output_device) {
|
||||
output_device_name = output_device;
|
||||
if (active) {
|
||||
_set_device(device_name);
|
||||
_set_device(output_device_name);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDriverCoreAudio::capture_set_device(const String &p_name) {
|
||||
capture_device_name = p_name;
|
||||
void AudioDriverCoreAudio::set_input_device(const String &p_name) {
|
||||
input_device_name = p_name;
|
||||
if (active) {
|
||||
_set_device(capture_device_name, true);
|
||||
_set_device(input_device_name, true);
|
||||
}
|
||||
}
|
||||
|
||||
PackedStringArray AudioDriverCoreAudio::capture_get_device_list() {
|
||||
PackedStringArray AudioDriverCoreAudio::get_input_device_list() {
|
||||
return _get_device_list(true);
|
||||
}
|
||||
|
||||
String AudioDriverCoreAudio::capture_get_device() {
|
||||
return capture_device_name;
|
||||
String AudioDriverCoreAudio::get_input_device() {
|
||||
return input_device_name;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -47,8 +47,8 @@ class AudioDriverCoreAudio : public AudioDriver {
|
|||
bool active = false;
|
||||
Mutex mutex;
|
||||
|
||||
String device_name = "Default";
|
||||
String capture_device_name = "Default";
|
||||
String output_device_name = "Default";
|
||||
String input_device_name = "Default";
|
||||
|
||||
int mix_rate = 0;
|
||||
unsigned int channels = 2;
|
||||
|
@ -60,7 +60,7 @@ class AudioDriverCoreAudio : public AudioDriver {
|
|||
|
||||
#ifdef MACOS_ENABLED
|
||||
PackedStringArray _get_device_list(bool capture = false);
|
||||
void _set_device(const String &device, bool capture = false);
|
||||
void _set_device(const String &output_device, bool capture = false);
|
||||
|
||||
static OSStatus input_device_address_cb(AudioObjectID inObjectID,
|
||||
UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses,
|
||||
|
@ -107,13 +107,13 @@ public:
|
|||
void stop();
|
||||
|
||||
#ifdef MACOS_ENABLED
|
||||
virtual PackedStringArray get_device_list();
|
||||
virtual String get_device();
|
||||
virtual void set_device(String device);
|
||||
virtual PackedStringArray get_output_device_list();
|
||||
virtual String get_output_device();
|
||||
virtual void set_output_device(String output_device);
|
||||
|
||||
virtual PackedStringArray capture_get_device_list();
|
||||
virtual void capture_set_device(const String &p_name);
|
||||
virtual String capture_get_device();
|
||||
virtual PackedStringArray get_input_device_list();
|
||||
virtual void set_input_device(const String &p_name);
|
||||
virtual String get_input_device();
|
||||
#endif
|
||||
|
||||
AudioDriverCoreAudio();
|
||||
|
|
|
@ -106,15 +106,15 @@ void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_inf
|
|||
ERR_FAIL_COND_MSG(!i, "PulseAudio server info is null.");
|
||||
AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata);
|
||||
|
||||
ad->capture_default_device = i->default_source_name;
|
||||
ad->default_device = i->default_sink_name;
|
||||
ad->default_input_device = i->default_source_name;
|
||||
ad->default_output_device = i->default_sink_name;
|
||||
ad->pa_status++;
|
||||
}
|
||||
|
||||
Error AudioDriverPulseAudio::detect_channels(bool capture) {
|
||||
pa_channel_map_init_stereo(capture ? &pa_rec_map : &pa_map);
|
||||
Error AudioDriverPulseAudio::detect_channels(bool input) {
|
||||
pa_channel_map_init_stereo(input ? &pa_rec_map : &pa_map);
|
||||
|
||||
String device = capture ? capture_device_name : device_name;
|
||||
String device = input ? input_device_name : output_device_name;
|
||||
if (device == "Default") {
|
||||
// Get the default output device name
|
||||
pa_status = 0;
|
||||
|
@ -136,7 +136,7 @@ Error AudioDriverPulseAudio::detect_channels(bool capture) {
|
|||
|
||||
char dev[1024];
|
||||
if (device == "Default") {
|
||||
strcpy(dev, capture ? capture_default_device.utf8().get_data() : default_device.utf8().get_data());
|
||||
strcpy(dev, input ? default_input_device.utf8().get_data() : default_output_device.utf8().get_data());
|
||||
} else {
|
||||
strcpy(dev, device.utf8().get_data());
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ Error AudioDriverPulseAudio::detect_channels(bool capture) {
|
|||
// Now using the device name get the amount of channels
|
||||
pa_status = 0;
|
||||
pa_operation *pa_op;
|
||||
if (capture) {
|
||||
if (input) {
|
||||
pa_op = pa_context_get_source_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_source_info_cb, (void *)this);
|
||||
} else {
|
||||
pa_op = pa_context_get_sink_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this);
|
||||
|
@ -165,7 +165,7 @@ Error AudioDriverPulseAudio::detect_channels(bool capture) {
|
|||
return FAILED;
|
||||
}
|
||||
} else {
|
||||
if (capture) {
|
||||
if (input) {
|
||||
ERR_PRINT("pa_context_get_source_info_by_name error");
|
||||
} else {
|
||||
ERR_PRINT("pa_context_get_sink_info_by_name error");
|
||||
|
@ -175,13 +175,13 @@ Error AudioDriverPulseAudio::detect_channels(bool capture) {
|
|||
return OK;
|
||||
}
|
||||
|
||||
Error AudioDriverPulseAudio::init_device() {
|
||||
// If there is a specified device check that it is really present
|
||||
if (device_name != "Default") {
|
||||
PackedStringArray list = get_device_list();
|
||||
if (list.find(device_name) == -1) {
|
||||
device_name = "Default";
|
||||
new_device = "Default";
|
||||
Error AudioDriverPulseAudio::init_output_device() {
|
||||
// If there is a specified output device, check that it is really present
|
||||
if (output_device_name != "Default") {
|
||||
PackedStringArray list = get_output_device_list();
|
||||
if (list.find(output_device_name) == -1) {
|
||||
output_device_name = "Default";
|
||||
new_output_device = "Default";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,7 +192,7 @@ Error AudioDriverPulseAudio::init_device() {
|
|||
Error err = detect_channels();
|
||||
if (err != OK) {
|
||||
// This most likely means there are no sinks.
|
||||
ERR_PRINT("PulseAudio: init device failed to detect number of output channels");
|
||||
ERR_PRINT("PulseAudio: init_output_device failed to detect number of output channels");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -256,7 +256,7 @@ Error AudioDriverPulseAudio::init_device() {
|
|||
attr.maxlength = (uint32_t)-1;
|
||||
attr.minreq = (uint32_t)-1;
|
||||
|
||||
const char *dev = device_name == "Default" ? nullptr : device_name.utf8().get_data();
|
||||
const char *dev = output_device_name == "Default" ? nullptr : output_device_name.utf8().get_data();
|
||||
pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);
|
||||
int error_code = pa_stream_connect_playback(pa_str, dev, &attr, flags, nullptr, nullptr);
|
||||
ERR_FAIL_COND_V(error_code < 0, ERR_CANT_OPEN);
|
||||
|
@ -346,7 +346,7 @@ Error AudioDriverPulseAudio::init() {
|
|||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
init_device();
|
||||
init_output_device();
|
||||
thread.start(AudioDriverPulseAudio::thread_func, this);
|
||||
|
||||
return OK;
|
||||
|
@ -448,18 +448,18 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
|
|||
}
|
||||
}
|
||||
|
||||
// User selected a new device, finish the current one so we'll init the new device
|
||||
if (ad->device_name != ad->new_device) {
|
||||
ad->device_name = ad->new_device;
|
||||
ad->finish_device();
|
||||
// User selected a new output device, finish the current one so we'll init the new output device
|
||||
if (ad->output_device_name != ad->new_output_device) {
|
||||
ad->output_device_name = ad->new_output_device;
|
||||
ad->finish_output_device();
|
||||
|
||||
Error err = ad->init_device();
|
||||
Error err = ad->init_output_device();
|
||||
if (err != OK) {
|
||||
ERR_PRINT("PulseAudio: init_device error");
|
||||
ad->device_name = "Default";
|
||||
ad->new_device = "Default";
|
||||
ERR_PRINT("PulseAudio: init_output_device error");
|
||||
ad->output_device_name = "Default";
|
||||
ad->new_output_device = "Default";
|
||||
|
||||
err = ad->init_device();
|
||||
err = ad->init_output_device();
|
||||
if (err != OK) {
|
||||
ad->active.clear();
|
||||
ad->exit_thread.set();
|
||||
|
@ -471,11 +471,11 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
|
|||
write_ofs = 0;
|
||||
}
|
||||
|
||||
// If we're using the default device check that the current device is still the default
|
||||
if (ad->device_name == "Default") {
|
||||
// If we're using the default output device, check that the current output device is still the default
|
||||
if (ad->output_device_name == "Default") {
|
||||
uint64_t msec = OS::get_singleton()->get_ticks_msec();
|
||||
if (msec > (default_device_msec + 1000)) {
|
||||
String old_default_device = ad->default_device;
|
||||
String old_default_device = ad->default_output_device;
|
||||
|
||||
default_device_msec = msec;
|
||||
|
||||
|
@ -494,12 +494,12 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
|
|||
ERR_PRINT("pa_context_get_server_info error: " + String(pa_strerror(pa_context_errno(ad->pa_ctx))));
|
||||
}
|
||||
|
||||
if (old_default_device != ad->default_device) {
|
||||
ad->finish_device();
|
||||
if (old_default_device != ad->default_output_device) {
|
||||
ad->finish_output_device();
|
||||
|
||||
Error err = ad->init_device();
|
||||
Error err = ad->init_output_device();
|
||||
if (err != OK) {
|
||||
ERR_PRINT("PulseAudio: init_device error");
|
||||
ERR_PRINT("PulseAudio: init_output_device error");
|
||||
ad->active.clear();
|
||||
ad->exit_thread.set();
|
||||
break;
|
||||
|
@ -541,18 +541,18 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
|
|||
}
|
||||
}
|
||||
|
||||
// User selected a new device, finish the current one so we'll init the new device
|
||||
if (ad->capture_device_name != ad->capture_new_device) {
|
||||
ad->capture_device_name = ad->capture_new_device;
|
||||
ad->capture_finish_device();
|
||||
// User selected a new input device, finish the current one so we'll init the new input device
|
||||
if (ad->input_device_name != ad->new_input_device) {
|
||||
ad->input_device_name = ad->new_input_device;
|
||||
ad->finish_input_device();
|
||||
|
||||
Error err = ad->capture_init_device();
|
||||
Error err = ad->init_input_device();
|
||||
if (err != OK) {
|
||||
ERR_PRINT("PulseAudio: capture_init_device error");
|
||||
ad->capture_device_name = "Default";
|
||||
ad->capture_new_device = "Default";
|
||||
ERR_PRINT("PulseAudio: init_input_device error");
|
||||
ad->input_device_name = "Default";
|
||||
ad->new_input_device = "Default";
|
||||
|
||||
err = ad->capture_init_device();
|
||||
err = ad->init_input_device();
|
||||
if (err != OK) {
|
||||
ad->active.clear();
|
||||
ad->exit_thread.set();
|
||||
|
@ -596,7 +596,7 @@ void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l,
|
|||
ad->pa_status++;
|
||||
}
|
||||
|
||||
PackedStringArray AudioDriverPulseAudio::get_device_list() {
|
||||
PackedStringArray AudioDriverPulseAudio::get_output_device_list() {
|
||||
pa_devices.clear();
|
||||
pa_devices.push_back("Default");
|
||||
|
||||
|
@ -606,7 +606,7 @@ PackedStringArray AudioDriverPulseAudio::get_device_list() {
|
|||
|
||||
lock();
|
||||
|
||||
// Get the device list
|
||||
// Get the output device list
|
||||
pa_status = 0;
|
||||
pa_operation *pa_op = pa_context_get_sink_info_list(pa_ctx, pa_sinklist_cb, (void *)this);
|
||||
if (pa_op) {
|
||||
|
@ -627,13 +627,13 @@ PackedStringArray AudioDriverPulseAudio::get_device_list() {
|
|||
return pa_devices;
|
||||
}
|
||||
|
||||
String AudioDriverPulseAudio::get_device() {
|
||||
return device_name;
|
||||
String AudioDriverPulseAudio::get_output_device() {
|
||||
return output_device_name;
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::set_device(String device) {
|
||||
void AudioDriverPulseAudio::set_output_device(String output_device) {
|
||||
lock();
|
||||
new_device = device;
|
||||
new_output_device = output_device;
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
@ -645,7 +645,7 @@ void AudioDriverPulseAudio::unlock() {
|
|||
mutex.unlock();
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::finish_device() {
|
||||
void AudioDriverPulseAudio::finish_output_device() {
|
||||
if (pa_str) {
|
||||
pa_stream_disconnect(pa_str);
|
||||
pa_stream_unref(pa_str);
|
||||
|
@ -661,7 +661,7 @@ void AudioDriverPulseAudio::finish() {
|
|||
exit_thread.set();
|
||||
thread.wait_to_finish();
|
||||
|
||||
finish_device();
|
||||
finish_output_device();
|
||||
|
||||
if (pa_ctx) {
|
||||
pa_context_disconnect(pa_ctx);
|
||||
|
@ -675,13 +675,13 @@ void AudioDriverPulseAudio::finish() {
|
|||
}
|
||||
}
|
||||
|
||||
Error AudioDriverPulseAudio::capture_init_device() {
|
||||
// If there is a specified device check that it is really present
|
||||
if (capture_device_name != "Default") {
|
||||
PackedStringArray list = capture_get_device_list();
|
||||
if (list.find(capture_device_name) == -1) {
|
||||
capture_device_name = "Default";
|
||||
capture_new_device = "Default";
|
||||
Error AudioDriverPulseAudio::init_input_device() {
|
||||
// If there is a specified input device, check that it is really present
|
||||
if (input_device_name != "Default") {
|
||||
PackedStringArray list = get_input_device_list();
|
||||
if (list.find(input_device_name) == -1) {
|
||||
input_device_name = "Default";
|
||||
new_input_device = "Default";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -718,7 +718,7 @@ Error AudioDriverPulseAudio::capture_init_device() {
|
|||
ERR_FAIL_V(ERR_CANT_OPEN);
|
||||
}
|
||||
|
||||
const char *dev = capture_device_name == "Default" ? nullptr : capture_device_name.utf8().get_data();
|
||||
const char *dev = input_device_name == "Default" ? nullptr : input_device_name.utf8().get_data();
|
||||
pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);
|
||||
int error_code = pa_stream_connect_record(pa_rec_str, dev, &attr, flags);
|
||||
if (error_code < 0) {
|
||||
|
@ -734,7 +734,7 @@ Error AudioDriverPulseAudio::capture_init_device() {
|
|||
return OK;
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::capture_finish_device() {
|
||||
void AudioDriverPulseAudio::finish_input_device() {
|
||||
if (pa_rec_str) {
|
||||
int ret = pa_stream_disconnect(pa_rec_str);
|
||||
if (ret != 0) {
|
||||
|
@ -745,25 +745,25 @@ void AudioDriverPulseAudio::capture_finish_device() {
|
|||
}
|
||||
}
|
||||
|
||||
Error AudioDriverPulseAudio::capture_start() {
|
||||
Error AudioDriverPulseAudio::input_start() {
|
||||
lock();
|
||||
Error err = capture_init_device();
|
||||
Error err = init_input_device();
|
||||
unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
Error AudioDriverPulseAudio::capture_stop() {
|
||||
Error AudioDriverPulseAudio::input_stop() {
|
||||
lock();
|
||||
capture_finish_device();
|
||||
finish_input_device();
|
||||
unlock();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::capture_set_device(const String &p_name) {
|
||||
void AudioDriverPulseAudio::set_input_device(const String &p_name) {
|
||||
lock();
|
||||
capture_new_device = p_name;
|
||||
new_input_device = p_name;
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
@ -782,7 +782,7 @@ void AudioDriverPulseAudio::pa_sourcelist_cb(pa_context *c, const pa_source_info
|
|||
ad->pa_status++;
|
||||
}
|
||||
|
||||
PackedStringArray AudioDriverPulseAudio::capture_get_device_list() {
|
||||
PackedStringArray AudioDriverPulseAudio::get_input_device_list() {
|
||||
pa_rec_devices.clear();
|
||||
pa_rec_devices.push_back("Default");
|
||||
|
||||
|
@ -813,9 +813,9 @@ PackedStringArray AudioDriverPulseAudio::capture_get_device_list() {
|
|||
return pa_rec_devices;
|
||||
}
|
||||
|
||||
String AudioDriverPulseAudio::capture_get_device() {
|
||||
String AudioDriverPulseAudio::get_input_device() {
|
||||
lock();
|
||||
String name = capture_device_name;
|
||||
String name = input_device_name;
|
||||
unlock();
|
||||
|
||||
return name;
|
||||
|
|
|
@ -51,13 +51,13 @@ class AudioDriverPulseAudio : public AudioDriver {
|
|||
pa_channel_map pa_map = {};
|
||||
pa_channel_map pa_rec_map = {};
|
||||
|
||||
String device_name = "Default";
|
||||
String new_device = "Default";
|
||||
String default_device;
|
||||
String output_device_name = "Default";
|
||||
String new_output_device = "Default";
|
||||
String default_output_device;
|
||||
|
||||
String capture_device_name;
|
||||
String capture_new_device;
|
||||
String capture_default_device;
|
||||
String input_device_name;
|
||||
String new_input_device;
|
||||
String default_input_device;
|
||||
|
||||
Vector<int32_t> samples_in;
|
||||
Vector<int16_t> samples_out;
|
||||
|
@ -83,11 +83,11 @@ class AudioDriverPulseAudio : public AudioDriver {
|
|||
static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata);
|
||||
static void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata);
|
||||
|
||||
Error init_device();
|
||||
void finish_device();
|
||||
Error init_output_device();
|
||||
void finish_output_device();
|
||||
|
||||
Error capture_init_device();
|
||||
void capture_finish_device();
|
||||
Error init_input_device();
|
||||
void finish_input_device();
|
||||
|
||||
Error detect_channels(bool capture = false);
|
||||
|
||||
|
@ -103,13 +103,13 @@ public:
|
|||
virtual int get_mix_rate() const;
|
||||
virtual SpeakerMode get_speaker_mode() const;
|
||||
|
||||
virtual PackedStringArray get_device_list();
|
||||
virtual String get_device();
|
||||
virtual void set_device(String device);
|
||||
virtual PackedStringArray get_output_device_list();
|
||||
virtual String get_output_device();
|
||||
virtual void set_output_device(String output_device);
|
||||
|
||||
virtual PackedStringArray capture_get_device_list();
|
||||
virtual void capture_set_device(const String &p_name);
|
||||
virtual String capture_get_device();
|
||||
virtual PackedStringArray get_input_device_list();
|
||||
virtual void set_input_device(const String &p_name);
|
||||
virtual String get_input_device();
|
||||
|
||||
virtual void lock();
|
||||
virtual void unlock();
|
||||
|
@ -117,8 +117,8 @@ public:
|
|||
|
||||
virtual float get_latency();
|
||||
|
||||
virtual Error capture_start();
|
||||
virtual Error capture_stop();
|
||||
virtual Error input_start();
|
||||
virtual Error input_stop();
|
||||
|
||||
AudioDriverPulseAudio();
|
||||
~AudioDriverPulseAudio() {}
|
||||
|
|
|
@ -118,8 +118,8 @@ const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
|
|||
|
||||
#define CAPTURE_BUFFER_CHANNELS 2
|
||||
|
||||
static bool default_render_device_changed = false;
|
||||
static bool default_capture_device_changed = false;
|
||||
static bool default_output_device_changed = false;
|
||||
static bool default_input_device_changed = false;
|
||||
|
||||
// Silence warning due to a COM API weirdness (GH-35194).
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
|
@ -181,9 +181,9 @@ public:
|
|||
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) {
|
||||
if (role == eConsole) {
|
||||
if (flow == eRender) {
|
||||
default_render_device_changed = true;
|
||||
default_output_device_changed = true;
|
||||
} else if (flow == eCapture) {
|
||||
default_capture_device_changed = true;
|
||||
default_input_device_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,10 +201,10 @@ public:
|
|||
|
||||
static CMMNotificationClient notif_client;
|
||||
|
||||
Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool p_reinit, bool p_no_audio_client_3) {
|
||||
Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_input, bool p_reinit, bool p_no_audio_client_3) {
|
||||
WAVEFORMATEX *pwfex;
|
||||
IMMDeviceEnumerator *enumerator = nullptr;
|
||||
IMMDevice *device = nullptr;
|
||||
IMMDevice *output_device = nullptr;
|
||||
|
||||
CoInitialize(nullptr);
|
||||
|
||||
|
@ -212,11 +212,11 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
|
|||
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
|
||||
|
||||
if (p_device->device_name == "Default") {
|
||||
hr = enumerator->GetDefaultAudioEndpoint(p_capture ? eCapture : eRender, eConsole, &device);
|
||||
hr = enumerator->GetDefaultAudioEndpoint(p_input ? eCapture : eRender, eConsole, &output_device);
|
||||
} else {
|
||||
IMMDeviceCollection *devices = nullptr;
|
||||
|
||||
hr = enumerator->EnumAudioEndpoints(p_capture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices);
|
||||
hr = enumerator->EnumAudioEndpoints(p_input ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices);
|
||||
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
|
||||
|
||||
LPWSTR strId = nullptr;
|
||||
|
@ -255,20 +255,20 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
|
|||
}
|
||||
|
||||
if (found) {
|
||||
hr = enumerator->GetDevice(strId, &device);
|
||||
hr = enumerator->GetDevice(strId, &output_device);
|
||||
}
|
||||
|
||||
if (strId) {
|
||||
CoTaskMemFree(strId);
|
||||
}
|
||||
|
||||
if (device == nullptr) {
|
||||
hr = enumerator->GetDefaultAudioEndpoint(p_capture ? eCapture : eRender, eConsole, &device);
|
||||
if (output_device == nullptr) {
|
||||
hr = enumerator->GetDefaultAudioEndpoint(p_input ? eCapture : eRender, eConsole, &output_device);
|
||||
}
|
||||
}
|
||||
|
||||
if (p_reinit) {
|
||||
// In case we're trying to re-initialize the device prevent throwing this error on the console,
|
||||
// In case we're trying to re-initialize the device, prevent throwing this error on the console,
|
||||
// otherwise if there is currently no device available this will spam the console.
|
||||
if (hr != S_OK) {
|
||||
return ERR_CANT_OPEN;
|
||||
|
@ -284,28 +284,28 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
|
|||
ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error");
|
||||
}
|
||||
|
||||
using_audio_client_3 = !p_capture; // IID_IAudioClient3 is only used for adjustable output latency (not input)
|
||||
using_audio_client_3 = !p_input; // IID_IAudioClient3 is only used for adjustable output latency (not input)
|
||||
|
||||
if (p_no_audio_client_3) {
|
||||
using_audio_client_3 = false;
|
||||
}
|
||||
|
||||
if (using_audio_client_3) {
|
||||
hr = device->Activate(IID_IAudioClient3, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client);
|
||||
hr = output_device->Activate(IID_IAudioClient3, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client);
|
||||
if (hr != S_OK) {
|
||||
// IID_IAudioClient3 will never activate on OS versions before Windows 10.
|
||||
// Older Windows versions should fall back gracefully.
|
||||
using_audio_client_3 = false;
|
||||
print_verbose("WASAPI: Couldn't activate device with IAudioClient3 interface, falling back to IAudioClient interface");
|
||||
print_verbose("WASAPI: Couldn't activate output_device with IAudioClient3 interface, falling back to IAudioClient interface");
|
||||
} else {
|
||||
print_verbose("WASAPI: Activated device using IAudioClient3 interface");
|
||||
print_verbose("WASAPI: Activated output_device using IAudioClient3 interface");
|
||||
}
|
||||
}
|
||||
if (!using_audio_client_3) {
|
||||
hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client);
|
||||
hr = output_device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client);
|
||||
}
|
||||
|
||||
SAFE_RELEASE(device)
|
||||
SAFE_RELEASE(output_device)
|
||||
|
||||
if (p_reinit) {
|
||||
if (hr != S_OK) {
|
||||
|
@ -339,7 +339,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
|
|||
WAVEFORMATEX *closest = nullptr;
|
||||
hr = p_device->audio_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, pwfex, &closest);
|
||||
if (hr == S_FALSE) {
|
||||
WARN_PRINT("WASAPI: Mix format is not supported by the Device");
|
||||
WARN_PRINT("WASAPI: Mix format is not supported by the output_device");
|
||||
if (closest) {
|
||||
print_verbose("WASAPI: closest->wFormatTag = " + itos(closest->wFormatTag));
|
||||
print_verbose("WASAPI: closest->nChannels = " + itos(closest->nChannels));
|
||||
|
@ -385,14 +385,14 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
|
|||
pwfex->nSamplesPerSec = mix_rate;
|
||||
pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8);
|
||||
}
|
||||
hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_capture ? REFTIMES_PER_SEC : 0, 0, pwfex, nullptr);
|
||||
hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_input ? REFTIMES_PER_SEC : 0, 0, pwfex, nullptr);
|
||||
ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: Initialize failed with error 0x" + String::num_uint64(hr, 16) + ".");
|
||||
UINT32 max_frames;
|
||||
hr = p_device->audio_client->GetBufferSize(&max_frames);
|
||||
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
|
||||
|
||||
// Due to WASAPI Shared Mode we have no control of the buffer size
|
||||
if (!p_capture) {
|
||||
if (!p_input) {
|
||||
buffer_frames = max_frames;
|
||||
|
||||
int64_t latency = 0;
|
||||
|
@ -421,8 +421,8 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
|
|||
if (hr != S_OK) {
|
||||
print_verbose("WASAPI: GetSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient.");
|
||||
CoTaskMemFree(pwfex);
|
||||
SAFE_RELEASE(device)
|
||||
return audio_device_init(p_device, p_capture, p_reinit, true);
|
||||
SAFE_RELEASE(output_device)
|
||||
return audio_device_init(p_device, p_input, p_reinit, true);
|
||||
}
|
||||
|
||||
// Period frames must be an integral multiple of fundamental_period_frames or IAudioClient3 initialization will fail,
|
||||
|
@ -443,8 +443,8 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
|
|||
if (hr != S_OK) {
|
||||
print_verbose("WASAPI: InitializeSharedAudioStream failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient.");
|
||||
CoTaskMemFree(pwfex);
|
||||
SAFE_RELEASE(device);
|
||||
return audio_device_init(p_device, p_capture, p_reinit, true);
|
||||
SAFE_RELEASE(output_device);
|
||||
return audio_device_init(p_device, p_input, p_reinit, true);
|
||||
} else {
|
||||
uint32_t output_latency_in_frames;
|
||||
WAVEFORMATEX *current_pwfex;
|
||||
|
@ -455,13 +455,13 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
|
|||
} else {
|
||||
print_verbose("WASAPI: GetCurrentSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient.");
|
||||
CoTaskMemFree(pwfex);
|
||||
SAFE_RELEASE(device);
|
||||
return audio_device_init(p_device, p_capture, p_reinit, true);
|
||||
SAFE_RELEASE(output_device);
|
||||
return audio_device_init(p_device, p_input, p_reinit, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p_capture) {
|
||||
if (p_input) {
|
||||
hr = p_device->audio_client->GetService(IID_IAudioCaptureClient, (void **)&p_device->capture_client);
|
||||
} else {
|
||||
hr = p_device->audio_client->GetService(IID_IAudioRenderClient, (void **)&p_device->render_client);
|
||||
|
@ -470,12 +470,12 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
|
|||
|
||||
// Free memory
|
||||
CoTaskMemFree(pwfex);
|
||||
SAFE_RELEASE(device)
|
||||
SAFE_RELEASE(output_device)
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error AudioDriverWASAPI::init_render_device(bool p_reinit) {
|
||||
Error AudioDriverWASAPI::init_output_device(bool p_reinit) {
|
||||
Error err = audio_device_init(&audio_output, false, p_reinit);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
|
@ -507,7 +507,7 @@ Error AudioDriverWASAPI::init_render_device(bool p_reinit) {
|
|||
return OK;
|
||||
}
|
||||
|
||||
Error AudioDriverWASAPI::init_capture_device(bool p_reinit) {
|
||||
Error AudioDriverWASAPI::init_input_device(bool p_reinit) {
|
||||
Error err = audio_device_init(&audio_input, true, p_reinit);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
|
@ -538,11 +538,11 @@ Error AudioDriverWASAPI::audio_device_finish(AudioDeviceWASAPI *p_device) {
|
|||
return OK;
|
||||
}
|
||||
|
||||
Error AudioDriverWASAPI::finish_render_device() {
|
||||
Error AudioDriverWASAPI::finish_output_device() {
|
||||
return audio_device_finish(&audio_output);
|
||||
}
|
||||
|
||||
Error AudioDriverWASAPI::finish_capture_device() {
|
||||
Error AudioDriverWASAPI::finish_input_device() {
|
||||
return audio_device_finish(&audio_input);
|
||||
}
|
||||
|
||||
|
@ -551,9 +551,9 @@ Error AudioDriverWASAPI::init() {
|
|||
|
||||
target_latency_ms = GLOBAL_GET("audio/driver/output_latency");
|
||||
|
||||
Error err = init_render_device();
|
||||
Error err = init_output_device();
|
||||
if (err != OK) {
|
||||
ERR_PRINT("WASAPI: init_render_device error");
|
||||
ERR_PRINT("WASAPI: init_output_device error");
|
||||
}
|
||||
|
||||
exit_thread.clear();
|
||||
|
@ -575,7 +575,7 @@ AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const {
|
|||
return get_speaker_mode_by_total_channels(channels);
|
||||
}
|
||||
|
||||
PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) {
|
||||
PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_input) {
|
||||
PackedStringArray list;
|
||||
IMMDeviceCollection *devices = nullptr;
|
||||
IMMDeviceEnumerator *enumerator = nullptr;
|
||||
|
@ -587,7 +587,7 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) {
|
|||
HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator);
|
||||
ERR_FAIL_COND_V(hr != S_OK, PackedStringArray());
|
||||
|
||||
hr = enumerator->EnumAudioEndpoints(p_capture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices);
|
||||
hr = enumerator->EnumAudioEndpoints(p_input ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices);
|
||||
ERR_FAIL_COND_V(hr != S_OK, PackedStringArray());
|
||||
|
||||
UINT count = 0;
|
||||
|
@ -595,13 +595,13 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) {
|
|||
ERR_FAIL_COND_V(hr != S_OK, PackedStringArray());
|
||||
|
||||
for (ULONG i = 0; i < count; i++) {
|
||||
IMMDevice *device = nullptr;
|
||||
IMMDevice *output_device = nullptr;
|
||||
|
||||
hr = devices->Item(i, &device);
|
||||
hr = devices->Item(i, &output_device);
|
||||
ERR_BREAK(hr != S_OK);
|
||||
|
||||
IPropertyStore *props = nullptr;
|
||||
hr = device->OpenPropertyStore(STGM_READ, &props);
|
||||
hr = output_device->OpenPropertyStore(STGM_READ, &props);
|
||||
ERR_BREAK(hr != S_OK);
|
||||
|
||||
PROPVARIANT propvar;
|
||||
|
@ -614,7 +614,7 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) {
|
|||
|
||||
PropVariantClear(&propvar);
|
||||
props->Release();
|
||||
device->Release();
|
||||
output_device->Release();
|
||||
}
|
||||
|
||||
devices->Release();
|
||||
|
@ -622,11 +622,11 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) {
|
|||
return list;
|
||||
}
|
||||
|
||||
PackedStringArray AudioDriverWASAPI::get_device_list() {
|
||||
PackedStringArray AudioDriverWASAPI::get_output_device_list() {
|
||||
return audio_device_get_list(false);
|
||||
}
|
||||
|
||||
String AudioDriverWASAPI::get_device() {
|
||||
String AudioDriverWASAPI::get_output_device() {
|
||||
lock();
|
||||
String name = audio_output.device_name;
|
||||
unlock();
|
||||
|
@ -634,9 +634,9 @@ String AudioDriverWASAPI::get_device() {
|
|||
return name;
|
||||
}
|
||||
|
||||
void AudioDriverWASAPI::set_device(String device) {
|
||||
void AudioDriverWASAPI::set_output_device(String output_device) {
|
||||
lock();
|
||||
audio_output.new_device = device;
|
||||
audio_output.new_device = output_device;
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
@ -769,13 +769,13 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
|
|||
avail_frames -= write_frames;
|
||||
written_frames += write_frames;
|
||||
} else if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
|
||||
// Device is not valid anymore, reopen it
|
||||
// output_device is not valid anymore, reopen it
|
||||
|
||||
Error err = ad->finish_render_device();
|
||||
Error err = ad->finish_output_device();
|
||||
if (err != OK) {
|
||||
ERR_PRINT("WASAPI: finish_render_device error");
|
||||
ERR_PRINT("WASAPI: finish_output_device error");
|
||||
} else {
|
||||
// We reopened the device and samples_in may have resized, so invalidate the current avail_frames
|
||||
// We reopened the output device and samples_in may have resized, so invalidate the current avail_frames
|
||||
avail_frames = 0;
|
||||
}
|
||||
} else {
|
||||
|
@ -790,37 +790,37 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
|
|||
}
|
||||
|
||||
if (invalidated) {
|
||||
// Device is not valid anymore
|
||||
WARN_PRINT("WASAPI: Current device invalidated, closing device");
|
||||
// output_device is not valid anymore
|
||||
WARN_PRINT("WASAPI: Current output_device invalidated, closing output_device");
|
||||
|
||||
Error err = ad->finish_render_device();
|
||||
Error err = ad->finish_output_device();
|
||||
if (err != OK) {
|
||||
ERR_PRINT("WASAPI: finish_render_device error");
|
||||
ERR_PRINT("WASAPI: finish_output_device error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we're using the Default device and it changed finish it so we'll re-init the device
|
||||
if (ad->audio_output.device_name == "Default" && default_render_device_changed) {
|
||||
Error err = ad->finish_render_device();
|
||||
// If we're using the Default output device and it changed finish it so we'll re-init the output device
|
||||
if (ad->audio_output.device_name == "Default" && default_output_device_changed) {
|
||||
Error err = ad->finish_output_device();
|
||||
if (err != OK) {
|
||||
ERR_PRINT("WASAPI: finish_render_device error");
|
||||
ERR_PRINT("WASAPI: finish_output_device error");
|
||||
}
|
||||
|
||||
default_render_device_changed = false;
|
||||
default_output_device_changed = false;
|
||||
}
|
||||
|
||||
// User selected a new device, finish the current one so we'll init the new device
|
||||
// User selected a new output device, finish the current one so we'll init the new output device
|
||||
if (ad->audio_output.device_name != ad->audio_output.new_device) {
|
||||
ad->audio_output.device_name = ad->audio_output.new_device;
|
||||
Error err = ad->finish_render_device();
|
||||
Error err = ad->finish_output_device();
|
||||
if (err != OK) {
|
||||
ERR_PRINT("WASAPI: finish_render_device error");
|
||||
ERR_PRINT("WASAPI: finish_output_device error");
|
||||
}
|
||||
}
|
||||
|
||||
if (!ad->audio_output.audio_client) {
|
||||
Error err = ad->init_render_device(true);
|
||||
Error err = ad->init_output_device(true);
|
||||
if (err == OK) {
|
||||
ad->start();
|
||||
}
|
||||
|
@ -873,29 +873,29 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
|
|||
}
|
||||
}
|
||||
|
||||
// If we're using the Default device and it changed finish it so we'll re-init the device
|
||||
if (ad->audio_input.device_name == "Default" && default_capture_device_changed) {
|
||||
Error err = ad->finish_capture_device();
|
||||
// If we're using the Default output device and it changed finish it so we'll re-init the output device
|
||||
if (ad->audio_input.device_name == "Default" && default_input_device_changed) {
|
||||
Error err = ad->finish_input_device();
|
||||
if (err != OK) {
|
||||
ERR_PRINT("WASAPI: finish_capture_device error");
|
||||
ERR_PRINT("WASAPI: finish_input_device error");
|
||||
}
|
||||
|
||||
default_capture_device_changed = false;
|
||||
default_input_device_changed = false;
|
||||
}
|
||||
|
||||
// User selected a new device, finish the current one so we'll init the new device
|
||||
// User selected a new input device, finish the current one so we'll init the new input device
|
||||
if (ad->audio_input.device_name != ad->audio_input.new_device) {
|
||||
ad->audio_input.device_name = ad->audio_input.new_device;
|
||||
Error err = ad->finish_capture_device();
|
||||
Error err = ad->finish_input_device();
|
||||
if (err != OK) {
|
||||
ERR_PRINT("WASAPI: finish_capture_device error");
|
||||
ERR_PRINT("WASAPI: finish_input_device error");
|
||||
}
|
||||
}
|
||||
|
||||
if (!ad->audio_input.audio_client) {
|
||||
Error err = ad->init_capture_device(true);
|
||||
Error err = ad->init_input_device(true);
|
||||
if (err == OK) {
|
||||
ad->capture_start();
|
||||
ad->input_start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -933,14 +933,14 @@ void AudioDriverWASAPI::finish() {
|
|||
exit_thread.set();
|
||||
thread.wait_to_finish();
|
||||
|
||||
finish_capture_device();
|
||||
finish_render_device();
|
||||
finish_input_device();
|
||||
finish_output_device();
|
||||
}
|
||||
|
||||
Error AudioDriverWASAPI::capture_start() {
|
||||
Error err = init_capture_device();
|
||||
Error AudioDriverWASAPI::input_start() {
|
||||
Error err = init_input_device();
|
||||
if (err != OK) {
|
||||
ERR_PRINT("WASAPI: init_capture_device error");
|
||||
ERR_PRINT("WASAPI: init_input_device error");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -953,7 +953,7 @@ Error AudioDriverWASAPI::capture_start() {
|
|||
return OK;
|
||||
}
|
||||
|
||||
Error AudioDriverWASAPI::capture_stop() {
|
||||
Error AudioDriverWASAPI::input_stop() {
|
||||
if (audio_input.active.is_set()) {
|
||||
audio_input.audio_client->Stop();
|
||||
audio_input.active.clear();
|
||||
|
@ -964,17 +964,17 @@ Error AudioDriverWASAPI::capture_stop() {
|
|||
return FAILED;
|
||||
}
|
||||
|
||||
void AudioDriverWASAPI::capture_set_device(const String &p_name) {
|
||||
void AudioDriverWASAPI::set_input_device(const String &p_name) {
|
||||
lock();
|
||||
audio_input.new_device = p_name;
|
||||
unlock();
|
||||
}
|
||||
|
||||
PackedStringArray AudioDriverWASAPI::capture_get_device_list() {
|
||||
PackedStringArray AudioDriverWASAPI::get_input_device_list() {
|
||||
return audio_device_get_list(true);
|
||||
}
|
||||
|
||||
String AudioDriverWASAPI::capture_get_device() {
|
||||
String AudioDriverWASAPI::get_input_device() {
|
||||
lock();
|
||||
String name = audio_input.device_name;
|
||||
unlock();
|
||||
|
|
|
@ -47,8 +47,8 @@ class AudioDriverWASAPI : public AudioDriver {
|
|||
class AudioDeviceWASAPI {
|
||||
public:
|
||||
IAudioClient *audio_client = nullptr;
|
||||
IAudioRenderClient *render_client = nullptr;
|
||||
IAudioCaptureClient *capture_client = nullptr;
|
||||
IAudioRenderClient *render_client = nullptr; // Output
|
||||
IAudioCaptureClient *capture_client = nullptr; // Input
|
||||
SafeFlag active;
|
||||
|
||||
WORD format_tag = 0;
|
||||
|
@ -56,8 +56,8 @@ class AudioDriverWASAPI : public AudioDriver {
|
|||
unsigned int channels = 0;
|
||||
unsigned int frame_size = 0;
|
||||
|
||||
String device_name = "Default";
|
||||
String new_device = "Default";
|
||||
String device_name = "Default"; // Output OR Input
|
||||
String new_device = "Default"; // Output OR Input
|
||||
|
||||
AudioDeviceWASAPI() {}
|
||||
};
|
||||
|
@ -83,15 +83,15 @@ class AudioDriverWASAPI : public AudioDriver {
|
|||
static _FORCE_INLINE_ int32_t read_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i);
|
||||
static void thread_func(void *p_udata);
|
||||
|
||||
Error init_render_device(bool p_reinit = false);
|
||||
Error init_capture_device(bool p_reinit = false);
|
||||
Error init_output_device(bool p_reinit = false);
|
||||
Error init_input_device(bool p_reinit = false);
|
||||
|
||||
Error finish_render_device();
|
||||
Error finish_capture_device();
|
||||
Error finish_output_device();
|
||||
Error finish_input_device();
|
||||
|
||||
Error audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool p_reinit, bool p_no_audio_client_3 = false);
|
||||
Error audio_device_init(AudioDeviceWASAPI *p_device, bool p_input, bool p_reinit, bool p_no_audio_client_3 = false);
|
||||
Error audio_device_finish(AudioDeviceWASAPI *p_device);
|
||||
PackedStringArray audio_device_get_list(bool p_capture);
|
||||
PackedStringArray audio_device_get_list(bool p_input);
|
||||
|
||||
public:
|
||||
virtual const char *get_name() const {
|
||||
|
@ -103,18 +103,18 @@ public:
|
|||
virtual int get_mix_rate() const;
|
||||
virtual float get_latency();
|
||||
virtual SpeakerMode get_speaker_mode() const;
|
||||
virtual PackedStringArray get_device_list();
|
||||
virtual String get_device();
|
||||
virtual void set_device(String device);
|
||||
virtual PackedStringArray get_output_device_list();
|
||||
virtual String get_output_device();
|
||||
virtual void set_output_device(String output_device);
|
||||
virtual void lock();
|
||||
virtual void unlock();
|
||||
virtual void finish();
|
||||
|
||||
virtual Error capture_start();
|
||||
virtual Error capture_stop();
|
||||
virtual PackedStringArray capture_get_device_list();
|
||||
virtual void capture_set_device(const String &p_name);
|
||||
virtual String capture_get_device();
|
||||
virtual Error input_start();
|
||||
virtual Error input_stop();
|
||||
virtual PackedStringArray get_input_device_list();
|
||||
virtual void set_input_device(const String &p_name);
|
||||
virtual String get_input_device();
|
||||
|
||||
AudioDriverWASAPI();
|
||||
};
|
||||
|
|
|
@ -1593,7 +1593,7 @@ void EditorFileSystem::_update_pending_script_classes() {
|
|||
_update_script_classes();
|
||||
} else {
|
||||
// In case the class cache file was removed somehow, regenerate it.
|
||||
if (ScriptServer::has_global_classes() && !FileAccess::exists(ScriptServer::get_global_class_cache_file_path())) {
|
||||
if (!FileAccess::exists(ScriptServer::get_global_class_cache_file_path())) {
|
||||
ScriptServer::save_global_classes();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "scene/animation/animation_blend_tree.h"
|
||||
#include "scene/gui/check_box.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
|
||||
StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const {
|
||||
|
@ -335,6 +336,7 @@ void AnimationNodeBlendSpace1DEditor::_update_space() {
|
|||
min_value->set_value(blend_space->get_min_space());
|
||||
|
||||
sync->set_pressed(blend_space->is_using_sync());
|
||||
interpolation->select(blend_space->get_blend_mode());
|
||||
|
||||
label_value->set_text(blend_space->get_value_label());
|
||||
|
||||
|
@ -361,6 +363,8 @@ void AnimationNodeBlendSpace1DEditor::_config_changed(double) {
|
|||
undo_redo->add_undo_method(blend_space.ptr(), "set_snap", blend_space->get_snap());
|
||||
undo_redo->add_do_method(blend_space.ptr(), "set_use_sync", sync->is_pressed());
|
||||
undo_redo->add_undo_method(blend_space.ptr(), "set_use_sync", blend_space->is_using_sync());
|
||||
undo_redo->add_do_method(blend_space.ptr(), "set_blend_mode", interpolation->get_selected());
|
||||
undo_redo->add_undo_method(blend_space.ptr(), "set_blend_mode", blend_space->get_blend_mode());
|
||||
undo_redo->add_do_method(this, "_update_space");
|
||||
undo_redo->add_undo_method(this, "_update_space");
|
||||
undo_redo->commit_action();
|
||||
|
@ -579,6 +583,10 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) {
|
|||
tool_erase->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
|
||||
snap->set_icon(get_theme_icon(SNAME("SnapGrid"), SNAME("EditorIcons")));
|
||||
open_editor->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")));
|
||||
interpolation->clear();
|
||||
interpolation->add_icon_item(get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")), "", 0);
|
||||
interpolation->add_icon_item(get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), "", 1);
|
||||
interpolation->add_icon_item(get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")), "", 2);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_PROCESS: {
|
||||
|
@ -639,6 +647,7 @@ void AnimationNodeBlendSpace1DEditor::edit(const Ref<AnimationNode> &p_node) {
|
|||
min_value->set_editable(!read_only);
|
||||
max_value->set_editable(!read_only);
|
||||
sync->set_disabled(read_only);
|
||||
interpolation->set_disabled(read_only);
|
||||
}
|
||||
|
||||
AnimationNodeBlendSpace1DEditor *AnimationNodeBlendSpace1DEditor::singleton = nullptr;
|
||||
|
@ -707,6 +716,13 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() {
|
|||
top_hb->add_child(sync);
|
||||
sync->connect("toggled", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed));
|
||||
|
||||
top_hb->add_child(memnew(VSeparator));
|
||||
|
||||
top_hb->add_child(memnew(Label(TTR("Blend:"))));
|
||||
interpolation = memnew(OptionButton);
|
||||
top_hb->add_child(interpolation);
|
||||
interpolation->connect("item_selected", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed));
|
||||
|
||||
edit_hb = memnew(HBoxContainer);
|
||||
top_hb->add_child(edit_hb);
|
||||
edit_hb->add_child(memnew(VSeparator));
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "scene/gui/tree.h"
|
||||
|
||||
class CheckBox;
|
||||
class OptionButton;
|
||||
class PanelContainer;
|
||||
|
||||
class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin {
|
||||
|
@ -66,6 +67,7 @@ class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin {
|
|||
SpinBox *min_value = nullptr;
|
||||
|
||||
CheckBox *sync = nullptr;
|
||||
OptionButton *interpolation = nullptr;
|
||||
|
||||
HBoxContainer *edit_hb = nullptr;
|
||||
SpinBox *edit_value = nullptr;
|
||||
|
|
|
@ -32,10 +32,11 @@
|
|||
|
||||
#include "modules/modules_enabled.gen.h"
|
||||
|
||||
const int ERROR_CODE = 77;
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
|
||||
const int ERROR_CODE = 77;
|
||||
|
||||
#include "modules/regex/regex.h"
|
||||
|
||||
#include "core/io/dir_access.h"
|
||||
|
@ -44,7 +45,7 @@ const int ERROR_CODE = 77;
|
|||
#include "core/templates/list.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
|
||||
static const char *enum_renames[][2] = {
|
||||
const char *ProjectConverter3To4::enum_renames[][2] = {
|
||||
//// constants
|
||||
{ "TYPE_COLOR_ARRAY", "TYPE_PACKED_COLOR_ARRAY" },
|
||||
{ "TYPE_FLOAT64_ARRAY", "TYPE_PACKED_FLOAT64_ARRAY" },
|
||||
|
@ -164,7 +165,7 @@ static const char *enum_renames[][2] = {
|
|||
{ nullptr, nullptr },
|
||||
};
|
||||
|
||||
static const char *gdscript_function_renames[][2] = {
|
||||
const char *ProjectConverter3To4::gdscript_function_renames[][2] = {
|
||||
// { "_set_name", "get_tracker_name"}, // XRPositionalTracker - CameraFeed use this
|
||||
// { "_unhandled_input", "_unhandled_key_input"}, // BaseButton, ViewportContainer broke Node, FileDialog,SubViewportContainer
|
||||
// { "create_gizmo", "_create_gizmo"}, // EditorNode3DGizmoPlugin - may be used
|
||||
|
@ -240,6 +241,9 @@ static const char *gdscript_function_renames[][2] = {
|
|||
{ "can_generate_small_preview", "_can_generate_small_preview" }, // EditorResourcePreviewGenerator
|
||||
{ "can_instance", "can_instantiate" }, // PackedScene, Script
|
||||
{ "canvas_light_set_scale", "canvas_light_set_texture_scale" }, // RenderingServer
|
||||
{ "capture_get_device", "get_input_device" }, // AudioServer
|
||||
{ "capture_get_device_list", "get_input_device_list" }, // AudioServer
|
||||
{ "capture_set_device", "set_input_device" }, // AudioServer
|
||||
{ "center_viewport_to_cursor", "center_viewport_to_caret" }, // TextEdit
|
||||
{ "change_scene", "change_scene_to_file" }, // SceneTree
|
||||
{ "change_scene_to", "change_scene_to_packed" }, // SceneTree
|
||||
|
@ -300,6 +304,8 @@ static const char *gdscript_function_renames[][2] = {
|
|||
{ "get_cursor_position", "get_caret_column" }, // LineEdit
|
||||
{ "get_d", "get_distance" }, // LineShape2D
|
||||
{ "get_depth_bias_enable", "get_depth_bias_enabled" }, // RDPipelineRasterizationState
|
||||
{ "get_device", "get_output_device" }, // AudioServer
|
||||
{ "get_device_list", "get_output_device_list" }, // AudioServer
|
||||
{ "get_drag_data", "_get_drag_data" }, // Control
|
||||
{ "get_editor_viewport", "get_editor_main_screen" }, // EditorPlugin
|
||||
{ "get_enabled_focus_mode", "get_focus_mode" }, // BaseButton
|
||||
|
@ -498,6 +504,7 @@ static const char *gdscript_function_renames[][2] = {
|
|||
{ "set_cursor_position", "set_caret_column" }, // LineEdit
|
||||
{ "set_d", "set_distance" }, // WorldMarginShape2D
|
||||
{ "set_depth_bias_enable", "set_depth_bias_enabled" }, // RDPipelineRasterizationState
|
||||
{ "set_device", "set_output_device" }, // AudioServer
|
||||
{ "set_doubleclick", "set_double_click" }, // InputEventMouseButton
|
||||
{ "set_draw_red", "set_draw_warning" }, // EditorProperty
|
||||
{ "set_enable_follow_smoothing", "set_position_smoothing_enabled" }, // Camera2D
|
||||
|
@ -619,7 +626,7 @@ static const char *gdscript_function_renames[][2] = {
|
|||
};
|
||||
|
||||
// gdscript_function_renames clone with CamelCase
|
||||
static const char *csharp_function_renames[][2] = {
|
||||
const char *ProjectConverter3To4::csharp_function_renames[][2] = {
|
||||
// { "_SetName", "GetTrackerName"}, // XRPositionalTracker - CameraFeed use this
|
||||
// { "_UnhandledInput", "_UnhandledKeyInput"}, // BaseButton, ViewportContainer broke Node, FileDialog,SubViewportContainer
|
||||
// { "CreateGizmo", "_CreateGizmo"}, // EditorNode3DGizmoPlugin - may be used
|
||||
|
@ -696,6 +703,9 @@ static const char *csharp_function_renames[][2] = {
|
|||
{ "CanGenerateSmallPreview", "_CanGenerateSmallPreview" }, // EditorResourcePreviewGenerator
|
||||
{ "CanInstance", "CanInstantiate" }, // PackedScene, Script
|
||||
{ "CanvasLightSetScale", "CanvasLightSetTextureScale" }, // RenderingServer
|
||||
{ "CaptureGetDevice", "GetInputDevice" }, // AudioServer
|
||||
{ "CaptureGetDeviceList", "GetInputDeviceList" }, // AudioServer
|
||||
{ "CaptureSetDevice", "SetInputDevice" }, // AudioServer
|
||||
{ "CenterViewportToCursor", "CenterViewportToCaret" }, // TextEdit
|
||||
{ "ChangeScene", "ChangeSceneToFile" }, // SceneTree
|
||||
{ "ChangeSceneTo", "ChangeSceneToPacked" }, // SceneTree
|
||||
|
@ -753,6 +763,8 @@ static const char *csharp_function_renames[][2] = {
|
|||
{ "GetCursorPosition", "GetCaretColumn" }, // LineEdit
|
||||
{ "GetD", "GetDistance" }, // LineShape2D
|
||||
{ "GetDepthBiasEnable", "GetDepthBiasEnabled" }, // RDPipelineRasterizationState
|
||||
{ "GetDevice", "GetOutputDevice" }, // AudioServer
|
||||
{ "GetDeviceList", "GetOutputDeviceList" }, // AudioServer
|
||||
{ "GetDragDataFw", "_GetDragDataFw" }, // ScriptEditor
|
||||
{ "GetEditorViewport", "GetViewport" }, // EditorPlugin
|
||||
{ "GetEnabledFocusMode", "GetFocusMode" }, // BaseButton
|
||||
|
@ -941,6 +953,7 @@ static const char *csharp_function_renames[][2] = {
|
|||
{ "SetCursorPosition", "SetCaretColumn" }, // LineEdit
|
||||
{ "SetD", "SetDistance" }, // WorldMarginShape2D
|
||||
{ "SetDepthBiasEnable", "SetDepthBiasEnabled" }, // RDPipelineRasterizationState
|
||||
{ "SetDevice", "SetOutputDevice" }, // AudioServer
|
||||
{ "SetDoubleclick", "SetDoubleClick" }, // InputEventMouseButton
|
||||
{ "SetEnableFollowSmoothing", "SetFollowSmoothingEnabled" }, // Camera2D
|
||||
{ "SetEnabledFocusMode", "SetFocusMode" }, // BaseButton
|
||||
|
@ -1056,7 +1069,7 @@ static const char *csharp_function_renames[][2] = {
|
|||
};
|
||||
|
||||
// Some needs to be disabled, because users can use this names as variables
|
||||
static const char *gdscript_properties_renames[][2] = {
|
||||
const char *ProjectConverter3To4::gdscript_properties_renames[][2] = {
|
||||
// // { "d", "distance" }, //WorldMarginShape2D - TODO, looks that polish letters ą ę are treaten as space, not as letter, so `będą` are renamed to `będistanceą`
|
||||
// // {"alt","alt_pressed"}, // This may broke a lot of comments and user variables
|
||||
// // {"command","command_pressed"},// This may broke a lot of comments and user variables
|
||||
|
@ -1069,6 +1082,7 @@ static const char *gdscript_properties_renames[][2] = {
|
|||
// // {"shift","shift_pressed"},// This may broke a lot of comments and user variables
|
||||
// { "autowrap", "autowrap_mode" }, // Label
|
||||
// { "cast_to", "target_position" }, // RayCast2D, RayCast3D
|
||||
// { "device", "output_device"}, // AudioServer - Too vague, most likely breaks comments & variables
|
||||
// { "doubleclick", "double_click" }, // InputEventMouseButton
|
||||
// { "group", "button_group" }, // BaseButton
|
||||
// { "process_mode", "process_callback" }, // AnimationTree, Camera2D
|
||||
|
@ -1084,6 +1098,7 @@ static const char *gdscript_properties_renames[][2] = {
|
|||
{ "bbcode_text", "text" }, // RichTextLabel
|
||||
{ "bg", "panel" }, // Theme
|
||||
{ "bg_focus", "focus" }, // Theme
|
||||
{ "capture_device", "input_device" }, // AudioServer
|
||||
{ "caret_blink_speed", "caret_blink_interval" }, // TextEdit, LineEdit
|
||||
{ "caret_moving_by_right_click", "caret_move_on_right_click" }, // TextEdit
|
||||
{ "caret_position", "caret_column" }, // LineEdit
|
||||
|
@ -1173,7 +1188,7 @@ static const char *gdscript_properties_renames[][2] = {
|
|||
};
|
||||
|
||||
// Some needs to be disabled, because users can use this names as variables
|
||||
static const char *csharp_properties_renames[][2] = {
|
||||
const char *ProjectConverter3To4::csharp_properties_renames[][2] = {
|
||||
// // { "D", "Distance" }, //WorldMarginShape2D - TODO, looks that polish letters ą ę are treaten as space, not as letter, so `będą` are renamed to `będistanceą`
|
||||
// // {"Alt","AltPressed"}, // This may broke a lot of comments and user variables
|
||||
// // {"Command","CommandPressed"},// This may broke a lot of comments and user variables
|
||||
|
@ -1278,7 +1293,7 @@ static const char *csharp_properties_renames[][2] = {
|
|||
{ nullptr, nullptr },
|
||||
};
|
||||
|
||||
static const char *gdscript_signals_renames[][2] = {
|
||||
const char *ProjectConverter3To4::gdscript_signals_renames[][2] = {
|
||||
// {"instantiate","instance"}, // FileSystemDock
|
||||
// { "hide", "hidden" }, // CanvasItem - function with same name exists
|
||||
// { "tween_all_completed","loop_finished"}, // Tween - TODO, not sure
|
||||
|
@ -1303,7 +1318,7 @@ static const char *gdscript_signals_renames[][2] = {
|
|||
{ nullptr, nullptr },
|
||||
};
|
||||
|
||||
static const char *csharp_signals_renames[][2] = {
|
||||
const char *ProjectConverter3To4::csharp_signals_renames[][2] = {
|
||||
// {"Instantiate","Instance"}, // FileSystemDock
|
||||
// { "Hide", "Hidden" }, // CanvasItem - function with same name exists
|
||||
// { "TweenAllCompleted","LoopFinished"}, // Tween - TODO, not sure
|
||||
|
@ -1327,7 +1342,7 @@ static const char *csharp_signals_renames[][2] = {
|
|||
|
||||
};
|
||||
|
||||
static const char *project_settings_renames[][2] = {
|
||||
const char *ProjectConverter3To4::project_settings_renames[][2] = {
|
||||
{ "audio/channel_disable_threshold_db", "audio/buses/channel_disable_threshold_db" },
|
||||
{ "audio/channel_disable_time", "audio/buses/channel_disable_time" },
|
||||
{ "audio/default_bus_layout", "audio/buses/default_bus_layout" },
|
||||
|
@ -1372,7 +1387,7 @@ static const char *project_settings_renames[][2] = {
|
|||
{ nullptr, nullptr },
|
||||
};
|
||||
|
||||
static const char *input_map_renames[][2] = {
|
||||
const char *ProjectConverter3To4::input_map_renames[][2] = {
|
||||
{ ",\"alt\":", ",\"alt_pressed\":" },
|
||||
{ ",\"shift\":", ",\"shift_pressed\":" },
|
||||
{ ",\"control\":", ",\"ctrl_pressed\":" },
|
||||
|
@ -1384,7 +1399,7 @@ static const char *input_map_renames[][2] = {
|
|||
{ nullptr, nullptr },
|
||||
};
|
||||
|
||||
static const char *builtin_types_renames[][2] = {
|
||||
const char *ProjectConverter3To4::builtin_types_renames[][2] = {
|
||||
{ "PoolByteArray", "PackedByteArray" },
|
||||
{ "PoolColorArray", "PackedColorArray" },
|
||||
{ "PoolIntArray", "PackedInt32Array" },
|
||||
|
@ -1398,7 +1413,7 @@ static const char *builtin_types_renames[][2] = {
|
|||
{ nullptr, nullptr },
|
||||
};
|
||||
|
||||
static const char *shaders_renames[][2] = {
|
||||
const char *ProjectConverter3To4::shaders_renames[][2] = {
|
||||
{ "ALPHA_SCISSOR", "ALPHA_SCISSOR_THRESHOLD" },
|
||||
{ "CAMERA_MATRIX", "INV_VIEW_MATRIX" },
|
||||
{ "INV_CAMERA_MATRIX", "VIEW_MATRIX" },
|
||||
|
@ -1416,7 +1431,7 @@ static const char *shaders_renames[][2] = {
|
|||
{ nullptr, nullptr },
|
||||
};
|
||||
|
||||
static const char *class_renames[][2] = {
|
||||
const char *ProjectConverter3To4::class_renames[][2] = {
|
||||
// { "BulletPhysicsDirectBodyState", "BulletPhysicsDirectBodyState3D" }, // Class is not visible in ClassDB
|
||||
// { "BulletPhysicsServer", "BulletPhysicsServer3D" }, // Class is not visible in ClassDB
|
||||
// { "GDScriptFunctionState", "Node3D" }, // TODO - not sure to which should be changed
|
||||
|
@ -1641,7 +1656,7 @@ static const char *class_renames[][2] = {
|
|||
{ nullptr, nullptr },
|
||||
};
|
||||
|
||||
static const char *color_renames[][2] = {
|
||||
const char *ProjectConverter3To4::ProjectConverter3To4::color_renames[][2] = {
|
||||
{ "aliceblue", "ALICE_BLUE" },
|
||||
{ "antiquewhite", "ANTIQUE_WHITE" },
|
||||
{ "aqua", "AQUA" },
|
||||
|
@ -4350,3 +4365,4 @@ int ProjectConverter3To4::validate_conversion() {
|
|||
}
|
||||
|
||||
#endif // MODULE_REGEX_ENABLED
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
/**************************************************************************/
|
||||
|
||||
#ifndef PROJECT_CONVERTER_3_TO_4_H
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
#define PROJECT_CONVERTER_3_TO_4_H
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
|
@ -41,6 +42,19 @@ class RegEx;
|
|||
class ProjectConverter3To4 {
|
||||
public:
|
||||
class RegExContainer;
|
||||
static const char *enum_renames[][2];
|
||||
static const char *gdscript_function_renames[][2];
|
||||
static const char *csharp_function_renames[][2];
|
||||
static const char *gdscript_properties_renames[][2];
|
||||
static const char *csharp_properties_renames[][2];
|
||||
static const char *gdscript_signals_renames[][2];
|
||||
static const char *csharp_signals_renames[][2];
|
||||
static const char *project_settings_renames[][2];
|
||||
static const char *input_map_renames[][2];
|
||||
static const char *builtin_types_renames[][2];
|
||||
static const char *shaders_renames[][2];
|
||||
static const char *class_renames[][2];
|
||||
static const char *color_renames[][2];
|
||||
|
||||
private:
|
||||
uint64_t maximum_file_size;
|
||||
|
@ -97,4 +111,6 @@ public:
|
|||
int convert();
|
||||
};
|
||||
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
#endif // PROJECT_CONVERTER_3_TO_4_H
|
||||
|
|
|
@ -563,6 +563,7 @@
|
|||
[/codeblock]
|
||||
[b]Note:[/b] Only the script can have a custom icon. Inner classes are not supported.
|
||||
[b]Note:[/b] As annotations describe their subject, the [code]@icon[/code] annotation must be placed before the class definition and inheritance.
|
||||
[b]Note:[/b] Unlike other annotations, the argument of the [code]@icon[/code] annotation must be a string literal (constant expressions are not supported).
|
||||
</description>
|
||||
</annotation>
|
||||
<annotation name="@onready">
|
||||
|
|
|
@ -1972,17 +1972,40 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
|
|||
}
|
||||
|
||||
if (p_return->return_value != nullptr) {
|
||||
reduce_expression(p_return->return_value);
|
||||
if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type()) {
|
||||
update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type());
|
||||
bool is_void_function = has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL;
|
||||
bool is_call = p_return->return_value->type == GDScriptParser::Node::CALL;
|
||||
if (is_void_function && is_call) {
|
||||
// Pretend the call is a root expression to allow those that are "void".
|
||||
reduce_call(static_cast<GDScriptParser::CallNode *>(p_return->return_value), false, true);
|
||||
} else {
|
||||
reduce_expression(p_return->return_value);
|
||||
}
|
||||
if (has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL) {
|
||||
push_error("A void function cannot return a value.", p_return);
|
||||
if (is_void_function) {
|
||||
p_return->void_return = true;
|
||||
const GDScriptParser::DataType &return_type = p_return->return_value->datatype;
|
||||
if (is_call && !return_type.is_hard_type()) {
|
||||
String function_name = parser->current_function->identifier ? parser->current_function->identifier->name.operator String() : String("<anonymous function>");
|
||||
String called_function_name = static_cast<GDScriptParser::CallNode *>(p_return->return_value)->function_name.operator String();
|
||||
#ifdef DEBUG_ENABLED
|
||||
parser->push_warning(p_return, GDScriptWarning::UNSAFE_VOID_RETURN, function_name, called_function_name);
|
||||
#endif
|
||||
mark_node_unsafe(p_return);
|
||||
} else if (!is_call) {
|
||||
push_error("A void function cannot return a value.", p_return);
|
||||
}
|
||||
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
||||
result.kind = GDScriptParser::DataType::BUILTIN;
|
||||
result.builtin_type = Variant::NIL;
|
||||
result.is_constant = true;
|
||||
} else {
|
||||
if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type()) {
|
||||
update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type());
|
||||
}
|
||||
if (has_expected_type && expected_type.is_hard_type() && p_return->return_value->is_constant) {
|
||||
update_const_expression_builtin_type(p_return->return_value, expected_type, "return");
|
||||
}
|
||||
result = p_return->return_value->get_datatype();
|
||||
}
|
||||
if (has_expected_type && expected_type.is_hard_type() && p_return->return_value->is_constant) {
|
||||
update_const_expression_builtin_type(p_return->return_value, expected_type, "return");
|
||||
}
|
||||
result = p_return->return_value->get_datatype();
|
||||
} else {
|
||||
// Return type is null by default.
|
||||
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
||||
|
@ -2464,6 +2487,62 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
|
|||
p_binary_op->set_datatype(result);
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
const char *GDScriptAnalyzer::get_rename_from_map(const char *map[][2], String key) {
|
||||
for (int index = 0; map[index][0]; index++) {
|
||||
if (map[index][0] == key) {
|
||||
return map[index][1];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Checks if an identifier/function name has been renamed in Godot 4, uses ProjectConverter3To4 for rename map.
|
||||
// Returns the new name if found, nullptr otherwise.
|
||||
const char *GDScriptAnalyzer::check_for_renamed_identifier(String identifier, GDScriptParser::Node::Type type) {
|
||||
switch (type) {
|
||||
case GDScriptParser::Node::IDENTIFIER: {
|
||||
// Check properties
|
||||
const char *result = get_rename_from_map(ProjectConverter3To4::gdscript_properties_renames, identifier);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
// Check enum values
|
||||
result = get_rename_from_map(ProjectConverter3To4::enum_renames, identifier);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
// Check color constants
|
||||
result = get_rename_from_map(ProjectConverter3To4::color_renames, identifier);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
// Check type names
|
||||
result = get_rename_from_map(ProjectConverter3To4::class_renames, identifier);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
return get_rename_from_map(ProjectConverter3To4::builtin_types_renames, identifier);
|
||||
}
|
||||
case GDScriptParser::Node::CALL: {
|
||||
const char *result = get_rename_from_map(ProjectConverter3To4::gdscript_function_renames, identifier);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
// Built-in Types are mistaken for function calls when the built-in type is not found.
|
||||
// Check built-in types if function rename not found
|
||||
return get_rename_from_map(ProjectConverter3To4::builtin_types_renames, identifier);
|
||||
}
|
||||
// Signal references don't get parsed through the GDScriptAnalyzer. No support for signal rename hints.
|
||||
default:
|
||||
// No rename found, return null
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
#endif // DISABLE_DEPRECATED
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_await, bool p_is_root) {
|
||||
bool all_is_constant = true;
|
||||
HashMap<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing.
|
||||
|
@ -2881,7 +2960,22 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||
}
|
||||
if (!found && (is_self || (base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN))) {
|
||||
String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string();
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
String rename_hint = String();
|
||||
if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
|
||||
const char *renamed_function_name = check_for_renamed_identifier(p_call->function_name, p_call->type);
|
||||
if (renamed_function_name) {
|
||||
rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", String(renamed_function_name) + "()");
|
||||
}
|
||||
}
|
||||
push_error(vformat(R"*(Function "%s()" not found in base %s.%s)*", p_call->function_name, base_name, rename_hint), p_call->is_super ? p_call : p_call->callee);
|
||||
#else // !DISABLE_DEPRECATED
|
||||
push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
|
||||
#endif // DISABLE_DEPRECATED
|
||||
#else
|
||||
push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
|
||||
#endif
|
||||
} else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) {
|
||||
push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type), p_call);
|
||||
}
|
||||
|
@ -3071,7 +3165,22 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
|
|||
p_identifier->reduced_value = result;
|
||||
p_identifier->set_datatype(type_from_variant(result, p_identifier));
|
||||
} else if (base.is_hard_type()) {
|
||||
push_error(vformat(R"(Cannot find constant "%s" on type "%s".)", name, base.to_string()), p_identifier);
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
String rename_hint = String();
|
||||
if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
|
||||
const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type);
|
||||
if (renamed_identifier_name) {
|
||||
rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name);
|
||||
}
|
||||
}
|
||||
push_error(vformat(R"(Cannot find constant "%s" on base "%s".%s)", name, base.to_string(), rename_hint), p_identifier);
|
||||
#else // !DISABLE_DEPRECATED
|
||||
push_error(vformat(R"(Cannot find constant "%s" on base "%s".)", name, base.to_string()), p_identifier);
|
||||
#endif // DISABLE_DEPRECATED
|
||||
#else
|
||||
push_error(vformat(R"(Cannot find constant "%s" on base "%s".)", name, base.to_string()), p_identifier);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
switch (base.builtin_type) {
|
||||
|
@ -3100,7 +3209,22 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
|
|||
}
|
||||
}
|
||||
if (base.is_hard_type()) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
String rename_hint = String();
|
||||
if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
|
||||
const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type);
|
||||
if (renamed_identifier_name) {
|
||||
rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name);
|
||||
}
|
||||
}
|
||||
push_error(vformat(R"(Cannot find property "%s" on base "%s".%s)", name, base.to_string(), rename_hint), p_identifier);
|
||||
#else // !DISABLE_DEPRECATED
|
||||
push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier);
|
||||
#endif // DISABLE_DEPRECATED
|
||||
#else
|
||||
push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3439,7 +3563,22 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|||
if (GDScriptUtilityFunctions::function_exists(name)) {
|
||||
push_error(vformat(R"(Built-in function "%s" cannot be used as an identifier.)", name), p_identifier);
|
||||
} else {
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
String rename_hint = String();
|
||||
if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
|
||||
const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type);
|
||||
if (renamed_identifier_name) {
|
||||
rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name);
|
||||
}
|
||||
}
|
||||
push_error(vformat(R"(Identifier "%s" not declared in the current scope.%s)", name, rename_hint), p_identifier);
|
||||
#else // !DISABLE_DEPRECATED
|
||||
push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier);
|
||||
#endif // DISABLE_DEPRECATED
|
||||
#else
|
||||
push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier);
|
||||
#endif
|
||||
}
|
||||
GDScriptParser::DataType dummy;
|
||||
dummy.kind = GDScriptParser::DataType::VARIANT;
|
||||
|
@ -4708,11 +4847,6 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) {
|
|||
}
|
||||
|
||||
Error GDScriptAnalyzer::resolve_inheritance() {
|
||||
// Apply annotations.
|
||||
for (GDScriptParser::AnnotationNode *&E : parser->head->annotations) {
|
||||
resolve_annotation(E);
|
||||
E->apply(parser, parser->head);
|
||||
}
|
||||
return resolve_class_inheritance(parser->head, true);
|
||||
}
|
||||
|
||||
|
@ -4746,6 +4880,12 @@ Error GDScriptAnalyzer::analyze() {
|
|||
return err;
|
||||
}
|
||||
|
||||
// Apply annotations.
|
||||
for (GDScriptParser::AnnotationNode *&E : parser->head->annotations) {
|
||||
resolve_annotation(E);
|
||||
E->apply(parser, parser->head);
|
||||
}
|
||||
|
||||
resolve_interface();
|
||||
resolve_body();
|
||||
if (!parser->errors.is_empty()) {
|
||||
|
|
|
@ -37,6 +37,10 @@
|
|||
#include "gdscript_cache.h"
|
||||
#include "gdscript_parser.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/project_converter_3_to_4.h"
|
||||
#endif
|
||||
|
||||
class GDScriptAnalyzer {
|
||||
GDScriptParser *parser = nullptr;
|
||||
HashMap<String, Ref<GDScriptParserRef>> depended_parsers;
|
||||
|
@ -133,6 +137,13 @@ class GDScriptAnalyzer {
|
|||
bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);
|
||||
#endif
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
const char *get_rename_from_map(const char *map[][2], String key);
|
||||
const char *check_for_renamed_identifier(String identifier, GDScriptParser::Node::Type type);
|
||||
#endif // DISABLE_DEPRECATED
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
public:
|
||||
Error resolve_inheritance();
|
||||
Error resolve_interface();
|
||||
|
|
|
@ -1859,7 +1859,12 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
|
|||
}
|
||||
}
|
||||
|
||||
gen->write_return(return_value);
|
||||
if (return_n->void_return) {
|
||||
// Always return "null", even if the expression is a call to a void function.
|
||||
gen->write_return(codegen.add_constant(Variant()));
|
||||
} else {
|
||||
gen->write_return(return_value);
|
||||
}
|
||||
if (return_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
|
||||
codegen.generator->pop_temporary();
|
||||
}
|
||||
|
|
|
@ -491,7 +491,12 @@ void GDScriptParser::parse_program() {
|
|||
AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
|
||||
if (annotation != nullptr) {
|
||||
if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
|
||||
head->annotations.push_back(annotation);
|
||||
// `@icon` needs to be applied in the parser. See GH-72444.
|
||||
if (annotation->name == SNAME("@icon")) {
|
||||
annotation->apply(this, head);
|
||||
} else {
|
||||
head->annotations.push_back(annotation);
|
||||
}
|
||||
} else {
|
||||
annotation_stack.push_back(annotation);
|
||||
// This annotation must appear after script-level annotations
|
||||
|
@ -809,7 +814,7 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) {
|
|||
if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
|
||||
push_error(R"(Expected newline after a standalone annotation.)");
|
||||
}
|
||||
if (annotation->name == "@export_category" || annotation->name == "@export_group" || annotation->name == "@export_subgroup") {
|
||||
if (annotation->name == SNAME("@export_category") || annotation->name == SNAME("@export_group") || annotation->name == SNAME("@export_subgroup")) {
|
||||
current_class->add_member_group(annotation);
|
||||
} else {
|
||||
// For potential non-group standalone annotations.
|
||||
|
@ -1436,7 +1441,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali
|
|||
match(GDScriptTokenizer::Token::NEWLINE); // Newline after annotation is optional.
|
||||
|
||||
if (valid) {
|
||||
valid = validate_annotation_argument_count(annotation);
|
||||
valid = validate_annotation_arguments(annotation);
|
||||
}
|
||||
|
||||
return valid ? annotation : nullptr;
|
||||
|
@ -3551,7 +3556,7 @@ bool GDScriptParser::AnnotationNode::applies_to(uint32_t p_target_kinds) const {
|
|||
return (info->target_kind & p_target_kinds) > 0;
|
||||
}
|
||||
|
||||
bool GDScriptParser::validate_annotation_argument_count(AnnotationNode *p_annotation) {
|
||||
bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation) {
|
||||
ERR_FAIL_COND_V_MSG(!valid_annotations.has(p_annotation->name), false, vformat(R"(Annotation "%s" not found to validate.)", p_annotation->name));
|
||||
|
||||
const MethodInfo &info = valid_annotations[p_annotation->name].info;
|
||||
|
@ -3566,6 +3571,27 @@ bool GDScriptParser::validate_annotation_argument_count(AnnotationNode *p_annota
|
|||
return false;
|
||||
}
|
||||
|
||||
// `@icon`'s argument needs to be resolved in the parser. See GH-72444.
|
||||
if (p_annotation->name == SNAME("@icon")) {
|
||||
ExpressionNode *argument = p_annotation->arguments[0];
|
||||
|
||||
if (argument->type != Node::LITERAL) {
|
||||
push_error(R"(Argument 1 of annotation "@icon" must be a string literal.)", argument);
|
||||
return false;
|
||||
}
|
||||
|
||||
Variant value = static_cast<LiteralNode *>(argument)->value;
|
||||
|
||||
if (value.get_type() != Variant::STRING) {
|
||||
push_error(R"(Argument 1 of annotation "@icon" must be a string literal.)", argument);
|
||||
return false;
|
||||
}
|
||||
|
||||
p_annotation->resolved_arguments.push_back(value);
|
||||
}
|
||||
|
||||
// For other annotations, see `GDScriptAnalyzer::resolve_annotation()`.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -970,6 +970,7 @@ public:
|
|||
|
||||
struct ReturnNode : public Node {
|
||||
ExpressionNode *return_value = nullptr;
|
||||
bool void_return = false;
|
||||
|
||||
ReturnNode() {
|
||||
type = RETURN;
|
||||
|
@ -1401,7 +1402,7 @@ private:
|
|||
// Annotations
|
||||
AnnotationNode *parse_annotation(uint32_t p_valid_targets);
|
||||
bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false);
|
||||
bool validate_annotation_argument_count(AnnotationNode *p_annotation);
|
||||
bool validate_annotation_arguments(AnnotationNode *p_annotation);
|
||||
void clear_unused_annotations();
|
||||
bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target);
|
||||
bool icon_annotation(const AnnotationNode *p_annotation, Node *p_target);
|
||||
|
|
|
@ -125,6 +125,10 @@ String GDScriptWarning::get_message() const {
|
|||
CHECK_SYMBOLS(4);
|
||||
return "The argument '" + symbols[0] + "' of the function '" + symbols[1] + "' requires a the subtype '" + symbols[2] + "' but the supertype '" + symbols[3] + "' was provided";
|
||||
} break;
|
||||
case UNSAFE_VOID_RETURN: {
|
||||
CHECK_SYMBOLS(2);
|
||||
return "The method '" + symbols[0] + "()' returns 'void' but it's trying to return a call to '" + symbols[1] + "()' that can't be ensured to also be 'void'.";
|
||||
} break;
|
||||
case DEPRECATED_KEYWORD: {
|
||||
CHECK_SYMBOLS(2);
|
||||
return "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'.";
|
||||
|
@ -163,6 +167,9 @@ String GDScriptWarning::get_message() const {
|
|||
CHECK_SYMBOLS(1);
|
||||
return vformat(R"(The identifier "%s" has misleading characters and might be confused with something else.)", symbols[0]);
|
||||
}
|
||||
case RENAMED_IN_GD4_HINT: {
|
||||
break; // Renamed identifier hint is taken care of by the GDScriptAnalyzer. No message needed here.
|
||||
}
|
||||
case WARNING_MAX:
|
||||
break; // Can't happen, but silences warning
|
||||
}
|
||||
|
@ -184,6 +191,9 @@ int GDScriptWarning::get_default_value(Code p_code) {
|
|||
|
||||
PropertyInfo GDScriptWarning::get_property_info(Code p_code) {
|
||||
// Making this a separate function in case a warning needs different PropertyInfo in the future.
|
||||
if (p_code == Code::RENAMED_IN_GD4_HINT) {
|
||||
return PropertyInfo(Variant::BOOL, get_settings_path_from_code(p_code));
|
||||
}
|
||||
return PropertyInfo(Variant::INT, get_settings_path_from_code(p_code), PROPERTY_HINT_ENUM, "Ignore,Warn,Error");
|
||||
}
|
||||
|
||||
|
@ -218,6 +228,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
|
|||
"UNSAFE_METHOD_ACCESS",
|
||||
"UNSAFE_CAST",
|
||||
"UNSAFE_CALL_ARGUMENT",
|
||||
"UNSAFE_VOID_RETURN",
|
||||
"DEPRECATED_KEYWORD",
|
||||
"STANDALONE_TERNARY",
|
||||
"ASSERT_ALWAYS_TRUE",
|
||||
|
@ -229,6 +240,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
|
|||
"INT_AS_ENUM_WITHOUT_MATCH",
|
||||
"STATIC_CALLED_ON_INSTANCE",
|
||||
"CONFUSABLE_IDENTIFIER",
|
||||
"RENAMED_IN_GODOT_4_HINT"
|
||||
};
|
||||
|
||||
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
|
||||
|
|
|
@ -69,6 +69,7 @@ public:
|
|||
UNSAFE_METHOD_ACCESS, // Function not found in the detected type (but can be in subtypes).
|
||||
UNSAFE_CAST, // Cast used in an unknown type.
|
||||
UNSAFE_CALL_ARGUMENT, // Function call argument is of a supertype of the require argument.
|
||||
UNSAFE_VOID_RETURN, // Function returns void but returned a call to a function that can't be type checked.
|
||||
DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced.
|
||||
STANDALONE_TERNARY, // Return value of ternary expression is discarded.
|
||||
ASSERT_ALWAYS_TRUE, // Expression for assert argument is always true.
|
||||
|
@ -80,6 +81,7 @@ public:
|
|||
INT_AS_ENUM_WITHOUT_MATCH, // An integer value was used as an enum value without matching enum member.
|
||||
STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself.
|
||||
CONFUSABLE_IDENTIFIER, // The identifier contains misleading characters that can be confused. E.g. "usеr" (has Cyrillic "е" instead of Latin "e").
|
||||
RENAMED_IN_GD4_HINT, // A variable or function that could not be found has been renamed in Godot 4
|
||||
WARNING_MAX,
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
func test():
|
||||
return_call()
|
||||
return_nothing()
|
||||
return_side_effect()
|
||||
var r = return_side_effect.call() # Untyped call to check return value.
|
||||
prints(r, typeof(r) == TYPE_NIL)
|
||||
print("end")
|
||||
|
||||
func side_effect(v):
|
||||
print("effect")
|
||||
return v
|
||||
|
||||
func return_call() -> void:
|
||||
return print("hello")
|
||||
|
||||
func return_nothing() -> void:
|
||||
return
|
||||
|
||||
func return_side_effect() -> void:
|
||||
return side_effect("x")
|
|
@ -0,0 +1,10 @@
|
|||
GDTEST_OK
|
||||
>> WARNING
|
||||
>> Line: 20
|
||||
>> UNSAFE_VOID_RETURN
|
||||
>> The method 'return_side_effect()' returns 'void' but it's trying to return a call to 'side_effect()' that can't be ensured to also be 'void'.
|
||||
hello
|
||||
effect
|
||||
effect
|
||||
<null> true
|
||||
end
|
|
@ -991,15 +991,6 @@ void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_wi
|
|||
wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - wsize.height / 3);
|
||||
window_set_position(wpos, p_window);
|
||||
}
|
||||
|
||||
// Don't let the mouse leave the window when resizing to a smaller resolution.
|
||||
if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
|
||||
RECT crect;
|
||||
GetClientRect(wd.hWnd, &crect);
|
||||
ClientToScreen(wd.hWnd, (POINT *)&crect.left);
|
||||
ClientToScreen(wd.hWnd, (POINT *)&crect.right);
|
||||
ClipCursor(&crect);
|
||||
}
|
||||
}
|
||||
|
||||
Point2i DisplayServerWindows::window_get_position(WindowID p_window) const {
|
||||
|
@ -1077,15 +1068,6 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window
|
|||
AdjustWindowRectEx(&rc, style, false, exStyle);
|
||||
MoveWindow(wd.hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
|
||||
|
||||
// Don't let the mouse leave the window when moved.
|
||||
if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
|
||||
RECT rect;
|
||||
GetClientRect(wd.hWnd, &rect);
|
||||
ClientToScreen(wd.hWnd, (POINT *)&rect.left);
|
||||
ClientToScreen(wd.hWnd, (POINT *)&rect.right);
|
||||
ClipCursor(&rect);
|
||||
}
|
||||
|
||||
wd.last_pos = p_position;
|
||||
_update_real_mouse_position(p_window);
|
||||
}
|
||||
|
@ -1227,15 +1209,6 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo
|
|||
}
|
||||
|
||||
MoveWindow(wd.hWnd, rect.left, rect.top, w, h, TRUE);
|
||||
|
||||
// Don't let the mouse leave the window when resizing to a smaller resolution.
|
||||
if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
|
||||
RECT crect;
|
||||
GetClientRect(wd.hWnd, &crect);
|
||||
ClientToScreen(wd.hWnd, (POINT *)&crect.left);
|
||||
ClientToScreen(wd.hWnd, (POINT *)&crect.right);
|
||||
ClipCursor(&crect);
|
||||
}
|
||||
}
|
||||
|
||||
Size2i DisplayServerWindows::window_get_size(WindowID p_window) const {
|
||||
|
@ -1423,15 +1396,6 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
|
|||
SystemParametersInfoA(SPI_SETMOUSETRAILS, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't let the mouse leave the window when resizing to a smaller resolution.
|
||||
if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
|
||||
RECT crect;
|
||||
GetClientRect(wd.hWnd, &crect);
|
||||
ClientToScreen(wd.hWnd, (POINT *)&crect.left);
|
||||
ClientToScreen(wd.hWnd, (POINT *)&crect.right);
|
||||
ClipCursor(&crect);
|
||||
}
|
||||
}
|
||||
|
||||
DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const {
|
||||
|
@ -3381,6 +3345,15 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
|
|||
Callable::CallError ce;
|
||||
window.rect_changed_callback.callp(args, 1, ret, ce);
|
||||
}
|
||||
|
||||
// Update cursor clip region after window rect has changed.
|
||||
if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
|
||||
RECT crect;
|
||||
GetClientRect(window.hWnd, &crect);
|
||||
ClientToScreen(window.hWnd, (POINT *)&crect.left);
|
||||
ClientToScreen(window.hWnd, (POINT *)&crect.right);
|
||||
ClipCursor(&crect);
|
||||
}
|
||||
}
|
||||
|
||||
// Return here to prevent WM_MOVE and WM_SIZE from being sent
|
||||
|
|
|
@ -50,7 +50,7 @@ void Camera2D::_update_scroll() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (current) {
|
||||
if (is_current()) {
|
||||
ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id));
|
||||
|
||||
Transform2D xform = get_camera_transform();
|
||||
|
@ -241,10 +241,6 @@ void Camera2D::_notification(int p_what) {
|
|||
viewport = get_viewport();
|
||||
}
|
||||
|
||||
if (is_current()) {
|
||||
viewport->_camera_2d_set(this);
|
||||
}
|
||||
|
||||
canvas = get_canvas();
|
||||
|
||||
RID vp = viewport->get_viewport_rid();
|
||||
|
@ -254,6 +250,10 @@ void Camera2D::_notification(int p_what) {
|
|||
add_to_group(group_name);
|
||||
add_to_group(canvas_group_name);
|
||||
|
||||
if (enabled && !viewport->get_camera_2d()) {
|
||||
make_current();
|
||||
}
|
||||
|
||||
_update_process_callback();
|
||||
first = true;
|
||||
_update_scroll();
|
||||
|
@ -261,11 +261,7 @@ void Camera2D::_notification(int p_what) {
|
|||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
if (is_current()) {
|
||||
if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) {
|
||||
viewport->set_canvas_transform(Transform2D());
|
||||
clear_current();
|
||||
current = true;
|
||||
}
|
||||
clear_current();
|
||||
}
|
||||
remove_from_group(group_name);
|
||||
remove_from_group(canvas_group_name);
|
||||
|
@ -397,19 +393,31 @@ void Camera2D::set_process_callback(Camera2DProcessCallback p_mode) {
|
|||
_update_process_callback();
|
||||
}
|
||||
|
||||
void Camera2D::set_enabled(bool p_enabled) {
|
||||
enabled = p_enabled;
|
||||
|
||||
if (enabled && is_inside_tree() && !viewport->get_camera_2d()) {
|
||||
make_current();
|
||||
} else if (!enabled && is_current()) {
|
||||
clear_current();
|
||||
}
|
||||
}
|
||||
|
||||
bool Camera2D::is_enabled() const {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
Camera2D::Camera2DProcessCallback Camera2D::get_process_callback() const {
|
||||
return process_callback;
|
||||
}
|
||||
|
||||
void Camera2D::_make_current(Object *p_which) {
|
||||
if (p_which == this) {
|
||||
current = true;
|
||||
if (is_inside_tree()) {
|
||||
get_viewport()->_camera_2d_set(this);
|
||||
queue_redraw();
|
||||
}
|
||||
} else {
|
||||
current = false;
|
||||
if (is_inside_tree()) {
|
||||
if (get_viewport()->get_camera_2d() == this) {
|
||||
get_viewport()->_camera_2d_set(nullptr);
|
||||
|
@ -419,45 +427,34 @@ void Camera2D::_make_current(Object *p_which) {
|
|||
}
|
||||
}
|
||||
|
||||
void Camera2D::set_current(bool p_current) {
|
||||
if (p_current) {
|
||||
make_current();
|
||||
} else {
|
||||
if (current) {
|
||||
clear_current();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Camera2D::_update_process_internal_for_smoothing() {
|
||||
bool is_not_in_scene_or_editor = !(is_inside_tree() && Engine::get_singleton()->is_editor_hint());
|
||||
bool is_any_smoothing_valid = position_smoothing_speed > 0 || rotation_smoothing_speed > 0;
|
||||
|
||||
bool enabled = is_any_smoothing_valid && is_not_in_scene_or_editor;
|
||||
set_process_internal(enabled);
|
||||
}
|
||||
|
||||
bool Camera2D::is_current() const {
|
||||
return current;
|
||||
bool enable = is_any_smoothing_valid && is_not_in_scene_or_editor;
|
||||
set_process_internal(enable);
|
||||
}
|
||||
|
||||
void Camera2D::make_current() {
|
||||
if (is_inside_tree()) {
|
||||
get_tree()->call_group(group_name, "_make_current", this);
|
||||
} else {
|
||||
current = true;
|
||||
}
|
||||
ERR_FAIL_COND(!enabled || !is_inside_tree());
|
||||
get_tree()->call_group(group_name, "_make_current", this);
|
||||
_update_scroll();
|
||||
}
|
||||
|
||||
void Camera2D::clear_current() {
|
||||
if (is_inside_tree()) {
|
||||
get_tree()->call_group(group_name, "_make_current", (Object *)nullptr);
|
||||
} else {
|
||||
current = false;
|
||||
ERR_FAIL_COND(!is_current());
|
||||
if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) {
|
||||
viewport->assign_next_enabled_camera_2d(group_name);
|
||||
}
|
||||
}
|
||||
|
||||
bool Camera2D::is_current() const {
|
||||
if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) {
|
||||
return viewport->get_camera_2d() == this;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Camera2D::set_limit(Side p_side, int p_limit) {
|
||||
ERR_FAIL_INDEX((int)p_side, 4);
|
||||
limit[p_side] = p_limit;
|
||||
|
@ -715,7 +712,10 @@ void Camera2D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_process_callback", "mode"), &Camera2D::set_process_callback);
|
||||
ClassDB::bind_method(D_METHOD("get_process_callback"), &Camera2D::get_process_callback);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_current", "current"), &Camera2D::set_current);
|
||||
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &Camera2D::set_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_enabled"), &Camera2D::is_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("make_current"), &Camera2D::make_current);
|
||||
ClassDB::bind_method(D_METHOD("is_current"), &Camera2D::is_current);
|
||||
ClassDB::bind_method(D_METHOD("_make_current"), &Camera2D::_make_current);
|
||||
|
||||
|
@ -779,7 +779,7 @@ void Camera2D::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_mode", PROPERTY_HINT_ENUM, "Fixed TopLeft,Drag Center"), "set_anchor_mode", "get_anchor_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_rotation"), "set_ignore_rotation", "is_ignoring_rotation");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "zoom", PROPERTY_HINT_LINK), "set_zoom", "get_zoom");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", PROPERTY_USAGE_NONE), "set_custom_viewport", "get_custom_viewport");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_callback", "get_process_callback");
|
||||
|
|
|
@ -64,7 +64,7 @@ protected:
|
|||
Vector2 zoom_scale = Vector2(1, 1);
|
||||
AnchorMode anchor_mode = ANCHOR_MODE_DRAG_CENTER;
|
||||
bool ignore_rotation = true;
|
||||
bool current = false;
|
||||
bool enabled = true;
|
||||
real_t position_smoothing_speed = 5.0;
|
||||
bool follow_smoothing_enabled = false;
|
||||
|
||||
|
@ -88,7 +88,6 @@ protected:
|
|||
void _update_scroll();
|
||||
|
||||
void _make_current(Object *p_which);
|
||||
void set_current(bool p_current);
|
||||
|
||||
void _set_old_smoothing(real_t p_enable);
|
||||
|
||||
|
@ -155,6 +154,9 @@ public:
|
|||
void set_process_callback(Camera2DProcessCallback p_mode);
|
||||
Camera2DProcessCallback get_process_callback() const;
|
||||
|
||||
void set_enabled(bool p_enabled);
|
||||
bool is_enabled() const;
|
||||
|
||||
void make_current();
|
||||
void clear_current();
|
||||
bool is_current() const;
|
||||
|
|
|
@ -30,12 +30,20 @@
|
|||
|
||||
#include "animation_blend_space_1d.h"
|
||||
|
||||
#include "animation_blend_tree.h"
|
||||
|
||||
void AnimationNodeBlendSpace1D::get_parameter_list(List<PropertyInfo> *r_list) const {
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, blend_position));
|
||||
r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
r_list->push_back(PropertyInfo(Variant::FLOAT, length_internal, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
|
||||
}
|
||||
|
||||
Variant AnimationNodeBlendSpace1D::get_parameter_default_value(const StringName &p_parameter) const {
|
||||
return 0;
|
||||
if (p_parameter == closest) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<AnimationNode> AnimationNodeBlendSpace1D::get_child_by_name(const StringName &p_name) {
|
||||
|
@ -77,6 +85,9 @@ void AnimationNodeBlendSpace1D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_value_label", "text"), &AnimationNodeBlendSpace1D::set_value_label);
|
||||
ClassDB::bind_method(D_METHOD("get_value_label"), &AnimationNodeBlendSpace1D::get_value_label);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_blend_mode", "mode"), &AnimationNodeBlendSpace1D::set_blend_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_blend_mode"), &AnimationNodeBlendSpace1D::get_blend_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlendSpace1D::set_use_sync);
|
||||
ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlendSpace1D::is_using_sync);
|
||||
|
||||
|
@ -91,7 +102,12 @@ void AnimationNodeBlendSpace1D::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_space", "get_max_space");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_snap", "get_snap");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_value_label", "get_value_label");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Interpolated,Discrete,Carry", PROPERTY_USAGE_NO_EDITOR), "set_blend_mode", "get_blend_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_use_sync", "is_using_sync");
|
||||
|
||||
BIND_ENUM_CONSTANT(BLEND_MODE_INTERPOLATED);
|
||||
BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE);
|
||||
BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE_CARRY);
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::get_child_nodes(List<ChildNode> *r_child_nodes) {
|
||||
|
@ -214,6 +230,14 @@ String AnimationNodeBlendSpace1D::get_value_label() const {
|
|||
return value_label;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::set_blend_mode(BlendMode p_blend_mode) {
|
||||
blend_mode = p_blend_mode;
|
||||
}
|
||||
|
||||
AnimationNodeBlendSpace1D::BlendMode AnimationNodeBlendSpace1D::get_blend_mode() const {
|
||||
return blend_mode;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1D::set_use_sync(bool p_sync) {
|
||||
sync = p_sync;
|
||||
}
|
||||
|
@ -241,79 +265,125 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_is_
|
|||
}
|
||||
|
||||
double blend_pos = get_parameter(blend_position);
|
||||
|
||||
float weights[MAX_BLEND_POINTS] = {};
|
||||
|
||||
int point_lower = -1;
|
||||
float pos_lower = 0.0;
|
||||
int point_higher = -1;
|
||||
float pos_higher = 0.0;
|
||||
|
||||
// find the closest two points to blend between
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
float pos = blend_points[i].position;
|
||||
|
||||
if (pos <= blend_pos) {
|
||||
if (point_lower == -1) {
|
||||
point_lower = i;
|
||||
pos_lower = pos;
|
||||
} else if ((blend_pos - pos) < (blend_pos - pos_lower)) {
|
||||
point_lower = i;
|
||||
pos_lower = pos;
|
||||
}
|
||||
} else {
|
||||
if (point_higher == -1) {
|
||||
point_higher = i;
|
||||
pos_higher = pos;
|
||||
} else if ((pos - blend_pos) < (pos_higher - blend_pos)) {
|
||||
point_higher = i;
|
||||
pos_higher = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fill in weights
|
||||
|
||||
if (point_lower == -1 && point_higher != -1) {
|
||||
// we are on the left side, no other point to the left
|
||||
// we just play the next point.
|
||||
|
||||
weights[point_higher] = 1.0;
|
||||
} else if (point_higher == -1) {
|
||||
// we are on the right side, no other point to the right
|
||||
// we just play the previous point
|
||||
|
||||
weights[point_lower] = 1.0;
|
||||
} else {
|
||||
// we are between two points.
|
||||
// figure out weights, then blend the animations
|
||||
|
||||
float distance_between_points = pos_higher - pos_lower;
|
||||
|
||||
float current_pos_inbetween = blend_pos - pos_lower;
|
||||
|
||||
float blend_percentage = current_pos_inbetween / distance_between_points;
|
||||
|
||||
float blend_lower = 1.0 - blend_percentage;
|
||||
float blend_higher = blend_percentage;
|
||||
|
||||
weights[point_lower] = blend_lower;
|
||||
weights[point_higher] = blend_higher;
|
||||
}
|
||||
|
||||
// actually blend the animations now
|
||||
|
||||
int cur_closest = get_parameter(closest);
|
||||
double cur_length_internal = get_parameter(length_internal);
|
||||
double max_time_remaining = 0.0;
|
||||
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
if (i == point_lower || i == point_higher) {
|
||||
double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, weights[i], FILTER_IGNORE, true);
|
||||
max_time_remaining = MAX(max_time_remaining, remaining);
|
||||
} else if (sync) {
|
||||
blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true);
|
||||
if (blend_mode == BLEND_MODE_INTERPOLATED) {
|
||||
float weights[MAX_BLEND_POINTS] = {};
|
||||
|
||||
int point_lower = -1;
|
||||
float pos_lower = 0.0;
|
||||
int point_higher = -1;
|
||||
float pos_higher = 0.0;
|
||||
|
||||
// find the closest two points to blend between
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
float pos = blend_points[i].position;
|
||||
|
||||
if (pos <= blend_pos) {
|
||||
if (point_lower == -1) {
|
||||
point_lower = i;
|
||||
pos_lower = pos;
|
||||
} else if ((blend_pos - pos) < (blend_pos - pos_lower)) {
|
||||
point_lower = i;
|
||||
pos_lower = pos;
|
||||
}
|
||||
} else {
|
||||
if (point_higher == -1) {
|
||||
point_higher = i;
|
||||
pos_higher = pos;
|
||||
} else if ((pos - blend_pos) < (pos_higher - blend_pos)) {
|
||||
point_higher = i;
|
||||
pos_higher = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fill in weights
|
||||
|
||||
if (point_lower == -1 && point_higher != -1) {
|
||||
// we are on the left side, no other point to the left
|
||||
// we just play the next point.
|
||||
|
||||
weights[point_higher] = 1.0;
|
||||
} else if (point_higher == -1) {
|
||||
// we are on the right side, no other point to the right
|
||||
// we just play the previous point
|
||||
|
||||
weights[point_lower] = 1.0;
|
||||
} else {
|
||||
// we are between two points.
|
||||
// figure out weights, then blend the animations
|
||||
|
||||
float distance_between_points = pos_higher - pos_lower;
|
||||
|
||||
float current_pos_inbetween = blend_pos - pos_lower;
|
||||
|
||||
float blend_percentage = current_pos_inbetween / distance_between_points;
|
||||
|
||||
float blend_lower = 1.0 - blend_percentage;
|
||||
float blend_higher = blend_percentage;
|
||||
|
||||
weights[point_lower] = blend_lower;
|
||||
weights[point_higher] = blend_higher;
|
||||
}
|
||||
|
||||
// actually blend the animations now
|
||||
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
if (i == point_lower || i == point_higher) {
|
||||
double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, weights[i], FILTER_IGNORE, true);
|
||||
max_time_remaining = MAX(max_time_remaining, remaining);
|
||||
} else if (sync) {
|
||||
blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int new_closest = -1;
|
||||
double new_closest_dist = 1e20;
|
||||
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
double d = abs(blend_points[i].position - blend_pos);
|
||||
if (d < new_closest_dist) {
|
||||
new_closest = i;
|
||||
new_closest_dist = d;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_closest != cur_closest && new_closest != -1) {
|
||||
double from = 0.0;
|
||||
if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) {
|
||||
//for ping-pong loop
|
||||
Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[cur_closest].node);
|
||||
Ref<AnimationNodeAnimation> na_n = static_cast<Ref<AnimationNodeAnimation>>(blend_points[new_closest].node);
|
||||
if (!na_c.is_null() && !na_n.is_null()) {
|
||||
na_n->set_backward(na_c->is_backward());
|
||||
}
|
||||
//see how much animation remains
|
||||
from = cur_length_internal - blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, false, p_is_external_seeking, 0.0, FILTER_IGNORE, true);
|
||||
}
|
||||
|
||||
max_time_remaining = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true);
|
||||
cur_length_internal = from + max_time_remaining;
|
||||
|
||||
cur_closest = new_closest;
|
||||
|
||||
} else {
|
||||
max_time_remaining = blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true);
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
for (int i = 0; i < blend_points_used; i++) {
|
||||
if (i != cur_closest) {
|
||||
blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_parameter(this->closest, cur_closest);
|
||||
set_parameter(this->length_internal, cur_length_internal);
|
||||
return max_time_remaining;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,14 @@
|
|||
class AnimationNodeBlendSpace1D : public AnimationRootNode {
|
||||
GDCLASS(AnimationNodeBlendSpace1D, AnimationRootNode);
|
||||
|
||||
public:
|
||||
enum BlendMode {
|
||||
BLEND_MODE_INTERPOLATED,
|
||||
BLEND_MODE_DISCRETE,
|
||||
BLEND_MODE_DISCRETE_CARRY,
|
||||
};
|
||||
|
||||
protected:
|
||||
enum {
|
||||
MAX_BLEND_POINTS = 64
|
||||
};
|
||||
|
@ -61,6 +69,10 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode {
|
|||
void _tree_changed();
|
||||
|
||||
StringName blend_position = "blend_position";
|
||||
StringName closest = "closest";
|
||||
StringName length_internal = "length_internal";
|
||||
|
||||
BlendMode blend_mode = BLEND_MODE_INTERPOLATED;
|
||||
|
||||
protected:
|
||||
bool sync = false;
|
||||
|
@ -95,6 +107,9 @@ public:
|
|||
void set_value_label(const String &p_label);
|
||||
String get_value_label() const;
|
||||
|
||||
void set_blend_mode(BlendMode p_blend_mode);
|
||||
BlendMode get_blend_mode() const;
|
||||
|
||||
void set_use_sync(bool p_sync);
|
||||
bool is_using_sync() const;
|
||||
|
||||
|
@ -107,4 +122,6 @@ public:
|
|||
~AnimationNodeBlendSpace1D();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationNodeBlendSpace1D::BlendMode)
|
||||
|
||||
#endif // ANIMATION_BLEND_SPACE_1D_H
|
||||
|
|
|
@ -692,6 +692,12 @@ Transform2D Control::get_transform() const {
|
|||
return xform;
|
||||
}
|
||||
|
||||
void Control::_toplevel_changed_on_parent() {
|
||||
// Update root control status.
|
||||
_notification(NOTIFICATION_EXIT_CANVAS);
|
||||
_notification(NOTIFICATION_ENTER_CANVAS);
|
||||
}
|
||||
|
||||
/// Anchors and offsets.
|
||||
|
||||
void Control::_set_anchor(Side p_side, real_t p_anchor) {
|
||||
|
|
|
@ -292,6 +292,9 @@ private:
|
|||
void _update_minimum_size();
|
||||
void _size_changed();
|
||||
|
||||
void _toplevel_changed() override{}; // Controls don't need to do anything, only other CanvasItems.
|
||||
void _toplevel_changed_on_parent() override;
|
||||
|
||||
void _clear_size_warning();
|
||||
|
||||
// Input events.
|
||||
|
|
|
@ -400,11 +400,28 @@ void CanvasItem::set_as_top_level(bool p_top_level) {
|
|||
|
||||
_exit_canvas();
|
||||
top_level = p_top_level;
|
||||
_toplevel_changed();
|
||||
_enter_canvas();
|
||||
|
||||
_notify_transform();
|
||||
}
|
||||
|
||||
void CanvasItem::_toplevel_changed() {
|
||||
// Inform children that toplevel status has changed on a parent.
|
||||
int childs = get_child_count();
|
||||
for (int i = 0; i < childs; i++) {
|
||||
CanvasItem *child = Object::cast_to<CanvasItem>(get_child(i));
|
||||
if (child) {
|
||||
child->_toplevel_changed_on_parent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasItem::_toplevel_changed_on_parent() {
|
||||
// Inform children that toplevel status has changed on a parent.
|
||||
_toplevel_changed();
|
||||
}
|
||||
|
||||
bool CanvasItem::is_set_as_top_level() const {
|
||||
return top_level;
|
||||
}
|
||||
|
|
|
@ -125,6 +125,9 @@ private:
|
|||
void _propagate_visibility_changed(bool p_parent_visible_in_tree);
|
||||
void _handle_visibility_change(bool p_visible);
|
||||
|
||||
virtual void _toplevel_changed();
|
||||
virtual void _toplevel_changed_on_parent();
|
||||
|
||||
void _redraw_callback();
|
||||
|
||||
void _enter_canvas();
|
||||
|
|
|
@ -1045,6 +1045,25 @@ Transform2D Viewport::get_final_transform() const {
|
|||
return _get_input_pre_xform().affine_inverse() * stretch_transform * global_canvas_transform;
|
||||
}
|
||||
|
||||
void Viewport::assign_next_enabled_camera_2d(const StringName &p_camera_group) {
|
||||
List<Node *> camera_list;
|
||||
get_tree()->get_nodes_in_group(p_camera_group, &camera_list);
|
||||
|
||||
Camera2D *new_camera = nullptr;
|
||||
for (const Node *E : camera_list) {
|
||||
const Camera2D *cam = Object::cast_to<Camera2D>(E);
|
||||
if (cam->is_enabled()) {
|
||||
new_camera = const_cast<Camera2D *>(cam);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_camera_2d_set(new_camera);
|
||||
if (!camera_2d) {
|
||||
set_canvas_transform(Transform2D());
|
||||
}
|
||||
}
|
||||
|
||||
void Viewport::_update_canvas_items(Node *p_node) {
|
||||
if (p_node != this) {
|
||||
Window *w = Object::cast_to<Window>(p_node);
|
||||
|
@ -1110,14 +1129,11 @@ Viewport::PositionalShadowAtlasQuadrantSubdiv Viewport::get_positional_shadow_at
|
|||
}
|
||||
|
||||
Transform2D Viewport::_get_input_pre_xform() const {
|
||||
Transform2D pre_xf;
|
||||
|
||||
if (to_screen_rect.size.x != 0 && to_screen_rect.size.y != 0) {
|
||||
pre_xf.columns[2] = -to_screen_rect.position;
|
||||
pre_xf.scale(Vector2(size) / to_screen_rect.size);
|
||||
const Window *this_window = Object::cast_to<Window>(this);
|
||||
if (this_window) {
|
||||
return this_window->window_transform.affine_inverse();
|
||||
}
|
||||
|
||||
return pre_xf;
|
||||
return Transform2D();
|
||||
}
|
||||
|
||||
Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) {
|
||||
|
|
|
@ -511,6 +511,7 @@ public:
|
|||
Transform2D get_global_canvas_transform() const;
|
||||
|
||||
Transform2D get_final_transform() const;
|
||||
void assign_next_enabled_camera_2d(const StringName &p_camera_group);
|
||||
|
||||
void gui_set_root_order_dirty();
|
||||
|
||||
|
|
|
@ -907,6 +907,7 @@ void Window::_update_viewport_size() {
|
|||
Rect2i attach_to_screen_rect(Point2i(), size);
|
||||
Transform2D stretch_transform_new;
|
||||
float font_oversampling = 1.0;
|
||||
window_transform = Transform2D();
|
||||
|
||||
if (content_scale_mode == CONTENT_SCALE_MODE_DISABLED || content_scale_size.x == 0 || content_scale_size.y == 0) {
|
||||
font_oversampling = content_scale_factor;
|
||||
|
@ -993,11 +994,18 @@ void Window::_update_viewport_size() {
|
|||
Size2 scale = Vector2(screen_size) / Vector2(final_size_override);
|
||||
stretch_transform_new.scale(scale);
|
||||
|
||||
window_transform.translate_local(margin);
|
||||
} break;
|
||||
case CONTENT_SCALE_MODE_VIEWPORT: {
|
||||
final_size = (viewport_size / content_scale_factor).floor();
|
||||
attach_to_screen_rect = Rect2(margin, screen_size);
|
||||
|
||||
window_transform.translate_local(margin);
|
||||
if (final_size.x != 0 && final_size.y != 0) {
|
||||
Transform2D scale_transform;
|
||||
scale_transform.scale(Vector2(attach_to_screen_rect.size) / Vector2(final_size));
|
||||
window_transform *= scale_transform;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
@ -2127,13 +2135,13 @@ Transform2D Window::get_popup_base_transform() const {
|
|||
if (is_embedding_subwindows()) {
|
||||
return Transform2D();
|
||||
}
|
||||
Transform2D window_transform;
|
||||
window_transform.set_origin(get_position());
|
||||
window_transform *= Viewport::get_screen_transform();
|
||||
Transform2D popup_base_transform;
|
||||
popup_base_transform.set_origin(get_position());
|
||||
popup_base_transform *= Viewport::get_screen_transform();
|
||||
if (_get_embedder()) {
|
||||
return _get_embedder()->get_popup_base_transform() * window_transform;
|
||||
return _get_embedder()->get_popup_base_transform() * popup_base_transform;
|
||||
}
|
||||
return window_transform;
|
||||
return popup_base_transform;
|
||||
}
|
||||
|
||||
void Window::_bind_methods() {
|
||||
|
|
|
@ -173,6 +173,8 @@ private:
|
|||
|
||||
Viewport *embedder = nullptr;
|
||||
|
||||
Transform2D window_transform;
|
||||
|
||||
friend class Viewport; //friend back, can call the methods below
|
||||
|
||||
void _window_input(const Ref<InputEvent> &p_ev);
|
||||
|
|
|
@ -6923,15 +6923,34 @@ void VisualShaderNodeSwitch::_bind_methods() { // static
|
|||
}
|
||||
|
||||
String VisualShaderNodeSwitch::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
|
||||
bool use_mix = false;
|
||||
switch (op_type) {
|
||||
case OP_TYPE_FLOAT: {
|
||||
use_mix = true;
|
||||
} break;
|
||||
case OP_TYPE_VECTOR_2D: {
|
||||
use_mix = true;
|
||||
} break;
|
||||
case OP_TYPE_VECTOR_3D: {
|
||||
use_mix = true;
|
||||
} break;
|
||||
case OP_TYPE_VECTOR_4D: {
|
||||
use_mix = true;
|
||||
} break;
|
||||
default: {
|
||||
} break;
|
||||
}
|
||||
|
||||
String code;
|
||||
code += " if(" + p_input_vars[0] + ")\n";
|
||||
code += " {\n";
|
||||
code += " " + p_output_vars[0] + " = " + p_input_vars[1] + ";\n";
|
||||
code += " }\n";
|
||||
code += " else\n";
|
||||
code += " {\n";
|
||||
code += " " + p_output_vars[0] + " = " + p_input_vars[2] + ";\n";
|
||||
code += " }\n";
|
||||
if (use_mix) {
|
||||
code += " " + p_output_vars[0] + " = mix(" + p_input_vars[2] + ", " + p_input_vars[1] + ", float(" + p_input_vars[0] + "));\n";
|
||||
} else {
|
||||
code += " if (" + p_input_vars[0] + ") {\n";
|
||||
code += " " + p_output_vars[0] + " = " + p_input_vars[1] + ";\n";
|
||||
code += " } else {\n";
|
||||
code += " " + p_output_vars[0] + " = " + p_input_vars[2] + ";\n";
|
||||
code += " }\n";
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,14 @@ RID World2D::get_canvas() const {
|
|||
}
|
||||
|
||||
RID World2D::get_space() const {
|
||||
if (space.is_null()) {
|
||||
space = PhysicsServer2D::get_singleton()->space_create();
|
||||
PhysicsServer2D::get_singleton()->space_set_active(space, true);
|
||||
PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY, GLOBAL_GET("physics/2d/default_gravity"));
|
||||
PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_GET("physics/2d/default_gravity_vector"));
|
||||
PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_LINEAR_DAMP, GLOBAL_GET("physics/2d/default_linear_damp"));
|
||||
PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_GET("physics/2d/default_angular_damp"));
|
||||
}
|
||||
return space;
|
||||
}
|
||||
|
||||
|
@ -71,19 +79,11 @@ void World2D::_bind_methods() {
|
|||
}
|
||||
|
||||
PhysicsDirectSpaceState2D *World2D::get_direct_space_state() {
|
||||
return PhysicsServer2D::get_singleton()->space_get_direct_state(space);
|
||||
return PhysicsServer2D::get_singleton()->space_get_direct_state(get_space());
|
||||
}
|
||||
|
||||
World2D::World2D() {
|
||||
canvas = RenderingServer::get_singleton()->canvas_create();
|
||||
|
||||
// Create and configure space2D to be more friendly with pixels than meters
|
||||
space = PhysicsServer2D::get_singleton()->space_create();
|
||||
PhysicsServer2D::get_singleton()->space_set_active(space, true);
|
||||
PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY, GLOBAL_DEF_BASIC("physics/2d/default_gravity", 980.0));
|
||||
PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF_BASIC("physics/2d/default_gravity_vector", Vector2(0, 1)));
|
||||
PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), 0.1));
|
||||
PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), 1.0));
|
||||
}
|
||||
|
||||
World2D::~World2D() {
|
||||
|
@ -91,7 +91,9 @@ World2D::~World2D() {
|
|||
ERR_FAIL_NULL(PhysicsServer2D::get_singleton());
|
||||
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
|
||||
RenderingServer::get_singleton()->free(canvas);
|
||||
PhysicsServer2D::get_singleton()->free(space);
|
||||
if (space.is_valid()) {
|
||||
PhysicsServer2D::get_singleton()->free(space);
|
||||
}
|
||||
if (navigation_map.is_valid()) {
|
||||
NavigationServer2D::get_singleton()->free(navigation_map);
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ class World2D : public Resource {
|
|||
GDCLASS(World2D, Resource);
|
||||
|
||||
RID canvas;
|
||||
RID space;
|
||||
mutable RID space;
|
||||
mutable RID navigation_map;
|
||||
|
||||
HashSet<Viewport *> viewports;
|
||||
|
|
|
@ -51,6 +51,14 @@ void World3D::_remove_camera(Camera3D *p_camera) {
|
|||
}
|
||||
|
||||
RID World3D::get_space() const {
|
||||
if (space.is_null()) {
|
||||
space = PhysicsServer3D::get_singleton()->space_create();
|
||||
PhysicsServer3D::get_singleton()->space_set_active(space, true);
|
||||
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY, GLOBAL_GET("physics/3d/default_gravity"));
|
||||
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_GET("physics/3d/default_gravity_vector"));
|
||||
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_LINEAR_DAMP, GLOBAL_GET("physics/3d/default_linear_damp"));
|
||||
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_GET("physics/3d/default_angular_damp"));
|
||||
}
|
||||
return space;
|
||||
}
|
||||
|
||||
|
@ -121,7 +129,7 @@ Ref<CameraAttributes> World3D::get_camera_attributes() const {
|
|||
}
|
||||
|
||||
PhysicsDirectSpaceState3D *World3D::get_direct_space_state() {
|
||||
return PhysicsServer3D::get_singleton()->space_get_direct_state(space);
|
||||
return PhysicsServer3D::get_singleton()->space_get_direct_state(get_space());
|
||||
}
|
||||
|
||||
void World3D::_bind_methods() {
|
||||
|
@ -145,22 +153,18 @@ void World3D::_bind_methods() {
|
|||
}
|
||||
|
||||
World3D::World3D() {
|
||||
space = PhysicsServer3D::get_singleton()->space_create();
|
||||
scenario = RenderingServer::get_singleton()->scenario_create();
|
||||
|
||||
PhysicsServer3D::get_singleton()->space_set_active(space, true);
|
||||
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY, GLOBAL_DEF_BASIC("physics/3d/default_gravity", 9.8));
|
||||
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF_BASIC("physics/3d/default_gravity_vector", Vector3(0, -1, 0)));
|
||||
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), 0.1));
|
||||
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), 0.1));
|
||||
}
|
||||
|
||||
World3D::~World3D() {
|
||||
ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
|
||||
ERR_FAIL_NULL(RenderingServer::get_singleton());
|
||||
ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
|
||||
ERR_FAIL_NULL(NavigationServer3D::get_singleton());
|
||||
PhysicsServer3D::get_singleton()->free(space);
|
||||
|
||||
RenderingServer::get_singleton()->free(scenario);
|
||||
if (space.is_valid()) {
|
||||
PhysicsServer3D::get_singleton()->free(space);
|
||||
}
|
||||
if (navigation_map.is_valid()) {
|
||||
NavigationServer3D::get_singleton()->free(navigation_map);
|
||||
}
|
||||
|
|
|
@ -45,8 +45,8 @@ class World3D : public Resource {
|
|||
GDCLASS(World3D, Resource);
|
||||
|
||||
private:
|
||||
RID space;
|
||||
RID scenario;
|
||||
mutable RID space;
|
||||
mutable RID navigation_map;
|
||||
|
||||
Ref<Environment> environment;
|
||||
|
|
|
@ -144,7 +144,7 @@ int AudioDriver::get_total_channels_by_speaker_mode(AudioDriver::SpeakerMode p_m
|
|||
ERR_FAIL_V(2);
|
||||
}
|
||||
|
||||
PackedStringArray AudioDriver::get_device_list() {
|
||||
PackedStringArray AudioDriver::get_output_device_list() {
|
||||
PackedStringArray list;
|
||||
|
||||
list.push_back("Default");
|
||||
|
@ -152,11 +152,11 @@ PackedStringArray AudioDriver::get_device_list() {
|
|||
return list;
|
||||
}
|
||||
|
||||
String AudioDriver::get_device() {
|
||||
String AudioDriver::get_output_device() {
|
||||
return "Default";
|
||||
}
|
||||
|
||||
PackedStringArray AudioDriver::capture_get_device_list() {
|
||||
PackedStringArray AudioDriver::get_input_device_list() {
|
||||
PackedStringArray list;
|
||||
|
||||
list.push_back("Default");
|
||||
|
@ -238,7 +238,7 @@ void AudioServer::_driver_process(int p_frames, int32_t *p_buffer) {
|
|||
#endif
|
||||
|
||||
if (channel_count != get_channel_count()) {
|
||||
// Amount of channels changed due to a device change
|
||||
// Amount of channels changed due to a output_device change
|
||||
// reinitialize the buses channels and buffers
|
||||
init_channels_and_buffers();
|
||||
}
|
||||
|
@ -1632,28 +1632,28 @@ Ref<AudioBusLayout> AudioServer::generate_bus_layout() const {
|
|||
return state;
|
||||
}
|
||||
|
||||
PackedStringArray AudioServer::get_device_list() {
|
||||
return AudioDriver::get_singleton()->get_device_list();
|
||||
PackedStringArray AudioServer::get_output_device_list() {
|
||||
return AudioDriver::get_singleton()->get_output_device_list();
|
||||
}
|
||||
|
||||
String AudioServer::get_device() {
|
||||
return AudioDriver::get_singleton()->get_device();
|
||||
String AudioServer::get_output_device() {
|
||||
return AudioDriver::get_singleton()->get_output_device();
|
||||
}
|
||||
|
||||
void AudioServer::set_device(String device) {
|
||||
AudioDriver::get_singleton()->set_device(device);
|
||||
void AudioServer::set_output_device(String output_device) {
|
||||
AudioDriver::get_singleton()->set_output_device(output_device);
|
||||
}
|
||||
|
||||
PackedStringArray AudioServer::capture_get_device_list() {
|
||||
return AudioDriver::get_singleton()->capture_get_device_list();
|
||||
PackedStringArray AudioServer::get_input_device_list() {
|
||||
return AudioDriver::get_singleton()->get_input_device_list();
|
||||
}
|
||||
|
||||
String AudioServer::capture_get_device() {
|
||||
return AudioDriver::get_singleton()->capture_get_device();
|
||||
String AudioServer::get_input_device() {
|
||||
return AudioDriver::get_singleton()->get_input_device();
|
||||
}
|
||||
|
||||
void AudioServer::capture_set_device(const String &p_name) {
|
||||
AudioDriver::get_singleton()->capture_set_device(p_name);
|
||||
void AudioServer::set_input_device(const String &p_name) {
|
||||
AudioDriver::get_singleton()->set_input_device(p_name);
|
||||
}
|
||||
|
||||
void AudioServer::set_enable_tagging_used_audio_streams(bool p_enable) {
|
||||
|
@ -1711,17 +1711,17 @@ void AudioServer::_bind_methods() {
|
|||
|
||||
ClassDB::bind_method(D_METHOD("get_speaker_mode"), &AudioServer::get_speaker_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioServer::get_mix_rate);
|
||||
ClassDB::bind_method(D_METHOD("get_device_list"), &AudioServer::get_device_list);
|
||||
ClassDB::bind_method(D_METHOD("get_device"), &AudioServer::get_device);
|
||||
ClassDB::bind_method(D_METHOD("set_device", "device"), &AudioServer::set_device);
|
||||
ClassDB::bind_method(D_METHOD("get_output_device_list"), &AudioServer::get_output_device_list);
|
||||
ClassDB::bind_method(D_METHOD("get_output_device"), &AudioServer::get_output_device);
|
||||
ClassDB::bind_method(D_METHOD("set_output_device", "output_device"), &AudioServer::set_output_device);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_time_to_next_mix"), &AudioServer::get_time_to_next_mix);
|
||||
ClassDB::bind_method(D_METHOD("get_time_since_last_mix"), &AudioServer::get_time_since_last_mix);
|
||||
ClassDB::bind_method(D_METHOD("get_output_latency"), &AudioServer::get_output_latency);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("capture_get_device_list"), &AudioServer::capture_get_device_list);
|
||||
ClassDB::bind_method(D_METHOD("capture_get_device"), &AudioServer::capture_get_device);
|
||||
ClassDB::bind_method(D_METHOD("capture_set_device", "name"), &AudioServer::capture_set_device);
|
||||
ClassDB::bind_method(D_METHOD("get_input_device_list"), &AudioServer::get_input_device_list);
|
||||
ClassDB::bind_method(D_METHOD("get_input_device"), &AudioServer::get_input_device);
|
||||
ClassDB::bind_method(D_METHOD("set_input_device", "name"), &AudioServer::set_input_device);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_bus_layout", "bus_layout"), &AudioServer::set_bus_layout);
|
||||
ClassDB::bind_method(D_METHOD("generate_bus_layout"), &AudioServer::generate_bus_layout);
|
||||
|
@ -1729,11 +1729,11 @@ void AudioServer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_enable_tagging_used_audio_streams", "enable"), &AudioServer::set_enable_tagging_used_audio_streams);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "bus_count"), "set_bus_count", "get_bus_count");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "device"), "set_device", "get_device");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "capture_device"), "capture_set_device", "capture_get_device");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "output_device"), "set_output_device", "get_output_device");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "input_device"), "set_input_device", "get_input_device");
|
||||
// The default value may be set to an empty string by the platform-specific audio driver.
|
||||
// Override for class reference generation purposes.
|
||||
ADD_PROPERTY_DEFAULT("capture_device", "Default");
|
||||
ADD_PROPERTY_DEFAULT("input_device", "Default");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_speed_scale"), "set_playback_speed_scale", "get_playback_speed_scale");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("bus_layout_changed"));
|
||||
|
|
|
@ -94,18 +94,18 @@ public:
|
|||
virtual void start() = 0;
|
||||
virtual int get_mix_rate() const = 0;
|
||||
virtual SpeakerMode get_speaker_mode() const = 0;
|
||||
virtual PackedStringArray get_device_list();
|
||||
virtual String get_device();
|
||||
virtual void set_device(String device) {}
|
||||
virtual PackedStringArray get_output_device_list();
|
||||
virtual String get_output_device();
|
||||
virtual void set_output_device(String output_device) {}
|
||||
virtual void lock() = 0;
|
||||
virtual void unlock() = 0;
|
||||
virtual void finish() = 0;
|
||||
|
||||
virtual Error capture_start() { return FAILED; }
|
||||
virtual Error capture_stop() { return FAILED; }
|
||||
virtual void capture_set_device(const String &p_name) {}
|
||||
virtual String capture_get_device() { return "Default"; }
|
||||
virtual PackedStringArray capture_get_device_list();
|
||||
virtual void set_input_device(const String &p_name) {}
|
||||
virtual String get_input_device() { return "Default"; }
|
||||
virtual PackedStringArray get_input_device_list();
|
||||
|
||||
virtual float get_latency() { return 0; }
|
||||
|
||||
|
@ -419,13 +419,13 @@ public:
|
|||
void set_bus_layout(const Ref<AudioBusLayout> &p_bus_layout);
|
||||
Ref<AudioBusLayout> generate_bus_layout() const;
|
||||
|
||||
PackedStringArray get_device_list();
|
||||
String get_device();
|
||||
void set_device(String device);
|
||||
PackedStringArray get_output_device_list();
|
||||
String get_output_device();
|
||||
void set_output_device(String output_device);
|
||||
|
||||
PackedStringArray capture_get_device_list();
|
||||
String capture_get_device();
|
||||
void capture_set_device(const String &p_name);
|
||||
PackedStringArray get_input_device_list();
|
||||
String get_input_device();
|
||||
void set_input_device(const String &p_name);
|
||||
|
||||
void set_enable_tagging_used_audio_streams(bool p_enable);
|
||||
|
||||
|
|
|
@ -1216,15 +1216,15 @@ GodotPhysicsDirectSpaceState2D *GodotSpace2D::get_direct_state() {
|
|||
}
|
||||
|
||||
GodotSpace2D::GodotSpace2D() {
|
||||
body_linear_velocity_sleep_threshold = GLOBAL_DEF("physics/2d/sleep_threshold_linear", 2.0);
|
||||
body_angular_velocity_sleep_threshold = GLOBAL_DEF("physics/2d/sleep_threshold_angular", Math::deg_to_rad(8.0));
|
||||
body_time_to_sleep = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5);
|
||||
solver_iterations = GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/2d/solver/solver_iterations", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), 16);
|
||||
contact_recycle_radius = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_recycle_radius", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 1.0);
|
||||
contact_max_separation = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_max_separation", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 1.5);
|
||||
contact_max_allowed_penetration = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 0.3);
|
||||
contact_bias = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/default_contact_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.8);
|
||||
constraint_bias = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/default_constraint_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.2);
|
||||
body_linear_velocity_sleep_threshold = GLOBAL_GET("physics/2d/sleep_threshold_linear");
|
||||
body_angular_velocity_sleep_threshold = GLOBAL_GET("physics/2d/sleep_threshold_angular");
|
||||
body_time_to_sleep = GLOBAL_GET("physics/2d/time_before_sleep");
|
||||
solver_iterations = GLOBAL_GET("physics/2d/solver/solver_iterations");
|
||||
contact_recycle_radius = GLOBAL_GET("physics/2d/solver/contact_recycle_radius");
|
||||
contact_max_separation = GLOBAL_GET("physics/2d/solver/contact_max_separation");
|
||||
contact_max_allowed_penetration = GLOBAL_GET("physics/2d/solver/contact_max_allowed_penetration");
|
||||
contact_bias = GLOBAL_GET("physics/2d/solver/default_contact_bias");
|
||||
constraint_bias = GLOBAL_GET("physics/2d/solver/default_constraint_bias");
|
||||
|
||||
broadphase = GodotBroadPhase2D::create_func();
|
||||
broadphase->set_pair_callback(_broadphase_pair, this);
|
||||
|
|
|
@ -1250,14 +1250,14 @@ GodotPhysicsDirectSpaceState3D *GodotSpace3D::get_direct_state() {
|
|||
}
|
||||
|
||||
GodotSpace3D::GodotSpace3D() {
|
||||
body_linear_velocity_sleep_threshold = GLOBAL_DEF("physics/3d/sleep_threshold_linear", 0.1);
|
||||
body_angular_velocity_sleep_threshold = GLOBAL_DEF("physics/3d/sleep_threshold_angular", Math::deg_to_rad(8.0));
|
||||
body_time_to_sleep = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5);
|
||||
solver_iterations = GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/3d/solver/solver_iterations", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), 16);
|
||||
contact_recycle_radius = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_recycle_radius", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.01);
|
||||
contact_max_separation = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_separation", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.05);
|
||||
contact_max_allowed_penetration = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.01);
|
||||
contact_bias = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/default_contact_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.8);
|
||||
body_linear_velocity_sleep_threshold = GLOBAL_GET("physics/3d/sleep_threshold_linear");
|
||||
body_angular_velocity_sleep_threshold = GLOBAL_GET("physics/3d/sleep_threshold_angular");
|
||||
body_time_to_sleep = GLOBAL_GET("physics/3d/time_before_sleep");
|
||||
solver_iterations = GLOBAL_GET("physics/3d/solver/solver_iterations");
|
||||
contact_recycle_radius = GLOBAL_GET("physics/3d/solver/contact_recycle_radius");
|
||||
contact_max_separation = GLOBAL_GET("physics/3d/solver/contact_max_separation");
|
||||
contact_max_allowed_penetration = GLOBAL_GET("physics/3d/solver/contact_max_allowed_penetration");
|
||||
contact_bias = GLOBAL_GET("physics/3d/solver/default_contact_bias");
|
||||
|
||||
broadphase = GodotBroadPhase3D::create_func();
|
||||
broadphase->set_pair_callback(_broadphase_pair, this);
|
||||
|
|
|
@ -879,6 +879,23 @@ void PhysicsServer2D::_bind_methods() {
|
|||
|
||||
PhysicsServer2D::PhysicsServer2D() {
|
||||
singleton = this;
|
||||
|
||||
// World2D physics space
|
||||
GLOBAL_DEF_BASIC("physics/2d/default_gravity", 980.0);
|
||||
GLOBAL_DEF_BASIC("physics/2d/default_gravity_vector", Vector2(0, 1));
|
||||
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), 0.1);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), 1.0);
|
||||
|
||||
// PhysicsServer2D
|
||||
GLOBAL_DEF("physics/2d/sleep_threshold_linear", 2.0);
|
||||
GLOBAL_DEF("physics/2d/sleep_threshold_angular", Math::deg_to_rad(8.0));
|
||||
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/2d/solver/solver_iterations", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), 16);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_recycle_radius", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 1.0);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_max_separation", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 1.5);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 0.3);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/default_contact_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.8);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/default_constraint_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.2);
|
||||
}
|
||||
|
||||
PhysicsServer2D::~PhysicsServer2D() {
|
||||
|
|
|
@ -1047,6 +1047,22 @@ void PhysicsServer3D::_bind_methods() {
|
|||
|
||||
PhysicsServer3D::PhysicsServer3D() {
|
||||
singleton = this;
|
||||
|
||||
// World3D physics space
|
||||
GLOBAL_DEF_BASIC("physics/3d/default_gravity", 9.8);
|
||||
GLOBAL_DEF_BASIC("physics/3d/default_gravity_vector", Vector3(0, -1, 0));
|
||||
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), 0.1);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), 0.1);
|
||||
|
||||
// PhysicsServer3D
|
||||
GLOBAL_DEF("physics/3d/sleep_threshold_linear", 0.1);
|
||||
GLOBAL_DEF("physics/3d/sleep_threshold_angular", Math::deg_to_rad(8.0));
|
||||
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/3d/solver/solver_iterations", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), 16);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_recycle_radius", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.01);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_separation", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.05);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.01);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/default_contact_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.8);
|
||||
}
|
||||
|
||||
PhysicsServer3D::~PhysicsServer3D() {
|
||||
|
|
150
tests/display_server_mock.h
Normal file
150
tests/display_server_mock.h
Normal file
|
@ -0,0 +1,150 @@
|
|||
/**************************************************************************/
|
||||
/* display_server_mock.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DISPLAY_SERVER_MOCK_H
|
||||
#define DISPLAY_SERVER_MOCK_H
|
||||
|
||||
#include "servers/display_server_headless.h"
|
||||
|
||||
#include "servers/rendering/dummy/rasterizer_dummy.h"
|
||||
|
||||
// Specialized DisplayServer for unittests based on DisplayServerHeadless, that
|
||||
// additionally supports rudimentary InputEvent handling and mouse position.
|
||||
class DisplayServerMock : public DisplayServerHeadless {
|
||||
private:
|
||||
friend class DisplayServer;
|
||||
|
||||
Point2i mouse_position = Point2i(-1, -1); // Outside of Window.
|
||||
bool window_over = false;
|
||||
Callable event_callback;
|
||||
Callable input_event_callback;
|
||||
|
||||
static Vector<String> get_rendering_drivers_func() {
|
||||
Vector<String> drivers;
|
||||
drivers.push_back("dummy");
|
||||
return drivers;
|
||||
}
|
||||
|
||||
static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) {
|
||||
r_error = OK;
|
||||
RasterizerDummy::make_current();
|
||||
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) {
|
||||
Variant ev = p_event;
|
||||
Variant *evp = &ev;
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
|
||||
if (input_event_callback.is_valid()) {
|
||||
input_event_callback.callp((const Variant **)&evp, 1, ret, ce);
|
||||
}
|
||||
}
|
||||
|
||||
void _set_mouse_position(const Point2i &p_position) {
|
||||
if (mouse_position == p_position) {
|
||||
return;
|
||||
}
|
||||
mouse_position = p_position;
|
||||
_set_window_over(Rect2i(Point2i(0, 0), window_get_size()).has_point(p_position));
|
||||
}
|
||||
|
||||
void _set_window_over(bool p_over) {
|
||||
if (p_over == window_over) {
|
||||
return;
|
||||
}
|
||||
window_over = p_over;
|
||||
_send_window_event(p_over ? WINDOW_EVENT_MOUSE_ENTER : WINDOW_EVENT_MOUSE_EXIT);
|
||||
}
|
||||
|
||||
void _send_window_event(WindowEvent p_event) {
|
||||
if (!event_callback.is_null()) {
|
||||
Variant event = int(p_event);
|
||||
Variant *eventp = &event;
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
event_callback.callp((const Variant **)&eventp, 1, ret, ce);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
bool has_feature(Feature p_feature) const override {
|
||||
switch (p_feature) {
|
||||
case FEATURE_MOUSE:
|
||||
return true;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String get_name() const override { return "mock"; }
|
||||
|
||||
// You can simulate DisplayServer-events by calling this function.
|
||||
// The events will be deliverd to Godot's Input-system.
|
||||
// Mouse-events (Button & Motion) will additionally update the DisplayServer's mouse position.
|
||||
void simulate_event(Ref<InputEvent> p_event) {
|
||||
Ref<InputEventMouse> me = p_event;
|
||||
if (me.is_valid()) {
|
||||
_set_mouse_position(me->get_position());
|
||||
}
|
||||
Input::get_singleton()->parse_input_event(p_event);
|
||||
}
|
||||
|
||||
virtual Point2i mouse_get_position() const override { return mouse_position; }
|
||||
|
||||
virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override {
|
||||
return Size2i(1920, 1080);
|
||||
}
|
||||
|
||||
virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override {
|
||||
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
|
|
@ -3271,6 +3271,7 @@ TEST_CASE("[SceneTree][TextEdit] mouse") {
|
|||
|
||||
TEST_CASE("[SceneTree][TextEdit] caret") {
|
||||
TextEdit *text_edit = memnew(TextEdit);
|
||||
text_edit->set_context_menu_enabled(false); // Prohibit sending InputEvents to the context menu.
|
||||
SceneTree::get_singleton()->get_root()->add_child(text_edit);
|
||||
|
||||
text_edit->set_size(Size2(800, 200));
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#ifndef TEST_MACROS_H
|
||||
#define TEST_MACROS_H
|
||||
|
||||
#include "display_server_mock.h"
|
||||
|
||||
#include "core/core_globals.h"
|
||||
#include "core/input/input_map.h"
|
||||
#include "core/object/message_queue.h"
|
||||
|
@ -139,13 +141,15 @@ int register_test_command(String p_command, TestFunc p_function);
|
|||
// SEND_GUI_MOUSE_MOTION_EVENT - takes an object, position, mouse mask and modifiers e.g SEND_GUI_MOUSE_MOTION_EVENT(code_edit, Vector2(50, 50), MouseButtonMask::LEFT, KeyModifierMask::META);
|
||||
// SEND_GUI_DOUBLE_CLICK - takes an object, position and modifiers. e.g SEND_GUI_DOUBLE_CLICK(code_edit, Vector2(50, 50), KeyModifierMask::META);
|
||||
|
||||
#define _SEND_DISPLAYSERVER_EVENT(m_event) ((DisplayServerMock *)(DisplayServer::get_singleton()))->simulate_event(m_event);
|
||||
|
||||
#define SEND_GUI_ACTION(m_object, m_action) \
|
||||
{ \
|
||||
const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(m_action); \
|
||||
const List<Ref<InputEvent>>::Element *first_event = events->front(); \
|
||||
Ref<InputEventKey> event = first_event->get(); \
|
||||
event->set_pressed(true); \
|
||||
m_object->get_viewport()->push_input(event); \
|
||||
_SEND_DISPLAYSERVER_EVENT(event); \
|
||||
MessageQueue::get_singleton()->flush(); \
|
||||
}
|
||||
|
||||
|
@ -153,7 +157,7 @@ int register_test_command(String p_command, TestFunc p_function);
|
|||
{ \
|
||||
Ref<InputEventKey> event = InputEventKey::create_reference(m_input); \
|
||||
event->set_pressed(true); \
|
||||
m_object->get_viewport()->push_input(event); \
|
||||
_SEND_DISPLAYSERVER_EVENT(event); \
|
||||
MessageQueue::get_singleton()->flush(); \
|
||||
}
|
||||
|
||||
|
@ -176,7 +180,7 @@ int register_test_command(String p_command, TestFunc p_function);
|
|||
#define SEND_GUI_MOUSE_BUTTON_EVENT(m_object, m_local_pos, m_input, m_mask, m_modifers) \
|
||||
{ \
|
||||
_CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, m_input, m_mask, m_modifers); \
|
||||
m_object->get_viewport()->push_input(event); \
|
||||
_SEND_DISPLAYSERVER_EVENT(event); \
|
||||
MessageQueue::get_singleton()->flush(); \
|
||||
}
|
||||
|
||||
|
@ -184,7 +188,7 @@ int register_test_command(String p_command, TestFunc p_function);
|
|||
{ \
|
||||
_CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, m_input, m_mask, m_modifers); \
|
||||
event->set_pressed(false); \
|
||||
m_object->get_viewport()->push_input(event); \
|
||||
_SEND_DISPLAYSERVER_EVENT(event); \
|
||||
MessageQueue::get_singleton()->flush(); \
|
||||
}
|
||||
|
||||
|
@ -192,7 +196,7 @@ int register_test_command(String p_command, TestFunc p_function);
|
|||
{ \
|
||||
_CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, MouseButton::LEFT, 0, m_modifers); \
|
||||
event->set_double_click(true); \
|
||||
m_object->get_viewport()->push_input(event); \
|
||||
_SEND_DISPLAYSERVER_EVENT(event); \
|
||||
MessageQueue::get_singleton()->flush(); \
|
||||
}
|
||||
|
||||
|
@ -207,7 +211,7 @@ int register_test_command(String p_command, TestFunc p_function);
|
|||
event->set_button_mask(m_mask); \
|
||||
event->set_relative(Vector2(10, 10)); \
|
||||
_UPDATE_EVENT_MODIFERS(event, m_modifers); \
|
||||
m_object->get_viewport()->push_input(event); \
|
||||
_SEND_DISPLAYSERVER_EVENT(event); \
|
||||
MessageQueue::get_singleton()->flush(); \
|
||||
CoreGlobals::print_error_enabled = errors_enabled; \
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@
|
|||
|
||||
#include "modules/modules_tests.gen.h"
|
||||
|
||||
#include "tests/display_server_mock.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
@ -126,6 +127,7 @@ int test_main(int argc, char *argv[]) {
|
|||
args.push_back(String::utf8(argv[i]));
|
||||
}
|
||||
OS::get_singleton()->set_cmdline("", args, List<String>());
|
||||
DisplayServerMock::register_mock_driver();
|
||||
|
||||
// Run custom test tools.
|
||||
if (test_commands) {
|
||||
|
@ -200,11 +202,12 @@ struct GodotTestCaseListener : public doctest::IReporter {
|
|||
memnew(MessageQueue);
|
||||
|
||||
memnew(Input);
|
||||
Input::get_singleton()->set_use_accumulated_input(false);
|
||||
|
||||
Error err = OK;
|
||||
OS::get_singleton()->set_has_server_feature_callback(nullptr);
|
||||
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
|
||||
if (String("headless") == DisplayServer::get_create_function_name(i)) {
|
||||
if (String("mock") == DisplayServer::get_create_function_name(i)) {
|
||||
DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), DisplayServer::SCREEN_PRIMARY, err);
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue