Merge pull request #575 from Faless/joy/remap_wizard

[Joypads] Add SDL config re-mapping tool.
This commit is contained in:
Aaron Franke
2021-02-07 16:24:18 -05:00
committed by GitHub
6 changed files with 670 additions and 5 deletions

View File

@@ -32,7 +32,7 @@ func _process(_delta):
# Display the name of the joypad if we haven't already.
if joy_num != cur_joy:
cur_joy = joy_num
joypad_name.set_text(Input.get_joy_name(joy_num))
joypad_name.set_text(Input.get_joy_name(joy_num) + "\n" + Input.get_joy_guid(joy_num))
# Loop through the axes and show their current values.
for axis in range(JOY_AXIS_MAX):
@@ -64,7 +64,10 @@ func _process(_delta):
# Called whenever a joypad has been connected or disconnected.
func _on_joy_connection_changed(device_id, connected):
if device_id == cur_joy:
joypad_name.set_text(Input.get_joy_name(device_id) if connected else "")
if connected:
joypad_name.set_text(Input.get_joy_name(device_id) + "\n" + Input.get_joy_guid(device_id))
else:
joypad_name.set_text("")
func _on_start_vibration_pressed():
@@ -76,3 +79,19 @@ func _on_start_vibration_pressed():
func _on_stop_vibration_pressed():
Input.stop_joy_vibration(cur_joy)
func _on_Remap_pressed():
$RemapWizard.start(cur_joy)
func _on_Clear_pressed():
var guid = Input.get_joy_guid(cur_joy)
if guid.empty():
printerr("No gamepad selected")
return
Input.remove_joy_mapping(guid)
func _on_Show_pressed():
$RemapWizard.show_map()

View File

@@ -1,7 +1,8 @@
[gd_scene load_steps=3 format=2]
[gd_scene load_steps=4 format=2]
[ext_resource path="res://joypads.gd" type="Script" id=1]
[ext_resource path="res://joypad_diagram.tscn" type="PackedScene" id=2]
[ext_resource path="res://remap/remap_wizard.tscn" type="PackedScene" id=3]
[node name="joypads" type="Control"]
anchor_left = 0.5
@@ -979,5 +980,42 @@ text = "Stop Vibration"
__meta__ = {
"_edit_use_anchors_": false
}
[node name="VBoxContainer" type="HBoxContainer" parent="."]
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = -250.0
margin_top = -42.0
margin_bottom = -12.0
alignment = 1
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Clear" type="Button" parent="VBoxContainer"]
margin_left = 11.0
margin_right = 130.0
margin_bottom = 30.0
text = "Set Raw Mapping"
[node name="Remap" type="Button" parent="VBoxContainer"]
margin_left = 134.0
margin_right = 190.0
margin_bottom = 30.0
text = "Remap"
[node name="Show" type="Button" parent="VBoxContainer"]
margin_left = 194.0
margin_right = 239.0
margin_bottom = 30.0
text = "Show"
[node name="RemapWizard" parent="." instance=ExtResource( 3 )]
[connection signal="pressed" from="Vibration/Buttons/Start" to="." method="_on_start_vibration_pressed"]
[connection signal="pressed" from="Vibration/Buttons/Stop" to="." method="_on_stop_vibration_pressed"]
[connection signal="pressed" from="VBoxContainer/Clear" to="." method="_on_Clear_pressed"]
[connection signal="pressed" from="VBoxContainer/Remap" to="." method="_on_Remap_pressed"]
[connection signal="pressed" from="VBoxContainer/Show" to="." method="_on_Show_pressed"]

View File

@@ -8,9 +8,14 @@
config_version=4
_global_script_classes=[ ]
_global_script_classes=[ {
"base": "Reference",
"class": "JoyMapping",
"language": "GDScript",
"path": "res://remap/joy_mapping.gd"
} ]
_global_script_class_icons={
"JoyMapping": ""
}
[application]

View File

