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 uint8_t press_, uint8_t release_)
26 : StateChange(time_)
27 , id(id_), press(press_), release(release_) {}
28 [[nodiscard]] auto getId() const { return id; }
29 [[nodiscard]] uint8_t getPress() const { return press; }
30 [[nodiscard]] uint8_t 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 uint8_t 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", SDLKey::createDown(SDLK_UP))
60 , down (commandController, tmpStrCat(nameForId(id_), ".down"),
61 "key for direction down", SDLKey::createDown(SDLK_DOWN))
62 , left (commandController, tmpStrCat(nameForId(id_), ".left"),
63 "key for direction left", SDLKey::createDown(SDLK_LEFT))
64 , right(commandController, tmpStrCat(nameForId(id_), ".right"),
65 "key for direction right", SDLKey::createDown(SDLK_RIGHT))
66 , trigA(commandController, tmpStrCat(nameForId(id_), ".triga"),
67 "key for trigger A", SDLKey::createDown(SDLK_SPACE))
68 , trigB(commandController, tmpStrCat(nameForId(id_), ".trigb"),
69 "key for trigger B", SDLKey::createDown(SDLK_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
110uint8_t KeyJoystick::read(EmuTime::param /*time*/)
111{
112 return pin8 ? 0x3F : status;
113}
114
115void KeyJoystick::write(uint8_t 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 uint8_t press = 0;
126 uint8_t release = 0;
127 auto getKey = [&](const KeyEvent& e) {
128 auto key = e.getKeyCode();
129 if (key == up .getKey().sym.sym) return JOY_UP;
130 else if (key == down .getKey().sym.sym) return JOY_DOWN;
131 else if (key == left .getKey().sym.sym) return JOY_LEFT;
132 else if (key == right.getKey().sym.sym) return JOY_RIGHT;
133 else if (key == trigA.getKey().sym.sym) return JOY_BUTTONA;
134 else if (key == trigB.getKey().sym.sym) return JOY_BUTTONB;
135 else return uint8_t(0);
136 };
137 visit(overloaded{
138 [&](const KeyDownEvent& e) { press = getKey(e); },
139 [&](const KeyUpEvent& e) { release = getKey(e); },
140 [](const EventBase&) { /*ignore*/ }
141 }, event);
142
143 if (((status & ~press) | release) != status) {
144 stateChangeDistributor.distributeNew<KeyJoyState>(
145 time, id, press, release);
146 }
147}
148
149// StateChangeListener
150void KeyJoystick::signalStateChange(const StateChange& event)
151{
152 const auto* kjs = dynamic_cast<const KeyJoyState*>(&event);
153 if (!kjs) return;
154 if (kjs->getId() != id) return;
155
156 status = (status & ~kjs->getPress()) | kjs->getRelease();
157}
158
159void KeyJoystick::stopReplay(EmuTime::param time) noexcept
160{
161 // TODO read actual host key state
162 uint8_t newStatus = JOY_UP | JOY_DOWN | JOY_LEFT | JOY_RIGHT |
163 JOY_BUTTONA | JOY_BUTTONB;
164 if (newStatus != status) {
165 uint8_t release = newStatus & ~status;
166 stateChangeDistributor.distributeNew<KeyJoyState>(
167 time, id, uint8_t(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.
175template<typename Archive>
176void 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
uintptr_t id
KeyJoyState(EmuTime::param time_, KeyJoystick::ID id_, uint8_t press_, uint8_t release_)
void serialize(Archive &ar, unsigned)
uint8_t getPress() const
uint8_t getRelease() const
KeyJoystick(CommandController &commandController, MSXEventDistributor &eventDistributor, StateChangeDistributor &stateChangeDistributor, ID id)
~KeyJoystick() override
void serialize(Archive &ar, unsigned version)
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.
constexpr double e
Definition Math.hh:21
This file implemented 3 utility functions:
Definition Autofire.cc:9
std::variant< KeyUpEvent, KeyDownEvent, MouseMotionEvent, MouseButtonUpEvent, MouseButtonDownEvent, MouseWheelEvent, JoystickAxisMotionEvent, JoystickHatEvent, JoystickButtonUpEvent, JoystickButtonDownEvent, OsdControlReleaseEvent, OsdControlPressEvent, WindowEvent, TextEvent, FileDropEvent, QuitEvent, FinishFrameEvent, CliCommandEvent, GroupEvent, BootEvent, FrameDrawnEvent, BreakEvent, SwitchRendererEvent, TakeReverseSnapshotEvent, AfterTimedEvent, MachineLoadedEvent, MachineActivatedEvent, MachineDeactivatedEvent, MidiInReaderEvent, MidiInWindowsEvent, MidiInCoreMidiEvent, MidiInCoreMidiVirtualEvent, MidiInALSAEvent, Rs232TesterEvent, Rs232NetEvent, ImGuiDelayedActionEvent, ImGuiActiveEvent > Event
Definition Event.hh:454
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define REGISTER_POLYMORPHIC_INITIALIZER(BASE, CLASS, NAME)
#define REGISTER_POLYMORPHIC_CLASS(BASE, CLASS, NAME)
TemporaryString tmpStrCat(Ts &&... ts)
Definition strCat.hh:742