2023-01-05 12:25:55 +00:00
/**************************************************************************/
/* input.cpp */
/**************************************************************************/
/* 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. */
/**************************************************************************/
2018-01-04 23:50:27 +00:00
2020-04-28 13:19:37 +00:00
# include "input.h"
2017-08-27 19:07:15 +00:00
2020-11-07 22:33:38 +00:00
# include "core/config/project_settings.h"
2020-03-01 22:14:37 +00:00
# include "core/input/default_controller_mappings.h"
# include "core/input/input_map.h"
2018-09-11 16:13:45 +00:00
# include "core/os/os.h"
2020-03-01 22:14:37 +00:00
2023-04-24 10:46:01 +00:00
# ifdef DEV_ENABLED
# include "core/os/thread.h"
# endif
2021-08-13 21:31:57 +00:00
static const char * _joy_buttons [ ( size_t ) JoyButton : : SDL_MAX ] = {
2020-04-23 12:01:36 +00:00
" a " ,
" b " ,
" x " ,
" y " ,
" back " ,
" guide " ,
" start " ,
" leftstick " ,
" rightstick " ,
" leftshoulder " ,
" rightshoulder " ,
" dpup " ,
" dpdown " ,
" dpleft " ,
" dpright " ,
2021-02-07 13:52:38 +00:00
" misc1 " ,
" paddle1 " ,
" paddle2 " ,
" paddle3 " ,
" paddle4 " ,
" touchpad " ,
2020-04-23 12:01:36 +00:00
} ;
2021-08-13 21:31:57 +00:00
static const char * _joy_axes [ ( size_t ) JoyAxis : : SDL_MAX ] = {
2020-04-23 12:01:36 +00:00
" leftx " ,
" lefty " ,
" rightx " ,
" righty " ,
" lefttrigger " ,
" righttrigger " ,
} ;
2020-04-28 13:19:37 +00:00
Input * Input : : singleton = nullptr ;
2020-03-01 22:14:37 +00:00
2020-04-28 13:19:37 +00:00
void ( * Input : : set_mouse_mode_func ) ( Input : : MouseMode ) = nullptr ;
Input : : MouseMode ( * Input : : get_mouse_mode_func ) ( ) = nullptr ;
2022-03-27 09:17:36 +00:00
void ( * Input : : warp_mouse_func ) ( const Vector2 & p_position ) = nullptr ;
2020-04-28 13:19:37 +00:00
Input : : CursorShape ( * Input : : get_current_cursor_shape_func ) ( ) = nullptr ;
2022-05-02 23:43:50 +00:00
void ( * Input : : set_custom_mouse_cursor_func ) ( const Ref < Resource > & , Input : : CursorShape , const Vector2 & ) = nullptr ;
2020-03-03 13:36:29 +00:00
2020-04-28 13:19:37 +00:00
Input * Input : : get_singleton ( ) {
2020-03-01 22:14:37 +00:00
return singleton ;
}
2020-04-28 13:19:37 +00:00
void Input : : set_mouse_mode ( MouseMode p_mode ) {
2021-03-30 22:35:08 +00:00
ERR_FAIL_INDEX ( ( int ) p_mode , 5 ) ;
2020-03-03 13:36:29 +00:00
set_mouse_mode_func ( p_mode ) ;
2020-03-01 22:14:37 +00:00
}
2020-04-28 13:19:37 +00:00
Input : : MouseMode Input : : get_mouse_mode ( ) const {
2020-03-03 13:36:29 +00:00
return get_mouse_mode_func ( ) ;
2020-03-01 22:14:37 +00:00
}
2020-04-28 13:19:37 +00:00
void Input : : _bind_methods ( ) {
2020-03-27 00:56:29 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_anything_pressed " ) , & Input : : is_anything_pressed ) ;
2020-04-28 13:19:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_key_pressed " , " keycode " ) , & Input : : is_key_pressed ) ;
2021-11-23 09:14:19 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_physical_key_pressed " , " keycode " ) , & Input : : is_physical_key_pressed ) ;
2022-12-10 23:21:22 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_key_label_pressed " , " keycode " ) , & Input : : is_key_label_pressed ) ;
2020-04-28 13:19:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_mouse_button_pressed " , " button " ) , & Input : : is_mouse_button_pressed ) ;
ClassDB : : bind_method ( D_METHOD ( " is_joy_button_pressed " , " device " , " button " ) , & Input : : is_joy_button_pressed ) ;
2021-02-19 12:35:31 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_action_pressed " , " action " , " exact_match " ) , & Input : : is_action_pressed , DEFVAL ( false ) ) ;
ClassDB : : bind_method ( D_METHOD ( " is_action_just_pressed " , " action " , " exact_match " ) , & Input : : is_action_just_pressed , DEFVAL ( false ) ) ;
ClassDB : : bind_method ( D_METHOD ( " is_action_just_released " , " action " , " exact_match " ) , & Input : : is_action_just_released , DEFVAL ( false ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_action_strength " , " action " , " exact_match " ) , & Input : : get_action_strength , DEFVAL ( false ) ) ;
2021-07-23 01:05:44 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_action_raw_strength " , " action " , " exact_match " ) , & Input : : get_action_raw_strength , DEFVAL ( false ) ) ;
2020-11-10 06:21:13 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_axis " , " negative_action " , " positive_action " ) , & Input : : get_axis ) ;
ClassDB : : bind_method ( D_METHOD ( " get_vector " , " negative_x " , " positive_x " , " negative_y " , " positive_y " , " deadzone " ) , & Input : : get_vector , DEFVAL ( - 1.0f ) ) ;
2020-04-28 13:19:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_joy_mapping " , " mapping " , " update_existing " ) , & Input : : add_joy_mapping , DEFVAL ( false ) ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_joy_mapping " , " guid " ) , & Input : : remove_joy_mapping ) ;
ClassDB : : bind_method ( D_METHOD ( " is_joy_known " , " device " ) , & Input : : is_joy_known ) ;
ClassDB : : bind_method ( D_METHOD ( " get_joy_axis " , " device " , " axis " ) , & Input : : get_joy_axis ) ;
ClassDB : : bind_method ( D_METHOD ( " get_joy_name " , " device " ) , & Input : : get_joy_name ) ;
ClassDB : : bind_method ( D_METHOD ( " get_joy_guid " , " device " ) , & Input : : get_joy_guid ) ;
2023-04-14 05:18:04 +00:00
ClassDB : : bind_method ( D_METHOD ( " should_ignore_device " , " vendor_id " , " product_id " ) , & Input : : should_ignore_device ) ;
2020-04-28 13:19:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_connected_joypads " ) , & Input : : get_connected_joypads ) ;
ClassDB : : bind_method ( D_METHOD ( " get_joy_vibration_strength " , " device " ) , & Input : : get_joy_vibration_strength ) ;
ClassDB : : bind_method ( D_METHOD ( " get_joy_vibration_duration " , " device " ) , & Input : : get_joy_vibration_duration ) ;
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 " ) , & Input : : vibrate_handheld , DEFVAL ( 500 ) ) ;
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 ) ;
ClassDB : : bind_method ( D_METHOD ( " get_gyroscope " ) , & Input : : get_gyroscope ) ;
2021-10-12 20:26:03 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_gravity " , " value " ) , & Input : : set_gravity ) ;
ClassDB : : bind_method ( D_METHOD ( " set_accelerometer " , " value " ) , & Input : : set_accelerometer ) ;
ClassDB : : bind_method ( D_METHOD ( " set_magnetometer " , " value " ) , & Input : : set_magnetometer ) ;
ClassDB : : bind_method ( D_METHOD ( " set_gyroscope " , " value " ) , & Input : : set_gyroscope ) ;
2021-12-29 13:22:22 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_last_mouse_velocity " ) , & Input : : get_last_mouse_velocity ) ;
2020-04-28 13:19:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_mouse_button_mask " ) , & Input : : get_mouse_button_mask ) ;
ClassDB : : bind_method ( D_METHOD ( " set_mouse_mode " , " mode " ) , & Input : : set_mouse_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_mouse_mode " ) , & Input : : get_mouse_mode ) ;
2022-03-27 09:17:36 +00:00
ClassDB : : bind_method ( D_METHOD ( " warp_mouse " , " position " ) , & Input : : warp_mouse ) ;
2020-04-28 13:19:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " action_press " , " action " , " strength " ) , & Input : : action_press , DEFVAL ( 1.f ) ) ;
ClassDB : : bind_method ( D_METHOD ( " action_release " , " action " ) , & Input : : action_release ) ;
ClassDB : : bind_method ( D_METHOD ( " set_default_cursor_shape " , " shape " ) , & Input : : set_default_cursor_shape , DEFVAL ( CURSOR_ARROW ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_current_cursor_shape " ) , & Input : : get_current_cursor_shape ) ;
ClassDB : : bind_method ( D_METHOD ( " set_custom_mouse_cursor " , " image " , " shape " , " hotspot " ) , & Input : : set_custom_mouse_cursor , DEFVAL ( CURSOR_ARROW ) , DEFVAL ( Vector2 ( ) ) ) ;
ClassDB : : bind_method ( D_METHOD ( " parse_input_event " , " event " ) , & Input : : parse_input_event ) ;
ClassDB : : bind_method ( D_METHOD ( " set_use_accumulated_input " , " enable " ) , & Input : : set_use_accumulated_input ) ;
2022-06-03 03:21:25 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_using_accumulated_input " ) , & Input : : is_using_accumulated_input ) ;
2021-10-14 16:58:15 +00:00
ClassDB : : bind_method ( D_METHOD ( " flush_buffered_events " ) , & Input : : flush_buffered_events ) ;
2020-03-01 22:14:37 +00:00
2022-06-03 03:21:25 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " mouse_mode " ) , " set_mouse_mode " , " get_mouse_mode " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " use_accumulated_input " ) , " set_use_accumulated_input " , " is_using_accumulated_input " ) ;
2020-03-01 22:14:37 +00:00
BIND_ENUM_CONSTANT ( MOUSE_MODE_VISIBLE ) ;
BIND_ENUM_CONSTANT ( MOUSE_MODE_HIDDEN ) ;
BIND_ENUM_CONSTANT ( MOUSE_MODE_CAPTURED ) ;
BIND_ENUM_CONSTANT ( MOUSE_MODE_CONFINED ) ;
2021-03-30 22:35:08 +00:00
BIND_ENUM_CONSTANT ( MOUSE_MODE_CONFINED_HIDDEN ) ;
2020-03-01 22:14:37 +00:00
BIND_ENUM_CONSTANT ( CURSOR_ARROW ) ;
BIND_ENUM_CONSTANT ( CURSOR_IBEAM ) ;
BIND_ENUM_CONSTANT ( CURSOR_POINTING_HAND ) ;
BIND_ENUM_CONSTANT ( CURSOR_CROSS ) ;
BIND_ENUM_CONSTANT ( CURSOR_WAIT ) ;
BIND_ENUM_CONSTANT ( CURSOR_BUSY ) ;
BIND_ENUM_CONSTANT ( CURSOR_DRAG ) ;
BIND_ENUM_CONSTANT ( CURSOR_CAN_DROP ) ;
BIND_ENUM_CONSTANT ( CURSOR_FORBIDDEN ) ;
BIND_ENUM_CONSTANT ( CURSOR_VSIZE ) ;
BIND_ENUM_CONSTANT ( CURSOR_HSIZE ) ;
BIND_ENUM_CONSTANT ( CURSOR_BDIAGSIZE ) ;
BIND_ENUM_CONSTANT ( CURSOR_FDIAGSIZE ) ;
BIND_ENUM_CONSTANT ( CURSOR_MOVE ) ;
BIND_ENUM_CONSTANT ( CURSOR_VSPLIT ) ;
BIND_ENUM_CONSTANT ( CURSOR_HSPLIT ) ;
BIND_ENUM_CONSTANT ( CURSOR_HELP ) ;
ADD_SIGNAL ( MethodInfo ( " joy_connection_changed " , PropertyInfo ( Variant : : INT , " device " ) , PropertyInfo ( Variant : : BOOL , " connected " ) ) ) ;
}
2020-04-28 13:19:37 +00:00
void Input : : get_argument_options ( const StringName & p_function , int p_idx , List < String > * r_options ) const {
2020-03-01 22:14:37 +00:00
String pf = p_function ;
2022-08-15 15:28:49 +00:00
if ( ( p_idx = = 0 & & ( pf = = " is_action_pressed " | | pf = = " action_press " | | pf = = " action_release " | | pf = = " is_action_just_pressed " | | pf = = " is_action_just_released " | | pf = = " get_action_strength " | | pf = = " get_action_raw_strength " ) ) | |
( p_idx < 2 & & pf = = " get_axis " ) | |
( p_idx < 4 & & pf = = " get_vector " ) ) {
2020-03-01 22:14:37 +00:00
List < PropertyInfo > pinfo ;
ProjectSettings : : get_singleton ( ) - > get_property_list ( & pinfo ) ;
2021-07-24 13:46:25 +00:00
for ( const PropertyInfo & pi : pinfo ) {
2020-05-14 14:41:43 +00:00
if ( ! pi . name . begins_with ( " input/ " ) ) {
2020-03-01 22:14:37 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2015-09-24 21:06:15 +00:00
2020-03-01 22:14:37 +00:00
String name = pi . name . substr ( pi . name . find ( " / " ) + 1 , pi . name . length ( ) ) ;
2021-10-01 15:06:48 +00:00
r_options - > push_back ( name . quote ( ) ) ;
2020-03-01 22:14:37 +00:00
}
}
}
2021-12-29 13:22:22 +00:00
void Input : : VelocityTrack : : update ( const Vector2 & p_delta_p ) {
2015-09-24 21:06:15 +00:00
uint64_t tick = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
uint32_t tdiff = tick - last_tick ;
float delta_t = tdiff / 1000000.0 ;
last_tick = tick ;
2022-01-13 18:13:50 +00:00
if ( delta_t > max_ref_frame ) {
// First movement in a long time, reset and start again.
velocity = Vector2 ( ) ;
accum = p_delta_p ;
accum_t = 0 ;
return ;
}
2015-09-24 21:06:15 +00:00
accum + = p_delta_p ;
accum_t + = delta_t ;
2022-01-13 18:13:50 +00:00
if ( accum_t < min_ref_frame ) {
// Not enough time has passed to calculate speed precisely.
return ;
2020-05-14 14:41:43 +00:00
}
2015-09-24 21:06:15 +00:00
2022-01-13 18:13:50 +00:00
velocity = accum / accum_t ;
accum = Vector2 ( ) ;
accum_t = 0 ;
2015-09-24 21:06:15 +00:00
}
2021-12-29 13:22:22 +00:00
void Input : : VelocityTrack : : reset ( ) {
2015-09-24 21:06:15 +00:00
last_tick = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
2021-12-29 13:22:22 +00:00
velocity = Vector2 ( ) ;
2022-01-13 18:13:50 +00:00
accum = Vector2 ( ) ;
2015-09-24 21:06:15 +00:00
accum_t = 0 ;
}
2021-12-29 13:22:22 +00:00
Input : : VelocityTrack : : VelocityTrack ( ) {
2015-09-24 21:06:15 +00:00
min_ref_frame = 0.1 ;
2022-01-13 18:13:50 +00:00
max_ref_frame = 3.0 ;
2015-09-24 21:06:15 +00:00
reset ( ) ;
}
2020-03-27 00:56:29 +00:00
bool Input : : is_anything_pressed ( ) const {
_THREAD_SAFE_METHOD_
2023-02-09 10:43:23 +00:00
if ( ! keys_pressed . is_empty ( ) | | ! joy_buttons_pressed . is_empty ( ) | | ! mouse_button_mask . is_empty ( ) ) {
return true ;
}
2022-05-13 13:04:37 +00:00
for ( const KeyValue < StringName , Input : : Action > & E : action_state ) {
if ( E . value . pressed ) {
2020-03-27 00:56:29 +00:00
return true ;
}
}
2023-02-09 10:43:23 +00:00
return false ;
2020-03-27 00:56:29 +00:00
}
2021-06-20 17:12:33 +00:00
bool Input : : is_key_pressed ( Key p_keycode ) const {
2015-09-24 21:06:15 +00:00
_THREAD_SAFE_METHOD_
2018-04-05 17:59:35 +00:00
return keys_pressed . has ( p_keycode ) ;
2015-09-24 21:06:15 +00:00
}
2021-11-23 09:14:19 +00:00
bool Input : : is_physical_key_pressed ( Key p_keycode ) const {
_THREAD_SAFE_METHOD_
return physical_keys_pressed . has ( p_keycode ) ;
}
2022-12-10 23:21:22 +00:00
bool Input : : is_key_label_pressed ( Key p_keycode ) const {
_THREAD_SAFE_METHOD_
return key_label_pressed . has ( p_keycode ) ;
}
2021-03-25 20:56:12 +00:00
bool Input : : is_mouse_button_pressed ( MouseButton p_button ) const {
2015-09-24 21:06:15 +00:00
_THREAD_SAFE_METHOD_
2023-01-07 23:55:54 +00:00
return mouse_button_mask . has_flag ( mouse_button_to_mask ( p_button ) ) ;
2015-09-24 21:06:15 +00:00
}
2021-08-13 21:31:57 +00:00
static JoyAxis _combine_device ( JoyAxis p_value , int p_device ) {
return JoyAxis ( ( int ) p_value | ( p_device < < 20 ) ) ;
}
static JoyButton _combine_device ( JoyButton p_value , int p_device ) {
return JoyButton ( ( int ) p_value | ( p_device < < 20 ) ) ;
2015-09-24 21:06:15 +00:00
}
2021-03-25 20:56:12 +00:00
bool Input : : is_joy_button_pressed ( int p_device , JoyButton p_button ) const {
2015-09-24 21:06:15 +00:00
_THREAD_SAFE_METHOD_
return joy_buttons_pressed . has ( _combine_device ( p_button , p_device ) ) ;
}
Allow checking for exact matches with Action events.
Added additional param to action related methods to test for exactness.
If "p_exact_match" is true, then the action will only be "matched" if the provided input event *exactly* matches with the action event.
Before:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* Is Action Pressed = True
Now:
You can still do the above, however you can optionally check that the input is exactly what the action event is:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* p_exact_match = True
* Is Action Pressed = False
* If the Input Event was only KEY_S, then the result would be true.
Usage:
```gdscript
Input.is_action_pressed(action_name: String, exact_match: bool)
Input.is_action_pressed("my_action", true)
InputMap.event_is_action(p_event, "my_action", true)
func _input(event: InputEvent):
event.is_action_pressed("my_action", false, true) # false = "allow_echo", true = "exact_match"
event.is_action("my_action", true)
```
2020-12-13 14:22:42 +00:00
bool Input : : is_action_pressed ( const StringName & p_action , bool p_exact ) const {
2021-08-16 17:57:34 +00:00
ERR_FAIL_COND_V_MSG ( ! InputMap : : get_singleton ( ) - > has_action ( p_action ) , false , InputMap : : get_singleton ( ) - > suggest_actions ( p_action ) ) ;
Allow checking for exact matches with Action events.
Added additional param to action related methods to test for exactness.
If "p_exact_match" is true, then the action will only be "matched" if the provided input event *exactly* matches with the action event.
Before:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* Is Action Pressed = True
Now:
You can still do the above, however you can optionally check that the input is exactly what the action event is:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* p_exact_match = True
* Is Action Pressed = False
* If the Input Event was only KEY_S, then the result would be true.
Usage:
```gdscript
Input.is_action_pressed(action_name: String, exact_match: bool)
Input.is_action_pressed("my_action", true)
InputMap.event_is_action(p_event, "my_action", true)
func _input(event: InputEvent):
event.is_action_pressed("my_action", false, true) # false = "allow_echo", true = "exact_match"
event.is_action("my_action", true)
```
2020-12-13 14:22:42 +00:00
return action_state . has ( p_action ) & & action_state [ p_action ] . pressed & & ( p_exact ? action_state [ p_action ] . exact : true ) ;
2015-09-24 21:06:15 +00:00
}
Allow checking for exact matches with Action events.
Added additional param to action related methods to test for exactness.
If "p_exact_match" is true, then the action will only be "matched" if the provided input event *exactly* matches with the action event.
Before:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* Is Action Pressed = True
Now:
You can still do the above, however you can optionally check that the input is exactly what the action event is:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* p_exact_match = True
* Is Action Pressed = False
* If the Input Event was only KEY_S, then the result would be true.
Usage:
```gdscript
Input.is_action_pressed(action_name: String, exact_match: bool)
Input.is_action_pressed("my_action", true)
InputMap.event_is_action(p_event, "my_action", true)
func _input(event: InputEvent):
event.is_action_pressed("my_action", false, true) # false = "allow_echo", true = "exact_match"
event.is_action("my_action", true)
```
2020-12-13 14:22:42 +00:00
bool Input : : is_action_just_pressed ( const StringName & p_action , bool p_exact ) const {
2021-08-16 17:57:34 +00:00
ERR_FAIL_COND_V_MSG ( ! InputMap : : get_singleton ( ) - > has_action ( p_action ) , false , InputMap : : get_singleton ( ) - > suggest_actions ( p_action ) ) ;
2022-05-13 13:04:37 +00:00
HashMap < StringName , Action > : : ConstIterator E = action_state . find ( p_action ) ;
2020-05-14 14:41:43 +00:00
if ( ! E ) {
2016-09-01 21:58:52 +00:00
return false ;
2020-05-14 14:41:43 +00:00
}
2016-09-01 21:58:52 +00:00
2022-05-13 13:04:37 +00:00
if ( p_exact & & E - > value . exact = = false ) {
Allow checking for exact matches with Action events.
Added additional param to action related methods to test for exactness.
If "p_exact_match" is true, then the action will only be "matched" if the provided input event *exactly* matches with the action event.
Before:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* Is Action Pressed = True
Now:
You can still do the above, however you can optionally check that the input is exactly what the action event is:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* p_exact_match = True
* Is Action Pressed = False
* If the Input Event was only KEY_S, then the result would be true.
Usage:
```gdscript
Input.is_action_pressed(action_name: String, exact_match: bool)
Input.is_action_pressed("my_action", true)
InputMap.event_is_action(p_event, "my_action", true)
func _input(event: InputEvent):
event.is_action_pressed("my_action", false, true) # false = "allow_echo", true = "exact_match"
event.is_action("my_action", true)
```
2020-12-13 14:22:42 +00:00
return false ;
}
2023-05-14 07:53:36 +00:00
// Backward compatibility for legacy behavior, only return true if currently pressed.
bool pressed_requirement = legacy_just_pressed_behavior ? E - > value . pressed : true ;
2017-09-30 14:19:07 +00:00
if ( Engine : : get_singleton ( ) - > is_in_physics_frame ( ) ) {
2023-05-14 07:53:36 +00:00
return pressed_requirement & & E - > value . pressed_physics_frame = = Engine : : get_singleton ( ) - > get_physics_frames ( ) ;
2016-09-01 21:58:52 +00:00
} else {
2023-05-14 07:53:36 +00:00
return pressed_requirement & & E - > value . pressed_process_frame = = Engine : : get_singleton ( ) - > get_process_frames ( ) ;
2016-09-01 21:58:52 +00:00
}
}
Allow checking for exact matches with Action events.
Added additional param to action related methods to test for exactness.
If "p_exact_match" is true, then the action will only be "matched" if the provided input event *exactly* matches with the action event.
Before:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* Is Action Pressed = True
Now:
You can still do the above, however you can optionally check that the input is exactly what the action event is:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* p_exact_match = True
* Is Action Pressed = False
* If the Input Event was only KEY_S, then the result would be true.
Usage:
```gdscript
Input.is_action_pressed(action_name: String, exact_match: bool)
Input.is_action_pressed("my_action", true)
InputMap.event_is_action(p_event, "my_action", true)
func _input(event: InputEvent):
event.is_action_pressed("my_action", false, true) # false = "allow_echo", true = "exact_match"
event.is_action("my_action", true)
```
2020-12-13 14:22:42 +00:00
bool Input : : is_action_just_released ( const StringName & p_action , bool p_exact ) const {
2021-08-16 17:57:34 +00:00
ERR_FAIL_COND_V_MSG ( ! InputMap : : get_singleton ( ) - > has_action ( p_action ) , false , InputMap : : get_singleton ( ) - > suggest_actions ( p_action ) ) ;
2022-05-13 13:04:37 +00:00
HashMap < StringName , Action > : : ConstIterator E = action_state . find ( p_action ) ;
2020-05-14 14:41:43 +00:00
if ( ! E ) {
2016-09-01 21:58:52 +00:00
return false ;
2020-05-14 14:41:43 +00:00
}
2016-09-01 21:58:52 +00:00
2022-05-13 13:04:37 +00:00
if ( p_exact & & E - > value . exact = = false ) {
Allow checking for exact matches with Action events.
Added additional param to action related methods to test for exactness.
If "p_exact_match" is true, then the action will only be "matched" if the provided input event *exactly* matches with the action event.
Before:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* Is Action Pressed = True
Now:
You can still do the above, however you can optionally check that the input is exactly what the action event is:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* p_exact_match = True
* Is Action Pressed = False
* If the Input Event was only KEY_S, then the result would be true.
Usage:
```gdscript
Input.is_action_pressed(action_name: String, exact_match: bool)
Input.is_action_pressed("my_action", true)
InputMap.event_is_action(p_event, "my_action", true)
func _input(event: InputEvent):
event.is_action_pressed("my_action", false, true) # false = "allow_echo", true = "exact_match"
event.is_action("my_action", true)
```
2020-12-13 14:22:42 +00:00
return false ;
}
2023-05-14 07:53:36 +00:00
// Backward compatibility for legacy behavior, only return true if currently released.
bool released_requirement = legacy_just_pressed_behavior ? ! E - > value . pressed : true ;
2017-09-30 14:19:07 +00:00
if ( Engine : : get_singleton ( ) - > is_in_physics_frame ( ) ) {
2023-05-14 07:53:36 +00:00
return released_requirement & & E - > value . released_physics_frame = = Engine : : get_singleton ( ) - > get_physics_frames ( ) ;
2016-09-01 21:58:52 +00:00
} else {
2023-05-14 07:53:36 +00:00
return released_requirement & & E - > value . released_process_frame = = Engine : : get_singleton ( ) - > get_process_frames ( ) ;
2016-09-01 21:58:52 +00:00
}
}
Allow checking for exact matches with Action events.
Added additional param to action related methods to test for exactness.
If "p_exact_match" is true, then the action will only be "matched" if the provided input event *exactly* matches with the action event.
Before:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* Is Action Pressed = True
Now:
You can still do the above, however you can optionally check that the input is exactly what the action event is:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* p_exact_match = True
* Is Action Pressed = False
* If the Input Event was only KEY_S, then the result would be true.
Usage:
```gdscript
Input.is_action_pressed(action_name: String, exact_match: bool)
Input.is_action_pressed("my_action", true)
InputMap.event_is_action(p_event, "my_action", true)
func _input(event: InputEvent):
event.is_action_pressed("my_action", false, true) # false = "allow_echo", true = "exact_match"
event.is_action("my_action", true)
```
2020-12-13 14:22:42 +00:00
float Input : : get_action_strength ( const StringName & p_action , bool p_exact ) const {
2021-08-16 17:57:34 +00:00
ERR_FAIL_COND_V_MSG ( ! InputMap : : get_singleton ( ) - > has_action ( p_action ) , 0.0 , InputMap : : get_singleton ( ) - > suggest_actions ( p_action ) ) ;
2022-05-13 13:04:37 +00:00
HashMap < StringName , Action > : : ConstIterator E = action_state . find ( p_action ) ;
2020-05-14 14:41:43 +00:00
if ( ! E ) {
2018-02-21 21:06:34 +00:00
return 0.0f ;
2020-05-14 14:41:43 +00:00
}
2018-02-21 21:06:34 +00:00
2022-05-13 13:04:37 +00:00
if ( p_exact & & E - > value . exact = = false ) {
Allow checking for exact matches with Action events.
Added additional param to action related methods to test for exactness.
If "p_exact_match" is true, then the action will only be "matched" if the provided input event *exactly* matches with the action event.
Before:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* Is Action Pressed = True
Now:
You can still do the above, however you can optionally check that the input is exactly what the action event is:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* p_exact_match = True
* Is Action Pressed = False
* If the Input Event was only KEY_S, then the result would be true.
Usage:
```gdscript
Input.is_action_pressed(action_name: String, exact_match: bool)
Input.is_action_pressed("my_action", true)
InputMap.event_is_action(p_event, "my_action", true)
func _input(event: InputEvent):
event.is_action_pressed("my_action", false, true) # false = "allow_echo", true = "exact_match"
event.is_action("my_action", true)
```
2020-12-13 14:22:42 +00:00
return 0.0f ;
}
2022-05-13 13:04:37 +00:00
return E - > value . strength ;
2018-02-21 21:06:34 +00:00
}
Allow checking for exact matches with Action events.
Added additional param to action related methods to test for exactness.
If "p_exact_match" is true, then the action will only be "matched" if the provided input event *exactly* matches with the action event.
Before:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* Is Action Pressed = True
Now:
You can still do the above, however you can optionally check that the input is exactly what the action event is:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* p_exact_match = True
* Is Action Pressed = False
* If the Input Event was only KEY_S, then the result would be true.
Usage:
```gdscript
Input.is_action_pressed(action_name: String, exact_match: bool)
Input.is_action_pressed("my_action", true)
InputMap.event_is_action(p_event, "my_action", true)
func _input(event: InputEvent):
event.is_action_pressed("my_action", false, true) # false = "allow_echo", true = "exact_match"
event.is_action("my_action", true)
```
2020-12-13 14:22:42 +00:00
float Input : : get_action_raw_strength ( const StringName & p_action , bool p_exact ) const {
2022-03-01 15:25:57 +00:00
ERR_FAIL_COND_V_MSG ( ! InputMap : : get_singleton ( ) - > has_action ( p_action ) , 0.0 , InputMap : : get_singleton ( ) - > suggest_actions ( p_action ) ) ;
2022-05-13 13:04:37 +00:00
HashMap < StringName , Action > : : ConstIterator E = action_state . find ( p_action ) ;
2020-10-24 09:22:35 +00:00
if ( ! E ) {
return 0.0f ;
}
2022-05-13 13:04:37 +00:00
if ( p_exact & & E - > value . exact = = false ) {
Allow checking for exact matches with Action events.
Added additional param to action related methods to test for exactness.
If "p_exact_match" is true, then the action will only be "matched" if the provided input event *exactly* matches with the action event.
Before:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* Is Action Pressed = True
Now:
You can still do the above, however you can optionally check that the input is exactly what the action event is:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* p_exact_match = True
* Is Action Pressed = False
* If the Input Event was only KEY_S, then the result would be true.
Usage:
```gdscript
Input.is_action_pressed(action_name: String, exact_match: bool)
Input.is_action_pressed("my_action", true)
InputMap.event_is_action(p_event, "my_action", true)
func _input(event: InputEvent):
event.is_action_pressed("my_action", false, true) # false = "allow_echo", true = "exact_match"
event.is_action("my_action", true)
```
2020-12-13 14:22:42 +00:00
return 0.0f ;
}
2022-05-13 13:04:37 +00:00
return E - > value . raw_strength ;
2020-10-24 09:22:35 +00:00
}
2020-11-10 06:21:13 +00:00
float Input : : get_axis ( const StringName & p_negative_action , const StringName & p_positive_action ) const {
return get_action_strength ( p_positive_action ) - get_action_strength ( p_negative_action ) ;
}
Vector2 Input : : get_vector ( const StringName & p_negative_x , const StringName & p_positive_x , const StringName & p_negative_y , const StringName & p_positive_y , float p_deadzone ) const {
Vector2 vector = Vector2 (
2023-02-20 01:52:35 +00:00
get_action_raw_strength ( p_positive_x ) - get_action_raw_strength ( p_negative_x ) ,
get_action_raw_strength ( p_positive_y ) - get_action_raw_strength ( p_negative_y ) ) ;
2020-11-10 06:21:13 +00:00
if ( p_deadzone < 0.0f ) {
// If the deadzone isn't specified, get it from the average of the actions.
2021-10-28 13:19:35 +00:00
p_deadzone = 0.25 *
( InputMap : : get_singleton ( ) - > action_get_deadzone ( p_positive_x ) +
InputMap : : get_singleton ( ) - > action_get_deadzone ( p_negative_x ) +
InputMap : : get_singleton ( ) - > action_get_deadzone ( p_positive_y ) +
InputMap : : get_singleton ( ) - > action_get_deadzone ( p_negative_y ) ) ;
2020-11-10 06:21:13 +00:00
}
// Circular length limiting and deadzone.
float length = vector . length ( ) ;
if ( length < = p_deadzone ) {
return Vector2 ( ) ;
} else if ( length > 1.0f ) {
return vector / length ;
} else {
// Inverse lerp length to map (p_deadzone, 1) to (0, 1).
return vector * ( Math : : inverse_lerp ( p_deadzone , 1.0f , length ) / length ) ;
}
}
2021-03-25 20:56:12 +00:00
float Input : : get_joy_axis ( int p_device , JoyAxis p_axis ) const {
2015-09-24 21:06:15 +00:00
_THREAD_SAFE_METHOD_
2021-08-13 21:31:57 +00:00
JoyAxis c = _combine_device ( p_axis , p_device ) ;
2015-12-18 05:12:53 +00:00
if ( _joy_axis . has ( c ) ) {
return _joy_axis [ c ] ;
2015-09-24 21:06:15 +00:00
} else {
return 0 ;
}
}
2020-04-28 13:19:37 +00:00
String Input : : get_joy_name ( int p_idx ) {
2015-09-24 21:06:15 +00:00
_THREAD_SAFE_METHOD_
2015-12-18 05:12:53 +00:00
return joy_names [ p_idx ] . name ;
2020-05-19 13:46:49 +00:00
}
2015-09-24 21:06:15 +00:00
2020-04-28 13:19:37 +00:00
Vector2 Input : : get_joy_vibration_strength ( int p_device ) {
2016-06-15 05:25:35 +00:00
if ( joy_vibration . has ( p_device ) ) {
return Vector2 ( joy_vibration [ p_device ] . weak_magnitude , joy_vibration [ p_device ] . strong_magnitude ) ;
} else {
return Vector2 ( 0 , 0 ) ;
}
}
2020-04-28 13:19:37 +00:00
uint64_t Input : : get_joy_vibration_timestamp ( int p_device ) {
2016-06-15 05:25:35 +00:00
if ( joy_vibration . has ( p_device ) ) {
return joy_vibration [ p_device ] . timestamp ;
} else {
return 0 ;
}
}
2020-04-28 13:19:37 +00:00
float Input : : get_joy_vibration_duration ( int p_device ) {
2016-06-15 05:25:35 +00:00
if ( joy_vibration . has ( p_device ) ) {
return joy_vibration [ p_device ] . duration ;
} else {
return 0.f ;
}
}
2015-12-18 05:12:53 +00:00
static String _hex_str ( uint8_t p_byte ) {
2015-12-18 18:15:32 +00:00
static const char * dict = " 0123456789abcdef " ;
char ret [ 3 ] ;
ret [ 2 ] = 0 ;
2015-12-18 05:12:53 +00:00
2015-12-18 18:15:32 +00:00
ret [ 0 ] = dict [ p_byte > > 4 ] ;
ret [ 1 ] = dict [ p_byte & 0xf ] ;
2015-12-18 05:12:53 +00:00
2015-12-18 18:15:32 +00:00
return ret ;
2020-05-19 13:46:49 +00:00
}
2015-12-18 05:12:53 +00:00
2020-04-28 13:19:37 +00:00
void Input : : joy_connection_changed ( int p_idx , bool p_connected , String p_name , String p_guid ) {
2015-12-18 05:12:53 +00:00
_THREAD_SAFE_METHOD_
2017-01-08 20:05:51 +00:00
Joypad js ;
2015-12-18 18:15:32 +00:00
js . name = p_connected ? p_name : " " ;
js . uid = p_connected ? p_guid : " " ;
if ( p_connected ) {
String uidname = p_guid ;
2021-12-09 09:42:46 +00:00
if ( p_guid . is_empty ( ) ) {
2015-12-18 18:15:32 +00:00
int uidlen = MIN ( p_name . length ( ) , 16 ) ;
for ( int i = 0 ; i < uidlen ; i + + ) {
uidname = uidname + _hex_str ( p_name [ i ] ) ;
2020-05-19 13:46:49 +00:00
}
}
2015-12-18 18:15:32 +00:00
js . uid = uidname ;
2016-07-05 12:07:46 +00:00
js . connected = true ;
2016-01-24 17:01:37 +00:00
int mapping = fallback_mapping ;
2015-12-18 18:15:32 +00:00
for ( int i = 0 ; i < map_db . size ( ) ; i + + ) {
if ( js . uid = = map_db [ i ] . uid ) {
mapping = i ;
2016-02-29 15:48:19 +00:00
js . name = map_db [ i ] . name ;
2020-05-19 13:46:49 +00:00
}
}
2015-12-18 18:15:32 +00:00
js . mapping = mapping ;
2016-01-26 03:40:04 +00:00
} else {
2016-07-05 12:07:46 +00:00
js . connected = false ;
2021-08-13 21:31:57 +00:00
for ( int i = 0 ; i < ( int ) JoyButton : : MAX ; i + + ) {
JoyButton c = _combine_device ( ( JoyButton ) i , p_idx ) ;
2016-01-26 03:40:04 +00:00
joy_buttons_pressed . erase ( c ) ;
2020-04-23 12:01:36 +00:00
}
2021-08-13 21:31:57 +00:00
for ( int i = 0 ; i < ( int ) JoyAxis : : MAX ; i + + ) {
2021-03-25 20:56:12 +00:00
set_joy_axis ( p_idx , ( JoyAxis ) i , 0.0f ) ;
2020-04-23 12:01:36 +00:00
}
2020-05-19 13:46:49 +00:00
}
2015-12-18 18:15:32 +00:00
joy_names [ p_idx ] = js ;
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " joy_connection_changed " ) , p_idx , p_connected ) ;
2020-05-19 13:46:49 +00:00
}
2015-09-24 21:06:15 +00:00
2020-04-28 13:19:37 +00:00
Vector3 Input : : get_gravity ( ) const {
2016-11-23 12:32:44 +00:00
_THREAD_SAFE_METHOD_
return gravity ;
}
2020-04-28 13:19:37 +00:00
Vector3 Input : : get_accelerometer ( ) const {
2015-09-24 21:06:15 +00:00
_THREAD_SAFE_METHOD_
return accelerometer ;
}
2020-04-28 13:19:37 +00:00
Vector3 Input : : get_magnetometer ( ) const {
2016-05-27 17:29:37 +00:00
_THREAD_SAFE_METHOD_
return magnetometer ;
}
2020-04-28 13:19:37 +00:00
Vector3 Input : : get_gyroscope ( ) const {
2016-07-15 07:31:34 +00:00
_THREAD_SAFE_METHOD_
return gyroscope ;
}
2020-04-28 13:19:37 +00:00
void Input : : _parse_input_event_impl ( const Ref < InputEvent > & p_event , bool p_is_emulated ) {
2023-04-24 10:46:01 +00:00
// This function does the final delivery of the input event to user land.
// Regardless where the event came from originally, this has to happen on the main thread.
DEV_ASSERT ( Thread : : get_caller_id ( ) = = Thread : : get_main_id ( ) ) ;
2018-10-14 15:55:03 +00:00
// Notes on mouse-touch emulation:
// - Emulated mouse events are parsed, that is, re-routed to this method, so they make the same effects
// as true mouse events. The only difference is the situation is flagged as emulated so they are not
// emulated back to touch events in an endless loop.
// - Emulated touch events are handed right to the main loop (i.e., the SceneTree) because they don't
// require additional handling by this class.
2017-05-20 15:38:03 +00:00
Ref < InputEventKey > k = p_event ;
2021-08-13 21:31:57 +00:00
if ( k . is_valid ( ) & & ! k - > is_echo ( ) & & k - > get_keycode ( ) ! = Key : : NONE ) {
2020-05-14 14:41:43 +00:00
if ( k - > is_pressed ( ) ) {
2018-04-05 17:59:35 +00:00
keys_pressed . insert ( k - > get_keycode ( ) ) ;
2020-05-14 14:41:43 +00:00
} else {
2018-04-05 17:59:35 +00:00
keys_pressed . erase ( k - > get_keycode ( ) ) ;
2020-05-14 14:41:43 +00:00
}
2017-05-20 15:38:03 +00:00
}
2021-11-23 09:14:19 +00:00
if ( k . is_valid ( ) & & ! k - > is_echo ( ) & & k - > get_physical_keycode ( ) ! = Key : : NONE ) {
if ( k - > is_pressed ( ) ) {
physical_keys_pressed . insert ( k - > get_physical_keycode ( ) ) ;
} else {
physical_keys_pressed . erase ( k - > get_physical_keycode ( ) ) ;
}
}
2022-12-10 23:21:22 +00:00
if ( k . is_valid ( ) & & ! k - > is_echo ( ) & & k - > get_key_label ( ) ! = Key : : NONE ) {
if ( k - > is_pressed ( ) ) {
key_label_pressed . insert ( k - > get_key_label ( ) ) ;
} else {
key_label_pressed . erase ( k - > get_key_label ( ) ) ;
}
}
2017-05-20 15:38:03 +00:00
Ref < InputEventMouseButton > mb = p_event ;
2018-07-10 09:28:06 +00:00
if ( mb . is_valid ( ) ) {
2017-09-16 15:12:11 +00:00
if ( mb - > is_pressed ( ) ) {
2023-01-07 23:55:54 +00:00
mouse_button_mask . set_flag ( mouse_button_to_mask ( mb - > get_button_index ( ) ) ) ;
2017-09-16 15:12:11 +00:00
} else {
2023-01-07 23:55:54 +00:00
mouse_button_mask . clear_flag ( mouse_button_to_mask ( mb - > get_button_index ( ) ) ) ;
2017-09-16 15:12:11 +00:00
}
2017-05-20 15:38:03 +00:00
2018-02-24 02:04:30 +00:00
Point2 pos = mb - > get_global_position ( ) ;
if ( mouse_pos ! = pos ) {
set_mouse_position ( pos ) ;
}
2021-08-13 21:31:57 +00:00
if ( event_dispatch_function & & emulate_touch_from_mouse & & ! p_is_emulated & & mb - > get_button_index ( ) = = MouseButton : : LEFT ) {
2017-05-20 15:38:03 +00:00
Ref < InputEventScreenTouch > touch_event ;
2021-06-17 22:03:09 +00:00
touch_event . instantiate ( ) ;
2017-05-20 15:38:03 +00:00
touch_event - > set_pressed ( mb - > is_pressed ( ) ) ;
2023-05-04 00:57:13 +00:00
touch_event - > set_canceled ( mb - > is_canceled ( ) ) ;
2017-06-03 08:54:24 +00:00
touch_event - > set_position ( mb - > get_position ( ) ) ;
2022-10-22 14:30:46 +00:00
touch_event - > set_double_tap ( mb - > is_double_click ( ) ) ;
2023-02-04 22:16:54 +00:00
touch_event - > set_device ( InputEvent : : DEVICE_ID_EMULATION ) ;
2023-04-24 10:46:01 +00:00
_THREAD_SAFE_UNLOCK_
2020-03-04 16:36:09 +00:00
event_dispatch_function ( touch_event ) ;
2023-04-24 10:46:01 +00:00
_THREAD_SAFE_LOCK_
2017-05-20 15:38:03 +00:00
}
}
Ref < InputEventMouseMotion > mm = p_event ;
if ( mm . is_valid ( ) ) {
2022-01-13 09:23:53 +00:00
Point2 position = mm - > get_global_position ( ) ;
if ( mouse_pos ! = position ) {
set_mouse_position ( position ) ;
2018-02-24 02:04:30 +00:00
}
2022-01-13 09:23:53 +00:00
Vector2 relative = mm - > get_relative ( ) ;
mouse_velocity_track . update ( relative ) ;
2018-02-24 02:04:30 +00:00
2023-01-07 23:55:54 +00:00
if ( event_dispatch_function & & emulate_touch_from_mouse & & ! p_is_emulated & & mm - > get_button_mask ( ) . has_flag ( MouseButtonMask : : LEFT ) ) {
2017-05-20 15:38:03 +00:00
Ref < InputEventScreenDrag > drag_event ;
2021-06-17 22:03:09 +00:00
drag_event . instantiate ( ) ;
2015-09-24 21:06:15 +00:00
2022-01-13 09:23:53 +00:00
drag_event - > set_position ( position ) ;
drag_event - > set_relative ( relative ) ;
2022-12-23 15:37:45 +00:00
drag_event - > set_tilt ( mm - > get_tilt ( ) ) ;
drag_event - > set_pen_inverted ( mm - > get_pen_inverted ( ) ) ;
drag_event - > set_pressure ( mm - > get_pressure ( ) ) ;
2022-01-13 09:23:53 +00:00
drag_event - > set_velocity ( get_last_mouse_velocity ( ) ) ;
2023-02-04 22:16:54 +00:00
drag_event - > set_device ( InputEvent : : DEVICE_ID_EMULATION ) ;
2017-05-20 15:38:03 +00:00
2023-04-24 10:46:01 +00:00
_THREAD_SAFE_UNLOCK_
2020-03-04 16:36:09 +00:00
event_dispatch_function ( drag_event ) ;
2023-04-24 10:46:01 +00:00
_THREAD_SAFE_LOCK_
2017-05-20 15:38:03 +00:00
}
}
2018-10-14 15:55:03 +00:00
Ref < InputEventScreenTouch > st = p_event ;
if ( st . is_valid ( ) ) {
if ( st - > is_pressed ( ) ) {
2021-12-29 13:22:22 +00:00
VelocityTrack & track = touch_velocity_track [ st - > get_index ( ) ] ;
2018-10-14 15:55:03 +00:00
track . reset ( ) ;
} else {
// Since a pointer index may not occur again (OSs may or may not reuse them),
// imperatively remove it from the map to keep no fossil entries in it
2021-12-29 13:22:22 +00:00
touch_velocity_track . erase ( st - > get_index ( ) ) ;
2018-10-14 15:55:03 +00:00
}
2018-02-24 02:04:30 +00:00
2018-10-14 15:55:03 +00:00
if ( emulate_mouse_from_touch ) {
2018-02-24 02:04:30 +00:00
bool translate = false ;
if ( st - > is_pressed ( ) ) {
if ( mouse_from_touch_index = = - 1 ) {
translate = true ;
mouse_from_touch_index = st - > get_index ( ) ;
}
} else {
if ( st - > get_index ( ) = = mouse_from_touch_index ) {
translate = true ;
mouse_from_touch_index = - 1 ;
}
}
if ( translate ) {
Ref < InputEventMouseButton > button_event ;
2021-06-17 22:03:09 +00:00
button_event . instantiate ( ) ;
2018-02-24 02:04:30 +00:00
2023-02-04 22:16:54 +00:00
button_event - > set_device ( InputEvent : : DEVICE_ID_EMULATION ) ;
2018-02-24 02:04:30 +00:00
button_event - > set_position ( st - > get_position ( ) ) ;
button_event - > set_global_position ( st - > get_position ( ) ) ;
button_event - > set_pressed ( st - > is_pressed ( ) ) ;
2023-05-04 00:57:13 +00:00
button_event - > set_canceled ( st - > is_canceled ( ) ) ;
2021-08-13 21:31:57 +00:00
button_event - > set_button_index ( MouseButton : : LEFT ) ;
2022-10-22 14:30:46 +00:00
button_event - > set_double_click ( st - > is_double_tap ( ) ) ;
2023-01-07 23:55:54 +00:00
BitField < MouseButtonMask > ev_bm = mouse_button_mask ;
2018-02-24 02:04:30 +00:00
if ( st - > is_pressed ( ) ) {
2023-01-07 23:55:54 +00:00
ev_bm . set_flag ( MouseButtonMask : : LEFT ) ;
2018-02-24 02:04:30 +00:00
} else {
2023-01-07 23:55:54 +00:00
ev_bm . clear_flag ( MouseButtonMask : : LEFT ) ;
2018-02-24 02:04:30 +00:00
}
2023-01-07 23:55:54 +00:00
button_event - > set_button_mask ( ev_bm ) ;
2018-02-24 02:04:30 +00:00
_parse_input_event_impl ( button_event , true ) ;
}
}
2018-10-14 15:55:03 +00:00
}
Ref < InputEventScreenDrag > sd = p_event ;
if ( sd . is_valid ( ) ) {
2021-12-29 13:22:22 +00:00
VelocityTrack & track = touch_velocity_track [ sd - > get_index ( ) ] ;
2018-10-14 15:55:03 +00:00
track . update ( sd - > get_relative ( ) ) ;
2021-12-29 13:22:22 +00:00
sd - > set_velocity ( track . velocity ) ;
2018-02-24 02:04:30 +00:00
2018-10-14 15:55:03 +00:00
if ( emulate_mouse_from_touch & & sd - > get_index ( ) = = mouse_from_touch_index ) {
2018-02-24 02:04:30 +00:00
Ref < InputEventMouseMotion > motion_event ;
2021-06-17 22:03:09 +00:00
motion_event . instantiate ( ) ;
2018-02-24 02:04:30 +00:00
2023-02-04 22:16:54 +00:00
motion_event - > set_device ( InputEvent : : DEVICE_ID_EMULATION ) ;
2022-12-23 15:37:45 +00:00
motion_event - > set_tilt ( sd - > get_tilt ( ) ) ;
motion_event - > set_pen_inverted ( sd - > get_pen_inverted ( ) ) ;
motion_event - > set_pressure ( sd - > get_pressure ( ) ) ;
2018-02-24 02:04:30 +00:00
motion_event - > set_position ( sd - > get_position ( ) ) ;
motion_event - > set_global_position ( sd - > get_position ( ) ) ;
motion_event - > set_relative ( sd - > get_relative ( ) ) ;
2021-12-29 13:22:22 +00:00
motion_event - > set_velocity ( sd - > get_velocity ( ) ) ;
2018-02-24 02:04:30 +00:00
motion_event - > set_button_mask ( mouse_button_mask ) ;
_parse_input_event_impl ( motion_event , true ) ;
}
}
2017-05-20 15:38:03 +00:00
Ref < InputEventJoypadButton > jb = p_event ;
if ( jb . is_valid ( ) ) {
2021-08-13 21:31:57 +00:00
JoyButton c = _combine_device ( jb - > get_button_index ( ) , jb - > get_device ( ) ) ;
2017-05-20 15:38:03 +00:00
2020-05-14 14:41:43 +00:00
if ( jb - > is_pressed ( ) ) {
2017-05-20 15:38:03 +00:00
joy_buttons_pressed . insert ( c ) ;
2020-05-14 14:41:43 +00:00
} else {
2017-05-20 15:38:03 +00:00
joy_buttons_pressed . erase ( c ) ;
2020-05-14 14:41:43 +00:00
}
2017-05-20 15:38:03 +00:00
}
2015-09-24 21:06:15 +00:00
2017-05-20 15:38:03 +00:00
Ref < InputEventJoypadMotion > jm = p_event ;
2015-09-24 21:06:15 +00:00
2017-05-20 15:38:03 +00:00
if ( jm . is_valid ( ) ) {
set_joy_axis ( jm - > get_device ( ) , jm - > get_axis ( ) , jm - > get_axis_value ( ) ) ;
2015-09-24 21:06:15 +00:00
}
2017-11-01 20:49:39 +00:00
Ref < InputEventGesture > ge = p_event ;
if ( ge . is_valid ( ) ) {
2020-03-04 16:36:09 +00:00
if ( event_dispatch_function ) {
2023-04-24 10:46:01 +00:00
_THREAD_SAFE_UNLOCK_
2020-03-04 16:36:09 +00:00
event_dispatch_function ( ge ) ;
2023-04-24 10:46:01 +00:00
_THREAD_SAFE_LOCK_
2017-11-01 20:49:39 +00:00
}
}
2022-05-08 08:09:19 +00:00
for ( const KeyValue < StringName , InputMap : : Action > & E : InputMap : : get_singleton ( ) - > get_action_map ( ) ) {
if ( InputMap : : get_singleton ( ) - > event_is_action ( p_event , E . key ) ) {
2023-05-14 07:53:36 +00:00
Action & action = action_state [ E . key ] ;
Allow checking for exact matches with Action events.
Added additional param to action related methods to test for exactness.
If "p_exact_match" is true, then the action will only be "matched" if the provided input event *exactly* matches with the action event.
Before:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* Is Action Pressed = True
Now:
You can still do the above, however you can optionally check that the input is exactly what the action event is:
* Action Event = KEY_S
* Input Event = KEY_CONTROL + KEY_S
* p_exact_match = True
* Is Action Pressed = False
* If the Input Event was only KEY_S, then the result would be true.
Usage:
```gdscript
Input.is_action_pressed(action_name: String, exact_match: bool)
Input.is_action_pressed("my_action", true)
InputMap.event_is_action(p_event, "my_action", true)
func _input(event: InputEvent):
event.is_action_pressed("my_action", false, true) # false = "allow_echo", true = "exact_match"
event.is_action("my_action", true)
```
2020-12-13 14:22:42 +00:00
// If not echo and action pressed state has changed
2022-05-08 08:09:19 +00:00
if ( ! p_event - > is_echo ( ) & & is_action_pressed ( E . key , false ) ! = p_event - > is_action_pressed ( E . key ) ) {
2023-05-14 07:53:36 +00:00
if ( p_event - > is_action_pressed ( E . key ) ) {
action . pressed = true ;
action . pressed_physics_frame = Engine : : get_singleton ( ) - > get_physics_frames ( ) ;
action . pressed_process_frame = Engine : : get_singleton ( ) - > get_process_frames ( ) ;
} else {
action . pressed = false ;
action . released_physics_frame = Engine : : get_singleton ( ) - > get_physics_frames ( ) ;
action . released_process_frame = Engine : : get_singleton ( ) - > get_process_frames ( ) ;
}
2020-10-24 09:22:35 +00:00
action . strength = 0.0f ;
action . raw_strength = 0.0f ;
2022-05-08 08:09:19 +00:00
action . exact = InputMap : : get_singleton ( ) - > event_is_action ( p_event , E . key , true ) ;
2016-09-01 21:58:52 +00:00
}
2023-05-14 07:53:36 +00:00
action . strength = p_event - > get_action_strength ( E . key ) ;
action . raw_strength = p_event - > get_action_raw_strength ( E . key ) ;
2016-09-01 21:58:52 +00:00
}
}
2020-05-14 14:41:43 +00:00
if ( event_dispatch_function ) {
2023-04-24 10:46:01 +00:00
_THREAD_SAFE_UNLOCK_
2020-03-04 16:36:09 +00:00
event_dispatch_function ( p_event ) ;
2023-04-24 10:46:01 +00:00
_THREAD_SAFE_LOCK_
2020-05-14 14:41:43 +00:00
}
2015-09-24 21:06:15 +00:00
}
2021-03-25 20:56:12 +00:00
void Input : : set_joy_axis ( int p_device , JoyAxis p_axis , float p_value ) {
2015-09-24 21:06:15 +00:00
_THREAD_SAFE_METHOD_
2021-08-13 21:31:57 +00:00
JoyAxis c = _combine_device ( p_axis , p_device ) ;
2015-12-18 05:12:53 +00:00
_joy_axis [ c ] = p_value ;
2015-09-24 21:06:15 +00:00
}
2020-04-28 13:19:37 +00:00
void Input : : start_joy_vibration ( int p_device , float p_weak_magnitude , float p_strong_magnitude , float p_duration ) {
2016-06-15 05:25:35 +00:00
_THREAD_SAFE_METHOD_
if ( p_weak_magnitude < 0.f | | p_weak_magnitude > 1.f | | p_strong_magnitude < 0.f | | p_strong_magnitude > 1.f ) {
return ;
}
VibrationInfo vibration ;
vibration . weak_magnitude = p_weak_magnitude ;
vibration . strong_magnitude = p_strong_magnitude ;
vibration . duration = p_duration ;
2016-06-15 12:40:57 +00:00
vibration . timestamp = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
2016-06-15 05:25:35 +00:00
joy_vibration [ p_device ] = vibration ;
}
2020-04-28 13:19:37 +00:00
void Input : : stop_joy_vibration ( int p_device ) {
2016-06-15 05:25:35 +00:00
_THREAD_SAFE_METHOD_
VibrationInfo vibration ;
vibration . weak_magnitude = 0 ;
vibration . strong_magnitude = 0 ;
vibration . duration = 0 ;
2016-06-20 07:56:41 +00:00
vibration . timestamp = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
2016-06-15 05:25:35 +00:00
joy_vibration [ p_device ] = vibration ;
}
2020-04-28 13:19:37 +00:00
void Input : : vibrate_handheld ( int p_duration_ms ) {
2019-08-17 15:27:29 +00:00
OS : : get_singleton ( ) - > vibrate_handheld ( p_duration_ms ) ;
}
2020-04-28 13:19:37 +00:00
void Input : : set_gravity ( const Vector3 & p_gravity ) {
2016-11-23 12:32:44 +00:00
_THREAD_SAFE_METHOD_
gravity = p_gravity ;
}
2020-04-28 13:19:37 +00:00
void Input : : set_accelerometer ( const Vector3 & p_accel ) {
2015-09-24 21:06:15 +00:00
_THREAD_SAFE_METHOD_
accelerometer = p_accel ;
}
2020-04-28 13:19:37 +00:00
void Input : : set_magnetometer ( const Vector3 & p_magnetometer ) {
2016-05-27 17:29:37 +00:00
_THREAD_SAFE_METHOD_
magnetometer = p_magnetometer ;
}
2020-04-28 13:19:37 +00:00
void Input : : set_gyroscope ( const Vector3 & p_gyroscope ) {
2016-07-15 07:31:34 +00:00
_THREAD_SAFE_METHOD_
gyroscope = p_gyroscope ;
}
2020-04-28 13:19:37 +00:00
void Input : : set_mouse_position ( const Point2 & p_posf ) {
2015-09-24 21:06:15 +00:00
mouse_pos = p_posf ;
}
2020-04-28 13:19:37 +00:00
Point2 Input : : get_mouse_position ( ) const {
2015-09-24 21:06:15 +00:00
return mouse_pos ;
}
2020-05-14 12:29:06 +00:00
2022-01-13 18:13:50 +00:00
Point2 Input : : get_last_mouse_velocity ( ) {
mouse_velocity_track . update ( Vector2 ( ) ) ;
2021-12-29 13:22:22 +00:00
return mouse_velocity_track . velocity ;
2015-09-24 21:06:15 +00:00
}
2023-01-07 23:55:54 +00:00
BitField < MouseButtonMask > Input : : get_mouse_button_mask ( ) const {
2018-02-21 16:30:55 +00:00
return mouse_button_mask ; // do not trust OS implementation, should remove it - OS::get_singleton()->get_mouse_button_state();
2015-09-24 21:06:15 +00:00
}
2022-03-27 09:17:36 +00:00
void Input : : warp_mouse ( const Vector2 & p_position ) {
warp_mouse_func ( p_position ) ;
2015-09-24 21:06:15 +00:00
}
2020-03-04 16:36:09 +00:00
2020-04-28 13:19:37 +00:00
Point2i Input : : warp_mouse_motion ( const Ref < InputEventMouseMotion > & p_motion , const Rect2 & p_rect ) {
2017-04-03 00:47:23 +00:00
// The relative distance reported for the next event after a warp is in the boundaries of the
2021-03-12 13:35:16 +00:00
// size of the rect on that axis, but it may be greater, in which case there's no problem as fmod()
2017-04-03 00:47:23 +00:00
// will warp it, but if the pointer has moved in the opposite direction between the pointer relocation
// and the subsequent event, the reported relative distance will be less than the size of the rect
// and thus fmod() will be disabled for handling the situation.
// And due to this mouse warping mechanism being stateless, we need to apply some heuristics to
// detect the warp: if the relative distance is greater than the half of the size of the relevant rect
// (checked per each axis), it will be considered as the consequence of a former pointer warp.
2021-10-15 23:22:57 +00:00
const Point2i rel_sign ( p_motion - > get_relative ( ) . x > = 0.0f ? 1 : - 1 , p_motion - > get_relative ( ) . y > = 0.0 ? 1 : - 1 ) ;
2017-04-03 00:47:23 +00:00
const Size2i warp_margin = p_rect . size * 0.5f ;
const Point2i rel_warped (
2021-10-15 23:22:57 +00:00
Math : : fmod ( p_motion - > get_relative ( ) . x + rel_sign . x * warp_margin . x , p_rect . size . x ) - rel_sign . x * warp_margin . x ,
Math : : fmod ( p_motion - > get_relative ( ) . y + rel_sign . y * warp_margin . y , p_rect . size . y ) - rel_sign . y * warp_margin . y ) ;
2017-04-03 00:47:23 +00:00
2017-06-03 22:25:13 +00:00
const Point2i pos_local = p_motion - > get_global_position ( ) - p_rect . position ;
2017-03-22 20:18:47 +00:00
const Point2i pos_warped ( Math : : fposmod ( pos_local . x , p_rect . size . x ) , Math : : fposmod ( pos_local . y , p_rect . size . y ) ) ;
if ( pos_warped ! = pos_local ) {
2022-03-27 09:17:36 +00:00
warp_mouse ( pos_warped + p_rect . position ) ;
2017-03-22 20:18:47 +00:00
}
2017-04-03 00:47:23 +00:00
2017-03-22 20:18:47 +00:00
return rel_warped ;
}
2020-04-28 13:19:37 +00:00
void Input : : action_press ( const StringName & p_action , float p_strength ) {
2023-05-14 07:53:36 +00:00
// Create or retrieve existing action.
Action & action = action_state [ p_action ] ;
2016-09-01 21:58:52 +00:00
2023-05-14 07:53:36 +00:00
action . pressed_physics_frame = Engine : : get_singleton ( ) - > get_physics_frames ( ) ;
action . pressed_process_frame = Engine : : get_singleton ( ) - > get_process_frames ( ) ;
2016-09-01 21:58:52 +00:00
action . pressed = true ;
2018-11-09 21:32:32 +00:00
action . strength = p_strength ;
2022-04-05 10:40:26 +00:00
action . raw_strength = p_strength ;
action . exact = true ;
2015-09-24 21:06:15 +00:00
}
2020-04-28 13:19:37 +00:00
void Input : : action_release ( const StringName & p_action ) {
2023-05-14 07:53:36 +00:00
// Create or retrieve existing action.
Action & action = action_state [ p_action ] ;
2016-09-01 21:58:52 +00:00
2023-05-14 07:53:36 +00:00
action . released_physics_frame = Engine : : get_singleton ( ) - > get_physics_frames ( ) ;
action . released_process_frame = Engine : : get_singleton ( ) - > get_process_frames ( ) ;
2016-09-10 02:57:21 +00:00
action . pressed = false ;
2023-05-14 07:53:36 +00:00
action . strength = 0.0f ;
action . raw_strength = 0.0f ;
2022-04-05 10:40:26 +00:00
action . exact = true ;
2015-09-24 21:06:15 +00:00
}
2020-04-28 13:19:37 +00:00
void Input : : set_emulate_touch_from_mouse ( bool p_emulate ) {
2018-02-24 02:04:30 +00:00
emulate_touch_from_mouse = p_emulate ;
}
2020-04-28 13:19:37 +00:00
bool Input : : is_emulating_touch_from_mouse ( ) const {
2018-02-24 02:04:30 +00:00
return emulate_touch_from_mouse ;
}
2021-03-12 13:35:16 +00:00
// Calling this whenever the game window is focused helps unsticking the "touch mouse"
2018-02-24 02:04:30 +00:00
// if the OS or its abstraction class hasn't properly reported that touch pointers raised
2020-04-28 13:19:37 +00:00
void Input : : ensure_touch_mouse_raised ( ) {
2023-05-12 02:06:03 +00:00
_THREAD_SAFE_METHOD_
2018-02-24 02:04:30 +00:00
if ( mouse_from_touch_index ! = - 1 ) {
mouse_from_touch_index = - 1 ;
Ref < InputEventMouseButton > button_event ;
2021-06-17 22:03:09 +00:00
button_event . instantiate ( ) ;
2018-02-24 02:04:30 +00:00
2023-02-04 22:16:54 +00:00
button_event - > set_device ( InputEvent : : DEVICE_ID_EMULATION ) ;
2018-02-24 02:04:30 +00:00
button_event - > set_position ( mouse_pos ) ;
button_event - > set_global_position ( mouse_pos ) ;
button_event - > set_pressed ( false ) ;
2021-08-13 21:31:57 +00:00
button_event - > set_button_index ( MouseButton : : LEFT ) ;
2023-01-07 23:55:54 +00:00
BitField < MouseButtonMask > ev_bm = mouse_button_mask ;
ev_bm . clear_flag ( MouseButtonMask : : LEFT ) ;
button_event - > set_button_mask ( ev_bm ) ;
2018-02-24 02:04:30 +00:00
_parse_input_event_impl ( button_event , true ) ;
}
}
2020-04-28 13:19:37 +00:00
void Input : : set_emulate_mouse_from_touch ( bool p_emulate ) {
2018-02-24 02:04:30 +00:00
emulate_mouse_from_touch = p_emulate ;
2015-09-24 21:06:15 +00:00
}
2020-04-28 13:19:37 +00:00
bool Input : : is_emulating_mouse_from_touch ( ) const {
2018-02-24 02:04:30 +00:00
return emulate_mouse_from_touch ;
2015-09-24 21:06:15 +00:00
}
2020-04-28 13:19:37 +00:00
Input : : CursorShape Input : : get_default_cursor_shape ( ) const {
2018-04-09 20:48:24 +00:00
return default_shape ;
}
2020-04-28 13:19:37 +00:00
void Input : : set_default_cursor_shape ( CursorShape p_shape ) {
2020-05-14 14:41:43 +00:00
if ( default_shape = = p_shape ) {
2019-07-29 16:23:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2019-07-29 16:23:30 +00:00
2018-04-09 20:48:24 +00:00
default_shape = p_shape ;
2018-09-15 18:54:22 +00:00
// The default shape is set in Viewport::_gui_input_event. To instantly
// see the shape in the viewport we need to trigger a mouse motion event.
Ref < InputEventMouseMotion > mm ;
2021-06-17 22:03:09 +00:00
mm . instantiate ( ) ;
2018-09-15 18:54:22 +00:00
mm - > set_position ( mouse_pos ) ;
mm - > set_global_position ( mouse_pos ) ;
2023-02-04 22:16:54 +00:00
mm - > set_device ( InputEvent : : DEVICE_ID_INTERNAL ) ;
2018-09-15 18:54:22 +00:00
parse_input_event ( mm ) ;
2018-04-09 20:48:24 +00:00
}
2020-04-28 13:19:37 +00:00
Input : : CursorShape Input : : get_current_cursor_shape ( ) const {
2020-03-03 13:36:29 +00:00
return get_current_cursor_shape_func ( ) ;
2019-04-15 15:30:20 +00:00
}
2022-05-02 23:43:50 +00:00
void Input : : set_custom_mouse_cursor ( const Ref < Resource > & p_cursor , CursorShape p_shape , const Vector2 & p_hotspot ) {
2020-05-14 14:41:43 +00:00
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2017-11-10 10:50:11 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2017-11-10 10:50:11 +00:00
2022-04-13 11:50:30 +00:00
ERR_FAIL_INDEX ( p_shape , CursorShape : : CURSOR_MAX ) ;
2020-03-03 13:36:29 +00:00
set_custom_mouse_cursor_func ( p_cursor , p_shape , p_hotspot ) ;
2015-09-24 21:06:15 +00:00
}
2021-08-12 22:31:16 +00:00
void Input : : parse_input_event ( const Ref < InputEvent > & p_event ) {
_THREAD_SAFE_METHOD_
2019-03-03 22:52:18 +00:00
ERR_FAIL_COND ( p_event . is_null ( ) ) ;
2023-01-19 12:18:34 +00:00
# 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
2023-02-01 07:45:41 +00:00
// of the possibilities of the target platform, project settings and engine internals, which may change
2023-01-19 12:18:34 +00:00
// 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
2021-08-12 22:38:41 +00:00
if ( use_accumulated_input ) {
if ( buffered_events . is_empty ( ) | | ! buffered_events . back ( ) - > get ( ) - > accumulate ( p_event ) ) {
buffered_events . push_back ( p_event ) ;
}
} else if ( use_input_buffering ) {
buffered_events . push_back ( p_event ) ;
} else {
2021-08-12 22:31:16 +00:00
_parse_input_event_impl ( p_event , false ) ;
2019-03-03 22:52:18 +00:00
}
}
2020-05-14 12:29:06 +00:00
2021-08-12 22:38:41 +00:00
void Input : : flush_buffered_events ( ) {
2021-08-12 22:31:16 +00:00
_THREAD_SAFE_METHOD_
2021-08-12 22:38:41 +00:00
while ( buffered_events . front ( ) ) {
2023-04-24 10:46:01 +00:00
// The final delivery of the input event involves releasing the lock.
// While the lock is released, another thread may lock it and add new events to the back.
// Therefore, we get each event and pop it while we still have the lock,
// to ensure the list is in a consistent state.
List < Ref < InputEvent > > : : Element * E = buffered_events . front ( ) ;
Ref < InputEvent > e = E - > get ( ) ;
2021-08-12 22:38:41 +00:00
buffered_events . pop_front ( ) ;
2023-04-24 10:46:01 +00:00
_parse_input_event_impl ( e , false ) ;
2019-03-03 22:52:18 +00:00
}
}
2021-08-12 22:38:41 +00:00
bool Input : : is_using_input_buffering ( ) {
return use_input_buffering ;
}
void Input : : set_use_input_buffering ( bool p_enable ) {
use_input_buffering = p_enable ;
}
2020-04-28 13:19:37 +00:00
void Input : : set_use_accumulated_input ( bool p_enable ) {
2019-03-03 22:52:18 +00:00
use_accumulated_input = p_enable ;
}
2022-06-03 03:21:25 +00:00
bool Input : : is_using_accumulated_input ( ) {
return use_accumulated_input ;
}
2020-04-28 13:19:37 +00:00
void Input : : release_pressed_events ( ) {
2021-08-12 22:38:41 +00:00
flush_buffered_events ( ) ; // this is needed to release actions strengths
2019-04-15 17:41:44 +00:00
keys_pressed . clear ( ) ;
2021-11-23 09:14:19 +00:00
physical_keys_pressed . clear ( ) ;
2022-12-10 23:21:22 +00:00
key_label_pressed . clear ( ) ;
2019-04-15 17:41:44 +00:00
joy_buttons_pressed . clear ( ) ;
_joy_axis . clear ( ) ;
2021-08-09 20:13:42 +00:00
for ( const KeyValue < StringName , Input : : Action > & E : action_state ) {
if ( E . value . pressed ) {
action_release ( E . key ) ;
2020-05-14 14:41:43 +00:00
}
2019-04-15 17:41:44 +00:00
}
}
2020-04-28 13:19:37 +00:00
void Input : : set_event_dispatch_function ( EventDispatchFunc p_function ) {
2020-03-04 16:36:09 +00:00
event_dispatch_function = p_function ;
}
2021-03-25 20:56:12 +00:00
void Input : : joy_button ( int p_device , JoyButton p_button , bool p_pressed ) {
2015-12-18 18:15:32 +00:00
_THREAD_SAFE_METHOD_ ;
2017-01-08 20:05:51 +00:00
Joypad & joy = joy_names [ p_device ] ;
2022-01-10 11:43:44 +00:00
ERR_FAIL_INDEX ( ( int ) p_button , ( int ) JoyButton : : MAX ) ;
2021-08-13 21:31:57 +00:00
if ( joy . last_buttons [ ( size_t ) p_button ] = = p_pressed ) {
2017-03-26 13:59:13 +00:00
return ;
2015-12-18 18:15:32 +00:00
}
2021-08-13 21:31:57 +00:00
joy . last_buttons [ ( size_t ) p_button ] = p_pressed ;
2015-12-18 18:15:32 +00:00
if ( joy . mapping = = - 1 ) {
2017-03-26 13:59:13 +00:00
_button_event ( p_device , p_button , p_pressed ) ;
return ;
2019-06-26 13:08:25 +00:00
}
2015-12-18 18:15:32 +00:00
2020-04-23 09:16:09 +00:00
JoyEvent map = _get_mapped_button_event ( map_db [ joy . mapping ] , p_button ) ;
2015-12-18 18:15:32 +00:00
if ( map . type = = TYPE_BUTTON ) {
2021-03-25 20:56:12 +00:00
_button_event ( p_device , ( JoyButton ) map . index , p_pressed ) ;
2017-03-26 13:59:13 +00:00
return ;
2019-06-26 13:08:25 +00:00
}
2015-12-18 18:15:32 +00:00
if ( map . type = = TYPE_AXIS ) {
2021-03-25 20:56:12 +00:00
_axis_event ( p_device , ( JoyAxis ) map . index , p_pressed ? map . value : 0.0 ) ;
2019-06-26 13:08:25 +00:00
}
// no event?
}
2015-12-18 05:12:53 +00:00
2021-12-25 09:29:08 +00:00
void Input : : joy_axis ( int p_device , JoyAxis p_axis , float p_value ) {
2015-12-18 18:15:32 +00:00
_THREAD_SAFE_METHOD_ ;
2015-12-18 05:12:53 +00:00
2021-08-13 21:31:57 +00:00
ERR_FAIL_INDEX ( ( int ) p_axis , ( int ) JoyAxis : : MAX ) ;
2017-06-22 14:52:11 +00:00
2017-01-08 20:05:51 +00:00
Joypad & joy = joy_names [ p_device ] ;
2015-12-18 05:12:53 +00:00
2021-12-25 09:29:08 +00:00
if ( joy . last_axis [ ( size_t ) p_axis ] = = p_value ) {
2017-03-26 13:59:13 +00:00
return ;
2015-12-18 18:15:32 +00:00
}
2015-12-18 05:12:53 +00:00
2021-12-25 09:29:08 +00:00
joy . last_axis [ ( size_t ) p_axis ] = p_value ;
2016-01-02 02:34:32 +00:00
2015-12-18 18:15:32 +00:00
if ( joy . mapping = = - 1 ) {
2021-12-25 09:29:08 +00:00
_axis_event ( p_device , p_axis , p_value ) ;
2017-04-10 21:41:47 +00:00
return ;
2020-05-19 13:46:49 +00:00
}
2015-12-18 05:12:53 +00:00
2021-12-25 09:29:08 +00:00
JoyEvent map = _get_mapped_axis_event ( map_db [ joy . mapping ] , p_axis , p_value ) ;
2015-12-18 05:12:53 +00:00
2015-12-18 18:15:32 +00:00
if ( map . type = = TYPE_BUTTON ) {
2020-10-14 16:06:29 +00:00
bool pressed = map . value > 0.5 ;
2022-08-17 08:12:47 +00:00
if ( pressed ! = joy_buttons_pressed . has ( _combine_device ( ( JoyButton ) map . index , p_device ) ) ) {
_button_event ( p_device , ( JoyButton ) map . index , pressed ) ;
2020-10-14 16:06:29 +00:00
}
2016-01-21 01:23:15 +00:00
2020-10-14 16:06:29 +00:00
// Ensure opposite D-Pad button is also released.
2021-08-13 21:31:57 +00:00
switch ( ( JoyButton ) map . index ) {
case JoyButton : : DPAD_UP :
if ( joy_buttons_pressed . has ( _combine_device ( JoyButton : : DPAD_DOWN , p_device ) ) ) {
_button_event ( p_device , JoyButton : : DPAD_DOWN , false ) ;
2016-01-21 01:23:15 +00:00
}
2020-10-14 16:06:29 +00:00
break ;
2021-08-13 21:31:57 +00:00
case JoyButton : : DPAD_DOWN :
if ( joy_buttons_pressed . has ( _combine_device ( JoyButton : : DPAD_UP , p_device ) ) ) {
_button_event ( p_device , JoyButton : : DPAD_UP , false ) ;
2016-01-21 01:23:15 +00:00
}
2020-10-14 16:06:29 +00:00
break ;
2021-08-13 21:31:57 +00:00
case JoyButton : : DPAD_LEFT :
if ( joy_buttons_pressed . has ( _combine_device ( JoyButton : : DPAD_RIGHT , p_device ) ) ) {
_button_event ( p_device , JoyButton : : DPAD_RIGHT , false ) ;
2016-01-21 01:23:15 +00:00
}
2020-10-14 16:06:29 +00:00
break ;
2021-08-13 21:31:57 +00:00
case JoyButton : : DPAD_RIGHT :
if ( joy_buttons_pressed . has ( _combine_device ( JoyButton : : DPAD_LEFT , p_device ) ) ) {
_button_event ( p_device , JoyButton : : DPAD_LEFT , false ) ;
2020-10-14 16:06:29 +00:00
}
break ;
default :
// Nothing to do.
break ;
2019-06-26 13:08:25 +00:00
}
2017-03-26 13:59:13 +00:00
return ;
2019-06-26 13:08:25 +00:00
}
2015-12-18 05:12:53 +00:00
2015-12-18 18:15:32 +00:00
if ( map . type = = TYPE_AXIS ) {
2021-12-25 09:29:08 +00:00
JoyAxis axis = JoyAxis ( map . index ) ;
float value = map . value ;
if ( axis = = JoyAxis : : TRIGGER_LEFT | | axis = = JoyAxis : : TRIGGER_RIGHT ) {
// Convert to a value between 0.0f and 1.0f.
value = 0.5f + value / 2.0f ;
}
_axis_event ( p_device , axis , value ) ;
2017-03-26 13:59:13 +00:00
return ;
2019-06-26 13:08:25 +00:00
}
}
2015-12-18 05:12:53 +00:00
2023-01-07 23:55:54 +00:00
void Input : : joy_hat ( int p_device , BitField < HatMask > p_val ) {
2015-12-18 18:15:32 +00:00
_THREAD_SAFE_METHOD_ ;
2017-01-08 20:05:51 +00:00
const Joypad & joy = joy_names [ p_device ] ;
2015-12-18 05:12:53 +00:00
2021-08-13 21:31:57 +00:00
JoyEvent map [ ( size_t ) HatDir : : MAX ] ;
2015-12-18 05:12:53 +00:00
2021-08-13 21:31:57 +00:00
map [ ( size_t ) HatDir : : UP ] . type = TYPE_BUTTON ;
map [ ( size_t ) HatDir : : UP ] . index = ( int ) JoyButton : : DPAD_UP ;
map [ ( size_t ) HatDir : : UP ] . value = 0 ;
2020-04-23 09:16:09 +00:00
2021-08-13 21:31:57 +00:00
map [ ( size_t ) HatDir : : RIGHT ] . type = TYPE_BUTTON ;
map [ ( size_t ) HatDir : : RIGHT ] . index = ( int ) JoyButton : : DPAD_RIGHT ;
map [ ( size_t ) HatDir : : RIGHT ] . value = 0 ;
2020-04-23 09:16:09 +00:00
2021-08-13 21:31:57 +00:00
map [ ( size_t ) HatDir : : DOWN ] . type = TYPE_BUTTON ;
map [ ( size_t ) HatDir : : DOWN ] . index = ( int ) JoyButton : : DPAD_DOWN ;
map [ ( size_t ) HatDir : : DOWN ] . value = 0 ;
2020-04-23 09:16:09 +00:00
2021-08-13 21:31:57 +00:00
map [ ( size_t ) HatDir : : LEFT ] . type = TYPE_BUTTON ;
map [ ( size_t ) HatDir : : LEFT ] . index = ( int ) JoyButton : : DPAD_LEFT ;
map [ ( size_t ) HatDir : : LEFT ] . value = 0 ;
2020-04-23 09:16:09 +00:00
if ( joy . mapping ! = - 1 ) {
2021-03-25 20:56:12 +00:00
_get_mapped_hat_events ( map_db [ joy . mapping ] , ( HatDir ) 0 , map ) ;
2020-05-19 13:46:49 +00:00
}
2015-12-18 05:12:53 +00:00
2015-12-18 18:15:32 +00:00
int cur_val = joy_names [ p_device ] . hat_current ;
2015-12-18 05:12:53 +00:00
2021-08-13 21:31:57 +00:00
for ( int hat_direction = 0 , hat_mask = 1 ; hat_direction < ( int ) HatDir : : MAX ; hat_direction + + , hat_mask < < = 1 ) {
if ( ( ( int ) p_val & hat_mask ) ! = ( cur_val & hat_mask ) ) {
2020-10-14 16:06:29 +00:00
if ( map [ hat_direction ] . type = = TYPE_BUTTON ) {
2021-08-13 21:31:57 +00:00
_button_event ( p_device , ( JoyButton ) map [ hat_direction ] . index , ( int ) p_val & hat_mask ) ;
2020-10-14 16:06:29 +00:00
}
if ( map [ hat_direction ] . type = = TYPE_AXIS ) {
2021-08-13 21:31:57 +00:00
_axis_event ( p_device , ( JoyAxis ) map [ hat_direction ] . index , ( ( int ) p_val & hat_mask ) ? map [ hat_direction ] . value : 0.0 ) ;
2020-10-14 16:06:29 +00:00
}
}
2019-06-26 13:08:25 +00:00
}
2015-12-18 05:12:53 +00:00
2021-08-13 21:31:57 +00:00
joy_names [ p_device ] . hat_current = ( int ) p_val ;
2019-06-26 13:08:25 +00:00
}
2015-12-18 05:12:53 +00:00
2021-03-25 20:56:12 +00:00
void Input : : _button_event ( int p_device , JoyButton p_index , bool p_pressed ) {
2017-05-20 15:38:03 +00:00
Ref < InputEventJoypadButton > ievent ;
2021-06-17 22:03:09 +00:00
ievent . instantiate ( ) ;
2017-05-20 15:38:03 +00:00
ievent - > set_device ( p_device ) ;
ievent - > set_button_index ( p_index ) ;
ievent - > set_pressed ( p_pressed ) ;
2015-12-18 05:12:53 +00:00
2015-12-18 18:15:32 +00:00
parse_input_event ( ievent ) ;
2019-06-26 13:08:25 +00:00
}
2015-12-18 05:12:53 +00:00
2021-03-25 20:56:12 +00:00
void Input : : _axis_event ( int p_device , JoyAxis p_axis , float p_value ) {
2017-05-20 15:38:03 +00:00
Ref < InputEventJoypadMotion > ievent ;
2021-06-17 22:03:09 +00:00
ievent . instantiate ( ) ;
2017-05-20 15:38:03 +00:00
ievent - > set_device ( p_device ) ;
ievent - > set_axis ( p_axis ) ;
ievent - > set_axis_value ( p_value ) ;
2015-12-18 05:12:53 +00:00
2015-12-18 18:15:32 +00:00
parse_input_event ( ievent ) ;
2020-05-19 13:46:49 +00:00
}
2015-12-18 05:12:53 +00:00
2021-03-25 20:56:12 +00:00
Input : : JoyEvent Input : : _get_mapped_button_event ( const JoyDeviceMapping & mapping , JoyButton p_button ) {
2020-04-23 09:16:09 +00:00
JoyEvent event ;
2015-12-18 05:12:53 +00:00
2020-04-23 09:16:09 +00:00
for ( int i = 0 ; i < mapping . bindings . size ( ) ; i + + ) {
2020-04-23 12:01:36 +00:00
const JoyBinding binding = mapping . bindings [ i ] ;
2020-04-23 09:16:09 +00:00
if ( binding . inputType = = TYPE_BUTTON & & binding . input . button = = p_button ) {
event . type = binding . outputType ;
switch ( binding . outputType ) {
case TYPE_BUTTON :
2021-08-13 21:31:57 +00:00
event . index = ( int ) binding . output . button ;
2020-05-04 14:42:38 +00:00
return event ;
2020-04-23 09:16:09 +00:00
case TYPE_AXIS :
2021-08-13 21:31:57 +00:00
event . index = ( int ) binding . output . axis . axis ;
2020-10-14 16:06:29 +00:00
switch ( binding . output . axis . range ) {
case POSITIVE_HALF_AXIS :
event . value = 1 ;
break ;
case NEGATIVE_HALF_AXIS :
event . value = - 1 ;
break ;
case FULL_AXIS :
// It doesn't make sense for a button to map to a full axis,
// but keeping as a default for a trigger with a positive half-axis.
event . value = 1 ;
break ;
}
2020-05-04 14:42:38 +00:00
return event ;
2020-04-23 09:16:09 +00:00
default :
ERR_PRINT_ONCE ( " Joypad button mapping error. " ) ;
}
}
}
return event ;
}
2015-12-18 05:12:53 +00:00
2021-03-25 20:56:12 +00:00
Input : : JoyEvent Input : : _get_mapped_axis_event ( const JoyDeviceMapping & mapping , JoyAxis p_axis , float p_value ) {
2020-04-23 09:16:09 +00:00
JoyEvent event ;
2015-12-18 05:12:53 +00:00
2020-04-23 09:16:09 +00:00
for ( int i = 0 ; i < mapping . bindings . size ( ) ; i + + ) {
2020-04-23 12:01:36 +00:00
const JoyBinding binding = mapping . bindings [ i ] ;
2020-04-23 09:16:09 +00:00
if ( binding . inputType = = TYPE_AXIS & & binding . input . axis . axis = = p_axis ) {
2020-10-14 16:06:29 +00:00
float value = p_value ;
2020-05-14 14:41:43 +00:00
if ( binding . input . axis . invert ) {
2020-05-04 14:42:38 +00:00
value = - value ;
2020-05-14 14:41:43 +00:00
}
2020-05-04 14:42:38 +00:00
if ( binding . input . axis . range = = FULL_AXIS | |
2022-08-17 08:12:47 +00:00
( binding . input . axis . range = = POSITIVE_HALF_AXIS & & value > = 0 ) | |
2020-05-04 14:42:38 +00:00
( binding . input . axis . range = = NEGATIVE_HALF_AXIS & & value < 0 ) ) {
event . type = binding . outputType ;
2020-10-14 16:06:29 +00:00
float shifted_positive_value = 0 ;
switch ( binding . input . axis . range ) {
case POSITIVE_HALF_AXIS :
shifted_positive_value = value ;
break ;
case NEGATIVE_HALF_AXIS :
shifted_positive_value = value + 1 ;
break ;
case FULL_AXIS :
shifted_positive_value = ( value + 1 ) / 2 ;
break ;
}
2020-05-04 14:42:38 +00:00
switch ( binding . outputType ) {
case TYPE_BUTTON :
2021-08-13 21:31:57 +00:00
event . index = ( int ) binding . output . button ;
2020-10-14 16:06:29 +00:00
switch ( binding . input . axis . range ) {
case POSITIVE_HALF_AXIS :
event . value = shifted_positive_value ;
break ;
case NEGATIVE_HALF_AXIS :
event . value = 1 - shifted_positive_value ;
break ;
case FULL_AXIS :
// It doesn't make sense for a full axis to map to a button,
// but keeping as a default for a trigger with a positive half-axis.
event . value = ( shifted_positive_value * 2 ) - 1 ;
break ;
}
2020-05-04 14:42:38 +00:00
return event ;
case TYPE_AXIS :
2021-08-13 21:31:57 +00:00
event . index = ( int ) binding . output . axis . axis ;
2020-05-04 14:42:38 +00:00
event . value = value ;
if ( binding . output . axis . range ! = binding . input . axis . range ) {
switch ( binding . output . axis . range ) {
case POSITIVE_HALF_AXIS :
event . value = shifted_positive_value ;
break ;
case NEGATIVE_HALF_AXIS :
event . value = shifted_positive_value - 1 ;
break ;
case FULL_AXIS :
event . value = ( shifted_positive_value * 2 ) - 1 ;
break ;
}
}
return event ;
default :
ERR_PRINT_ONCE ( " Joypad axis mapping error. " ) ;
}
2020-04-23 09:16:09 +00:00
}
}
}
return event ;
}
2015-12-18 05:12:53 +00:00
2022-08-04 11:06:17 +00:00
void Input : : _get_mapped_hat_events ( const JoyDeviceMapping & mapping , HatDir p_hat , JoyEvent r_events [ ( size_t ) HatDir : : MAX ] ) {
2020-04-23 09:16:09 +00:00
for ( int i = 0 ; i < mapping . bindings . size ( ) ; i + + ) {
2020-04-23 12:01:36 +00:00
const JoyBinding binding = mapping . bindings [ i ] ;
2020-04-23 09:16:09 +00:00
if ( binding . inputType = = TYPE_HAT & & binding . input . hat . hat = = p_hat ) {
2021-08-13 21:31:57 +00:00
HatDir hat_direction ;
2020-04-23 09:16:09 +00:00
switch ( binding . input . hat . hat_mask ) {
2021-08-13 21:31:57 +00:00
case HatMask : : UP :
hat_direction = HatDir : : UP ;
2020-04-23 09:16:09 +00:00
break ;
2021-08-13 21:31:57 +00:00
case HatMask : : RIGHT :
hat_direction = HatDir : : RIGHT ;
2020-04-23 09:16:09 +00:00
break ;
2021-08-13 21:31:57 +00:00
case HatMask : : DOWN :
hat_direction = HatDir : : DOWN ;
2020-04-23 09:16:09 +00:00
break ;
2021-08-13 21:31:57 +00:00
case HatMask : : LEFT :
hat_direction = HatDir : : LEFT ;
2020-04-23 09:16:09 +00:00
break ;
default :
ERR_PRINT_ONCE ( " Joypad button mapping error. " ) ;
continue ;
}
2021-08-13 21:31:57 +00:00
r_events [ ( size_t ) hat_direction ] . type = binding . outputType ;
2020-04-23 09:16:09 +00:00
switch ( binding . outputType ) {
case TYPE_BUTTON :
2021-08-13 21:31:57 +00:00
r_events [ ( size_t ) hat_direction ] . index = ( int ) binding . output . button ;
2020-04-23 09:16:09 +00:00
break ;
case TYPE_AXIS :
2021-08-13 21:31:57 +00:00
r_events [ ( size_t ) hat_direction ] . index = ( int ) binding . output . axis . axis ;
2020-10-14 16:06:29 +00:00
switch ( binding . output . axis . range ) {
case POSITIVE_HALF_AXIS :
2021-08-13 21:31:57 +00:00
r_events [ ( size_t ) hat_direction ] . value = 1 ;
2020-10-14 16:06:29 +00:00
break ;
case NEGATIVE_HALF_AXIS :
2021-08-13 21:31:57 +00:00
r_events [ ( size_t ) hat_direction ] . value = - 1 ;
2020-10-14 16:06:29 +00:00
break ;
case FULL_AXIS :
// It doesn't make sense for a hat direction to map to a full axis,
// but keeping as a default for a trigger with a positive half-axis.
2021-08-13 21:31:57 +00:00
r_events [ ( size_t ) hat_direction ] . value = 1 ;
2020-10-14 16:06:29 +00:00
break ;
}
2020-04-23 09:16:09 +00:00
break ;
default :
ERR_PRINT_ONCE ( " Joypad button mapping error. " ) ;
}
}
}
}
2020-05-13 22:27:34 +00:00
JoyButton Input : : _get_output_button ( String output ) {
2021-08-13 21:31:57 +00:00
for ( int i = 0 ; i < ( int ) JoyButton : : SDL_MAX ; i + + ) {
2020-05-14 14:41:43 +00:00
if ( output = = _joy_buttons [ i ] ) {
2020-05-13 22:27:34 +00:00
return JoyButton ( i ) ;
2020-05-14 14:41:43 +00:00
}
2020-04-23 09:16:09 +00:00
}
2021-08-13 21:31:57 +00:00
return JoyButton : : INVALID ;
2020-04-23 09:16:09 +00:00
}
2020-05-13 22:27:34 +00:00
JoyAxis Input : : _get_output_axis ( String output ) {
2021-08-13 21:31:57 +00:00
for ( int i = 0 ; i < ( int ) JoyAxis : : SDL_MAX ; i + + ) {
2020-05-14 14:41:43 +00:00
if ( output = = _joy_axes [ i ] ) {
2020-05-13 22:27:34 +00:00
return JoyAxis ( i ) ;
2020-05-14 14:41:43 +00:00
}
2020-04-23 09:16:09 +00:00
}
2021-08-13 21:31:57 +00:00
return JoyAxis : : INVALID ;
2020-04-23 09:16:09 +00:00
}
2020-04-28 13:19:37 +00:00
void Input : : parse_mapping ( String p_mapping ) {
2015-12-18 18:15:32 +00:00
_THREAD_SAFE_METHOD_ ;
JoyDeviceMapping mapping ;
2015-12-18 05:12:53 +00:00
2015-12-18 18:15:32 +00:00
Vector < String > entry = p_mapping . split ( " , " ) ;
2018-03-06 03:10:17 +00:00
if ( entry . size ( ) < 2 ) {
return ;
}
2015-12-18 18:15:32 +00:00
CharString uid ;
uid . resize ( 17 ) ;
2015-12-18 05:12:53 +00:00
2015-12-18 18:15:32 +00:00
mapping . uid = entry [ 0 ] ;
2016-02-29 15:48:19 +00:00
mapping . name = entry [ 1 ] ;
2015-12-18 05:12:53 +00:00
2015-12-18 18:15:32 +00:00
int idx = 1 ;
while ( + + idx < entry . size ( ) ) {
2021-12-09 09:42:46 +00:00
if ( entry [ idx ] . is_empty ( ) ) {
2015-12-18 18:15:32 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2015-12-18 05:12:53 +00:00
2020-04-23 09:16:09 +00:00
String output = entry [ idx ] . get_slice ( " : " , 0 ) . replace ( " " , " " ) ;
String input = entry [ idx ] . get_slice ( " : " , 1 ) . replace ( " " , " " ) ;
2023-06-01 20:15:27 +00:00
if ( output . length ( ) < 1 | | input . length ( ) < 2 ) {
continue ;
}
2015-12-18 05:12:53 +00:00
2020-12-03 09:06:40 +00:00
if ( output = = " platform " | | output = = " hint " ) {
2015-12-18 18:15:32 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2015-12-18 05:12:53 +00:00
2020-04-23 09:16:09 +00:00
JoyAxisRange output_range = FULL_AXIS ;
if ( output [ 0 ] = = ' + ' | | output [ 0 ] = = ' - ' ) {
2021-11-03 18:29:30 +00:00
ERR_CONTINUE_MSG ( output . length ( ) < 2 ,
vformat ( " Invalid output entry \" %s \" in mapping: \n %s " , entry [ idx ] , p_mapping ) ) ;
2020-05-14 14:41:43 +00:00
if ( output [ 0 ] = = ' + ' ) {
2020-04-23 09:16:09 +00:00
output_range = POSITIVE_HALF_AXIS ;
2020-05-14 14:41:43 +00:00
} else if ( output [ 0 ] = = ' - ' ) {
2020-04-23 09:16:09 +00:00
output_range = NEGATIVE_HALF_AXIS ;
2020-05-14 14:41:43 +00:00
}
2020-02-13 15:42:49 +00:00
output = output . substr ( 1 ) ;
2020-04-23 09:16:09 +00:00
}
2015-12-18 05:12:53 +00:00
2020-04-23 09:16:09 +00:00
JoyAxisRange input_range = FULL_AXIS ;
if ( input [ 0 ] = = ' + ' ) {
input_range = POSITIVE_HALF_AXIS ;
2020-02-13 15:42:49 +00:00
input = input . substr ( 1 ) ;
2020-04-23 09:16:09 +00:00
} else if ( input [ 0 ] = = ' - ' ) {
input_range = NEGATIVE_HALF_AXIS ;
2020-02-13 15:42:49 +00:00
input = input . substr ( 1 ) ;
2020-04-23 09:16:09 +00:00
}
bool invert_axis = false ;
2020-05-14 14:41:43 +00:00
if ( input [ input . length ( ) - 1 ] = = ' ~ ' ) {
2020-04-23 09:16:09 +00:00
invert_axis = true ;
2020-10-14 16:06:29 +00:00
input = input . left ( input . length ( ) - 1 ) ;
2020-05-14 14:41:43 +00:00
}
2020-04-23 09:16:09 +00:00
2020-05-13 22:27:34 +00:00
JoyButton output_button = _get_output_button ( output ) ;
JoyAxis output_axis = _get_output_axis ( output ) ;
2023-02-13 14:08:38 +00:00
if ( output_button = = JoyButton : : INVALID & & output_axis = = JoyAxis : : INVALID ) {
print_verbose ( vformat ( " Unrecognized output string \" %s \" in mapping: \n %s " , output , p_mapping ) ) ;
}
2021-08-13 21:31:57 +00:00
ERR_CONTINUE_MSG ( output_button ! = JoyButton : : INVALID & & output_axis ! = JoyAxis : : INVALID ,
2021-11-03 18:29:30 +00:00
vformat ( " Output string \" %s \" matched both button and axis in mapping: \n %s " , output , p_mapping ) ) ;
2020-04-23 09:16:09 +00:00
2020-04-23 12:01:36 +00:00
JoyBinding binding ;
2021-08-13 21:31:57 +00:00
if ( output_button ! = JoyButton : : INVALID ) {
2020-04-23 09:16:09 +00:00
binding . outputType = TYPE_BUTTON ;
binding . output . button = output_button ;
2021-08-13 21:31:57 +00:00
} else if ( output_axis ! = JoyAxis : : INVALID ) {
2020-04-23 09:16:09 +00:00
binding . outputType = TYPE_AXIS ;
binding . output . axis . axis = output_axis ;
binding . output . axis . range = output_range ;
}
2015-12-18 05:12:53 +00:00
2020-04-23 09:16:09 +00:00
switch ( input [ 0 ] ) {
case ' b ' :
binding . inputType = TYPE_BUTTON ;
2021-03-25 20:56:12 +00:00
binding . input . button = ( JoyButton ) input . substr ( 1 ) . to_int ( ) ;
2020-04-23 09:16:09 +00:00
break ;
case ' a ' :
binding . inputType = TYPE_AXIS ;
2021-03-25 20:56:12 +00:00
binding . input . axis . axis = ( JoyAxis ) input . substr ( 1 ) . to_int ( ) ;
2020-04-23 09:16:09 +00:00
binding . input . axis . range = input_range ;
2020-05-04 14:42:38 +00:00
binding . input . axis . invert = invert_axis ;
2020-04-23 09:16:09 +00:00
break ;
case ' h ' :
ERR_CONTINUE_MSG ( input . length ( ) ! = 4 | | input [ 2 ] ! = ' . ' ,
2021-11-03 18:29:30 +00:00
vformat ( " Invalid had input \" %s \" in mapping: \n %s " , input , p_mapping ) ) ;
2020-04-23 09:16:09 +00:00
binding . inputType = TYPE_HAT ;
2021-03-25 20:56:12 +00:00
binding . input . hat . hat = ( HatDir ) input . substr ( 1 , 1 ) . to_int ( ) ;
2020-02-13 15:42:49 +00:00
binding . input . hat . hat_mask = static_cast < HatMask > ( input . substr ( 3 ) . to_int ( ) ) ;
2020-04-23 09:16:09 +00:00
break ;
default :
2021-11-03 18:29:30 +00:00
ERR_CONTINUE_MSG ( true , vformat ( " Unrecognized input string \" %s \" in mapping: \n %s " , input , p_mapping ) ) ;
2020-04-23 09:16:09 +00:00
}
2015-12-18 05:12:53 +00:00
2020-04-23 09:16:09 +00:00
mapping . bindings . push_back ( binding ) ;
2020-05-19 13:46:49 +00:00
}
2020-04-23 09:16:09 +00:00
2015-12-18 18:15:32 +00:00
map_db . push_back ( mapping ) ;
2020-05-19 13:46:49 +00:00
}
2016-01-07 23:40:41 +00:00
2020-04-28 13:19:37 +00:00
void Input : : add_joy_mapping ( String p_mapping , bool p_update_existing ) {
2016-01-07 23:40:41 +00:00
parse_mapping ( p_mapping ) ;
if ( p_update_existing ) {
Vector < String > entry = p_mapping . split ( " , " ) ;
String uid = entry [ 0 ] ;
2021-08-09 20:13:42 +00:00
for ( KeyValue < int , Joypad > & E : joy_names ) {
Joypad & joy = E . value ;
2021-04-17 19:29:04 +00:00
if ( joy . uid = = uid ) {
joy . mapping = map_db . size ( ) - 1 ;
2016-01-07 23:40:41 +00:00
}
}
}
}
2020-04-28 13:19:37 +00:00
void Input : : remove_joy_mapping ( String p_guid ) {
2016-01-07 23:40:41 +00:00
for ( int i = map_db . size ( ) - 1 ; i > = 0 ; i - - ) {
if ( p_guid = = map_db [ i ] . uid ) {
2021-07-03 22:17:03 +00:00
map_db . remove_at ( i ) ;
2016-01-07 23:40:41 +00:00
}
}
2021-08-09 20:13:42 +00:00
for ( KeyValue < int , Joypad > & E : joy_names ) {
Joypad & joy = E . value ;
2021-04-17 19:29:04 +00:00
if ( joy . uid = = p_guid ) {
joy . mapping = - 1 ;
2016-01-07 23:40:41 +00:00
}
}
}
2020-04-28 13:19:37 +00:00
void Input : : set_fallback_mapping ( String p_guid ) {
2016-01-27 11:18:34 +00:00
for ( int i = 0 ; i < map_db . size ( ) ; i + + ) {
if ( map_db [ i ] . uid = = p_guid ) {
fallback_mapping = i ;
return ;
}
}
}
2016-01-07 23:40:41 +00:00
//platforms that use the remapping system can override and call to these ones
2020-04-28 13:19:37 +00:00
bool Input : : is_joy_known ( int p_device ) {
2021-04-17 19:29:04 +00:00
if ( joy_names . has ( p_device ) ) {
int mapping = joy_names [ p_device ] . mapping ;
if ( mapping ! = - 1 & & mapping ! = fallback_mapping ) {
return true ;
}
}
return false ;
2016-01-07 23:40:41 +00:00
}
2020-04-28 13:19:37 +00:00
String Input : : get_joy_guid ( int p_device ) const {
2016-08-28 15:14:21 +00:00
ERR_FAIL_COND_V ( ! joy_names . has ( p_device ) , " " ) ;
2016-01-07 23:40:41 +00:00
return joy_names [ p_device ] . uid ;
}
2016-07-05 12:07:46 +00:00
2023-04-14 05:18:04 +00:00
bool Input : : should_ignore_device ( int p_vendor_id , int p_product_id ) const {
uint32_t full_id = ( ( ( uint32_t ) p_vendor_id ) < < 16 ) | ( ( uint16_t ) p_product_id ) ;
return ignored_device_ids . has ( full_id ) ;
}
2022-06-03 16:29:10 +00:00
TypedArray < int > Input : : get_connected_joypads ( ) {
TypedArray < int > ret ;
2022-05-13 13:04:37 +00:00
HashMap < int , Joypad > : : Iterator elem = joy_names . begin ( ) ;
2016-07-05 12:07:46 +00:00
while ( elem ) {
2022-05-13 13:04:37 +00:00
if ( elem - > value . connected ) {
ret . push_back ( elem - > key ) ;
2016-07-05 12:07:46 +00:00
}
2022-05-13 13:04:37 +00:00
+ + elem ;
2016-07-05 12:07:46 +00:00
}
return ret ;
}
2016-09-20 20:12:52 +00:00
2020-04-28 13:19:37 +00:00
int Input : : get_unused_joy_id ( ) {
2017-02-21 16:02:49 +00:00
for ( int i = 0 ; i < JOYPADS_MAX ; i + + ) {
if ( ! joy_names . has ( i ) | | ! joy_names [ i ] . connected ) {
return i ;
}
}
return - 1 ;
}
2020-04-28 13:19:37 +00:00
Input : : Input ( ) {
2020-03-01 22:14:37 +00:00
singleton = this ;
// Parse default mappings.
{
int i = 0 ;
while ( DefaultControllerMappings : : mappings [ i ] ) {
parse_mapping ( DefaultControllerMappings : : mappings [ i + + ] ) ;
}
}
// If defined, parse SDL_GAMECONTROLLERCONFIG for possible new mappings/overrides.
String env_mapping = OS : : get_singleton ( ) - > get_environment ( " SDL_GAMECONTROLLERCONFIG " ) ;
2021-12-09 09:42:46 +00:00
if ( ! env_mapping . is_empty ( ) ) {
2020-03-01 22:14:37 +00:00
Vector < String > entries = env_mapping . split ( " \n " ) ;
for ( int i = 0 ; i < entries . size ( ) ; i + + ) {
2021-12-09 09:42:46 +00:00
if ( entries [ i ] . is_empty ( ) ) {
2020-03-01 22:14:37 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2020-03-01 22:14:37 +00:00
parse_mapping ( entries [ i ] ) ;
}
}
2023-05-14 07:53:36 +00:00
2023-04-14 05:18:04 +00:00
String env_ignore_devices = OS : : get_singleton ( ) - > get_environment ( " SDL_GAMECONTROLLER_IGNORE_DEVICES " ) ;
if ( ! env_ignore_devices . is_empty ( ) ) {
Vector < String > entries = env_ignore_devices . split ( " , " ) ;
for ( int i = 0 ; i < entries . size ( ) ; i + + ) {
Vector < String > vid_pid = entries [ i ] . split ( " / " ) ;
if ( vid_pid . size ( ) < 2 ) {
continue ;
}
print_verbose ( vformat ( " Device Ignored -- Vendor: %s Product: %s " , vid_pid [ 0 ] , vid_pid [ 1 ] ) ) ;
const uint16_t vid_unswapped = vid_pid [ 0 ] . hex_to_int ( ) ;
const uint16_t pid_unswapped = vid_pid [ 1 ] . hex_to_int ( ) ;
const uint16_t vid = BSWAP16 ( vid_unswapped ) ;
const uint16_t pid = BSWAP16 ( pid_unswapped ) ;
uint32_t full_id = ( ( ( uint32_t ) vid ) < < 16 ) | ( ( uint16_t ) pid ) ;
ignored_device_ids . insert ( full_id ) ;
}
}
2023-05-14 07:53:36 +00:00
legacy_just_pressed_behavior = GLOBAL_DEF ( " input_devices/compatibility/legacy_just_pressed_behavior " , false ) ;
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2023-06-16 06:45:35 +00:00
// Always use standard behavior in the editor.
2023-05-14 07:53:36 +00:00
legacy_just_pressed_behavior = false ;
}
2020-03-01 22:14:37 +00:00
}
2022-04-22 16:39:12 +00:00
Input : : ~ Input ( ) {
singleton = nullptr ;
}
2020-03-01 22:14:37 +00:00
//////////////////////////////////////////////////////////