@@ -0,0 +1,137 @@
extends Reference
class_name JoyMapping
enum TYPE {NONE, BTN, AXIS}
enum AXIS {FULL, HALF_PLUS, HALF_MINUS}
const PLATFORMS = {
# From gamecontrollerdb
"Windows": "Windows",
"OSX": "Mac OS X",
"X11": "Linux",
"Android": "Android",
"iOS": "iOS",
# Godot customs
"HTML5": "Javascript",
"UWP": "UWP",
# 4.x compat
"Linux": "Linux",
"FreeBSD": "Linux",
"NetBSD": "Linux",
"BSD": "Linux",
"macOS": "Mac OS X",
}
const BASE = {
# Buttons
"a": JOY_XBOX_A,
"b": JOY_XBOX_B,
"y": JOY_XBOX_Y,
"x": JOY_XBOX_X,
"start": JOY_START,
"back": JOY_SELECT,
"leftstick": JOY_BUTTON_8,
"rightstick": JOY_BUTTON_9,
"leftshoulder": JOY_L,
"rightshoulder": JOY_R,
"dpup": JOY_DPAD_UP,
"dpleft": JOY_DPAD_LEFT,
"dpdown": JOY_DPAD_DOWN,
"dpright": JOY_DPAD_RIGHT,
# Axis
"leftx": JOY_AXIS_0,
"lefty": JOY_AXIS_1,
"rightx": JOY_AXIS_2,
"righty": JOY_AXIS_3,
"lefttrigger": JOY_ANALOG_L2,
"righttrigger": JOY_ANALOG_R2,
}
const XBOX = {
"a": "b0",
"b": "b1",
"y": "b3",
"x": "b2",
"start": "b7",
"guide": "b8",
"back": "b6",
"leftstick": "b9",
"rightstick": "b10",
"leftshoulder": "b4",
"rightshoulder": "b5",
"dpup": "-a7",
"dpleft":"-a6",
"dpdown": "+a7",
"dpright": "+a6",
"leftx": "a0",
"lefty": "a1",
"rightx": "a3",
"righty": "a4",
"lefttrigger": "a2",
"righttrigger": "a5",
}
const XBOX_OSX = {
"a": "b11",
"b": "b12",
"y": "b14",
"x": "b13",
"start": "b4",
"back": "b5",
"leftstick": "b6",
"rightstick": "b7",
"leftshoulder": "b8",
"rightshoulder": "b9",
"dpup": "b0",
"dpleft": "b2",
"dpdown": "b1",
"dpright": "b3",
"leftx": "a0",
"lefty": "a1",
"rightx": "a2",
"righty": "a3",
"lefttrigger": "a4",
"righttrigger":"a5",
}
var type = TYPE.NONE
var idx = -1
var axis = AXIS.FULL
var inverted = false
func _init(p_type = TYPE.NONE, p_idx = -1, p_axis = AXIS.FULL):
type = p_type
idx = p_idx
axis = p_axis
func _to_string():
if type == TYPE.NONE:
return ""
var ts = "b" if type == TYPE.BTN else "a"
var prefix = ""
var suffix = "~" if inverted else ""
match axis:
AXIS.HALF_PLUS:
prefix = "+"
AXIS.HALF_MINUS:
prefix = "-"
return "%s%s%d%s" % [prefix, ts, idx, suffix]
func to_human_string():
if type == TYPE.BTN:
return "Button %d" % idx
if type == TYPE.AXIS:
var prefix = ""
match axis:
AXIS.HALF_PLUS:
prefix = "(+) "
AXIS.HALF_MINUS:
prefix = "(-) "
var suffix = " (inverted)" if inverted else ""
return "Axis %s%d%s" % [prefix, idx, suffix]
return ""

View File

