openMSX
BooleanInput.cc
Go to the documentation of this file.
1#include "BooleanInput.hh"
2
3#include "Event.hh"
4#include "SDLKey.hh"
5
6#include "narrow.hh"
7#include "one_of.hh"
8#include "stl.hh"
9#include "strCat.hh"
10#include "StringOp.hh"
11#include "unreachable.hh"
12
13#include <tuple>
14
15namespace openmsx {
16
17std::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
47std::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 joystick = parseValueWithPrefix(type, "joy")) {
72 if (*joystick == 0) return std::nullopt;
73 auto joyId = JoystickId(*joystick - 1);
74
75 auto subType = *it++;
76 if (auto button = parseValueWithPrefix(subType, "button")) {
77 if (*button > 255) return std::nullopt;
78 if (it != et) return std::nullopt;
79 return BooleanJoystickButton(joyId, narrow<uint8_t>(*button));
80
81 } else if (auto hat = parseValueWithPrefix(subType, "hat")) {
82 if (*hat > 255) return std::nullopt;
83 if (it == et) return std::nullopt;
84 auto valueStr = *it++;
85 if (it != et) return std::nullopt;
86
89 if (valueStr == "up" ) value = UP;
90 else if (valueStr == "right") value = RIGHT;
91 else if (valueStr == "down" ) value = DOWN;
92 else if (valueStr == "left" ) value = LEFT;
93 else return std::nullopt;
94
95 return BooleanJoystickHat(joyId, narrow<uint8_t>(*hat), value);
96
97 } else if (auto pAxis = parseValueWithPrefix(subType, "+axis")) {
98 if (*pAxis > 255) return std::nullopt;
99 if (it != et) return std::nullopt;
100 return BooleanJoystickAxis(joyId, narrow<uint8_t>(*pAxis), BooleanJoystickAxis::Direction::POS);
101 } else if (auto nAxis = parseValueWithPrefix(subType, "-axis")) {
102 if (*nAxis > 255) return std::nullopt;
103 if (it != et) return std::nullopt;
104 return BooleanJoystickAxis(joyId, narrow<uint8_t>(*nAxis), BooleanJoystickAxis::Direction::NEG);
105 }
106 }
107 return std::nullopt;
108}
109
110std::optional<BooleanInput> captureBooleanInput(const Event& event, function_ref<int(JoystickId)> getJoyDeadZone)
111{
112 return std::visit(overloaded{
113 [](const KeyDownEvent& e) -> std::optional<BooleanInput> {
114 return BooleanKeyboard(e.getKeyCode());
115 },
116 [](const MouseButtonDownEvent& e) -> std::optional<BooleanInput> {
117 return BooleanMouseButton(e.getButton());
118 },
119 [](const JoystickButtonDownEvent& e) -> std::optional<BooleanInput> {
120 return BooleanJoystickButton(e.getJoystick(), narrow<uint8_t>(e.getButton()));
121 },
122 [](const JoystickHatEvent& e) -> std::optional<BooleanInput> {
123 auto value = e.getValue();
124 if (value != one_of(SDL_HAT_UP, SDL_HAT_RIGHT, SDL_HAT_DOWN, SDL_HAT_LEFT)) {
125 return std::nullopt;
126 }
127 return BooleanJoystickHat(e.getJoystick(), e.getHat(),
129 },
130 [&](const JoystickAxisMotionEvent& e) -> std::optional<BooleanInput> {
131 auto joyId = e.getJoystick();
132 int deadZone = getJoyDeadZone(joyId); // percentage 0..100
133 int threshold = (deadZone * 32768) / 100;
134
135 auto value = e.getValue();
136 if ((-threshold <= value) && (value <= threshold)) {
137 return std::nullopt;
138 }
139 return BooleanJoystickAxis(joyId, e.getAxis(),
142 },
143 [](const EventBase&) -> std::optional<BooleanInput> {
144 return std::nullopt;
145 }
146 }, event);
147}
148
149bool operator==(const BooleanInput& x, const BooleanInput& y)
150{
151 return std::visit(overloaded{
152 [](const BooleanKeyboard& k1, const BooleanKeyboard& k2) {
153 return k1.getKeyCode() == k2.getKeyCode();
154 },
155 [](const BooleanMouseButton& m1, const BooleanMouseButton& m2) {
156 return m1.getButton() == m2.getButton();
157 },
158 [](const BooleanJoystickButton& j1, const BooleanJoystickButton& j2) {
159 return std::tuple(j1.getJoystick(), j1.getButton()) ==
160 std::tuple(j2.getJoystick(), j2.getButton());
161 },
162 [](const BooleanJoystickHat& j1, const BooleanJoystickHat& j2) {
163 return std::tuple(j1.getJoystick(), j1.getHat(), j1.getValue()) ==
164 std::tuple(j2.getJoystick(), j2.getHat(), j2.getValue());
165 },
166 [](const BooleanJoystickAxis& j1, const BooleanJoystickAxis& j2) {
167 return std::tuple(j1.getJoystick(), j1.getAxis(), j1.getDirection()) ==
168 std::tuple(j2.getJoystick(), j2.getAxis(), j2.getDirection());
169 },
170 [](const auto&, const auto&) { // mixed types
171 return false;
172 }
173 }, x, y);
174}
175
176std::optional<bool> match(const BooleanInput& binding, const Event& event,
177 function_ref<int(JoystickId)> getJoyDeadZone)
178{
179 return std::visit(overloaded{
180 [](const BooleanKeyboard& bind, const KeyDownEvent& down) -> std::optional<bool> {
181 if (bind.getKeyCode() == down.getKeyCode()) return true;
182 return std::nullopt;
183 },
184 [](const BooleanKeyboard& bind, const KeyUpEvent& up) -> std::optional<bool> {
185 if (bind.getKeyCode() == up.getKeyCode()) return false; // no longer pressed
186 return std::nullopt;
187 },
188
189 [](const BooleanMouseButton& bind, const MouseButtonDownEvent& down) -> std::optional<bool> {
190 if (bind.getButton() == down.getButton()) return true;
191 return std::nullopt;
192 },
193 [](const BooleanMouseButton& bind, const MouseButtonUpEvent& up) -> std::optional<bool> {
194 if (bind.getButton() == up.getButton()) return false; // no longer pressed
195 return std::nullopt;
196 },
197
198 [](const BooleanJoystickButton& bind, const JoystickButtonDownEvent& down) -> std::optional<bool> {
199 if (bind.getJoystick() != down.getJoystick()) return std::nullopt;
200 if (bind.getButton() == down.getButton()) return true;
201 return std::nullopt;
202 },
203 [](const BooleanJoystickButton& bind, const JoystickButtonUpEvent& up) -> std::optional<bool> {
204 if (bind.getJoystick() != up.getJoystick()) return std::nullopt;
205 if (bind.getButton() == up.getButton()) return false; // no longer pressed
206 return std::nullopt;
207 },
208
209 [](const BooleanJoystickHat& bind, const JoystickHatEvent& e) -> std::optional<bool> {
210 if (bind.getJoystick() != e.getJoystick()) return std::nullopt;
211 if (bind.getHat() != e.getHat()) return std::nullopt;
212 return to_underlying(bind.getValue()) & e.getValue();
213 },
214
215 [&](const BooleanJoystickAxis& bind, const JoystickAxisMotionEvent& e) -> std::optional<bool> {
216 if (bind.getJoystick() != e.getJoystick()) return std::nullopt;
217 if (bind.getAxis() != e.getAxis()) return std::nullopt;
218 int deadZone = getJoyDeadZone(bind.getJoystick()); // percentage 0..100
219 int threshold = (deadZone * 32768) / 100;
220 if (bind.getDirection() == BooleanJoystickAxis::Direction::POS) {
221 return e.getValue() > threshold;
222 } else {
223 return e.getValue() < -threshold;
224 }
225 },
226
227 [](const auto& /*bind*/, const auto& /*event*/) -> std::optional<bool> {
228 return std::nullopt;
229 }
230 }, binding, event);
231}
232
233} // namespace openmsx
This file implemented 3 utility functions:
Definition Autofire.cc:11
std::optional< BooleanInput > captureBooleanInput(const Event &event, function_ref< int(JoystickId)> getJoyDeadZone)
bool operator==(const BooleanInput &x, const BooleanInput &y)
std::optional< bool > match(const BooleanInput &binding, const Event &event, function_ref< int(JoystickId)> getJoyDeadZone)
std::optional< BooleanInput > parseBooleanInput(std::string_view text)
std::variant< BooleanKeyboard, BooleanMouseButton, BooleanJoystickButton, BooleanJoystickHat, BooleanJoystickAxis > BooleanInput
std::string toString(const BooleanInput &input)
std::variant< KeyUpEvent, KeyDownEvent, MouseMotionEvent, MouseButtonUpEvent, MouseButtonDownEvent, MouseWheelEvent, JoystickAxisMotionEvent, JoystickHatEvent, JoystickButtonUpEvent, JoystickButtonDownEvent, OsdControlReleaseEvent, OsdControlPressEvent, WindowEvent, TextEvent, FileDropEvent, QuitEvent, FinishFrameEvent, CliCommandEvent, GroupEvent, BootEvent, FrameDrawnEvent, BreakEvent, SwitchRendererEvent, TakeReverseSnapshotEvent, AfterTimedEvent, MachineLoadedEvent, MachineActivatedEvent, MachineDeactivatedEvent, MidiInReaderEvent, MidiInWindowsEvent, MidiInCoreMidiEvent, MidiInCoreMidiVirtualEvent, MidiInALSAEvent, Rs232TesterEvent, Rs232NetEvent, ImGuiDelayedActionEvent, ImGuiActiveEvent > Event
Definition Event.hh:446
constexpr auto to_underlying(E e) noexcept
Definition stl.hh:465
std::string strCat()
Definition strCat.hh:703
void strAppend(std::string &result, Ts &&...ts)
Definition strCat.hh:752
static SDL_Keycode keycodeFromString(zstring_view name)
Definition SDLKey.cc:109
std::string toString() const
Definition SDLKey.cc:230