Browse Source

Support SDL2 half axes and inverted axes mappings.

Marcel Admiraal 5 years ago
parent
commit
ebff150680

+ 1 - 0
core/global_constants.cpp

@@ -392,6 +392,7 @@ void register_global_constants() {
 	BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_XBUTTON2);
 	BIND_GLOBAL_ENUM_CONSTANT(BUTTON_MASK_XBUTTON2);
 
 
 	//joypads
 	//joypads
+	BIND_GLOBAL_ENUM_CONSTANT(JOY_INVALID_OPTION);
 	BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_0);
 	BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_0);
 	BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_1);
 	BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_1);
 	BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_2);
 	BIND_GLOBAL_ENUM_CONSTANT(JOY_BUTTON_2);

+ 2 - 0
core/os/input_event.h

@@ -61,6 +61,8 @@ enum ButtonList {
 
 
 enum JoystickList {
 enum JoystickList {
 
 
+	JOY_INVALID_OPTION = -1,
+
 	JOY_BUTTON_0 = 0,
 	JOY_BUTTON_0 = 0,
 	JOY_BUTTON_1 = 1,
 	JOY_BUTTON_1 = 1,
 	JOY_BUTTON_2 = 2,
 	JOY_BUTTON_2 = 2,

+ 3 - 0
doc/classes/@GlobalScope.xml

@@ -937,6 +937,9 @@
 		<constant name="BUTTON_MASK_XBUTTON2" value="256" enum="ButtonList">
 		<constant name="BUTTON_MASK_XBUTTON2" value="256" enum="ButtonList">
 			Extra mouse button 2 mask.
 			Extra mouse button 2 mask.
 		</constant>
 		</constant>
+		<constant name="JOY_INVALID_OPTION" value="-1" enum="JoystickList">
+			Invalid button or axis.
+		</constant>
 		<constant name="JOY_BUTTON_0" value="0" enum="JoystickList">
 		<constant name="JOY_BUTTON_0" value="0" enum="JoystickList">
 			Gamepad button 0.
 			Gamepad button 0.
 		</constant>
 		</constant>

+ 220 - 97
main/input_default.cpp

@@ -709,22 +709,6 @@ InputDefault::InputDefault() {
 	main_loop = NULL;
 	main_loop = NULL;
 	default_shape = CURSOR_ARROW;
 	default_shape = CURSOR_ARROW;
 
 
-	hat_map_default[HAT_UP].type = TYPE_BUTTON;
-	hat_map_default[HAT_UP].index = JOY_DPAD_UP;
-	hat_map_default[HAT_UP].value = 0;
-
-	hat_map_default[HAT_RIGHT].type = TYPE_BUTTON;
-	hat_map_default[HAT_RIGHT].index = JOY_DPAD_RIGHT;
-	hat_map_default[HAT_RIGHT].value = 0;
-
-	hat_map_default[HAT_DOWN].type = TYPE_BUTTON;
-	hat_map_default[HAT_DOWN].index = JOY_DPAD_DOWN;
-	hat_map_default[HAT_DOWN].value = 0;
-
-	hat_map_default[HAT_LEFT].type = TYPE_BUTTON;
-	hat_map_default[HAT_LEFT].index = JOY_DPAD_LEFT;
-	hat_map_default[HAT_LEFT].value = 0;
-
 	fallback_mapping = -1;
 	fallback_mapping = -1;
 
 
 	// Parse default mappings.
 	// Parse default mappings.
@@ -761,14 +745,8 @@ void InputDefault::joy_button(int p_device, int p_button, bool p_pressed) {
 		return;
 		return;
 	}
 	}
 
 
-	const Map<int, JoyEvent>::Element *el = map_db[joy.mapping].buttons.find(p_button);
-	if (!el) {
-		//don't process un-mapped events for now, it could mess things up badly for devices with additional buttons/axis
-		//return _button_event(p_last_id, p_device, p_button, p_pressed);
-		return;
-	}
+	JoyEvent map = _get_mapped_button_event(map_db[joy.mapping], p_button);
 
 
-	JoyEvent map = el->get();
 	if (map.type == TYPE_BUTTON) {
 	if (map.type == TYPE_BUTTON) {
 		//fake additional axis event for triggers
 		//fake additional axis event for triggers
 		if (map.index == JOY_L2 || map.index == JOY_R2) {
 		if (map.index == JOY_L2 || map.index == JOY_R2) {
@@ -831,13 +809,7 @@ void InputDefault::joy_axis(int p_device, int p_axis, const JoyAxis &p_value) {
 		return;
 		return;
 	};
 	};
 
 
-	const Map<int, JoyEvent>::Element *el = map_db[joy.mapping].axis.find(p_axis);
-	if (!el) {
-		//return _axis_event(p_last_id, p_device, p_axis, p_value);
-		return;
-	};
-
-	JoyEvent map = el->get();
+	JoyEvent map = _get_mapped_axis_event(map_db[joy.mapping], p_axis, p_value);
 
 
 	if (map.type == TYPE_BUTTON) {
 	if (map.type == TYPE_BUTTON) {
 		//send axis event for triggers
 		//send axis event for triggers
@@ -906,12 +878,26 @@ void InputDefault::joy_hat(int p_device, int p_val) {
 	_THREAD_SAFE_METHOD_;
 	_THREAD_SAFE_METHOD_;
 	const Joypad &joy = joy_names[p_device];
 	const Joypad &joy = joy_names[p_device];
 
 
-	const JoyEvent *map;
+	JoyEvent map[HAT_MAX];
 
 
-	if (joy.mapping == -1) {
-		map = hat_map_default;
-	} else {
-		map = map_db[joy.mapping].hat;
+	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);
 	};
 	};
 
 
 	int cur_val = joy_names[p_device].hat_current;
 	int cur_val = joy_names[p_device].hat_current;
@@ -955,50 +941,153 @@ void InputDefault::_axis_event(int p_device, int p_axis, float p_value) {
 	parse_input_event(ievent);
 	parse_input_event(ievent);
 };
 };
 
 
-InputDefault::JoyEvent InputDefault::_find_to_event(String p_to) {
+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;
+					return event;
+				default:
+					ERR_PRINT_ONCE("Joypad button mapping error.");
+			}
+		}
+	}
+	return event;
+}
+
+InputDefault::JoyEvent InputDefault::_get_mapped_axis_event(const JoyDeviceMapping &mapping, int p_axis, const JoyAxis &p_value) {
+
+	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) {
+			float value = p_value.value;
+			if (binding.input.axis.invert)
+				value = -value;
+			if (binding.input.axis.range == FULL_AXIS ||
+					(binding.input.axis.range == POSITIVE_HALF_AXIS && value > 0) ||
+					(binding.input.axis.range == NEGATIVE_HALF_AXIS && value < 0)) {
+				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;
+						event.value = value;
+						if (binding.output.axis.range != binding.input.axis.range) {
+							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;
+							}
+							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;
+}
+
+void InputDefault::_get_mapped_hat_events(const JoyDeviceMapping &mapping, int p_hat, JoyEvent r_events[]) {
 
 
-	// string names of the SDL buttons in the same order as input_event.h godot buttons
-	static const char *buttons[] = { "a", "b", "x", "y", "leftshoulder", "rightshoulder", "lefttrigger", "righttrigger", "leftstick", "rightstick", "back", "start", "dpup", "dpdown", "dpleft", "dpright", "guide", NULL };
+	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) {
 
 
-	static const char *axis[] = { "leftx", "lefty", "rightx", "righty", NULL };
+			int index;
+			switch (binding.input.hat.hat_mask) {
+				case HAT_MASK_UP:
+					index = 0;
+					break;
+				case HAT_MASK_RIGHT:
+					index = 1;
+					break;
+				case HAT_MASK_DOWN:
+					index = 2;
+					break;
+				case HAT_MASK_LEFT:
+					index = 3;
+					break;
+				default:
+					ERR_PRINT_ONCE("Joypad button mapping error.");
+					continue;
+			}
 
 
-	JoyEvent ret;
-	ret.type = -1;
-	ret.index = 0;
+			r_events[index].type = binding.outputType;
+			switch (binding.outputType) {
+				case TYPE_BUTTON:
+					r_events[index].index = binding.output.button;
+					break;
+				case TYPE_AXIS:
+					r_events[index].index = binding.output.axis.axis;
+					break;
+				default:
+					ERR_PRINT_ONCE("Joypad button mapping error.");
+			}
+		}
+	}
+}
 
 
-	int i = 0;
-	while (buttons[i]) {
+// string names of the SDL buttons in the same order as input_event.h godot buttons
+static const char *_joy_buttons[] = { "a", "b", "x", "y", "leftshoulder", "rightshoulder", "lefttrigger", "righttrigger", "leftstick", "rightstick", "back", "start", "dpup", "dpdown", "dpleft", "dpright", "guide", nullptr };
+static const char *_joy_axes[] = { "leftx", "lefty", "rightx", "righty", nullptr };
 
 
-		if (p_to == buttons[i]) {
-			ret.type = TYPE_BUTTON;
-			ret.index = i;
-			ret.value = 0;
-			return ret;
-		};
-		++i;
-	};
+JoystickList InputDefault::_get_output_button(String output) {
 
 
-	i = 0;
-	while (axis[i]) {
+	for (int i = 0; _joy_buttons[i]; i++) {
+		if (output == _joy_buttons[i])
+			return JoystickList(i);
+	}
+	return JoystickList::JOY_INVALID_OPTION;
+}
 
 
-		if (p_to == axis[i]) {
-			ret.type = TYPE_AXIS;
-			ret.index = i;
-			ret.value = 0;
-			return ret;
-		};
-		++i;
-	};
+JoystickList InputDefault::_get_output_axis(String output) {
 
 
-	return ret;
-};
+	for (int i = 0; _joy_axes[i]; i++) {
+		if (output == _joy_axes[i])
+			return JoystickList(i);
+	}
+	return JoystickList::JOY_INVALID_OPTION;
+}
 
 
 void InputDefault::parse_mapping(String p_mapping) {
 void InputDefault::parse_mapping(String p_mapping) {
 
 
 	_THREAD_SAFE_METHOD_;
 	_THREAD_SAFE_METHOD_;
 	JoyDeviceMapping mapping;
 	JoyDeviceMapping mapping;
-	for (int i = 0; i < HAT_MAX; ++i)
-		mapping.hat[i].index = 1024 + i;
 
 
 	Vector<String> entry = p_mapping.split(",");
 	Vector<String> entry = p_mapping.split(",");
 	if (entry.size() < 2) {
 	if (entry.size() < 2) {
@@ -1017,45 +1106,79 @@ void InputDefault::parse_mapping(String p_mapping) {
 		if (entry[idx] == "")
 		if (entry[idx] == "")
 			continue;
 			continue;
 
 
-		String from = entry[idx].get_slice(":", 1).replace(" ", "");
-		String to = entry[idx].get_slice(":", 0).replace(" ", "");
+		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,
+				String(entry[idx] + "\nInvalid device mapping entry: " + entry[idx]));
 
 
-		JoyEvent to_event = _find_to_event(to);
-		if (to_event.type == -1)
+		if (output == "platform")
 			continue;
 			continue;
 
 
-		String etype = from.substr(0, 1);
-		if (etype == "a") {
-
-			int aid = from.substr(1, from.length() - 1).to_int();
-			mapping.axis[aid] = to_event;
-
-		} else if (etype == "b") {
+		JoyAxisRange output_range = FULL_AXIS;
+		if (output[0] == '+' || output[0] == '-') {
+			ERR_CONTINUE_MSG(output.length() < 2, String(entry[idx] + "\nInvalid output: " + entry[idx]));
+			output = output.right(1);
+			if (output[0] == '+')
+				output_range = POSITIVE_HALF_AXIS;
+			else if (output[0] == '-')
+				output_range = NEGATIVE_HALF_AXIS;
+		}
 
 
-			int bid = from.substr(1, from.length() - 1).to_int();
-			mapping.buttons[bid] = to_event;
+		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;
+		if (input[input.length() - 1] == '~')
+			invert_axis = true;
+
+		JoystickList output_button = _get_output_button(output);
+		JoystickList output_axis = _get_output_axis(output);
+		ERR_CONTINUE_MSG(output_button == JOY_INVALID_OPTION && output_axis == JOY_INVALID_OPTION,
+				String(entry[idx] + "\nUnrecognised output string: " + output));
+		ERR_CONTINUE_MSG(output_button != JOY_INVALID_OPTION && output_axis != JOY_INVALID_OPTION,
+				String("BUG: Output string matched both button and axis: " + output));
+
+		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;
+		}
 
 
-		} else if (etype == "h") {
+		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] != '.',
+						String(entry[idx] + "\nInvalid hat input: " + input));
+				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:
+				ERR_CONTINUE_MSG(true, String(entry[idx] + "\nUnrecognised input string: " + input));
+		}
 
 
-			int hat_value = from.get_slice(".", 1).to_int();
-			switch (hat_value) {
-				case 1:
-					mapping.hat[HAT_UP] = to_event;
-					break;
-				case 2:
-					mapping.hat[HAT_RIGHT] = to_event;
-					break;
-				case 4:
-					mapping.hat[HAT_DOWN] = to_event;
-					break;
-				case 8:
-					mapping.hat[HAT_LEFT] = to_event;
-					break;
-			};
-		};
+		mapping.bindings.push_back(binding);
 	};
 	};