@@ -0,0 +1,189 @@
extends Node
const DEADZONE = 0.3
var joy_guid = ""
var joy_name = ""
var steps = JoyMapping.BASE.keys()
var cur_step = -1
var cur_mapping = {}
var last_mapping = ""
onready var joy_buttons = $Mapping/Margin/VBox/ViewportContainer/Viewport/JoypadDiagram/Buttons
onready var joy_axes = $Mapping/Margin/VBox/ViewportContainer/Viewport/JoypadDiagram/Axes
onready var joy_mapping_text = $Mapping/Margin/VBox/Info/Text/Value
onready var joy_mapping_full_axis = $Mapping/Margin/VBox/Info/Extra/FullAxis
onready var joy_mapping_axis_invert = $Mapping/Margin/VBox/Info/Extra/InvertAxis
func _input(event):
if cur_step == -1:
return
if event is InputEventJoypadMotion:
get_tree().set_input_as_handled()
var motion = event as InputEventJoypadMotion
if abs(motion.axis_value) > DEADZONE:
var idx = motion.axis
var map = JoyMapping.new(JoyMapping.TYPE.AXIS, idx)
map.inverted = joy_mapping_axis_invert.pressed
if joy_mapping_full_axis.pressed:
map.axis = JoyMapping.AXIS.FULL
else:
var plus = motion.axis_value > 0
map.axis = JoyMapping.AXIS.HALF_PLUS if plus else JoyMapping.AXIS.HALF_MINUS
joy_mapping_text.text = map.to_human_string()
cur_mapping[steps[cur_step]] = map
elif event is InputEventJoypadButton and event.pressed:
get_tree().set_input_as_handled()
var btn = event as InputEventJoypadButton
var map = JoyMapping.new(JoyMapping.TYPE.BTN, btn.button_index)
joy_mapping_text.text = map.to_human_string()
cur_mapping[steps[cur_step]] = map
func create_mapping_string(mapping):
var string = "%s,%s," % [joy_guid, joy_name]
for k in mapping:
var m = mapping[k]
if typeof(m) == TYPE_OBJECT and m.type == JoyMapping.TYPE.NONE:
continue
string += "%s:%s," % [k, str(m)]
var platform = "Unknown"
if JoyMapping.PLATFORMS.keys().has(OS.get_name()):
platform = JoyMapping.PLATFORMS[OS.get_name()]
return string + "platform:" + platform
func start(idx):
joy_guid = Input.get_joy_guid(idx)
joy_name = Input.get_joy_name(idx)
if joy_guid.empty():
printerr("Unable to find controller")
return
if OS.get_name() == "HTML5":
# Propose trying known mapping on HTML5.
$Start.window_title = "%s - %s" % [joy_guid, joy_name]
$Start.popup_centered()
else:
# Run wizard directly.
_on_Wizard_pressed()
func remap_and_close(mapping):
last_mapping = create_mapping_string(mapping)
Input.add_joy_mapping(last_mapping, true)
reset()
show_map()
func reset():
$Start.hide()
$Mapping.hide()
joy_guid = ""
joy_name = ""
cur_mapping = {}
cur_step = -1
func step_next():
$Mapping.window_title = "Step: %d/%d" % [cur_step + 1, steps.size()]
joy_mapping_text.text = ""
if cur_step >= steps.size():
remap_and_close(cur_mapping)
else:
_update_step()
func show_map():
if OS.get_name() == "HTML5":
JavaScript.eval("window.prompt('This is the resulting remap string', '" + last_mapping + "')")
else:
$MapWindow/Margin/VBoxContainer/TextEdit.text = last_mapping
$MapWindow.popup_centered()
func _update_step():
$Mapping/Margin/VBox/Info/Buttons/Next.grab_focus()
for btn in joy_buttons.get_children():
btn.hide()
for axis in joy_axes.get_children():
axis.hide()
var key = steps[cur_step]
var idx = JoyMapping.BASE[key]
if key in ["leftx", "lefty", "rightx", "righty"]:
joy_axes.get_node(str(idx) + "+").show()
joy_axes.get_node(str(idx) + "-").show()
else:
joy_buttons.get_node(str(idx)).show()
joy_mapping_full_axis.pressed = key in ["leftx", "lefty", "rightx", "righty", "righttrigger", "lefttrigger"]
joy_mapping_axis_invert.pressed = false
if cur_mapping.has(key):
var cur = cur_mapping[steps[cur_step]]
joy_mapping_text.text = cur.to_human_string()
if cur.type == JoyMapping.TYPE.AXIS:
joy_mapping_full_axis.pressed = cur.axis == JoyMapping.AXIS.FULL
joy_mapping_axis_invert.pressed = cur.inverted
func _on_Wizard_pressed():
Input.remove_joy_mapping(joy_guid)
$Start.hide()
$Mapping.popup_centered()
cur_step = 0
step_next()
func _on_Cancel_pressed():
reset()
func _on_xbox_pressed():
remap_and_close(JoyMapping.XBOX)
func _on_xboxosx_pressed():
remap_and_close(JoyMapping.XBOX_OSX)
func _on_Mapping_popup_hide():
reset()
func _on_Next_pressed():
cur_step += 1
step_next()
func _on_Prev_pressed():
if cur_step > 0:
cur_step -= 1
step_next()
func _on_Skip_pressed():
var key = steps[cur_step]
if cur_mapping.has(key):
cur_mapping.erase(key)
cur_step += 1
step_next()
func _on_FullAxis_toggled(button_pressed):
if cur_step == -1 or not button_pressed:
return
var key = steps[cur_step]
if cur_mapping.has(key) and cur_mapping[key].type == JoyMapping.TYPE.AXIS:
cur_mapping[key].axis = JoyMapping.AXIS.FULL
joy_mapping_text.text = cur_mapping[key].to_human_string()
func _on_InvertAxis_toggled(button_pressed):
if cur_step == -1:
return
var key = steps[cur_step]
if cur_mapping.has(key) and cur_mapping[key].type == JoyMapping.TYPE.AXIS:
cur_mapping[key].inverted = button_pressed
joy_mapping_text.text = cur_mapping[key].to_human_string()

View File

