Merge pull request #115119 from Nintorch/joypad-unfocused
Add project setting to ignore joypad events if the app is unfocused
This commit is contained in:
commit
bb1e018314
9 changed files with 167 additions and 6 deletions
|
|
@ -159,6 +159,8 @@ void Input::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0));
|
||||
ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration);
|
||||
ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms", "amplitude"), &Input::vibrate_handheld, DEFVAL(500), DEFVAL(-1.0));
|
||||
ClassDB::bind_method(D_METHOD("set_ignore_joypad_on_unfocused_application", "enable"), &Input::set_ignore_joypad_on_unfocused_application);
|
||||
ClassDB::bind_method(D_METHOD("is_ignoring_joypad_on_unfocused_application"), &Input::is_ignoring_joypad_on_unfocused_application);
|
||||
ClassDB::bind_method(D_METHOD("get_gravity"), &Input::get_gravity);
|
||||
ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer);
|
||||
ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer);
|
||||
|
|
@ -207,6 +209,7 @@ void Input::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_accumulated_input"), "set_use_accumulated_input", "is_using_accumulated_input");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emulate_mouse_from_touch"), "set_emulate_mouse_from_touch", "is_emulating_mouse_from_touch");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emulate_touch_from_mouse"), "set_emulate_touch_from_mouse", "is_emulating_touch_from_mouse");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_joypad_on_unfocused_application"), "set_ignore_joypad_on_unfocused_application", "is_ignoring_joypad_on_unfocused_application");
|
||||
|
||||
BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE);
|
||||
BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN);
|
||||
|
|
@ -376,6 +379,10 @@ bool Input::is_mouse_button_pressed(MouseButton p_button) const {
|
|||
return mouse_button_mask.has_flag(mouse_button_to_mask(p_button));
|
||||
}
|
||||
|
||||
bool Input::_should_ignore_joypad_events() const {
|
||||
return ignore_joypad_on_unfocused_application && !application_focused && !embedder_focused;
|
||||
}
|
||||
|
||||
static JoyAxis _combine_device(JoyAxis p_value, int p_device) {
|
||||
return JoyAxis((int)p_value | (p_device << 20));
|
||||
}
|
||||
|
|
@ -1011,6 +1018,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
|
|||
device_state.pressed[event_index] = is_pressed;
|
||||
device_state.strength[event_index] = p_event->get_action_strength(E.key);
|
||||
device_state.raw_strength[event_index] = p_event->get_action_raw_strength(E.key);
|
||||
device_state.event_type[event_index] = p_event->get_type();
|
||||
|
||||
// Update the action's global state and cache.
|
||||
if (!is_pressed) {
|
||||
|
|
@ -1057,6 +1065,12 @@ void Input::set_joy_features(int p_device, JoypadFeatures *p_features) {
|
|||
}
|
||||
|
||||
void Input::set_joy_light(int p_device, const Color &p_color) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
if (_should_ignore_joypad_events()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Joypad *joypad = joy_names.getptr(p_device);
|
||||
if (!joypad || !joypad->has_light || joypad->features == nullptr) {
|
||||
return;
|
||||
|
|
@ -1072,6 +1086,11 @@ bool Input::has_joy_light(int p_device) const {
|
|||
|
||||
Vector3 Input::get_joy_accelerometer(int p_device) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
if (_should_ignore_joypad_events()) {
|
||||
return Vector3();
|
||||
}
|
||||
|
||||
const MotionInfo *motion = joy_motion.getptr(p_device);
|
||||
if (motion == nullptr) {
|
||||
return Vector3();
|
||||
|
|
@ -1090,6 +1109,11 @@ Vector3 Input::get_joy_accelerometer(int p_device) const {
|
|||
|
||||
Vector3 Input::get_joy_gravity(int p_device) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
if (_should_ignore_joypad_events()) {
|
||||
return Vector3();
|
||||
}
|
||||
|
||||
const MotionInfo *motion = joy_motion.getptr(p_device);
|
||||
if (motion == nullptr) {
|
||||
return Vector3();
|
||||
|
|
@ -1104,6 +1128,11 @@ Vector3 Input::get_joy_gravity(int p_device) const {
|
|||
|
||||
Vector3 Input::get_joy_gyroscope(int p_device) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
if (_should_ignore_joypad_events()) {
|
||||
return Vector3();
|
||||
}
|
||||
|
||||
const MotionInfo *motion = joy_motion.getptr(p_device);
|
||||
if (motion == nullptr) {
|
||||
return Vector3();
|
||||
|
|
@ -1262,6 +1291,11 @@ void Input::set_joy_motion_sensors_rate(int p_device, float p_rate) {
|
|||
|
||||
void Input::start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
if (_should_ignore_joypad_events()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1287,6 +1321,17 @@ void Input::vibrate_handheld(int p_duration_ms, float p_amplitude) {
|
|||
OS::get_singleton()->vibrate_handheld(p_duration_ms, p_amplitude);
|
||||
}
|
||||
|
||||
void Input::set_ignore_joypad_on_unfocused_application(bool p_ignore) {
|
||||
ignore_joypad_on_unfocused_application = p_ignore;
|
||||
if (_should_ignore_joypad_events()) {
|
||||
release_pressed_events();
|
||||
}
|
||||
}
|
||||
|
||||
bool Input::is_ignoring_joypad_on_unfocused_application() const {
|
||||
return ignore_joypad_on_unfocused_application;
|
||||
}
|
||||
|
||||
void Input::set_gravity(const Vector3 &p_gravity) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
|
|
@ -1553,17 +1598,49 @@ bool Input::is_using_accumulated_input() {
|
|||
}
|
||||
|
||||
void Input::release_pressed_events() {
|
||||
// Don't release the events if the application (or the window it's embedded in) is still focused.
|
||||
if (application_focused || embedder_focused) {
|
||||
return;
|
||||
}
|
||||
|
||||
flush_buffered_events(); // this is needed to release actions strengths
|
||||
|
||||
keys_pressed.clear();
|
||||
physical_keys_pressed.clear();
|
||||
key_label_pressed.clear();
|
||||
joy_buttons_pressed.clear();
|
||||
_joy_axis.clear();
|
||||
if (ignore_joypad_on_unfocused_application) {
|
||||
joy_buttons_pressed.clear();
|
||||
_joy_axis.clear();
|
||||
|
||||
for (KeyValue<StringName, Input::ActionState> &E : action_states) {
|
||||
if (E.value.cache.pressed) {
|
||||
action_release(E.key);
|
||||
for (KeyValue<StringName, Input::ActionState> &E : action_states) {
|
||||
if (E.value.cache.pressed) {
|
||||
action_release(E.key);
|
||||
}
|
||||
}
|
||||
|
||||
for (int device : get_connected_joypads()) {
|
||||
stop_joy_vibration(device);
|
||||
}
|
||||
} else {
|
||||
for (KeyValue<StringName, Input::ActionState> &E : action_states) {
|
||||
if (E.value.cache.pressed) {
|
||||
bool is_only_joypad_pressed = true;
|
||||
int max_event = InputMap::get_singleton()->action_get_events(E.key)->size() + 1; // +1 comes from InputEventAction.
|
||||
|
||||
for (const KeyValue<int, ActionState::DeviceState> &kv : E.value.device_states) {
|
||||
const ActionState::DeviceState &device_state = kv.value;
|
||||
for (int i = 0; i < max_event; i++) {
|
||||
if (device_state.event_type[i] != InputEventType::JOY_MOTION && device_state.event_type[i] != InputEventType::JOY_BUTTON && device_state.strength[i] > 0.0f) {
|
||||
is_only_joypad_pressed = false;
|
||||
action_release(E.key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!is_only_joypad_pressed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1574,6 +1651,11 @@ void Input::set_event_dispatch_function(EventDispatchFunc p_function) {
|
|||
|
||||
void Input::joy_button(int p_device, JoyButton p_button, bool p_pressed) {
|
||||
_THREAD_SAFE_METHOD_;
|
||||
|
||||
if (_should_ignore_joypad_events()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Joypad &joy = joy_names[p_device];
|
||||
ERR_FAIL_INDEX((int)p_button, (int)JoyButton::MAX);
|
||||
|
||||
|
|
@ -1602,6 +1684,10 @@ void Input::joy_button(int p_device, JoyButton p_button, bool p_pressed) {
|
|||
void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) {
|
||||
_THREAD_SAFE_METHOD_;
|
||||
|
||||
if (_should_ignore_joypad_events()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ERR_FAIL_INDEX((int)p_axis, (int)JoyAxis::MAX);
|
||||
|
||||
Joypad &joy = joy_names[p_device];
|
||||
|
|
@ -1671,6 +1757,11 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) {
|
|||
|
||||
void Input::joy_hat(int p_device, BitField<HatMask> p_val) {
|
||||
_THREAD_SAFE_METHOD_;
|
||||
|
||||
if (_should_ignore_joypad_events()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Joypad &joy = joy_names[p_device];
|
||||
|
||||
JoyEvent map[(size_t)HatDir::MAX];
|
||||
|
|
@ -1713,6 +1804,11 @@ void Input::joy_hat(int p_device, BitField<HatMask> p_val) {
|
|||
|
||||
void Input::joy_motion_sensors(int p_device, const Vector3 &p_accelerometer, const Vector3 &p_gyroscope) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
if (_should_ignore_joypad_events()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: events
|
||||
MotionInfo *motion = joy_motion.getptr(p_device);
|
||||
if (motion == nullptr) {
|
||||
|
|
@ -2275,6 +2371,7 @@ Input::Input() {
|
|||
gravity_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gravity", false);
|
||||
gyroscope_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gyroscope", false);
|
||||
magnetometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_magnetometer", false);
|
||||
ignore_joypad_on_unfocused_application = GLOBAL_DEF_RST_BASIC("input_devices/joypads/ignore_joypad_on_unfocused_application", false);
|
||||
}
|
||||
|
||||
Input::~Input() {
|
||||
|
|
|
|||
|
|
@ -126,6 +126,9 @@ private:
|
|||
int64_t mouse_window = 0;
|
||||
bool legacy_just_pressed_behavior = false;
|
||||
bool disable_input = false;
|
||||
bool ignore_joypad_on_unfocused_application = false;
|
||||
bool application_focused = true;
|
||||
bool embedder_focused = false;
|
||||
|
||||
struct ActionState {
|
||||
uint64_t pressed_physics_frame = UINT64_MAX;
|
||||
|
|
@ -140,6 +143,7 @@ private:
|
|||
bool pressed[MAX_EVENT] = { false };
|
||||
float strength[MAX_EVENT] = { 0.0 };
|
||||
float raw_strength[MAX_EVENT] = { 0.0 };
|
||||
InputEventType event_type[MAX_EVENT] = { InputEventType::INVALID };
|
||||
};
|
||||
bool api_pressed = false;
|
||||
float api_strength = 0.0;
|
||||
|
|
@ -307,6 +311,8 @@ private:
|
|||
#endif
|
||||
|
||||
friend class DisplayServer;
|
||||
friend class SceneTree;
|
||||
friend class SceneDebugger;
|
||||
|
||||
static void (*set_mouse_mode_func)(MouseMode);
|
||||
static MouseMode (*get_mouse_mode_func)();
|
||||
|
|
@ -321,6 +327,8 @@ private:
|
|||
|
||||
EventDispatchFunc event_dispatch_function = nullptr;
|
||||
|
||||
bool _should_ignore_joypad_events() const;
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void _vibrate_handheld_bind_compat_91143(int p_duration_ms = 500);
|
||||
static void _bind_compatibility_methods();
|
||||
|
|
@ -424,6 +432,9 @@ public:
|
|||
void stop_joy_vibration(int p_device);
|
||||
void vibrate_handheld(int p_duration_ms = 500, float p_amplitude = -1.0);
|
||||
|
||||
void set_ignore_joypad_on_unfocused_application(bool p_ignore);
|
||||
bool is_ignoring_joypad_on_unfocused_application() const;
|
||||
|
||||
void set_mouse_position(const Point2 &p_posf);
|
||||
|
||||
void action_press(const StringName &p_action, float p_strength = 1.f);
|
||||
|
|
|
|||
|
|
@ -751,6 +751,9 @@
|
|||
<member name="emulate_touch_from_mouse" type="bool" setter="set_emulate_touch_from_mouse" getter="is_emulating_touch_from_mouse">
|
||||
If [code]true[/code], sends touch input events when clicking or dragging the mouse. See also [member ProjectSettings.input_devices/pointing/emulate_touch_from_mouse].
|
||||
</member>
|
||||
<member name="ignore_joypad_on_unfocused_application" type="bool" setter="set_ignore_joypad_on_unfocused_application" getter="is_ignoring_joypad_on_unfocused_application">
|
||||
If [code]true[/code], joypad input (including motion sensors) and LED light changes will be ignored and joypad vibration will be stopped when the application is not focused.
|
||||
</member>
|
||||
<member name="mouse_mode" type="int" setter="set_mouse_mode" getter="get_mouse_mode" enum="Input.MouseMode">
|
||||
Controls the mouse mode.
|
||||
</member>
|
||||
|
|
|
|||
|
|
@ -1662,6 +1662,9 @@
|
|||
If [code]false[/code], no input will be lost.
|
||||
[b]Note:[/b] You should in nearly all cases prefer the [code]false[/code] setting. The legacy behavior is to enable supporting old projects that rely on the old logic, without changes to script.
|
||||
</member>
|
||||
<member name="input_devices/joypads/ignore_joypad_on_unfocused_application" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], joypad input (including motion sensors) and LED light changes will be ignored and joypad vibration will be stopped when the application is not focused.
|
||||
</member>
|
||||
<member name="input_devices/pen_tablet/driver" type="String" setter="" getter="">
|
||||
Specifies the tablet driver to use. If left empty, the default driver will be used.
|
||||
[b]Note:[/b] The driver in use can be overridden at runtime via the [code]--tablet-driver[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url].
|
||||
|
|
|
|||
|
|
@ -248,6 +248,17 @@ void GameViewDebugger::set_camera_manipulate_mode(EditorDebuggerNode::CameraOver
|
|||
}
|
||||
}
|
||||
|
||||
void GameViewDebugger::report_window_focused(bool p_focused) {
|
||||
Array message;
|
||||
message.append(p_focused);
|
||||
|
||||
for (Ref<EditorDebuggerSession> &I : sessions) {
|
||||
if (I->is_active()) {
|
||||
I->send_message("scene:report_window_focused", message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameViewDebugger::reset_camera_2d_position() {
|
||||
for (Ref<EditorDebuggerSession> &I : sessions) {
|
||||
if (I->is_active()) {
|
||||
|
|
@ -987,6 +998,14 @@ void GameView::_notification(int p_what) {
|
|||
|
||||
_update_ui();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_WM_WINDOW_FOCUS_IN:
|
||||
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
|
||||
if (embed_on_play) {
|
||||
debugger->report_window_focused(p_what == NOTIFICATION_WM_WINDOW_FOCUS_IN);
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_WM_POSITION_CHANGED: {
|
||||
if (window_wrapper->get_window_enabled()) {
|
||||
_update_floating_window_settings();
|
||||
|
|
|
|||
|
|
@ -101,6 +101,8 @@ public:
|
|||
void set_camera_override(bool p_enabled);
|
||||
void set_camera_manipulate_mode(EditorDebuggerNode::CameraOverride p_mode);
|
||||
|
||||
void report_window_focused(bool p_focused);
|
||||
|
||||
void reset_camera_2d_position();
|
||||
void reset_camera_3d_position();
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include "core/config/engine.h"
|
||||
#include "core/debugger/debugger_marshalls.h"
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/input/input.h"
|
||||
#include "core/input/shortcut.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
|
|
@ -506,6 +507,17 @@ Error SceneDebugger::_msg_rq_screenshot(const Array &p_args) {
|
|||
return OK;
|
||||
}
|
||||
|
||||
Error SceneDebugger::_msg_report_window_focused(const Array &p_args) {
|
||||
ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA);
|
||||
|
||||
bool focused = p_args[0];
|
||||
Input::get_singleton()->embedder_focused = focused;
|
||||
if (Input::get_singleton()->_should_ignore_joypad_events()) {
|
||||
Input::get_singleton()->release_pressed_events();
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
HashMap<String, SceneDebugger::ParseMessageFunc> SceneDebugger::message_handlers;
|
||||
|
|
@ -582,6 +594,7 @@ void SceneDebugger::_init_message_handlers() {
|
|||
message_handlers["runtime_node_select_reset_camera_3d"] = _msg_runtime_node_select_reset_camera_3d;
|
||||
#endif
|
||||
message_handlers["rq_screenshot"] = _msg_rq_screenshot;
|
||||
message_handlers["report_window_focused"] = _msg_report_window_focused;
|
||||
}
|
||||
|
||||
void SceneDebugger::_save_node(ObjectID id, const String &p_path) {
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ private:
|
|||
static Error _msg_runtime_node_select_set_avoid_locked(const Array &p_args);
|
||||
static Error _msg_runtime_node_select_set_prefer_group(const Array &p_args);
|
||||
static Error _msg_rq_screenshot(const Array &p_args);
|
||||
static Error _msg_report_window_focused(const Array &p_args);
|
||||
|
||||
static Error _msg_runtime_node_select_reset_camera_2d(const Array &p_args);
|
||||
static Error _msg_transform_camera_2d(const Array &p_args);
|
||||
|
|
|
|||
|
|
@ -921,9 +921,21 @@ void SceneTree::_notification(int p_notification) {
|
|||
case NOTIFICATION_WM_ABOUT:
|
||||
case NOTIFICATION_CRASH:
|
||||
case NOTIFICATION_APPLICATION_RESUMED:
|
||||
case NOTIFICATION_APPLICATION_PAUSED:
|
||||
case NOTIFICATION_APPLICATION_PAUSED: {
|
||||
// Pass these to nodes, since they are mirrored.
|
||||
get_root()->propagate_notification(p_notification);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_APPLICATION_FOCUS_IN:
|
||||
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
|
||||
if (Input::get_singleton()) {
|
||||
Input::get_singleton()->application_focused = p_notification == NOTIFICATION_APPLICATION_FOCUS_IN;
|
||||
|
||||
if (Input::get_singleton()->_should_ignore_joypad_events()) {
|
||||
Input::get_singleton()->release_pressed_events();
|
||||
}
|
||||
}
|
||||
|
||||
// Pass these to nodes, since they are mirrored.
|
||||
get_root()->propagate_notification(p_notification);
|
||||
} break;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue