Kaynağa Gözat

Merge pull request #575 from Faless/joy/remap_wizard

[Joypads] Add SDL config re-mapping tool.
Aaron Franke 4 yıl önce
ebeveyn
işleme
b946d20762

+ 21 - 2
misc/joypads/joypads.gd

@@ -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()

+ 39 - 1
misc/joypads/joypads.tscn

@@ -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"]

+ 7 - 2
misc/joypads/project.godot

@@ -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]

+ 137 - 0
misc/joypads/remap/joy_mapping.gd

@@ -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 ""

+ 189 - 0
misc/joypads/remap/remap_wizard.gd

@@ -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()

+ 277 - 0
misc/joypads/remap/remap_wizard.tscn

@@ -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"]