Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 102 additions & 5 deletions core/input/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,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);
Expand Down Expand Up @@ -206,6 +208,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);
Expand Down Expand Up @@ -375,6 +378,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));
}
Expand Down Expand Up @@ -1010,6 +1017,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) {
Expand Down Expand Up @@ -1056,6 +1064,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;
Expand All @@ -1071,6 +1085,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();
Expand All @@ -1089,6 +1108,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();
Expand All @@ -1103,6 +1127,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();
Expand Down Expand Up @@ -1261,6 +1290,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;
}
Expand All @@ -1286,6 +1320,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_

Expand Down Expand Up @@ -1552,17 +1597,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;
}
}
}
}
}
}
Expand All @@ -1573,6 +1650,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);

Expand Down Expand Up @@ -1601,6 +1683,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];
Expand Down Expand Up @@ -1670,6 +1756,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];
Expand Down Expand Up @@ -1712,6 +1803,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) {
Expand Down Expand Up @@ -2274,6 +2370,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() {
Expand Down
11 changes: 11 additions & 0 deletions core/input/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ class Input : public Object {
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;
Expand All @@ -139,6 +142,7 @@ class Input : public Object {
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;
Expand Down Expand Up @@ -306,6 +310,8 @@ class Input : public Object {
#endif

friend class DisplayServer;
friend class SceneTree;
friend class SceneDebugger;

static void (*set_mouse_mode_func)(MouseMode);
static MouseMode (*get_mouse_mode_func)();
Expand All @@ -320,6 +326,8 @@ class Input : public Object {

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();
Expand Down Expand Up @@ -423,6 +431,9 @@ class Input : public Object {
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);
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/Input.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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].
Expand Down
19 changes: 19 additions & 0 deletions editor/run/game_view_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,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()) {
Expand Down Expand Up @@ -984,6 +995,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();
Expand Down
2 changes: 2 additions & 0 deletions editor/run/game_view_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ class GameViewDebugger : public EditorDebuggerPlugin {
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();

Expand Down
13 changes: 13 additions & 0 deletions scene/debugger/scene_debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#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"
Expand Down Expand Up @@ -503,6 +504,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;
Expand Down Expand Up @@ -579,6 +591,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) {
Expand Down
1 change: 1 addition & 0 deletions scene/debugger/scene_debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class SceneDebugger {
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);
Expand Down
14 changes: 13 additions & 1 deletion scene/main/scene_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -918,9 +918,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;
Expand Down