@@ -0,0 +1,277 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://remap/remap_wizard.gd" type="Script" id=1]
[ext_resource path="res://joypad_diagram.tscn" type="PackedScene" id=2]
[node name="RemapWizard" type="Node"]
script = ExtResource( 1 )
[node name="Start" type="WindowDialog" parent="."]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -128.0
margin_top = -96.0
margin_right = 128.0
margin_bottom = 96.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Margin" type="MarginContainer" parent="Start"]
anchor_right = 1.0
anchor_bottom = 1.0
custom_constants/margin_right = 12
custom_constants/margin_top = 12
custom_constants/margin_left = 12
custom_constants/margin_bottom = 12
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Layout" type="VBoxContainer" parent="Start/Margin"]
margin_left = 12.0
margin_top = 12.0
margin_right = 244.0
margin_bottom = 180.0
custom_constants/separation = 20
alignment = 1
__meta__ = {
"_edit_use_anchors_": false
}
[node name="HTML5" type="VBoxContainer" parent="Start/Margin/Layout"]
margin_top = 28.0
margin_right = 232.0
margin_bottom = 66.0
[node name="Label" type="Label" parent="Start/Margin/Layout/HTML5"]
margin_right = 232.0
margin_bottom = 14.0
text = "Try a common mapping:"
[node name="known" type="HBoxContainer" parent="Start/Margin/Layout/HTML5"]
margin_top = 18.0
margin_right = 232.0
margin_bottom = 38.0
custom_constants/separation = 20
alignment = 1
[node name="Xbox" type="Button" parent="Start/Margin/Layout/HTML5/known"]
margin_left = 44.0
margin_right = 87.0
margin_bottom = 20.0
text = "Xbox"
[node name="XboxOSX" type="Button" parent="Start/Margin/Layout/HTML5/known"]
margin_left = 107.0
margin_right = 188.0
margin_bottom = 20.0
text = "Xbox (OSX)"
[node name="Label" type="Label" parent="Start/Margin/Layout"]
margin_top = 86.0
margin_right = 232.0
margin_bottom = 100.0
text = "Or start the wizard"
[node name="Buttons" type="HBoxContainer" parent="Start/Margin/Layout"]
margin_top = 120.0
margin_right = 232.0
margin_bottom = 140.0
[node name="Cancel" type="Button" parent="Start/Margin/Layout/Buttons"]
margin_right = 54.0
margin_bottom = 20.0
text = "Cancel"
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Control" type="Control" parent="Start/Margin/Layout/Buttons"]
margin_left = 58.0
margin_right = 172.0
margin_bottom = 20.0
size_flags_horizontal = 3
[node name="Wizard" type="Button" parent="Start/Margin/Layout/Buttons"]
margin_left = 176.0
margin_right = 232.0
margin_bottom = 20.0
text = "Wizard"
[node name="Mapping" type="WindowDialog" parent="."]
margin_right = 305.0
margin_bottom = 437.0
rect_min_size = Vector2( 330, 440 )
popup_exclusive = true
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Margin" type="MarginContainer" parent="Mapping"]
anchor_right = 1.0
anchor_bottom = 1.0
custom_constants/margin_right = 12
custom_constants/margin_top = 12
custom_constants/margin_left = 12
custom_constants/margin_bottom = 12
__meta__ = {
"_edit_use_anchors_": false
}
[node name="VBox" type="VBoxContainer" parent="Mapping/Margin"]
margin_left = 12.0
margin_top = 12.0
margin_right = 318.0
margin_bottom = 428.0
custom_constants/separation = 5
[node name="ViewportContainer" type="ViewportContainer" parent="Mapping/Margin/VBox"]
margin_right = 306.0
margin_bottom = 305.0
rect_min_size = Vector2( 305, 305 )
stretch = true
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Viewport" type="Viewport" parent="Mapping/Margin/VBox/ViewportContainer"]
size = Vector2( 306, 305 )
handle_input_locally = false
render_target_update_mode = 0
[node name="JoypadDiagram" parent="Mapping/Margin/VBox/ViewportContainer/Viewport" instance=ExtResource( 2 )]
position = Vector2( 0, 0 )
[node name="Camera2D" type="Camera2D" parent="Mapping/Margin/VBox/ViewportContainer/Viewport"]
current = true
[node name="Info" type="VBoxContainer" parent="Mapping/Margin/VBox"]
margin_top = 310.0
margin_right = 306.0
margin_bottom = 408.0
custom_constants/separation = 20
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Text" type="HBoxContainer" parent="Mapping/Margin/VBox/Info"]
margin_right = 306.0
margin_bottom = 14.0
[node name="Text" type="Label" parent="Mapping/Margin/VBox/Info/Text"]
margin_right = 125.0
margin_bottom = 14.0
text = "Currently selected: "
[node name="Value" type="Label" parent="Mapping/Margin/VBox/Info/Text"]
margin_left = 129.0
margin_right = 129.0
margin_bottom = 14.0
[node name="Extra" type="HBoxContainer" parent="Mapping/Margin/VBox/Info"]
margin_top = 34.0
margin_right = 306.0
margin_bottom = 58.0
[node name="FullAxis" type="CheckBox" parent="Mapping/Margin/VBox/Info/Extra"]
margin_right = 80.0
margin_bottom = 24.0
hint_tooltip = "Check this if the sign of the axis keep changing from + to - when pressing and releasing.
Do not check it otherwise"
text = "Full axis"
[node name="InvertAxis" type="CheckBox" parent="Mapping/Margin/VBox/Info/Extra"]
margin_left = 84.0
margin_right = 181.0
margin_bottom = 24.0
hint_tooltip = "Check this if you think the axis should be inverted"
text = "Invert Axis"
[node name="Buttons" type="HBoxContainer" parent="Mapping/Margin/VBox/Info"]
margin_top = 78.0
margin_right = 306.0
margin_bottom = 98.0
[node name="Prev" type="Button" parent="Mapping/Margin/VBox/Info/Buttons"]
margin_right = 67.0
margin_bottom = 20.0
text = "Previous"
[node name="Control" type="Control" parent="Mapping/Margin/VBox/Info/Buttons"]
margin_left = 71.0
margin_right = 142.0
margin_bottom = 20.0
size_flags_horizontal = 3
[node name="Skip" type="Button" parent="Mapping/Margin/VBox/Info/Buttons"]
margin_left = 146.0
margin_right = 184.0
margin_bottom = 20.0
text = "Skip"
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Control2" type="Control" parent="Mapping/Margin/VBox/Info/Buttons"]
margin_left = 188.0
margin_right = 260.0
margin_bottom = 20.0
size_flags_horizontal = 3
[node name="Next" type="Button" parent="Mapping/Margin/VBox/Info/Buttons"]
margin_left = 264.0
margin_right = 306.0
margin_bottom = 20.0
text = "Next"
[node name="MapWindow" type="WindowDialog" parent="."]
margin_right = 400.0
margin_bottom = 200.0
popup_exclusive = true
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Margin" type="MarginContainer" parent="MapWindow"]
anchor_right = 1.0
anchor_bottom = 1.0
custom_constants/margin_right = 12
custom_constants/margin_top = 12
custom_constants/margin_left = 12
custom_constants/margin_bottom = 12
[node name="VBoxContainer" type="VBoxContainer" parent="MapWindow/Margin"]
margin_left = 12.0
margin_top = 12.0
margin_right = 388.0
margin_bottom = 188.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Label" type="Label" parent="MapWindow/Margin/VBoxContainer"]
margin_right = 376.0
margin_bottom = 14.0
text = "This is the resulting remap string:"
[node name="TextEdit" type="TextEdit" parent="MapWindow/Margin/VBoxContainer"]
margin_top = 18.0
margin_right = 376.0
margin_bottom = 176.0
size_flags_vertical = 3
readonly = true
wrap_enabled = true
[connection signal="pressed" from="Start/Margin/Layout/HTML5/known/Xbox" to="." method="_on_xbox_pressed"]
[connection signal="pressed" from="Start/Margin/Layout/HTML5/known/XboxOSX" to="." method="_on_xboxosx_pressed"]
[connection signal="pressed" from="Start/Margin/Layout/Buttons/Cancel" to="." method="_on_Cancel_pressed"]
[connection signal="pressed" from="Start/Margin/Layout/Buttons/Wizard" to="." method="_on_Wizard_pressed"]
[connection signal="popup_hide" from="Mapping" to="." method="_on_Mapping_popup_hide"]
[connection signal="toggled" from="Mapping/Margin/VBox/Info/Extra/FullAxis" to="." method="_on_FullAxis_toggled"]
[connection signal="toggled" from="Mapping/Margin/VBox/Info/Extra/InvertAxis" to="." method="_on_InvertAxis_toggled"]
[connection signal="pressed" from="Mapping/Margin/VBox/Info/Buttons/Prev" to="." method="_on_Prev_pressed"]
[connection signal="pressed" from="Mapping/Margin/VBox/Info/Buttons/Skip" to="." method="_on_Skip_pressed"]
[connection signal="pressed" from="Mapping/Margin/VBox/Info/Buttons/Next" to="." method="_on_Next_pressed"]