| 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++); |
| 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&) { |
| 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; |
| 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; |
| 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; |
| 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& , const auto& ) -> std::optional<bool> { |
| 222 |
return std::nullopt; |
| 223 |
} |
| 224 |
}, binding, event); |
| 225 |
} |
| 226 |
|
| 227 |
} |