21 using std::shared_ptr;
30 byte press_,
byte release_)
32 , deltaX(deltaX_), deltaY(deltaY_)
33 , press(press_), release(release_) {}
34 [[nodiscard]]
int getDeltaX()
const {
return deltaX; }
35 [[nodiscard]]
int getDeltaY()
const {
return deltaY; }
36 [[nodiscard]]
byte getPress()
const {
return press; }
37 [[nodiscard]]
byte getRelease()
const {
return release; }
39 template<
typename Archive>
void serialize(Archive& ar,
unsigned )
41 ar.template serializeBase<StateChange>(*
this);
42 ar.serialize(
"deltaX", deltaX,
56 : eventDistributor(eventDistributor_)
57 , stateChangeDistributor(stateChangeDistributor_)
58 , lastSync(EmuTime::zero())
59 , targetDeltaX(0), targetDeltaY(0)
60 , currentDeltaX(0), currentDeltaY(0)
62 , status(JOY_BUTTONA | JOY_BUTTONB)
70 Trackball::unplugHelper(EmuTime::dummy());
76 std::string_view Trackball::getName()
const
81 std::string_view Trackball::getDescription()
const
83 return "MSX Trackball";
86 void Trackball::plugHelper(
Connector& , EmuTime::param time)
97 void Trackball::unplugHelper(EmuTime::param )
104 byte Trackball::read(EmuTime::param time)
125 syncCurrentWithTarget(time);
126 auto delta = (lastValue & 4) ? currentDeltaY : currentDeltaX;
127 return (status & ~0x0F) | ((delta + 8) & 0x0F);
130 void Trackball::write(
byte value, EmuTime::param time)
132 syncCurrentWithTarget(time);
133 byte diff = lastValue ^ value;
138 targetDeltaX =
std::clamp(targetDeltaX - currentDeltaX, -8, 7);
141 targetDeltaY =
std::clamp(targetDeltaY - currentDeltaY, -8, 7);
147 void Trackball::syncCurrentWithTarget(EmuTime::param time)
184 currentDeltaX = targetDeltaX;
185 currentDeltaY = targetDeltaY;
191 int maxSteps = (time - lastSync) / INTERVAL;
192 lastSync += INTERVAL * maxSteps;
194 if (targetDeltaX >= currentDeltaX) {
195 currentDeltaX = std::min<int>(currentDeltaX + maxSteps, targetDeltaX);
197 currentDeltaX = std::max<int>(currentDeltaX - maxSteps, targetDeltaX);
199 if (targetDeltaY >= currentDeltaY) {
200 currentDeltaY = std::min<int>(currentDeltaY + maxSteps, targetDeltaY);
202 currentDeltaY = std::max<int>(currentDeltaY - maxSteps, targetDeltaY);
207 void Trackball::signalMSXEvent(
const shared_ptr<const Event>& event,
208 EmuTime::param time) noexcept
210 switch (event->getType()) {
212 const auto& mev = checked_cast<const MouseMotionEvent&>(*event);
213 constexpr
int SCALE = 2;
214 int dx = mev.getX() /
SCALE;
215 int dy = mev.getY() /
SCALE;
216 if ((dx != 0) || (dy != 0)) {
217 createTrackballStateChange(time, dx, dy, 0, 0);
222 const auto& butEv = checked_cast<const MouseButtonEvent&>(*event);
223 switch (butEv.getButton()) {
225 createTrackballStateChange(time, 0, 0, JOY_BUTTONA, 0);
228 createTrackballStateChange(time, 0, 0, JOY_BUTTONB, 0);
237 const auto& butEv = checked_cast<const MouseButtonEvent&>(*event);
238 switch (butEv.getButton()) {
240 createTrackballStateChange(time, 0, 0, 0, JOY_BUTTONA);
243 createTrackballStateChange(time, 0, 0, 0, JOY_BUTTONB);
257 void Trackball::createTrackballStateChange(
258 EmuTime::param time,
int deltaX,
int deltaY,
byte press,
byte release)
260 stateChangeDistributor.
distributeNew(std::make_shared<TrackballState>(
261 time, deltaX, deltaY, press, release));
265 void Trackball::signalStateChange(
const shared_ptr<StateChange>& event)
267 const auto* ts =
dynamic_cast<const TrackballState*
>(
event.get());
270 targetDeltaX =
std::clamp(targetDeltaX + ts->getDeltaX(), -8, 7);
271 targetDeltaY =
std::clamp(targetDeltaY + ts->getDeltaY(), -8, 7);
272 status = (status & ~ts->getPress()) | ts->getRelease();
275 void Trackball::stopReplay(EmuTime::param time) noexcept
277 syncCurrentWithTarget(time);
279 byte release = (JOY_BUTTONA | JOY_BUTTONB) & ~status;
280 if ((currentDeltaX != 0) || (currentDeltaY != 0) || (release != 0)) {
281 stateChangeDistributor.distributeNew(
282 std::make_shared<TrackballState>(
283 time, -currentDeltaX, -currentDeltaY, 0, release));
289 template<
typename Archive>
292 if (ar.versionAtLeast(version, 2)) {
293 ar.serialize(
"lastSync", lastSync,
294 "targetDeltaX", targetDeltaX,
295 "targetDeltaY", targetDeltaY,
296 "currentDeltaX", currentDeltaX,
297 "currentDeltaY", currentDeltaY);
299 ar.serialize(
"deltaX", targetDeltaX,
300 "deltaY", targetDeltaY);
301 currentDeltaX = targetDeltaX;
302 currentDeltaY = targetDeltaY;
305 ar.serialize(
"lastValue", lastValue,
static constexpr EmuDuration msec(unsigned x)
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.
Connector * getConnector() const
Get the connector this Pluggable is plugged into.
void registerListener(StateChangeListener &listener)
(Un)registers the given object to receive state change events.
void distributeNew(const EventPtr &event)
Deliver the event to all registered listeners MSX input devices should call the distributeNew() versi...
void unregisterListener(StateChangeListener &listener)
Base class for all external MSX state changing events.
TrackballState(EmuTime::param time_, int deltaX_, int deltaY_, byte press_, byte release_)
void serialize(Archive &ar, unsigned)
Trackball(MSXEventDistributor &eventDistributor, StateChangeDistributor &stateChangeDistributor)
void serialize(Archive &ar, unsigned version)
constexpr vecN< N, T > clamp(const vecN< N, T > &x, const vecN< N, T > &minVal, const vecN< N, T > &maxVal)
This file implemented 3 utility functions:
@ OPENMSX_MOUSE_BUTTON_DOWN_EVENT
@ OPENMSX_MOUSE_BUTTON_UP_EVENT
@ OPENMSX_MOUSE_MOTION_EVENT
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, CassettePlayer, "CassettePlayer")
REGISTER_POLYMORPHIC_CLASS(DiskContainer, NowindRomDisk, "NowindRomDisk")
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)