+
 	map_db.push_back(mapping);
 	map_db.push_back(mapping);
-	//printf("added mapping with uuid %ls\n", mapping.uid.c_str());
 };
 };
 
 
 void InputDefault::add_joy_mapping(String p_mapping, bool p_update_existing) {
 void InputDefault::add_joy_mapping(String p_mapping, bool p_update_existing) {

+ 43 - 8
main/input_default.h

@@ -148,26 +148,61 @@ private:
 		TYPE_MAX,
 		TYPE_MAX,
 	};
 	};
 
 
+	enum JoyAxisRange {
+		NEGATIVE_HALF_AXIS = -1,
+		FULL_AXIS = 0,
+		POSITIVE_HALF_AXIS = 1
+	};
+
 	struct JoyEvent {
 	struct JoyEvent {
 		int type;
 		int type;
 		int index;
 		int index;
-		int value;
+		float value;
 	};
 	};
 
 
-	struct JoyDeviceMapping {
+	struct JoyBinding {
+		JoyType inputType;
+		union {
+			int button;
+
+			struct {
+				int axis;
+				JoyAxisRange range;
+				bool invert;
+			} axis;
+
+			struct {
+				int hat;
+				HatMask hat_mask;
+			} hat;
+
+		} input;
 
 
+		JoyType outputType;
+		union {
+			JoystickList button;
+
+			struct {
+				JoystickList axis;
+				JoyAxisRange range;
+			} axis;
+
+		} output;
+	};
+
+	struct JoyDeviceMapping {
 		String uid;
 		String uid;
 		String name;
 		String name;
-		Map<int, JoyEvent> buttons;
-		Map<int, JoyEvent> axis;
-		JoyEvent hat[HAT_MAX];
+		Vector<JoyBinding> bindings;
 	};
 	};
 
 
