2023-01-10 14:26:54 +00:00
/**************************************************************************/
/* input_default.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
2015-09-24 21:06:15 +00:00
# include "input_default.h"
2017-08-27 19:07:15 +00:00
2018-09-11 16:13:45 +00:00
# include "core/input_map.h"
# include "core/os/os.h"
2023-05-13 18:44:06 +00:00
# include "core/project_settings.h"
2018-09-11 16:13:45 +00:00
# include "main/default_controller_mappings.h"
2016-07-03 17:36:12 +00:00
# include "scene/resources/texture.h"
2017-03-05 15:44:50 +00:00
# include "servers/visual_server.h"
2015-09-24 21:06:15 +00:00
2023-04-24 10:46:01 +00:00
# ifdef DEV_ENABLED
# include "core/os/thread.h"
# endif
2017-03-05 15:44:50 +00:00
void InputDefault : : SpeedTrack : : update ( const Vector2 & p_delta_p ) {
2015-09-24 21:06:15 +00:00
uint64_t tick = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
2017-03-05 15:44:50 +00:00
uint32_t tdiff = tick - last_tick ;
2015-09-24 21:06:15 +00:00
float delta_t = tdiff / 1000000.0 ;
2017-03-05 15:44:50 +00:00
last_tick = tick ;
2015-09-24 21:06:15 +00:00
2022-01-13 18:00:25 +00:00
if ( delta_t > max_ref_frame ) {
// First movement in a long time, reset and start again.
speed = Vector2 ( ) ;
accum = p_delta_p ;
accum_t = 0 ;
return ;
}
2017-03-05 15:44:50 +00:00
accum + = p_delta_p ;
accum_t + = delta_t ;
2015-09-24 21:06:15 +00:00
2022-01-13 18:00:25 +00:00
if ( accum_t < min_ref_frame ) {
// Not enough time has passed to calculate speed precisely.
return ;
2021-05-05 10:44:11 +00:00
}
2015-09-24 21:06:15 +00:00
2022-01-13 18:00:25 +00:00
speed = accum / accum_t ;
accum = Vector2 ( ) ;
accum_t = 0 ;
2015-09-24 21:06:15 +00:00
}
void InputDefault : : SpeedTrack : : reset ( ) {
last_tick = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
2017-03-05 15:44:50 +00:00
speed = Vector2 ( ) ;
2022-01-13 18:00:25 +00:00
accum = Vector2 ( ) ;
2017-03-05 15:44:50 +00:00
accum_t = 0 ;
2015-09-24 21:06:15 +00:00
}
InputDefault : : SpeedTrack : : SpeedTrack ( ) {
2017-03-05 15:44:50 +00:00
min_ref_frame = 0.1 ;
2022-01-13 18:00:25 +00:00
max_ref_frame = 3.0 ;
2017-03-05 15:44:50 +00:00
reset ( ) ;
2015-09-24 21:06:15 +00:00
}
2016-09-01 21:58:52 +00:00
bool InputDefault : : is_key_pressed ( int p_scancode ) const {
2015-09-24 21:06:15 +00:00
_THREAD_SAFE_METHOD_
return keys_pressed . has ( p_scancode ) ;
}
2021-11-23 09:14:19 +00:00
bool InputDefault : : is_physical_key_pressed ( int p_scancode ) const {
_THREAD_SAFE_METHOD_
return physical_keys_pressed . has ( p_scancode ) ;
}
2016-09-01 21:58:52 +00:00
bool InputDefault : : is_mouse_button_pressed ( int p_button ) const {
2015-09-24 21:06:15 +00:00
_THREAD_SAFE_METHOD_
2017-09-16 15:12:11 +00:00
return ( mouse_button_mask & ( 1 < < ( p_button - 1 ) ) ) ! = 0 ;
2015-09-24 21:06:15 +00:00
}
2017-03-05 15:44:50 +00:00
static int _combine_device ( int p_value , int p_device ) {
return p_value | ( p_device < < 20 ) ;
2015-09-24 21:06:15 +00:00
}
2017-03-05 15:44:50 +00:00
bool InputDefault : : is_joy_button_pressed ( int p_device , int p_button ) const {
2015-09-24 21:06:15 +00:00
_THREAD_SAFE_METHOD_
2017-03-05 15:44:50 +00:00
return joy_buttons_pressed . has ( _combine_device ( p_button , p_device ) ) ;
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)
```
Co-authored-by: Eric M <itsjusteza@gmail.com>
2020-12-13 14:22:42 +00:00
bool InputDefault : : 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)
```
Co-authored-by: Eric M <itsjusteza@gmail.com>
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)
```
Co-authored-by: Eric M <itsjusteza@gmail.com>
2020-12-13 14:22:42 +00:00
bool InputDefault : : 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 ) ) ;
2017-03-05 15:44:50 +00:00
const Map < StringName , Action > : : Element * E = action_state . find ( p_action ) ;
2021-05-05 10:44:11 +00:00
if ( ! E ) {
2016-09-01 21:58:52 +00:00
return false ;
2021-05-05 10:44:11 +00:00
}
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)
```
Co-authored-by: Eric M <itsjusteza@gmail.com>
2020-12-13 14:22:42 +00:00
if ( p_exact & & E - > get ( ) . exact = = false ) {
return false ;
}
2023-05-13 18:44:06 +00:00
// Backward compatibility for legacy behavior, only return true if currently pressed.
bool pressed_requirement = legacy_just_pressed_behavior ? E - > get ( ) . pressed : true ;
2017-09-30 14:19:07 +00:00
if ( Engine : : get_singleton ( ) - > is_in_physics_frame ( ) ) {
2023-05-13 18:44:06 +00:00
return pressed_requirement & & E - > get ( ) . pressed_physics_frame = = Engine : : get_singleton ( ) - > get_physics_frames ( ) ;
2016-09-01 21:58:52 +00:00
} else {
2023-05-13 18:44:06 +00:00
return pressed_requirement & & E - > get ( ) . pressed_idle_frame = = Engine : : get_singleton ( ) - > get_idle_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)
```
Co-authored-by: Eric M <itsjusteza@gmail.com>
2020-12-13 14:22:42 +00:00
bool InputDefault : : 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 ) ) ;
2017-03-05 15:44:50 +00:00
const Map < StringName , Action > : : Element * E = action_state . find ( p_action ) ;
2021-05-05 10:44:11 +00:00
if ( ! E ) {
2016-09-01 21:58:52 +00:00
return false ;
2021-05-05 10:44:11 +00:00
}
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)
```
Co-authored-by: Eric M <itsjusteza@gmail.com>
2020-12-13 14:22:42 +00:00
if ( p_exact & & E - > get ( ) . exact = = false ) {
return false ;
}
2023-05-13 18:44:06 +00:00
// Backward compatibility for legacy behavior, only return true if currently released.
bool released_requirement = legacy_just_pressed_behavior ? ! E - > get ( ) . pressed : true ;
2017-09-30 14:19:07 +00:00
if ( Engine : : get_singleton ( ) - > is_in_physics_frame ( ) ) {
2023-05-13 18:44:06 +00:00
return released_requirement & & E - > get ( ) . released_physics_frame = = Engine : : get_singleton ( ) - > get_physics_frames ( ) ;
2016-09-01 21:58:52 +00:00
} else {
2023-05-13 18:44:06 +00:00
return released_requirement & & E - > get ( ) . released_idle_frame = = Engine : : get_singleton ( ) - > get_idle_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)
```
Co-authored-by: Eric M <itsjusteza@gmail.com>
2020-12-13 14:22:42 +00:00
float InputDefault : : 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 ) ) ;
2018-02-21 21:06:34 +00:00
const Map < StringName , Action > : : Element * E = action_state . find ( p_action ) ;
2021-05-05 10:44:11 +00:00
if ( ! E ) {
2018-02-21 21:06:34 +00:00
return 0.0f ;
2021-05-05 10:44:11 +00:00
}
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)
```
Co-authored-by: Eric M <itsjusteza@gmail.com>
2020-12-13 14:22:42 +00:00
if ( p_exact & & E - > get ( ) . exact = = false ) {
return 0.0f ;
}
2018-02-21 21:06:34 +00:00
return E - > get ( ) . strength ;
}
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)
```
Co-authored-by: Eric M <itsjusteza@gmail.com>
2020-12-13 14:22:42 +00:00
float InputDefault : : get_action_raw_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 ) ) ;
2021-07-23 00:28:08 +00:00
const Map < StringName , Action > : : Element * E = action_state . find ( p_action ) ;
if ( ! E ) {
return 0.0f ;
}
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)
```
Co-authored-by: Eric M <itsjusteza@gmail.com>
2020-12-13 14:22:42 +00:00
if ( p_exact & & E - > get ( ) . exact = = false ) {
return 0.0f ;
}
2021-07-23 00:28:08 +00:00
return E - > get ( ) . raw_strength ;
}
2021-07-23 01:02:18 +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 (
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 ) ) ;
if ( p_deadzone < 0.0f ) {
// If the deadzone isn't specified, get it from the average of the actions.
2021-10-28 11:23:24 +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 ) ) ;
2021-07-23 01:02:18 +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 ) ;
}
return vector ;
}
2017-03-05 15:44:50 +00:00
float InputDefault : : get_joy_axis ( int p_device , int p_axis ) const {
2015-09-24 21:06:15 +00:00
_THREAD_SAFE_METHOD_
2017-03-05 15:44:50 +00:00
int 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 ;
}
}
String InputDefault : : get_joy_name ( int p_idx ) {
_THREAD_SAFE_METHOD_
2015-12-18 05:12:53 +00:00
return joy_names [ p_idx ] . name ;
2015-09-24 21:06:15 +00:00
} ;
2016-06-15 05:25:35 +00:00
Vector2 InputDefault : : get_joy_vibration_strength ( int p_device ) {
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 ) ;
}
}
uint64_t InputDefault : : get_joy_vibration_timestamp ( int p_device ) {
if ( joy_vibration . has ( p_device ) ) {
return joy_vibration [ p_device ] . timestamp ;
} else {
return 0 ;
}
}
float InputDefault : : get_joy_vibration_duration ( int p_device ) {
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 ) {
2017-03-05 15:44:50 +00:00
static const char * dict = " 0123456789abcdef " ;
2015-12-18 18:15:32 +00:00
char ret [ 3 ] ;
ret [ 2 ] = 0 ;
2015-12-18 05:12:53 +00:00
2017-03-05 15:44:50 +00:00
ret [ 0 ] = dict [ p_byte > > 4 ] ;
2015-12-18 18:15:32 +00:00
ret [ 1 ] = dict [ p_byte & 0xf ] ;
2015-12-18 05:12:53 +00:00
2015-12-18 18:15:32 +00:00
return ret ;
2015-12-18 05:12:53 +00:00
} ;
void InputDefault : : joy_connection_changed ( int p_idx , bool p_connected , String p_name , String p_guid ) {
_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 ;
if ( p_guid = = " " ) {
int uidlen = MIN ( p_name . length ( ) , 16 ) ;
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < uidlen ; i + + ) {
2015-12-18 18:15:32 +00:00
uidname = uidname + _hex_str ( p_name [ i ] ) ;
} ;
} ;
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 ;
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < map_db . size ( ) ; i + + ) {
2015-12-18 18:15:32 +00:00
if ( js . uid = = map_db [ i ] . uid ) {
mapping = i ;
2016-02-29 15:48:19 +00:00
js . name = map_db [ i ] . name ;
2015-12-18 18:15:32 +00:00
} ;
} ;
js . mapping = mapping ;
2017-03-05 15:44:50 +00:00
} else {
2016-07-05 12:07:46 +00:00
js . connected = false ;
2016-01-26 03:40:04 +00:00
for ( int i = 0 ; i < JOY_BUTTON_MAX ; i + + ) {
2021-05-05 10:44:11 +00:00
if ( i < JOY_AXIS_MAX ) {
2016-01-26 03:40:04 +00:00
set_joy_axis ( p_idx , i , 0.0f ) ;
2021-05-05 10:44:11 +00:00
}
2016-01-26 03:40:04 +00:00
int c = _combine_device ( i , p_idx ) ;
joy_buttons_pressed . erase ( c ) ;
} ;
2015-12-18 18:15:32 +00:00
} ;
joy_names [ p_idx ] = js ;
2023-08-08 20:51:33 +00:00
// Ensure this signal is emitted on the main thread, as some platforms (e.g. Linux) call this from a different thread.
call_deferred ( " emit_signal " , " joy_connection_changed " , p_idx , p_connected ) ;
}
2015-09-24 21:06:15 +00:00
2017-03-05 15:44:50 +00:00
Vector3 InputDefault : : get_gravity ( ) const {
2016-11-23 12:32:44 +00:00
_THREAD_SAFE_METHOD_
return gravity ;
}
2017-03-05 15:44:50 +00:00
Vector3 InputDefault : : get_accelerometer ( ) const {
2015-09-24 21:06:15 +00:00
_THREAD_SAFE_METHOD_
return accelerometer ;
}
2017-03-05 15:44:50 +00:00
Vector3 InputDefault : : get_magnetometer ( ) const {
2016-05-27 17:29:37 +00:00
_THREAD_SAFE_METHOD_
return magnetometer ;
}
2016-09-01 21:58:52 +00:00
Vector3 InputDefault : : get_gyroscope ( ) const {
2016-07-15 07:31:34 +00:00
_THREAD_SAFE_METHOD_
return gyroscope ;
}
2018-02-24 02:04:30 +00:00
void InputDefault : : _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 ;
2017-05-25 20:58:37 +00:00
if ( k . is_valid ( ) & & ! k - > is_echo ( ) & & k - > get_scancode ( ) ! = 0 ) {
2021-05-05 10:44:11 +00:00
if ( k - > is_pressed ( ) ) {
2017-05-20 15:38:03 +00:00
keys_pressed . insert ( k - > get_scancode ( ) ) ;
2021-05-05 10:44:11 +00:00
} else {
2017-05-20 15:38:03 +00:00
keys_pressed . erase ( k - > get_scancode ( ) ) ;
2021-05-05 10:44:11 +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_scancode ( ) ! = 0 ) {
if ( k - > is_pressed ( ) ) {
physical_keys_pressed . insert ( k - > get_physical_scancode ( ) ) ;
} else {
physical_keys_pressed . erase ( k - > get_physical_scancode ( ) ) ;
}
}
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 ( ) ) {
mouse_button_mask | = ( 1 < < ( mb - > get_button_index ( ) - 1 ) ) ;
} else {
mouse_button_mask & = ~ ( 1 < < ( mb - > get_button_index ( ) - 1 ) ) ;
}
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 ) ;
}
if ( main_loop & & emulate_touch_from_mouse & & ! p_is_emulated & & mb - > get_button_index ( ) = = 1 ) {
2017-05-20 15:38:03 +00:00
Ref < InputEventScreenTouch > touch_event ;
touch_event . instance ( ) ;
touch_event - > set_pressed ( mb - > is_pressed ( ) ) ;
2023-05-03 21:23:34 +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-19 03:03:31 +00:00
touch_event - > set_double_tap ( mb - > is_doubleclick ( ) ) ;
2023-04-24 10:46:01 +00:00
_THREAD_SAFE_UNLOCK_
2017-05-20 15:38:03 +00:00
main_loop - > input_event ( 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:17:10 +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:17:10 +00:00
Vector2 relative = mm - > get_relative ( ) ;
mouse_speed_track . update ( relative ) ;
2018-02-24 02:04:30 +00:00
if ( main_loop & & emulate_touch_from_mouse & & ! p_is_emulated & & mm - > get_button_mask ( ) & 1 ) {
2017-05-20 15:38:03 +00:00
Ref < InputEventScreenDrag > drag_event ;
drag_event . instance ( ) ;
2015-09-24 21:06:15 +00:00
2022-01-13 09:17:10 +00:00
drag_event - > set_position ( position ) ;
drag_event - > set_relative ( relative ) ;
drag_event - > set_speed ( get_last_mouse_speed ( ) ) ;
2017-05-20 15:38:03 +00:00
2023-04-24 10:46:01 +00:00
_THREAD_SAFE_UNLOCK_
2017-05-20 15:38:03 +00:00
main_loop - > input_event ( 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 ( ) ) {
SpeedTrack & track = touch_speed_track [ st - > get_index ( ) ] ;
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
touch_speed_track . erase ( st - > get_index ( ) ) ;
}
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 ;
button_event . instance ( ) ;
2019-03-09 21:03:27 +00:00
button_event - > set_device ( InputEvent : : DEVICE_ID_TOUCH_MOUSE ) ;
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-03 21:23:34 +00:00
button_event - > set_canceled ( st - > is_canceled ( ) ) ;
2018-02-24 02:04:30 +00:00
button_event - > set_button_index ( BUTTON_LEFT ) ;
2022-10-19 03:03:31 +00:00
button_event - > set_doubleclick ( st - > is_double_tap ( ) ) ;
2018-02-24 02:04:30 +00:00
if ( st - > is_pressed ( ) ) {
2018-09-26 14:35:32 +00:00
button_event - > set_button_mask ( mouse_button_mask | ( 1 < < ( BUTTON_LEFT - 1 ) ) ) ;
2018-02-24 02:04:30 +00:00
} else {
2018-09-26 14:35:32 +00:00
button_event - > set_button_mask ( mouse_button_mask & ~ ( 1 < < ( BUTTON_LEFT - 1 ) ) ) ;
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 ( ) ) {
SpeedTrack & track = touch_speed_track [ sd - > get_index ( ) ] ;
track . update ( sd - > get_relative ( ) ) ;
sd - > set_speed ( track . speed ) ;
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 ;
motion_event . instance ( ) ;
2019-03-09 21:03:27 +00:00
motion_event - > set_device ( InputEvent : : DEVICE_ID_TOUCH_MOUSE ) ;
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 ( ) ) ;
motion_event - > set_speed ( sd - > get_speed ( ) ) ;
motion_event - > set_button_mask ( mouse_button_mask ) ;
2021-03-30 16:42:28 +00:00
motion_event - > set_pressure ( 1.f ) ;
2018-02-24 02:04:30 +00:00
_parse_input_event_impl ( motion_event , true ) ;
}
}
2017-05-20 15:38:03 +00:00
Ref < InputEventJoypadButton > jb = p_event ;
if ( jb . is_valid ( ) ) {
int c = _combine_device ( jb - > get_button_index ( ) , jb - > get_device ( ) ) ;
2021-05-05 10:44:11 +00:00
if ( jb - > is_pressed ( ) ) {
2017-05-20 15:38:03 +00:00
joy_buttons_pressed . insert ( c ) ;
2021-05-05 10:44:11 +00:00
} else {
2017-05-20 15:38:03 +00:00
joy_buttons_pressed . erase ( c ) ;
2021-05-05 10:44:11 +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 ( ) ) {
if ( main_loop ) {
2023-04-24 10:46:01 +00:00
_THREAD_SAFE_UNLOCK_
2017-11-01 20:49:39 +00:00
main_loop - > input_event ( ge ) ;
2023-04-24 10:46:01 +00:00
_THREAD_SAFE_LOCK_
2017-11-01 20:49:39 +00:00
}
}
2018-02-21 21:06:34 +00:00
for ( const Map < StringName , InputMap : : Action > : : Element * E = InputMap : : get_singleton ( ) - > get_action_map ( ) . front ( ) ; E ; E = E - > next ( ) ) {
if ( InputMap : : get_singleton ( ) - > event_is_action ( p_event , E - > key ( ) ) ) {
2023-05-13 18:44:06 +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)
```
Co-authored-by: Eric M <itsjusteza@gmail.com>
2020-12-13 14:22:42 +00:00
// If not echo and action pressed state has changed
if ( ! p_event - > is_echo ( ) & & is_action_pressed ( E - > key ( ) , false ) ! = p_event - > is_action_pressed ( E - > key ( ) ) ) {
2024-06-09 14:34:23 +00:00
// As input may come in part way through a physics tick, the earliest we can react to it is the next physics tick.
2023-05-13 18:44:06 +00:00
if ( p_event - > is_action_pressed ( E - > key ( ) ) ) {
action . pressed = true ;
2024-06-09 14:34:23 +00:00
action . pressed_physics_frame = Engine : : get_singleton ( ) - > get_physics_frames ( ) + 1 ;
2023-05-13 18:44:06 +00:00
action . pressed_idle_frame = Engine : : get_singleton ( ) - > get_idle_frames ( ) ;
} else {
action . pressed = false ;
2024-06-09 14:34:23 +00:00
action . released_physics_frame = Engine : : get_singleton ( ) - > get_physics_frames ( ) + 1 ;
2023-05-13 18:44:06 +00:00
action . released_idle_frame = Engine : : get_singleton ( ) - > get_idle_frames ( ) ;
}
2021-07-23 00:28:08 +00:00
action . strength = 0.0f ;
action . raw_strength = 0.0f ;
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)
```
Co-authored-by: Eric M <itsjusteza@gmail.com>
2020-12-13 14:22:42 +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-13 18:44:06 +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
}
}
2021-05-05 10:44:11 +00:00
if ( main_loop ) {
2023-04-24 10:46:01 +00:00
_THREAD_SAFE_UNLOCK_
2015-09-24 21:06:15 +00:00
main_loop - > input_event ( p_event ) ;
2023-04-24 10:46:01 +00:00
_THREAD_SAFE_LOCK_
2021-05-05 10:44:11 +00:00
}
2015-09-24 21:06:15 +00:00
}
2017-03-05 15:44:50 +00:00
void InputDefault : : set_joy_axis ( int p_device , int p_axis , float p_value ) {
2015-09-24 21:06:15 +00:00
_THREAD_SAFE_METHOD_
2017-03-05 15:44:50 +00:00
int c = _combine_device ( p_axis , p_device ) ;
_joy_axis [ c ] = p_value ;
2015-09-24 21:06:15 +00:00
}
2016-06-15 05:25:35 +00:00
void InputDefault : : start_joy_vibration ( int p_device , float p_weak_magnitude , float p_strong_magnitude , float p_duration ) {
_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 ;
}
void InputDefault : : stop_joy_vibration ( int p_device ) {
_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 ;
}
2019-08-17 15:27:29 +00:00
void InputDefault : : vibrate_handheld ( int p_duration_ms ) {
OS : : get_singleton ( ) - > vibrate_handheld ( p_duration_ms ) ;
}
2017-03-05 15:44:50 +00:00
void InputDefault : : set_gravity ( const Vector3 & p_gravity ) {
2016-11-23 12:32:44 +00:00
_THREAD_SAFE_METHOD_
2017-03-05 15:44:50 +00:00
gravity = p_gravity ;
2016-11-23 12:32:44 +00:00
}
2017-03-05 15:44:50 +00:00
void InputDefault : : set_accelerometer ( const Vector3 & p_accel ) {
2015-09-24 21:06:15 +00:00
_THREAD_SAFE_METHOD_
2017-03-05 15:44:50 +00:00
accelerometer = p_accel ;
2015-09-24 21:06:15 +00:00
}
2017-03-05 15:44:50 +00:00
void InputDefault : : set_magnetometer ( const Vector3 & p_magnetometer ) {
2016-05-27 17:29:37 +00:00
_THREAD_SAFE_METHOD_
2017-03-05 15:44:50 +00:00
magnetometer = p_magnetometer ;
2016-05-27 17:29:37 +00:00
}
2017-03-05 15:44:50 +00:00
void InputDefault : : set_gyroscope ( const Vector3 & p_gyroscope ) {
2016-07-15 07:31:34 +00:00
_THREAD_SAFE_METHOD_
2017-03-05 15:44:50 +00:00
gyroscope = p_gyroscope ;
2016-07-15 07:31:34 +00:00
}
2015-09-24 21:06:15 +00:00
void InputDefault : : set_main_loop ( MainLoop * p_main_loop ) {
2017-03-05 15:44:50 +00:00
main_loop = p_main_loop ;
2015-09-24 21:06:15 +00:00
}
2017-03-29 15:29:38 +00:00
void InputDefault : : set_mouse_position ( const Point2 & p_posf ) {
2017-03-05 15:44:50 +00:00
mouse_pos = p_posf ;
2015-09-24 21:06:15 +00:00
}
2017-03-29 15:29:38 +00:00
Point2 InputDefault : : get_mouse_position ( ) const {
2015-09-24 21:06:15 +00:00
return mouse_pos ;
}
2022-01-13 18:00:25 +00:00
Point2 InputDefault : : get_last_mouse_speed ( ) {
mouse_speed_track . update ( Vector2 ( ) ) ;
2015-09-24 21:06:15 +00:00
return mouse_speed_track . speed ;
}
int InputDefault : : 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
}
2017-09-10 13:37:49 +00:00
void InputDefault : : warp_mouse_position ( const Vector2 & p_to ) {
OS : : get_singleton ( ) - > warp_mouse_position ( p_to ) ;
2015-09-24 21:06:15 +00:00
}
2017-05-20 15:38:03 +00:00
Point2i InputDefault : : 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
// size of the rect on that axis, but it may be greater, in which case there's not problem as fmod()
// 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.
2017-05-20 15:38:03 +00:00
const Point2i rel_sgn ( 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 (
2017-05-20 15:38:03 +00:00
Math : : fmod ( p_motion - > get_relative ( ) . x + rel_sgn . x * warp_margin . x , p_rect . size . x ) - rel_sgn . x * warp_margin . x ,
Math : : fmod ( p_motion - > get_relative ( ) . y + rel_sgn . y * warp_margin . y , p_rect . size . y ) - rel_sgn . 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 ) {
2017-09-10 13:37:49 +00:00
OS : : get_singleton ( ) - > warp_mouse_position ( 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 ;
}
2015-09-24 21:06:15 +00:00
void InputDefault : : iteration ( float p_step ) {
}
2018-11-09 21:32:32 +00:00
void InputDefault : : action_press ( const StringName & p_action , float p_strength ) {
2023-05-13 18:44:06 +00:00
// Create or retrieve existing action.
Action & action = action_state [ p_action ] ;
2016-09-01 21:58:52 +00:00
2024-07-16 06:11:27 +00:00
// As input may come in part way through a physics tick, the earliest we can react to it is the next physics tick.
action . pressed_physics_frame = Engine : : get_singleton ( ) - > get_physics_frames ( ) + 1 ;
2023-05-13 18:44:06 +00:00
action . pressed_idle_frame = Engine : : get_singleton ( ) - > get_idle_frames ( ) ;
2017-03-05 15:44:50 +00:00
action . pressed = true ;
2023-05-13 18:44:06 +00:00
action . exact = true ;
2018-11-09 21:32:32 +00:00
action . strength = p_strength ;
2022-09-27 01:25:10 +00:00
action . raw_strength = p_strength ;
2015-09-24 21:06:15 +00:00
}
2017-03-05 15:44:50 +00:00
void InputDefault : : action_release ( const StringName & p_action ) {
2023-05-13 18:44:06 +00:00
// Create or retrieve existing action.
Action & action = action_state [ p_action ] ;
2016-09-01 21:58:52 +00:00
2024-07-16 06:11:27 +00:00
// As input may come in part way through a physics tick, the earliest we can react to it is the next physics tick.
action . released_physics_frame = Engine : : get_singleton ( ) - > get_physics_frames ( ) + 1 ;
2023-05-13 18:44:06 +00:00
action . released_idle_frame = Engine : : get_singleton ( ) - > get_idle_frames ( ) ;
2017-03-05 15:44:50 +00:00
action . pressed = false ;
2022-09-27 01:25:10 +00:00
action . exact = true ;
2023-05-13 18:44:06 +00:00
action . strength = 0.0f ;
action . raw_strength = 0.0f ;
2015-09-24 21:06:15 +00:00
}
2018-02-24 02:04:30 +00:00
void InputDefault : : set_emulate_touch_from_mouse ( bool p_emulate ) {
emulate_touch_from_mouse = p_emulate ;
}
bool InputDefault : : is_emulating_touch_from_mouse ( ) const {
return emulate_touch_from_mouse ;
}
// Calling this whenever the game window is focused helps unstucking the "touch mouse"
// if the OS or its abstraction class hasn't properly reported that touch pointers raised
void InputDefault : : ensure_touch_mouse_raised ( ) {
2023-05-12 02:10:31 +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 ;
button_event . instance ( ) ;
2019-03-09 21:03:27 +00:00
button_event - > set_device ( InputEvent : : DEVICE_ID_TOUCH_MOUSE ) ;
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 ) ;
button_event - > set_button_index ( BUTTON_LEFT ) ;
2018-09-26 14:35:32 +00:00
button_event - > set_button_mask ( mouse_button_mask & ~ ( 1 < < ( BUTTON_LEFT - 1 ) ) ) ;
2018-02-24 02:04:30 +00:00
_parse_input_event_impl ( button_event , true ) ;
}
}
void InputDefault : : set_emulate_mouse_from_touch ( bool p_emulate ) {
emulate_mouse_from_touch = p_emulate ;
2015-09-24 21:06:15 +00:00
}
2018-02-24 02:04:30 +00:00
bool InputDefault : : is_emulating_mouse_from_touch ( ) const {
return emulate_mouse_from_touch ;
2015-09-24 21:06:15 +00:00
}
2019-04-15 15:30:20 +00:00
Input : : CursorShape InputDefault : : get_default_cursor_shape ( ) const {
2018-04-09 20:48:24 +00:00
return default_shape ;
}
void InputDefault : : set_default_cursor_shape ( CursorShape p_shape ) {
2021-05-05 10:44:11 +00:00
if ( default_shape = = p_shape ) {
2019-07-29 16:23:30 +00:00
return ;
2021-05-05 10:44:11 +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 ;
mm . instance ( ) ;
mm - > set_position ( mouse_pos ) ;
mm - > set_global_position ( mouse_pos ) ;
parse_input_event ( mm ) ;
2018-04-09 20:48:24 +00:00
}
2019-04-15 15:30:20 +00:00
Input : : CursorShape InputDefault : : get_current_cursor_shape ( ) const {
return ( Input : : CursorShape ) OS : : get_singleton ( ) - > get_cursor_shape ( ) ;
}
2017-11-10 10:50:11 +00:00
void InputDefault : : set_custom_mouse_cursor ( const RES & p_cursor , CursorShape p_shape , const Vector2 & p_hotspot ) {
2021-05-05 10:44:11 +00:00
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2017-11-10 10:50:11 +00:00
return ;
2021-05-05 10:44:11 +00:00
}
2017-11-10 10:50:11 +00:00
2022-04-13 11:50:30 +00:00
ERR_FAIL_INDEX ( p_shape , Input : : CURSOR_MAX ) ;
2017-11-10 10:50:11 +00:00
OS : : get_singleton ( ) - > set_custom_mouse_cursor ( p_cursor , ( OS : : CursorShape ) p_shape , p_hotspot ) ;
2015-09-24 21:06:15 +00:00
}
2020-09-20 19:30:34 +00:00
void InputDefault : : 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:22:58 +00:00
# ifdef DEBUG_ENABLED
uint64_t curr_frame = Engine : : get_singleton ( ) - > get_idle_frames ( ) ;
if ( curr_frame ! = last_parsed_frame ) {
frame_parsed_events . clear ( ) ;
last_parsed_frame = curr_frame ;
frame_parsed_events . insert ( p_event ) ;
} else if ( frame_parsed_events . has ( p_event ) ) {
// It would be technically safe to send the same event in cases such as:
// - After an explicit flush.
// - In platforms using buffering when agile flushing is enabled, after one of the mid-frame flushes.
// - If platform doesn't use buffering and event accumulation is disabled.
// - If platform doesn't use buffering and the event type is not accumulable.
// However, it wouldn't be reasonable to ask users to remember the full ruleset and be aware at all times
// of the possibilites of the target platform, project settings and engine internals, which may change
// without prior notice.
// Therefore, the guideline is, "don't send the same event object more than once per frame".
WARN_PRINT_ONCE (
" An input event object is being parsed more than once in the same frame, which is unsafe. \n "
" If you are generating events in a script, you have to instantiate a new event instead of sending the same one more than once, unless the original one was sent on an earlier frame. \n "
" You can call duplicate() on the event to get a new instance with identical values. " ) ;
} else {
frame_parsed_events . insert ( p_event ) ;
}
# endif
2021-08-03 16:59:20 +00:00
if ( use_accumulated_input ) {
if ( buffered_events . 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 {
2020-09-20 19:30:34 +00:00
_parse_input_event_impl ( p_event , false ) ;
2019-03-03 22:52:18 +00:00
}
}
2021-08-03 16:59:20 +00:00
void InputDefault : : flush_buffered_events ( ) {
2020-09-20 19:30:34 +00:00
_THREAD_SAFE_METHOD_
2021-08-03 16:59:20 +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-03 16:59:20 +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-03 16:59:20 +00:00
bool InputDefault : : is_using_input_buffering ( ) {
return use_input_buffering ;
}
void InputDefault : : set_use_input_buffering ( bool p_enable ) {
use_input_buffering = p_enable ;
}
2022-06-10 09:33:12 +00:00
bool InputDefault : : is_using_accumulated_input ( ) {
return use_accumulated_input ;
}
2019-03-03 22:52:18 +00:00
void InputDefault : : set_use_accumulated_input ( bool p_enable ) {
use_accumulated_input = p_enable ;
}
2019-04-15 17:41:44 +00:00
void InputDefault : : release_pressed_events ( ) {
2021-08-03 16:59:20 +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 ( ) ;
2019-04-15 17:41:44 +00:00
joy_buttons_pressed . clear ( ) ;
_joy_axis . clear ( ) ;
for ( Map < StringName , InputDefault : : Action > : : Element * E = action_state . front ( ) ; E ; E = E - > next ( ) ) {
2021-05-05 10:44:11 +00:00
if ( E - > get ( ) . pressed ) {
2019-07-24 10:44:08 +00:00
action_release ( E - > key ( ) ) ;
2021-05-05 10:44:11 +00:00
}
2019-04-15 17:41:44 +00:00
}
}
2015-09-24 21:06:15 +00:00
InputDefault : : InputDefault ( ) {
2021-08-03 16:59:20 +00:00
use_input_buffering = false ;
2022-07-07 20:45:32 +00:00
use_accumulated_input = true ;
2017-03-05 15:44:50 +00:00
mouse_button_mask = 0 ;
2018-02-24 02:04:30 +00:00
emulate_touch_from_mouse = false ;
emulate_mouse_from_touch = false ;
mouse_from_touch_index = - 1 ;
2021-05-04 14:00:45 +00:00
main_loop = nullptr ;
2018-09-26 09:22:59 +00:00
default_shape = CURSOR_ARROW ;
2015-12-18 05:12:53 +00:00
2016-01-24 17:01:37 +00:00
fallback_mapping = - 1 ;
2023-05-13 18:44:06 +00:00
legacy_just_pressed_behavior = GLOBAL_DEF ( " input_devices/compatibility/legacy_just_pressed_behavior " , false ) ;
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
// Always use standard behaviour in the editor.
legacy_just_pressed_behavior = false ;
}
2020-01-08 10:43:44 +00:00
// 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.
2015-12-18 05:12:53 +00:00
String env_mapping = OS : : get_singleton ( ) - > get_environment ( " SDL_GAMECONTROLLERCONFIG " ) ;
if ( env_mapping ! = " " ) {
Vector < String > entries = env_mapping . split ( " \n " ) ;
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < entries . size ( ) ; i + + ) {
2021-05-05 10:44:11 +00:00
if ( entries [ i ] = = " " ) {
2015-12-18 05:12:53 +00:00
continue ;
2021-05-05 10:44:11 +00:00
}
2015-12-18 05:12:53 +00:00
parse_mapping ( entries [ i ] ) ;
2019-06-26 13:08:25 +00:00
}
}
2023-07-20 12:56:48 +00:00
String env_ignore_devices = OS : : get_singleton ( ) - > get_environment ( " SDL_GAMECONTROLLER_IGNORE_DEVICES " ) ;
if ( ! env_ignore_devices . 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 ) ;
}
}
2015-09-24 21:06:15 +00:00
}
2015-12-18 05:12:53 +00:00
2017-03-26 13:59:13 +00:00
void InputDefault : : joy_button ( int p_device , int p_button , bool p_pressed ) {
2015-12-18 18:15:32 +00:00
_THREAD_SAFE_METHOD_ ;
2017-03-05 15:44:50 +00:00
Joypad & joy = joy_names [ p_device ] ;
2022-01-10 11:43:44 +00:00
ERR_FAIL_INDEX ( p_button , JOY_BUTTON_MAX ) ;
2015-12-18 18:15:32 +00:00
if ( joy . last_buttons [ p_button ] = = p_pressed ) {
2017-03-26 13:59:13 +00:00
return ;
2015-12-18 18:15:32 +00:00
}
joy . last_buttons [ p_button ] = p_pressed ;
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-05-13 15:37:59 +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 ) {
2016-01-02 02:34:32 +00:00
//fake additional axis event for triggers
if ( map . index = = JOY_L2 | | map . index = = JOY_R2 ) {
float value = p_pressed ? 1.0f : 0.0f ;
int axis = map . index = = JOY_L2 ? JOY_ANALOG_L2 : JOY_ANALOG_R2 ;
2017-03-26 13:59:13 +00:00
_axis_event ( p_device , axis , value ) ;
2016-01-02 02:34:32 +00:00
}
2017-03-26 13:59:13 +00:00
_button_event ( p_device , map . index , p_pressed ) ;
return ;
2019-06-26 13:08:25 +00:00
}
2015-12-18 18:15:32 +00:00
if ( map . type = = TYPE_AXIS ) {
2020-10-14 16:07:50 +00:00
_axis_event ( p_device , 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 08:45:37 +00:00
void InputDefault : : joy_axis ( int p_device , int p_axis , float p_value ) {
2015-12-18 18:15:32 +00:00
_THREAD_SAFE_METHOD_ ;
2015-12-18 05:12:53 +00:00
2017-06-22 14:52:11 +00:00
ERR_FAIL_INDEX ( p_axis , JOY_AXIS_MAX ) ;
2017-03-05 15:44:50 +00:00
Joypad & joy = joy_names [ p_device ] ;
2015-12-18 05:12:53 +00:00
2021-12-25 08:45:37 +00:00
if ( joy . last_axis [ 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 08:45:37 +00:00
joy . last_axis [ 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 08:45:37 +00:00
_axis_event ( p_device , p_axis , p_value ) ;
2017-04-10 21:41:47 +00:00
return ;
2015-12-18 18:15:32 +00:00
} ;
2015-12-18 05:12:53 +00:00
2021-12-25 08:45:37 +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 ) {
2021-12-25 08:45:37 +00:00
// Send axis event for triggers
2016-01-02 02:34:32 +00:00
if ( map . index = = JOY_L2 | | map . index = = JOY_R2 ) {
2021-12-25 08:45:37 +00:00
// Convert to a value between 0.0f and 1.0f.
float value = 0.5f + p_value / 2.0f ;
_axis_event ( p_device , map . index , value ) ;
2016-01-02 02:34:32 +00:00
}
2016-01-21 01:23:15 +00:00
2020-10-14 16:07:50 +00:00
bool pressed = map . value > 0.5 ;
2022-08-17 08:10:36 +00:00
if ( pressed ! = joy_buttons_pressed . has ( _combine_device ( map . index , p_device ) ) ) {
_button_event ( p_device , map . index , pressed ) ;
2020-10-14 16:07:50 +00:00
}
2016-01-21 01:23:15 +00:00
2020-10-14 16:07:50 +00:00
// Ensure opposite D-Pad button is also released.
switch ( map . index ) {
case JOY_DPAD_UP :
2016-01-21 01:23:15 +00:00
if ( joy_buttons_pressed . has ( _combine_device ( JOY_DPAD_DOWN , p_device ) ) ) {
2017-03-26 13:59:13 +00:00
_button_event ( p_device , JOY_DPAD_DOWN , false ) ;
2016-01-21 01:23:15 +00:00
}
2020-10-14 16:07:50 +00:00
break ;
case JOY_DPAD_DOWN :
if ( joy_buttons_pressed . has ( _combine_device ( JOY_DPAD_UP , p_device ) ) ) {
_button_event ( p_device , JOY_DPAD_UP , false ) ;
2016-01-21 01:23:15 +00:00
}
2020-10-14 16:07:50 +00:00
break ;
case JOY_DPAD_LEFT :
2016-01-21 01:23:15 +00:00
if ( joy_buttons_pressed . has ( _combine_device ( JOY_DPAD_RIGHT , p_device ) ) ) {
2017-03-26 13:59:13 +00:00
_button_event ( p_device , JOY_DPAD_RIGHT , false ) ;
2016-01-21 01:23:15 +00:00
}
2020-10-14 16:07:50 +00:00
break ;
case JOY_DPAD_RIGHT :
if ( joy_buttons_pressed . has ( _combine_device ( JOY_DPAD_LEFT , p_device ) ) ) {
_button_event ( p_device , JOY_DPAD_LEFT , false ) ;
}
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 08:45:37 +00:00
_axis_event ( p_device , map . index , p_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
2017-03-26 13:59:13 +00:00
void InputDefault : : joy_hat ( int p_device , int p_val ) {
2015-12-18 18:15:32 +00:00
_THREAD_SAFE_METHOD_ ;
2017-03-05 15:44:50 +00:00
const Joypad & joy = joy_names [ p_device ] ;
2015-12-18 05:12:53 +00:00
2020-05-13 15:37:59 +00:00
JoyEvent map [ HAT_MAX ] ;
2015-12-18 05:12:53 +00:00
2020-05-13 15:37:59 +00:00
map [ HAT_UP ] . type = TYPE_BUTTON ;
map [ HAT_UP ] . index = JOY_DPAD_UP ;
map [ HAT_UP ] . value = 0 ;
map [ HAT_RIGHT ] . type = TYPE_BUTTON ;
map [ HAT_RIGHT ] . index = JOY_DPAD_RIGHT ;
map [ HAT_RIGHT ] . value = 0 ;
map [ HAT_DOWN ] . type = TYPE_BUTTON ;
map [ HAT_DOWN ] . index = JOY_DPAD_DOWN ;
map [ HAT_DOWN ] . value = 0 ;
map [ HAT_LEFT ] . type = TYPE_BUTTON ;
map [ HAT_LEFT ] . index = JOY_DPAD_LEFT ;
map [ HAT_LEFT ] . value = 0 ;
if ( joy . mapping ! = - 1 ) {
_get_mapped_hat_events ( map_db [ joy . mapping ] , 0 , map ) ;
2015-12-18 18:15:32 +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
2020-10-14 16:07:50 +00:00
for ( int hat_direction = 0 , hat_mask = 1 ; hat_direction < HAT_MAX ; hat_direction + + , hat_mask < < = 1 ) {
if ( ( p_val & hat_mask ) ! = ( cur_val & hat_mask ) ) {
if ( map [ hat_direction ] . type = = TYPE_BUTTON ) {
_button_event ( p_device , map [ hat_direction ] . index , p_val & hat_mask ) ;
}
if ( map [ hat_direction ] . type = = TYPE_AXIS ) {
_axis_event ( p_device , map [ hat_direction ] . index , ( p_val & hat_mask ) ? map [ hat_direction ] . value : 0.0 ) ;
}
}
2019-06-26 13:08:25 +00:00
}
2015-12-18 05:12:53 +00:00
2015-12-18 18:15:32 +00:00
joy_names [ p_device ] . hat_current = p_val ;
2019-06-26 13:08:25 +00:00
}
2015-12-18 05:12:53 +00:00
2017-03-26 13:59:13 +00:00
void InputDefault : : _button_event ( int p_device , int p_index , bool p_pressed ) {
2017-05-20 15:38:03 +00:00
Ref < InputEventJoypadButton > ievent ;
ievent . instance ( ) ;
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
2017-03-26 13:59:13 +00:00
void InputDefault : : _axis_event ( int p_device , int p_axis , float p_value ) {
2017-05-20 15:38:03 +00:00
Ref < InputEventJoypadMotion > ievent ;
ievent . instance ( ) ;
ievent - > set_device ( p_device ) ;
ievent - > set_axis ( p_axis ) ;
ievent - > set_axis_value ( p_value ) ;
2015-12-18 05:12:53 +00:00
2017-03-05 15:44:50 +00:00
parse_input_event ( ievent ) ;
2015-12-18 05:12:53 +00:00
} ;
2020-05-13 15:37:59 +00:00
InputDefault : : JoyEvent InputDefault : : _get_mapped_button_event ( const JoyDeviceMapping & mapping , int p_button ) {
JoyEvent event ;
event . type = TYPE_MAX ;
for ( int i = 0 ; i < mapping . bindings . size ( ) ; i + + ) {
const JoyBinding binding = mapping . bindings [ i ] ;
if ( binding . inputType = = TYPE_BUTTON & & binding . input . button = = p_button ) {
event . type = binding . outputType ;
switch ( binding . outputType ) {
case TYPE_BUTTON :
event . index = binding . output . button ;
return event ;
case TYPE_AXIS :
event . index = binding . output . axis . axis ;
2020-10-14 16:07:50 +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-13 15:37:59 +00:00
return event ;
default :
ERR_PRINT_ONCE ( " Joypad button mapping error. " ) ;
}
}
}
return event ;
}
2015-12-18 05:12:53 +00:00
2020-10-14 16:07:50 +00:00
InputDefault : : JoyEvent InputDefault : : _get_mapped_axis_event ( const JoyDeviceMapping & mapping , int p_axis , float p_value ) {
2020-05-13 15:37:59 +00:00
JoyEvent event ;
event . type = TYPE_MAX ;
for ( int i = 0 ; i < mapping . bindings . size ( ) ; i + + ) {
const JoyBinding binding = mapping . bindings [ i ] ;
if ( binding . inputType = = TYPE_AXIS & & binding . input . axis . axis = = p_axis ) {
2020-10-14 16:07:50 +00:00
float value = p_value ;
2021-05-05 10:44:11 +00:00
if ( binding . input . axis . invert ) {
2020-05-13 15:37:59 +00:00
value = - value ;
2021-05-05 10:44:11 +00:00
}
2020-05-13 15:37:59 +00:00
if ( binding . input . axis . range = = FULL_AXIS | |
2022-08-17 08:10:36 +00:00
( binding . input . axis . range = = POSITIVE_HALF_AXIS & & value > = 0 ) | |
2020-05-13 15:37:59 +00:00
( binding . input . axis . range = = NEGATIVE_HALF_AXIS & & value < 0 ) ) {
event . type = binding . outputType ;
2020-10-14 16:07:50 +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-13 15:37:59 +00:00
switch ( binding . outputType ) {
case TYPE_BUTTON :
event . index = binding . output . button ;
2020-10-14 16:07:50 +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-13 15:37:59 +00:00
return event ;
case TYPE_AXIS :
event . index = binding . output . axis . axis ;
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. " ) ;
}
}
}
}
return event ;
}
2015-12-18 05:12:53 +00:00
2022-08-04 11:06:17 +00:00
void InputDefault : : _get_mapped_hat_events ( const JoyDeviceMapping & mapping , int p_hat , JoyEvent r_events [ ( size_t ) HAT_MAX ] ) {
2020-05-13 15:37:59 +00:00
for ( int i = 0 ; i < mapping . bindings . size ( ) ; i + + ) {
const JoyBinding binding = mapping . bindings [ i ] ;
if ( binding . inputType = = TYPE_HAT & & binding . input . hat . hat = = p_hat ) {
2020-10-14 16:07:50 +00:00
int hat_direction ;
2020-05-13 15:37:59 +00:00
switch ( binding . input . hat . hat_mask ) {
case HAT_MASK_UP :
2020-10-14 16:07:50 +00:00
hat_direction = HAT_UP ;
2020-05-13 15:37:59 +00:00
break ;
case HAT_MASK_RIGHT :
2020-10-14 16:07:50 +00:00
hat_direction = HAT_RIGHT ;
2020-05-13 15:37:59 +00:00
break ;
case HAT_MASK_DOWN :
2020-10-14 16:07:50 +00:00
hat_direction = HAT_DOWN ;
2020-05-13 15:37:59 +00:00
break ;
case HAT_MASK_LEFT :
2020-10-14 16:07:50 +00:00
hat_direction = HAT_LEFT ;
2020-05-13 15:37:59 +00:00
break ;
default :
ERR_PRINT_ONCE ( " Joypad button mapping error. " ) ;
continue ;
}
2015-12-18 05:12:53 +00:00
2020-10-14 16:07:50 +00:00
r_events [ hat_direction ] . type = binding . outputType ;
2020-05-13 15:37:59 +00:00
switch ( binding . outputType ) {
case TYPE_BUTTON :
2020-10-14 16:07:50 +00:00
r_events [ hat_direction ] . index = binding . output . button ;
2020-05-13 15:37:59 +00:00
break ;
case TYPE_AXIS :
2020-10-14 16:07:50 +00:00
r_events [ hat_direction ] . index = binding . output . axis . axis ;
switch ( binding . output . axis . range ) {
case POSITIVE_HALF_AXIS :
r_events [ hat_direction ] . value = 1 ;
break ;
case NEGATIVE_HALF_AXIS :
r_events [ hat_direction ] . value = - 1 ;
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.
r_events [ hat_direction ] . value = 1 ;
break ;
}
2020-05-13 15:37:59 +00:00
break ;
default :
ERR_PRINT_ONCE ( " Joypad button mapping error. " ) ;
}
}
}
}
2015-12-18 05:12:53 +00:00
2020-05-13 15:37:59 +00:00
// string names of the SDL buttons in the same order as input_event.h godot buttons
2021-02-07 16:12:51 +00:00
static const char * _joy_buttons [ ] = { " a " , " b " , " x " , " y " , " leftshoulder " , " rightshoulder " , " lefttrigger " , " righttrigger " , " leftstick " , " rightstick " , " back " , " start " , " dpup " , " dpdown " , " dpleft " , " dpright " , " guide " , " misc1 " , " paddle1 " , " paddle2 " , " paddle3 " , " paddle4 " , " touchpad " , nullptr } ;
2020-05-13 15:37:59 +00:00
static const char * _joy_axes [ ] = { " leftx " , " lefty " , " rightx " , " righty " , nullptr } ;
2015-12-18 05:12:53 +00:00
2020-05-13 15:37:59 +00:00
JoystickList InputDefault : : _get_output_button ( String output ) {
for ( int i = 0 ; _joy_buttons [ i ] ; i + + ) {
2021-05-05 10:44:11 +00:00
if ( output = = _joy_buttons [ i ] ) {
2020-05-13 15:37:59 +00:00
return JoystickList ( i ) ;
2021-05-05 10:44:11 +00:00
}
2020-05-13 15:37:59 +00:00
}
return JoystickList : : JOY_INVALID_OPTION ;
}
JoystickList InputDefault : : _get_output_axis ( String output ) {
for ( int i = 0 ; _joy_axes [ i ] ; i + + ) {
2021-05-05 10:44:11 +00:00
if ( output = = _joy_axes [ i ] ) {
2020-05-13 15:37:59 +00:00
return JoystickList ( i ) ;
2021-05-05 10:44:11 +00:00
}
2020-05-13 15:37:59 +00:00
}
return JoystickList : : JOY_INVALID_OPTION ;
}
2015-12-18 05:12:53 +00:00
void InputDefault : : 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-05-05 10:44:11 +00:00
if ( entry [ idx ] = = " " ) {
2015-12-18 18:15:32 +00:00
continue ;
2021-05-05 10:44:11 +00:00
}
2015-12-18 05:12:53 +00:00
2020-05-13 15:37:59 +00:00
String output = entry [ idx ] . get_slice ( " : " , 0 ) . replace ( " " , " " ) ;
String input = entry [ idx ] . get_slice ( " : " , 1 ) . replace ( " " , " " ) ;
ERR_CONTINUE_MSG ( output . length ( ) < 1 | | input . length ( ) < 2 ,
2021-11-03 18:29:30 +00:00
vformat ( " Invalid device mapping entry \" %s \" in mapping: \n %s " , entry [ idx ] , p_mapping ) ) ;
2015-12-18 05:12:53 +00:00
2021-05-05 10:44:11 +00:00
if ( output = = " platform " | | output = = " hint " ) {
2015-12-18 18:15:32 +00:00
continue ;
2021-05-05 10:44:11 +00:00
}
2015-12-18 05:12:53 +00:00
2020-05-13 15:37:59 +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 ) ) ;
2021-05-05 10:44:11 +00:00
if ( output [ 0 ] = = ' + ' ) {
2020-05-13 15:37:59 +00:00
output_range = POSITIVE_HALF_AXIS ;
2021-05-05 10:44:11 +00:00
} else if ( output [ 0 ] = = ' - ' ) {
2020-05-13 15:37:59 +00:00
output_range = NEGATIVE_HALF_AXIS ;
2021-05-05 10:44:11 +00:00
}
2020-10-14 16:07:50 +00:00
output = output . right ( 1 ) ;
2020-05-13 15:37:59 +00:00
}
2015-12-18 05:12:53 +00:00
2020-05-13 15:37:59 +00:00
JoyAxisRange input_range = FULL_AXIS ;
if ( input [ 0 ] = = ' + ' ) {
input_range = POSITIVE_HALF_AXIS ;
input = input . right ( 1 ) ;
} else if ( input [ 0 ] = = ' - ' ) {
input_range = NEGATIVE_HALF_AXIS ;
input = input . right ( 1 ) ;
}
bool invert_axis = false ;
2020-10-14 16:07:50 +00:00
if ( input [ input . length ( ) - 1 ] = = ' ~ ' ) {
2020-05-13 15:37:59 +00:00
invert_axis = true ;
2020-10-14 16:07:50 +00:00
input = input . left ( input . length ( ) - 1 ) ;
}
2020-05-13 15:37:59 +00:00
JoystickList output_button = _get_output_button ( output ) ;
JoystickList output_axis = _get_output_axis ( output ) ;
2023-02-13 14:08:38 +00:00
if ( output_button = = JOY_INVALID_OPTION & & output_axis = = JOY_INVALID_OPTION ) {
print_verbose ( vformat ( " Unrecognized output string \" %s \" in mapping: \n %s " , output , p_mapping ) ) ;
}
2020-05-13 15:37:59 +00:00
ERR_CONTINUE_MSG ( output_button ! = JOY_INVALID_OPTION & & output_axis ! = JOY_INVALID_OPTION ,
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-05-13 15:37:59 +00:00
JoyBinding binding ;
if ( output_button ! = JOY_INVALID_OPTION ) {
binding . outputType = TYPE_BUTTON ;
binding . output . button = output_button ;
} else if ( output_axis ! = JOY_INVALID_OPTION ) {
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-05-13 15:37:59 +00:00
switch ( input [ 0 ] ) {
case ' b ' :
binding . inputType = TYPE_BUTTON ;
binding . input . button = input . right ( 1 ) . to_int ( ) ;
break ;
case ' a ' :
binding . inputType = TYPE_AXIS ;
binding . input . axis . axis = input . right ( 1 ) . to_int ( ) ;
binding . input . axis . range = input_range ;
binding . input . axis . invert = invert_axis ;
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-05-13 15:37:59 +00:00
binding . inputType = TYPE_HAT ;
binding . input . hat . hat = input . substr ( 1 , 1 ) . to_int ( ) ;
binding . input . hat . hat_mask = static_cast < HatMask > ( input . right ( 3 ) . to_int ( ) ) ;
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-05-13 15:37:59 +00:00
}
2015-12-18 05:12:53 +00:00
2020-05-13 15:37:59 +00:00
mapping . bindings . push_back ( binding ) ;
2015-12-18 18:15:32 +00:00
} ;
2020-05-13 15:37:59 +00:00
2015-12-18 18:15:32 +00:00
map_db . push_back ( mapping ) ;
2015-12-18 05:12:53 +00:00
} ;
2016-01-07 23:40:41 +00:00
void InputDefault : : add_joy_mapping ( String p_mapping , bool p_update_existing ) {
parse_mapping ( p_mapping ) ;
if ( p_update_existing ) {
Vector < String > entry = p_mapping . split ( " , " ) ;
String uid = entry [ 0 ] ;
2021-04-17 19:25:18 +00:00
for ( Map < int , Joypad > : : Element * E = joy_names . front ( ) ; E ; E = E - > next ( ) ) {
Joypad & joy = E - > get ( ) ;
if ( joy . uid = = uid ) {
joy . mapping = map_db . size ( ) - 1 ;
2016-01-07 23:40:41 +00:00
}
}
}
}
void InputDefault : : remove_joy_mapping ( String p_guid ) {
2017-03-05 15:44:50 +00:00
for ( int i = map_db . size ( ) - 1 ; i > = 0 ; i - - ) {
2016-01-07 23:40:41 +00:00
if ( p_guid = = map_db [ i ] . uid ) {
map_db . remove ( i ) ;
}
}
2021-04-17 19:25:18 +00:00
for ( Map < int , Joypad > : : Element * E = joy_names . front ( ) ; E ; E = E - > next ( ) ) {
Joypad & joy = E - > get ( ) ;
if ( joy . uid = = p_guid ) {
joy . mapping = - 1 ;
2016-01-07 23:40:41 +00:00
}
}
}
2016-01-27 11:18:34 +00:00
void InputDefault : : set_fallback_mapping ( String p_guid ) {
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
//Defaults to simple implementation for platforms with a fixed gamepad layout, like consoles.
bool InputDefault : : is_joy_known ( int p_device ) {
return OS : : get_singleton ( ) - > is_joy_known ( p_device ) ;
}
String InputDefault : : get_joy_guid ( int p_device ) const {
return OS : : get_singleton ( ) - > get_joy_guid ( p_device ) ;
}
2023-07-20 12:56:48 +00:00
bool InputDefault : : 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 ) ;
}
2016-01-07 23:40:41 +00:00
//platforms that use the remapping system can override and call to these ones
bool InputDefault : : is_joy_mapped ( int p_device ) {
2021-04-17 19:25:18 +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
}
String InputDefault : : get_joy_guid_remapped ( 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
2017-01-08 20:05:51 +00:00
Array InputDefault : : get_connected_joypads ( ) {
2016-07-05 12:07:46 +00:00
Array ret ;
2017-01-08 20:05:51 +00:00
Map < int , Joypad > : : Element * elem = joy_names . front ( ) ;
2016-07-05 12:07:46 +00:00
while ( elem ) {
if ( elem - > get ( ) . connected ) {
ret . push_back ( elem - > key ( ) ) ;
}
elem = elem - > next ( ) ;
}
return ret ;
}
2016-09-20 20:12:52 +00:00
2019-01-05 00:31:52 +00:00
static const char * _buttons [ JOY_BUTTON_MAX ] = {
2016-09-20 20:12:52 +00:00
" Face Button Bottom " ,
" Face Button Right " ,
" Face Button Left " ,
" Face Button Top " ,
" L " ,
" R " ,
" L2 " ,
" R2 " ,
" L3 " ,
" R3 " ,
" Select " ,
" Start " ,
" DPAD Up " ,
" DPAD Down " ,
" DPAD Left " ,
2021-03-04 16:27:15 +00:00
" DPAD Right " ,
2021-05-21 16:17:42 +00:00
" Guide " ,
2021-02-07 16:12:51 +00:00
" Misc 1 " ,
" Paddle 1 " ,
" Paddle 2 " ,
" Paddle 3 " ,
" Paddle 4 " ,
" Touchpad " ,
2016-09-20 20:12:52 +00:00
} ;
2019-01-05 00:31:52 +00:00
static const char * _axes [ JOY_AXIS_MAX ] = {
2016-09-20 20:12:52 +00:00
" Left Stick X " ,
" Left Stick Y " ,
" Right Stick X " ,
" Right Stick Y " ,
" " ,
" " ,
" L2 " ,
2019-01-05 00:31:52 +00:00
" R2 " ,
" " ,
" "
2016-09-20 20:12:52 +00:00
} ;
String InputDefault : : get_joy_button_string ( int p_button ) {
ERR_FAIL_INDEX_V ( p_button , JOY_BUTTON_MAX , " " ) ;
return _buttons [ p_button ] ;
}
int InputDefault : : get_joy_button_index_from_string ( String p_button ) {
for ( int i = 0 ; i < JOY_BUTTON_MAX ; i + + ) {
2022-03-16 14:13:02 +00:00
if ( _buttons [ i ] = = nullptr ) {
break ;
}
if ( p_button = = String ( _buttons [ i ] ) ) {
2016-09-20 20:12:52 +00:00
return i ;
}
}
2022-03-16 14:13:02 +00:00
ERR_FAIL_V_MSG ( - 1 , vformat ( " Could not find a button index matching the string \" %s \" . " , p_button ) ) ;
2016-09-20 20:12:52 +00:00
}
2017-02-21 16:02:49 +00:00
int InputDefault : : get_unused_joy_id ( ) {
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < JOYPADS_MAX ; i + + ) {
2017-02-21 16:02:49 +00:00
if ( ! joy_names . has ( i ) | | ! joy_names [ i ] . connected ) {
return i ;
}
}
return - 1 ;
}
2016-09-20 20:12:52 +00:00
String InputDefault : : get_joy_axis_string ( int p_axis ) {
ERR_FAIL_INDEX_V ( p_axis , JOY_AXIS_MAX , " " ) ;
return _axes [ p_axis ] ;
}
int InputDefault : : get_joy_axis_index_from_string ( String p_axis ) {
for ( int i = 0 ; i < JOY_AXIS_MAX ; i + + ) {
2022-03-16 14:13:02 +00:00
if ( _axes [ i ] = = nullptr ) {
break ;
}
if ( p_axis = = String ( _axes [ i ] ) ) {
2016-09-20 20:12:52 +00:00
return i ;
}
}
2022-03-16 14:13:02 +00:00
ERR_FAIL_V_MSG ( - 1 , vformat ( " Could not find an axis index matching the string \" %s \" . " , p_axis ) ) ;
2016-09-20 20:12:52 +00:00
}