29 #ifdef SDL_JOYSTICK_DISABLED
30 (void)eventDistributor;
31 (void)stateChangeDistributor;
32 (void)commandController;
36 for (
auto i :
xrange(SDL_NumJoysticks())) {
37 if (SDL_Joystick* joystick = SDL_JoystickOpen(i)) {
46 std::make_unique<Joystick>(
48 stateChangeDistributor,
63 JoyState(EmuTime::param time_,
unsigned joyNum_,
byte press_,
byte release_)
65 , joyNum(joyNum_), press(press_), release(release_)
67 assert((press != 0) || (release != 0));
68 assert((press & release) == 0);
71 [[nodiscard]]
byte getPress()
const {
return press; }
72 [[nodiscard]]
byte getRelease()
const {
return release; }
74 template<
typename Archive>
void serialize(Archive& ar,
unsigned )
76 ar.template serializeBase<StateChange>(*
this);
77 ar.serialize(
"joyNum", joyNum,
88 #ifndef SDL_JOYSTICK_DISABLED
95 for (
unsigned i = 0; i < n; i += 2) {
98 if (key !=
one_of(
"A",
"B",
"LEFT",
"RIGHT",
"UP",
"DOWN")) {
99 throw CommandException(
100 "Invalid MSX joystick action: must be one of "
101 "'A', 'B', 'LEFT', 'RIGHT', 'UP', 'DOWN'.");
103 for (
auto j :
xrange(value.getListLength(interp))) {
104 std::string_view host = value.getListIndex(interp, j).getString();
105 if (!host.starts_with(
"button") &&
106 !host.starts_with(
"+axis") &&
107 !host.starts_with(
"-axis") &&
108 !host.starts_with(
"L_hat") &&
109 !host.starts_with(
"R_hat") &&
110 !host.starts_with(
"U_hat") &&
111 !host.starts_with(
"D_hat")) {
112 throw CommandException(
113 "Invalid host joystick action: must be "
114 "one of 'button<N>', '+axis<N>', '-axis<N>', "
115 "'L_hat<N>', 'R_hat<N>', 'U_hat<N>', 'D_hat<N>'");
121 [[nodiscard]]
static std::string getJoystickName(
unsigned joyNum)
123 return strCat(
"joystick",
char(
'1' + joyNum));
126 [[nodiscard]]
static TclObject getConfigValue(SDL_Joystick* joystick)
128 TclObject listA, listB;
133 listB.addListElement(button);
135 listA.addListElement(button);
139 value.addDictKeyValues(
"LEFT",
makeTclList(
"-axis0",
"L_hat0"),
156 SDL_Joystick* joystick_)
157 : eventDistributor(eventDistributor_)
158 , stateChangeDistributor(stateChangeDistributor_)
159 , joystick(joystick_)
160 , joyNum(SDL_JoystickInstanceID(joystick_))
161 , deadSetting(globalSettings.getJoyDeadzoneSetting(joyNum))
162 , name(getJoystickName(joyNum))
163 , desc(SDL_JoystickName(joystick_))
164 , configSetting(commandController,
tmpStrCat(name,
"_config"),
165 "joystick configuration", getConfigValue(joystick).getString())
170 checkJoystickConfig(interp, newValue); });
179 SDL_JoystickClose(joystick);
200 status = calcState();
203 void Joystick::plugHelper2()
219 return pin8 ? 0x3F : status;
224 pin8 = (value & 0x04) != 0;
227 byte Joystick::calcState()
232 int threshold = (deadSetting.
getInt() * 32768) / 100;
234 const auto& dict = configSetting.
getValue();
235 if (getState(interp, dict,
"A" , threshold)) result &= ~
JOY_BUTTONA;
236 if (getState(interp, dict,
"B" , threshold)) result &= ~
JOY_BUTTONB;
237 if (getState(interp, dict,
"UP" , threshold)) result &= ~
JOY_UP;
238 if (getState(interp, dict,
"DOWN" , threshold)) result &= ~
JOY_DOWN;
239 if (getState(interp, dict,
"LEFT" , threshold)) result &= ~
JOY_LEFT;
240 if (getState(interp, dict,
"RIGHT", threshold)) result &= ~
JOY_RIGHT;
245 bool Joystick::getState(Interpreter& interp,
const TclObject& dict,
246 std::string_view key,
int threshold)
249 const auto& list = dict.getDictValue(interp, key);
250 for (
auto i :
xrange(list.getListLength(interp))) {
251 const auto& elem = list.getListIndex(interp, i).getString();
252 if (elem.starts_with(
"button")) {
253 if (
auto n = StringOp::stringToBase<10, unsigned>(elem.substr(6))) {
258 }
else if (elem.starts_with(
"+axis")) {
259 if (
auto n = StringOp::stringToBase<10, unsigned>(elem.substr(5))) {
260 if (SDL_JoystickGetAxis(joystick, *n) > threshold) {
264 }
else if (elem.starts_with(
"-axis")) {
265 if (
auto n = StringOp::stringToBase<10, unsigned>(elem.substr(5))) {
266 if (SDL_JoystickGetAxis(joystick, *n) < -threshold) {
270 }
else if (elem.starts_with(
"L_hat")) {
271 if (
auto n = StringOp::stringToBase<10, unsigned>(elem.substr(5))) {
272 if (SDL_JoystickGetHat(joystick, *n) & SDL_HAT_LEFT) {
276 }
else if (elem.starts_with(
"R_hat")) {
277 if (
auto n = StringOp::stringToBase<10, unsigned>(elem.substr(5))) {
278 if (SDL_JoystickGetHat(joystick, *n) & SDL_HAT_RIGHT) {
282 }
else if (elem.starts_with(
"U_hat")) {
283 if (
auto n = StringOp::stringToBase<10, unsigned>(elem.substr(5))) {
284 if (SDL_JoystickGetHat(joystick, *n) & SDL_HAT_UP) {
288 }
else if (elem.starts_with(
"D_hat")) {
289 if (
auto n = StringOp::stringToBase<10, unsigned>(elem.substr(5))) {
290 if (SDL_JoystickGetHat(joystick, *n) & SDL_HAT_DOWN) {
304 void Joystick::signalMSXEvent(
const Event& event,
305 EmuTime::param time) noexcept
307 const auto* joyEvent = get_if<JoystickEvent>(event);
308 if (!joyEvent)
return;
312 if (joyEvent->getJoystick() != joyNum)
return;
321 createEvent(time, calcState());
324 void Joystick::createEvent(EmuTime::param time,
byte newStatus)
326 byte diff = status ^ newStatus;
332 byte press = status & diff;
333 byte release = newStatus & diff;
335 time, joyNum, press, release);
339 void Joystick::signalStateChange(
const StateChange& event)
341 const auto* js =
dynamic_cast<const JoyState*
>(&event);
349 if (js->getJoystick() != joyNum)
return;
351 status = (status & ~js->getPress()) | js->getRelease();
354 void Joystick::stopReplay(EmuTime::param time) noexcept
356 createEvent(time, calcState());
362 template<
typename Archive>
365 if (ar.versionAtLeast(version, 2)) {
366 ar.serialize(
"status", status);
368 if constexpr (Archive::IS_LOADER) {
virtual Interpreter & getInterpreter()=0
Represents something you can plug devices into.
This class contains settings that are used by several other class (including some singletons).
int getInt() const noexcept
unsigned getJoystick() const
void serialize(Archive &ar, unsigned)
JoyState(EmuTime::param time_, unsigned joyNum_, byte press_, byte release_)
static constexpr int JOY_BUTTONA
static constexpr int JOY_RIGHT
static constexpr int JOY_LEFT
static constexpr int JOY_DOWN
static constexpr int JOY_BUTTONB
static constexpr int JOY_UP
Uses an SDL joystick to emulate an MSX joystick.
std::string_view getName() const override
Name used to identify this pluggable.
void unplugHelper(EmuTime::param time) override
std::string_view getDescription() const override
Description for this pluggable.
void write(byte value, EmuTime::param time) override
Write a value to the joystick device.
void plugHelper(Connector &connector, EmuTime::param time) override
static void registerAll(MSXEventDistributor &eventDistributor, StateChangeDistributor &stateChangeDistributor, CommandController &commandController, GlobalSettings &globalSettings, PluggingController &controller)
Register all available SDL joysticks.
void serialize(Archive &ar, unsigned version)
byte read(EmuTime::param time) override
Read from the joystick device.
Joystick(MSXEventDistributor &eventDistributor, StateChangeDistributor &stateChangeDistributor, CommandController &commandController, GlobalSettings &globalSettings, SDL_Joystick *joystick)
void registerEventListener(MSXEventListener &listener)
Registers a given object to receive certain events.
void unregisterEventListener(MSXEventListener &listener)
Unregisters a previously registered event listener.
Thrown when a plug action fails.
bool isPluggedIn() const
Returns true if this pluggable is currently plugged into a connector.
Central administration of Connectors and Pluggables.
void registerPluggable(std::unique_ptr< Pluggable > pluggable)
Add a Pluggable to the registry.
void setChecker(std::function< void(TclObject &)> checkFunc_)
Set value-check-callback.
const TclObject & getValue() const final
Gets the current value of this setting as a TclObject.
Interpreter & getInterpreter() const
void registerListener(StateChangeListener &listener)
(Un)registers the given object to receive state change events.
void distributeNew(EmuTime::param time, Args &&...args)
Deliver the event to all registered listeners MSX input devices should call the distributeNew() versi...
void unregisterListener(StateChangeListener &listener)
Base class for all external MSX state changing events.
unsigned getListLength(Interpreter &interp) const
TclObject getListIndex(Interpreter &interp, unsigned index) const
zstring_view getString() const
This file implemented 3 utility functions:
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, CassettePlayer, "CassettePlayer")
REGISTER_POLYMORPHIC_CLASS(StateChange, AutofireStateChange, "AutofireStateChange")
TclObject makeTclList(Args &&... args)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
TemporaryString tmpStrCat(Ts &&... ts)
std::string strCat(Ts &&...ts)
constexpr auto xrange(T e)