openMSX
KeyJoystick.cc
Go to the documentation of this file.
1#include "KeyJoystick.hh"
4#include "Event.hh"
5#include "StateChange.hh"
6#include "serialize.hh"
7#include "serialize_meta.hh"
8
9namespace openmsx {
10
11static std::string_view nameForId(KeyJoystick::ID id)
12{
13 switch (id) {
14 case KeyJoystick::ID1: return "keyjoystick1";
15 case KeyJoystick::ID2: return "keyjoystick2";
16 default: return "unknown-keyjoystick";
17 }
18}
19
20class KeyJoyState final : public StateChange
21{
22public:
23 KeyJoyState() = default; // for serialize
24 KeyJoyState(EmuTime::param time_, KeyJoystick::ID id_,
25 byte press_, byte release_)
26 : StateChange(time_)
27 , id(id_), press(press_), release(release_) {}
28 [[nodiscard]] auto getId() const { return id; }
29 [[nodiscard]] byte getPress() const { return press; }
30 [[nodiscard]] byte getRelease() const { return release; }
31 template<typename Archive> void serialize(Archive& ar, unsigned /*version*/)
32 {
33 ar.template serializeBase<StateChange>(*this);
34 // for backwards compatibility serialize 'id' as 'name'
35 std::string name = Archive::IS_LOADER ? "" : std::string(nameForId(id));
36 ar.serialize("name", name,
37 "press", press,
38 "release", release);
39 if constexpr (Archive::IS_LOADER) {
40 id = (name == nameForId(KeyJoystick::ID1)) ? KeyJoystick::ID1
41 : (name == nameForId(KeyJoystick::ID2)) ? KeyJoystick::ID2
43 }
44 }
45
46private:
48 byte press, release;
49};
51
53 MSXEventDistributor& eventDistributor_,
54 StateChangeDistributor& stateChangeDistributor_,
55 ID id_)
56 : eventDistributor(eventDistributor_)
57 , stateChangeDistributor(stateChangeDistributor_)
58 , up (commandController, tmpStrCat(nameForId(id_), ".up"),
59 "key for direction up", Keys::K_UP)
60 , down (commandController, tmpStrCat(nameForId(id_), ".down"),
61 "key for direction down", Keys::K_DOWN)
62 , left (commandController, tmpStrCat(nameForId(id_), ".left"),
63 "key for direction left", Keys::K_LEFT)
64 , right(commandController, tmpStrCat(nameForId(id_), ".right"),
65 "key for direction right", Keys::K_RIGHT)
66 , trigA(commandController, tmpStrCat(nameForId(id_), ".triga"),
67 "key for trigger A", Keys::K_SPACE)
68 , trigB(commandController, tmpStrCat(nameForId(id_), ".trigb"),
69 "key for trigger B", Keys::K_M)
70 , id(id_)
71 , status(JOY_UP | JOY_DOWN | JOY_LEFT | JOY_RIGHT |
72 JOY_BUTTONA | JOY_BUTTONB)
73{
74}
75
77{
78 if (isPluggedIn()) {
79 KeyJoystick::unplugHelper(EmuTime::dummy());
80 }
81}
82
83
84// Pluggable
85std::string_view KeyJoystick::getName() const
86{
87 return nameForId(id);
88}
89
90std::string_view KeyJoystick::getDescription() const
91{
92 return "Key-Joystick, use your keyboard to emulate an MSX joystick. "
93 "See manual for information on how to configure this.";
94}
95
96void KeyJoystick::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/)
97{
98 eventDistributor.registerEventListener(*this);
99 stateChangeDistributor.registerListener(*this);
100}
101
102void KeyJoystick::unplugHelper(EmuTime::param /*time*/)
103{
104 stateChangeDistributor.unregisterListener(*this);
105 eventDistributor.unregisterEventListener(*this);
106}
107
108
109// KeyJoystickDevice
110byte KeyJoystick::read(EmuTime::param /*time*/)
111{
112 return pin8 ? 0x3F : status;
113}
114
115void KeyJoystick::write(byte value, EmuTime::param /*time*/)
116{
117 pin8 = (value & 0x04) != 0;
118}
119
120
121// MSXEventListener
122void KeyJoystick::signalMSXEvent(const Event& event,
123 EmuTime::param time) noexcept
124{
125 byte press = 0;
126 byte release = 0;
127 auto getKey = [&](const KeyEvent& e) {
128 auto key = static_cast<Keys::KeyCode>(
129 int(e.getKeyCode()) & int(Keys::K_MASK));
130 if (key == up .getKey()) return JOY_UP;
131 else if (key == down .getKey()) return JOY_DOWN;
132 else if (key == left .getKey()) return JOY_LEFT;
133 else if (key == right.getKey()) return JOY_RIGHT;
134 else if (key == trigA.getKey()) return JOY_BUTTONA;
135 else if (key == trigB.getKey()) return JOY_BUTTONB;
136 else return 0;
137 };
139 [&](const KeyDownEvent& e) { press = getKey(e); },
140 [&](const KeyUpEvent& e) { release = getKey(e); },
141 [](const EventBase&) { /*ignore*/ }
142 }, event);
143
144 if (((status & ~press) | release) != status) {
145 stateChangeDistributor.distributeNew<KeyJoyState>(
146 time, id, press, release);
147 }
148}
149
150// StateChangeListener
151void KeyJoystick::signalStateChange(const StateChange& event)
152{
153 const auto* kjs = dynamic_cast<const KeyJoyState*>(&event);
154 if (!kjs) return;
155 if (kjs->getId() != id) return;
156
157 status = (status & ~kjs->getPress()) | kjs->getRelease();
158}
159
160void KeyJoystick::stopReplay(EmuTime::param time) noexcept
161{
162 // TODO read actual host key state
163 byte newStatus = JOY_UP | JOY_DOWN | JOY_LEFT | JOY_RIGHT |
164 JOY_BUTTONA | JOY_BUTTONB;
165 if (newStatus != status) {
166 byte release = newStatus & ~status;
167 stateChangeDistributor.distributeNew<KeyJoyState>(
168 time, id, 0, release);
169 }
170}
171
172
173// version 1: Initial version, the variable status was not serialized.
174// version 2: Also serialize the above variable, this is required for
175// record/replay, see comment in Keyboard.cc for more details.
176template<typename Archive>
177void KeyJoystick::serialize(Archive& ar, unsigned version)
178{
179 if (ar.versionAtLeast(version, 2)) {
180 ar.serialize("status", status);
181 }
182 if constexpr (Archive::IS_LOADER) {
183 if (isPluggedIn()) {
184 plugHelper(*getConnector(), EmuTime::dummy());
185 }
186 }
187 // no need to serialize 'pin8'
188}
191
192} // namespace openmsx
uintptr_t id
Definition: Interpreter.cc:26
void serialize(Archive &ar, unsigned)
Definition: KeyJoystick.cc:31
auto getId() const
Definition: KeyJoystick.cc:28
KeyJoyState(EmuTime::param time_, KeyJoystick::ID id_, byte press_, byte release_)
Definition: KeyJoystick.cc:24
byte getPress() const
Definition: KeyJoystick.cc:29
byte getRelease() const
Definition: KeyJoystick.cc:30
KeyJoystick(CommandController &commandController, MSXEventDistributor &eventDistributor, StateChangeDistributor &stateChangeDistributor, ID id)
Definition: KeyJoystick.cc:52
~KeyJoystick() override
Definition: KeyJoystick.cc:76
void serialize(Archive &ar, unsigned version)
Definition: KeyJoystick.cc:177
void registerEventListener(MSXEventListener &listener)
Registers a given object to receive certain events.
void unregisterEventListener(MSXEventListener &listener)
Unregisters a previously registered event listener.
bool isPluggedIn() const
Returns true if this pluggable is currently plugged into a connector.
Definition: Pluggable.hh:49
Connector * getConnector() const
Get the connector this Pluggable is plugged into.
Definition: Pluggable.hh:43
void registerListener(StateChangeListener &listener)
(Un)registers the given object to receive state change events.
void unregisterListener(StateChangeListener &listener)
Base class for all external MSX state changing events.
Definition: StateChange.hh:20
constexpr double e
Definition: Math.hh:18
KeyCode
Constants that identify keys and key modifiers.
Definition: Keys.hh:26
This file implemented 3 utility functions:
Definition: Autofire.cc:9
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, CassettePlayer, "CassettePlayer")
auto visit(Visitor &&visitor, const Event &event)
Definition: Event.hh:652
REGISTER_POLYMORPHIC_CLASS(StateChange, AutofireStateChange, "AutofireStateChange")
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1009
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:617