openMSX
KeyJoystick.cc
Go to the documentation of this file.
1 #include "KeyJoystick.hh"
2 #include "MSXEventDistributor.hh"
4 #include "Event.hh"
5 #include "StateChange.hh"
6 #include "serialize.hh"
7 #include "serialize_meta.hh"
8 
9 namespace openmsx {
10 
11 static 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 
20 class KeyJoyState final : public StateChange
21 {
22 public:
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 
46 private:
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 {
72  status = JOY_UP | JOY_DOWN | JOY_LEFT | JOY_RIGHT |
74 }
75 
77 {
78  if (isPluggedIn()) {
79  KeyJoystick::unplugHelper(EmuTime::dummy());
80  }
81 }
82 
83 
84 // Pluggable
85 std::string_view KeyJoystick::getName() const
86 {
87  return nameForId(id);
88 }
89 
90 std::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 
96 void KeyJoystick::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/)
97 {
98  eventDistributor.registerEventListener(*this);
99  stateChangeDistributor.registerListener(*this);
100 }
101 
102 void KeyJoystick::unplugHelper(EmuTime::param /*time*/)
103 {
104  stateChangeDistributor.unregisterListener(*this);
105  eventDistributor.unregisterEventListener(*this);
106 }
107 
108 
109 // KeyJoystickDevice
110 byte KeyJoystick::read(EmuTime::param /*time*/)
111 {
112  return pin8 ? 0x3F : status;
113 }
114 
115 void KeyJoystick::write(byte value, EmuTime::param /*time*/)
116 {
117  pin8 = (value & 0x04) != 0;
118 }
119 
120 
121 // MSXEventListener
122 void 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
151 void 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 
160 void 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.
176 template<typename Archive>
177 void 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
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
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
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:653
REGISTER_POLYMORPHIC_CLASS(StateChange, AutofireStateChange, "AutofireStateChange")
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:998
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:659