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