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