OXYGENHUB
◆ main openmsx / src / events / BooleanInput.cc
8.1 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "BooleanInput.hh"
2
3
#include "Event.hh"
4
#include "SDLKey.hh"
5
6
#include "StringOp.hh"
7
#include "narrow.hh"
8
#include "one_of.hh"
9
#include "stl.hh"
10
#include "strCat.hh"
11
12
#include <tuple>
13
#include <utility>
14
15
namespace openmsx {
16
17
std::string toString(const BooleanInput& input)
18
{
19
	return std::visit(overloaded{
20
		[](const BooleanKeyboard& k) {
21
			return strCat("keyb ", SDLKey::toString(k.getKeyCode()));
22
		},
23
		[](const BooleanMouseButton& m) {
24
			return strCat("mouse button", m.getButton());
25
		},
26
		[](const BooleanJoystickButton& j) {
27
			return strCat(j.getJoystick().str(), " button", j.getButton());
28
		},
29
		[](const BooleanJoystickHat& h) {
30
			return strCat(h.getJoystick().str(), " hat", h.getHat(), ' ', toString(h.getValue()));
31
		},
32
		[&](const BooleanJoystickAxis& a) {
33
			return strCat(a.getJoystick().str(), ' ',
34
			              (a.getDirection() == BooleanJoystickAxis::Direction::POS ? '+' : '-'),
35
			              "axis", a.getAxis());
36
		}
37
	}, input);
38
}
39
40
[[nodiscard]] static std::optional<unsigned> parseValueWithPrefix(std::string_view str, std::string_view prefix)
41
{
42
	if (!str.starts_with(prefix)) return std::nullopt;
43
	str.remove_prefix(prefix.size());
44
	return StringOp::stringToBase<10, unsigned>(str);
45
}
46
47
std::optional<BooleanInput> parseBooleanInput(std::string_view text)
48
{
49
	auto tokenizer = StringOp::split_view<StringOp::EmptyParts::REMOVE>(text, ' ');
50
	auto it = tokenizer.begin();
51
	auto et = tokenizer.end();
52
53
	if (it == et) return std::nullopt;
54
	auto type = *it++;
55
	if (it == et) return std::nullopt;
56
	if (type == "keyb") {
57
		std::string key{*it++};
58
		while (it != et) strAppend(key, ' ', *it++); // allow for key-name containing space chars
59
		auto keycode = SDLKey::keycodeFromString(key);
60
		if (keycode == SDLK_UNKNOWN) return std::nullopt;
61
		return BooleanKeyboard(keycode);
62
63
	} else if (type == "mouse") {
64
		auto button = *it++;
65
		if (it != et) return std::nullopt;
66
		auto n = parseValueWithPrefix(button, "button");
67
		if (!n) return std::nullopt;
68
		if (*n > 255) return std::nullopt;
69
		return BooleanMouseButton(narrow<uint8_t>(*n));
70
71
	} else if (auto joyId = JoystickId::parse(type)) {
72
		auto subType = *it++;
73
		if (auto button = parseValueWithPrefix(subType, "button")) {
74
			if (*button > 255) return std::nullopt;
75
			if (it != et) return std::nullopt;
76
			return BooleanJoystickButton(*joyId, narrow<uint8_t>(*button));
77
78
		} else if (auto hat = parseValueWithPrefix(subType, "hat")) {
79
			if (*hat > 255) return std::nullopt;
80
			if (it == et) return std::nullopt;
81
			auto valueStr = *it++;
82
			if (it != et) return std::nullopt;
83
84
			BooleanJoystickHat::Direction value;
85
			using enum BooleanJoystickHat::Direction;
86
			if      (valueStr == "up"   ) value = UP;
87
			else if (valueStr == "right") value = RIGHT;
88
			else if (valueStr == "down" ) value = DOWN;
89
			else if (valueStr == "left" ) value = LEFT;
90
			else return std::nullopt;
91
92
			return BooleanJoystickHat(*joyId, narrow<uint8_t>(*hat), value);
93
94
		} else if (auto pAxis = parseValueWithPrefix(subType, "+axis")) {
95
			if (*pAxis > 255) return std::nullopt;
96
			if (it != et) return std::nullopt;
97
			return BooleanJoystickAxis(*joyId, narrow<uint8_t>(*pAxis), BooleanJoystickAxis::Direction::POS);
98
		} else if (auto nAxis = parseValueWithPrefix(subType, "-axis")) {
99
			if (*nAxis > 255) return std::nullopt;
100
			if (it != et) return std::nullopt;
101
			return BooleanJoystickAxis(*joyId, narrow<uint8_t>(*nAxis), BooleanJoystickAxis::Direction::NEG);
102
		}
103
	}
104
	return std::nullopt;
105
}
106
107
std::optional<BooleanInput> captureBooleanInput(const Event& event, function_ref<int(JoystickId)> getJoyDeadThreshold)
108
{
109
	return std::visit(overloaded{
110
		[](const KeyDownEvent& e) -> std::optional<BooleanInput> {
111
			return BooleanKeyboard(e.getKeyCode());
112
		},
113
		[](const MouseButtonDownEvent& e) -> std::optional<BooleanInput> {
114
			return BooleanMouseButton(e.getButton());
115
		},
116
		[](const JoystickButtonDownEvent& e) -> std::optional<BooleanInput> {
117
			return BooleanJoystickButton(e.getJoystick(), narrow<uint8_t>(e.getButton()));
118
		},
119
		[](const JoystickHatEvent& e) -> std::optional<BooleanInput> {
120
			auto value = e.getValue();
121
			if (value != one_of(SDL_HAT_UP, SDL_HAT_RIGHT, SDL_HAT_DOWN, SDL_HAT_LEFT)) {
122
				return std::nullopt;
123
			}
124
			return BooleanJoystickHat(e.getJoystick(), e.getHat(),
125
			                          BooleanJoystickHat::Direction(value));
126
		},
127
		[&](const JoystickAxisMotionEvent& e) -> std::optional<BooleanInput> {
128
			auto joyId = e.getJoystick();
129
			int threshold = getJoyDeadThreshold(joyId);
130
			auto value = e.getValue();
131
			if ((-threshold <= value) && (value <= threshold)) {
132
				return std::nullopt;
133
			}
134
			return BooleanJoystickAxis(joyId, e.getAxis(),
135
			                     (value > 0 ? BooleanJoystickAxis::Direction::POS
136
			                                : BooleanJoystickAxis::Direction::NEG));
137
		},
138
		[](const EventBase&) -> std::optional<BooleanInput> {
139
			return std::nullopt;
140
		}
141
	}, event);
142
}
143
144
bool operator==(const BooleanInput& x, const BooleanInput& y)
145
{
146
	return std::visit(overloaded{
147
		[](const BooleanKeyboard& k1, const BooleanKeyboard& k2) {
148
			return k1.getKeyCode() == k2.getKeyCode();
149
		},
150
		[](const BooleanMouseButton& m1, const BooleanMouseButton& m2) {
151
			return m1.getButton() == m2.getButton();
152
		},
153
		[](const BooleanJoystickButton& j1, const BooleanJoystickButton& j2) {
154
			return std::tuple(j1.getJoystick(), j1.getButton()) ==
155
			       std::tuple(j2.getJoystick(), j2.getButton());
156
		},
157
		[](const BooleanJoystickHat& j1, const BooleanJoystickHat& j2) {
158
			return std::tuple(j1.getJoystick(), j1.getHat(), j1.getValue()) ==
159
			       std::tuple(j2.getJoystick(), j2.getHat(), j2.getValue());
160
		},
161
		[](const BooleanJoystickAxis& j1, const BooleanJoystickAxis& j2) {
162
			return std::tuple(j1.getJoystick(), j1.getAxis(), j1.getDirection()) ==
163
			       std::tuple(j2.getJoystick(), j2.getAxis(), j2.getDirection());
164
		},
165
		[](const auto&, const auto&) { // mixed types
166
			return false;
167
		}
168
	}, x, y);
169
}
170
171
std::optional<bool> match(const BooleanInput& binding, const Event& event,
172
                          function_ref<int(JoystickId)> getJoyDeadThreshold)
173
{
174
	return std::visit(overloaded{
175
		[](const BooleanKeyboard& bind, const KeyDownEvent& down) -> std::optional<bool> {
176
			if (bind.getKeyCode() == down.getKeyCode()) return true;
177
			return std::nullopt;
178
		},
179
		[](const BooleanKeyboard& bind, const KeyUpEvent& up) -> std::optional<bool> {
180
			if (bind.getKeyCode() == up.getKeyCode()) return false; // no longer pressed
181
			return std::nullopt;
182
		},
183
184
		[](const BooleanMouseButton& bind, const MouseButtonDownEvent& down) -> std::optional<bool> {
185
			if (bind.getButton() == down.getButton()) return true;
186
			return std::nullopt;
187
		},
188
		[](const BooleanMouseButton& bind, const MouseButtonUpEvent& up) -> std::optional<bool> {
189
			if (bind.getButton() == up.getButton()) return false; // no longer pressed
190
			return std::nullopt;
191
		},
192
193
		[](const BooleanJoystickButton& bind, const JoystickButtonDownEvent& down) -> std::optional<bool> {
194
			if (bind.getJoystick() != down.getJoystick()) return std::nullopt;
195
			if (bind.getButton() == down.getButton()) return true;
196
			return std::nullopt;
197
		},
198
		[](const BooleanJoystickButton& bind, const JoystickButtonUpEvent& up) -> std::optional<bool> {
199
			if (bind.getJoystick() != up.getJoystick()) return std::nullopt;
200
			if (bind.getButton() == up.getButton()) return false; // no longer pressed
201
			return std::nullopt;
202
		},
203
204
		[](const BooleanJoystickHat& bind, const JoystickHatEvent& e) -> std::optional<bool> {
205
			if (bind.getJoystick() != e.getJoystick()) return std::nullopt;
206
			if (bind.getHat() != e.getHat()) return std::nullopt;
207
			return std::to_underlying(bind.getValue()) & e.getValue();
208
		},
209
210
		[&](const BooleanJoystickAxis& bind, const JoystickAxisMotionEvent& e) -> std::optional<bool> {
211
			if (bind.getJoystick() != e.getJoystick()) return std::nullopt;
212
			if (bind.getAxis() != e.getAxis()) return std::nullopt;
213
			int threshold = getJoyDeadThreshold(bind.getJoystick());
214
			if (bind.getDirection() == BooleanJoystickAxis::Direction::POS) {
215
				return e.getValue() > threshold;
216
			} else {
217
				return e.getValue() < -threshold;
218
			}
219
		},
220
221
		[](const auto& /*bind*/, const auto& /*event*/) -> std::optional<bool> {
222
			return std::nullopt;
223
		}
224
	}, binding, event);
225
}
226
227
} // namespace openmsx