openMSX
EventDelay.cc
Go to the documentation of this file.
1 #include "EventDelay.hh"
2 #include "EventDistributor.hh"
3 #include "MSXEventDistributor.hh"
4 #include "ReverseManager.hh"
5 #include "InputEvents.hh"
6 #include "FloatSetting.hh"
7 #include "Timer.hh"
8 #include "MSXException.hh"
9 #include "checked_cast.hh"
10 #include "memory.hh"
11 #include "stl.hh"
12 #include <cassert>
13 
14 using std::make_shared;
15 
16 namespace openmsx {
17 
20  EventDistributor& eventDistributor_,
21  MSXEventDistributor& msxEventDistributor_,
22  ReverseManager& reverseManager)
23  : Schedulable(scheduler)
24  , eventDistributor(eventDistributor_)
25  , msxEventDistributor(msxEventDistributor_)
26  , prevEmu(EmuTime::zero)
27  , prevReal(Timer::getTime())
28  , delaySetting(make_unique<FloatSetting>(
29  commandController, "inputdelay",
30  "delay input to avoid key-skips", 0.0, 0.0, 10.0))
31 {
32  eventDistributor.registerEventListener(
34  eventDistributor.registerEventListener(
36 
37  eventDistributor.registerEventListener(
39  eventDistributor.registerEventListener(
41  eventDistributor.registerEventListener(
43 
44  eventDistributor.registerEventListener(
46  eventDistributor.registerEventListener(
48  eventDistributor.registerEventListener(
50  eventDistributor.registerEventListener(
52 
53  reverseManager.registerEventDelay(*this);
54 }
55 
57 {
58  eventDistributor.unregisterEventListener(
59  OPENMSX_KEY_DOWN_EVENT, *this);
60  eventDistributor.unregisterEventListener(
61  OPENMSX_KEY_UP_EVENT, *this);
62 
63  eventDistributor.unregisterEventListener(
65  eventDistributor.unregisterEventListener(
67  eventDistributor.unregisterEventListener(
69 
70  eventDistributor.unregisterEventListener(
72  eventDistributor.unregisterEventListener(
73  OPENMSX_JOY_HAT_EVENT, *this);
74  eventDistributor.unregisterEventListener(
76  eventDistributor.unregisterEventListener(
78 }
79 
80 int EventDelay::signalEvent(const EventPtr& event)
81 {
82  toBeScheduledEvents.push_back(event);
83  if (delaySetting->getDouble() == 0.0) {
85  }
86  return 0;
87 }
88 
90 {
91  auto curRealTime = Timer::getTime();
92  auto realDuration = curRealTime - prevReal;
93  prevReal = curRealTime;
94  auto emuDuration = curEmu - prevEmu;
95  prevEmu = curEmu;
96 
97  double factor = emuDuration.toDouble() / realDuration;
98  EmuDuration extraDelay(delaySetting->getDouble());
99 
100 #if PLATFORM_ANDROID
101  // The virtual keyboard on Android sends a key press and the
102  // corresponding key release event directly after each other, without a
103  // delay. It sends both events either when the user has finished a
104  // short tap or alternatively after the user has hold the button
105  // pressed for a few seconds and then has selected the appropriate
106  // character from the multi-character-popup that the virtual keyboard
107  // displays when the user holds a button pressed for a short while.
108  // Either way, the key release event comes way too short after the
109  // press event for the MSX to process it. The two events follow each
110  // other within a few milliseconds at most. Therefore, on Android,
111  // special logic must be foreseen to delay the release event for a
112  // short while. This special logic correlates each key release event
113  // with the corresponding press event for the same key. If they are
114  // less then 2/50 second apart, the release event gets delayed until
115  // the next sync call. The 2/50 second has been chosen because it can
116  // take up to 2 vertical interrupts (2 screen refreshes) for the MSX to
117  // see the key press in the keyboard matrix, thus, 2/50 seconds is the
118  // minimum delay required for an MSX running in PAL mode.
119  std::vector<EventPtr> toBeRescheduledEvents;
120 #endif
121 
122  EmuTime time = curEmu + extraDelay;
123  for (auto& e : toBeScheduledEvents) {
124 #if PLATFORM_ANDROID
125  if (e->getType() == OPENMSX_KEY_DOWN_EVENT ||
126  e->getType() == OPENMSX_KEY_UP_EVENT) {
127  auto keyEvent = checked_cast<const KeyEvent*>(e.get());
128  int maskedKeyCode = int(keyEvent->getKeyCode()) & int(Keys::K_MASK);
129  auto it = find_if(begin(nonMatchedKeyPresses), end(nonMatchedKeyPresses),
130  EqualTupleValue<0>(maskedKeyCode));
131  if (e->getType() == OPENMSX_KEY_DOWN_EVENT) {
132  if (it == end(nonMatchedKeyPresses)) {
133  nonMatchedKeyPresses.emplace_back(maskedKeyCode, e);
134  } else {
135  it->second = e;
136  }
137  } else {
138  if (it != end(nonMatchedKeyPresses)) {
139  auto timedPressEvent = checked_cast<const TimedEvent*>(it->second.get());
140  auto timedReleaseEvent = checked_cast<const TimedEvent*>(e.get());
141  auto pressRealTime = timedPressEvent->getRealTime();
142  auto releaseRealTime = timedReleaseEvent->getRealTime();
143  auto deltaTime = releaseRealTime - pressRealTime;
144  if (deltaTime <= 2000000 / 50) {
145  // The key release came less then 2 MSX interrupts from the key press.
146  // Reschedule it for the next sync, with the realTime updated to now, so that it seems like the
147  // key was released now and not when android released it.
148  // Otherwise, the offset calculation for the emutime further down below will go wrong on the next sync
149  EventPtr newKeyupEvent = make_shared<KeyUpEvent>(keyEvent->getKeyCode(), keyEvent->getUnicode());
150  toBeRescheduledEvents.push_back(newKeyupEvent);
151  continue; // continue with next to be scheduled event
152  }
153  auto backIt = end(nonMatchedKeyPresses) - 1;
154  if (it != backIt) std::swap(*it, *backIt);
155  nonMatchedKeyPresses.pop_back();
156  }
157  }
158  }
159 #endif
160  scheduledEvents.push_back(e);
161  auto timedEvent = checked_cast<const TimedEvent*>(e.get());
162  auto eventRealTime = timedEvent->getRealTime();
163  assert(eventRealTime <= curRealTime);
164  auto offset = curRealTime - eventRealTime;
165  EmuDuration emuOffset(factor * offset);
166  auto schedTime = (emuOffset < extraDelay)
167  ? time - emuOffset
168  : curEmu;
169  assert(curEmu <= schedTime);
170  setSyncPoint(schedTime);
171  }
172  toBeScheduledEvents.clear();
173 
174 #if PLATFORM_ANDROID
175  toBeScheduledEvents.insert(end(toBeScheduledEvents),
176  make_move_iterator(begin(toBeRescheduledEvents)),
177  make_move_iterator(end (toBeRescheduledEvents)));
178 #endif
179 }
180 
181 void EventDelay::executeUntil(EmuTime::param time, int /*userData*/)
182 {
183  try {
184  auto event = std::move(scheduledEvents.front());
185  scheduledEvents.pop_front();
186  msxEventDistributor.distributeEvent(std::move(event), time);
187  } catch (MSXException&) {
188  // ignore
189  }
190 }
191 
193 {
194  EmuTime time = getCurrentTime();
195 
196  for (auto& e : scheduledEvents) {
197  msxEventDistributor.distributeEvent(e, time);
198  }
199  scheduledEvents.clear();
200 
201  for (auto& e : toBeScheduledEvents) {
202  msxEventDistributor.distributeEvent(e, time);
203  }
204  toBeScheduledEvents.clear();
205 
207 }
208 
209 } // namespace openmsx
signed char offset
Definition: CPUCore.cc:252
void sync(EmuTime::param time)
Definition: EventDelay.cc:89
A Setting with a floating point value.
Definition: FloatSetting.hh:10
string_ref::const_iterator end(const string_ref &x)
Definition: string_ref.hh:135
void registerEventListener(EventType type, EventListener &listener, Priority priority=OTHER)
Registers a given object to receive certain events.
void registerEventDelay(EventDelay &eventDelay_)
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
void setSyncPoint(EmuTime::param timestamp, int userData=0)
Definition: Schedulable.cc:25
std::shared_ptr< const Event > EventPtr
void distributeEvent(const EventPtr &event, EmuTime::param time)
Deliver the event to all registered listeners.
Every class that wants to get scheduled at some point must inherit from this class.
Definition: Schedulable.hh:16
uint64_t getRealTime() const
Query creation time.
Definition: InputEvents.hh:15
EventDelay(Scheduler &scheduler, CommandController &commandController, EventDistributor &eventDistributor, MSXEventDistributor &msxEventDistributor, ReverseManager &reverseManager)
Definition: EventDelay.cc:18
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
Definition: Schedulable.cc:51
eventDistributor(eventDistributor_)
commandController(commandController_)
uint64_t getTime()
Get current (real) time in us.
Definition: Timer.cc:24
string_ref::const_iterator begin(const string_ref &x)
Definition: string_ref.hh:134
std::unique_ptr< T > make_unique()
Definition: memory.hh:27