-	JoyEvent hat_map_default[HAT_MAX];
-
 	Vector<JoyDeviceMapping> map_db;
 	Vector<JoyDeviceMapping> map_db;
 
 
-	JoyEvent _find_to_event(String p_to);
+	JoyEvent _get_mapped_button_event(const JoyDeviceMapping &mapping, int p_button);
+	JoyEvent _get_mapped_axis_event(const JoyDeviceMapping &mapping, int p_axis, const JoyAxis &p_value);
+	void _get_mapped_hat_events(const JoyDeviceMapping &mapping, int p_hat, JoyEvent r_events[HAT_MAX]);
+	JoystickList _get_output_button(String output);
+	JoystickList _get_output_axis(String output);
 	void _button_event(int p_device, int p_index, bool p_pressed);
 	void _button_event(int p_device, int p_index, bool p_pressed);
 	void _axis_event(int p_device, int p_axis, float p_value);
 	void _axis_event(int p_device, int p_axis, float p_value);
 	float _handle_deadzone(int p_device, int p_axis, float p_value);
 	float _handle_deadzone(int p_device, int p_axis, float p_value);

+ 1 - 12
main/main_builders.py

@@ -99,18 +99,7 @@ def make_default_controller_mappings(target, source, env):
                             src_path, current_platform, platform_mappings[current_platform][guid]
                             src_path, current_platform, platform_mappings[current_platform][guid]
                         )
                         )
                     )
                     )
-                valid_mapping = True
-                for input_map in line_parts[2:]:
-                    if "+" in input_map or "-" in input_map or "~" in input_map:
-                        g.write(
-                            "// WARNING - DISCARDED UNSUPPORTED MAPPING TYPE FROM DATABASE {}: {} {}\n".format(
-                                src_path, current_platform, line
-                            )
-                        )
-                        valid_mapping = False
-                        break
-                if valid_mapping:
-                    platform_mappings[current_platform][guid] = line
+                platform_mappings[current_platform][guid] = line
 
 
     platform_variables = {
     platform_variables = {
         "Linux": "#if X11_ENABLED",
         "Linux": "#if X11_ENABLED",