openMSX
InputEventGenerator.cc
Go to the documentation of this file.
1 #include "InputEventGenerator.hh"
2 #include "EventDistributor.hh"
3 #include "InputEvents.hh"
4 #include "IntegerSetting.hh"
5 #include "GlobalSettings.hh"
6 #include "Keys.hh"
7 #include "checked_cast.hh"
8 #include "memory.hh"
9 #include "outer.hh"
10 #include "unreachable.hh"
11 #include "build-info.hh"
12 #include <cassert>
13 #include <iostream>
14 
15 using std::string;
16 using std::vector;
17 using std::make_shared;
18 
19 namespace openmsx {
20 
21 bool InputEventGenerator::androidButtonA = false;
22 bool InputEventGenerator::androidButtonB = false;
23 
25  EventDistributor& eventDistributor_,
26  GlobalSettings& globalSettings_)
27  : eventDistributor(eventDistributor_)
28  , globalSettings(globalSettings_)
29  , grabInput(
30  commandController, "grabinput",
31  "This setting controls if openMSX takes over mouse and keyboard input",
32  false, Setting::DONT_SAVE)
33  , escapeGrabCmd(commandController)
34  , escapeGrabState(ESCAPE_GRAB_WAIT_CMD)
35  , keyRepeat(false)
36 {
37  setGrabInput(grabInput.getBoolean());
38  grabInput.attach(*this);
39  eventDistributor.registerEventListener(OPENMSX_FOCUS_EVENT, *this);
40 
41  reinit();
42 
43  osdControlButtonsState = unsigned(~0); // 0 is pressed, 1 is released
44 
45 #ifndef SDL_JOYSTICK_DISABLED
46  SDL_JoystickEventState(SDL_ENABLE); // joysticks generate events
47 #endif
48 }
49 
51 {
52  eventDistributor.unregisterEventListener(OPENMSX_FOCUS_EVENT, *this);
53  grabInput.detach(*this);
54 }
55 
57 {
58  SDL_EnableUNICODE(1);
59  setKeyRepeat(keyRepeat);
60 }
61 
63 {
64  // SDL bug workaround
65  if (!SDL_WasInit(SDL_INIT_VIDEO)) {
66  SDL_Delay(100);
67  }
68 
69  if (SDL_WaitEvent(nullptr)) {
70  poll();
71  }
72 }
73 
75 {
76  SDL_Event event;
77  while (SDL_PollEvent(&event) == 1) {
78 #if 0
79  string t;
80  switch (event.type) {
81  case SDL_ACTIVEEVENT: t = "SDL_ACTIVEEVENT"; break;
82  case SDL_KEYDOWN: t = "SDL_KEYDOWN"; break;
83  case SDL_KEYUP: t = "SDL_KEYUP"; break;
84  case SDL_MOUSEMOTION: t = "SDL_MOUSEMOTION"; break;
85  case SDL_MOUSEBUTTONDOWN: t = "SDL_MOUSEBUTTONDOWN"; break;
86  case SDL_MOUSEBUTTONUP: t = "SDL_MOUSEBUTTONUP"; break;
87  case SDL_JOYAXISMOTION: t = "SDL_JOYAXISMOTION"; break;
88  case SDL_JOYBALLMOTION: t = "SDL_JOYBALLMOTION"; break;
89  case SDL_JOYHATMOTION: t = "SDL_JOYHATMOTION"; break;
90  case SDL_JOYBUTTONDOWN: t = "SDL_JOYBUTTONDOWN"; break;
91  case SDL_JOYBUTTONUP: t = "SDL_JOYBUTTONUP"; break;
92  case SDL_QUIT: t = "SDL_QUIT"; break;
93  case SDL_SYSWMEVENT: t = "SDL_SYSWMEVENT"; break;
94  case SDL_VIDEORESIZE: t = "SDL_VIDEORESIZE"; break;
95  case SDL_VIDEOEXPOSE: t = "SDL_VIDEOEXPOSE"; break;
96  case SDL_USEREVENT: t = "SDL_USEREVENT"; break;
97  default: t = "UNKNOWN"; break;
98  }
99  std::cerr << "SDL event received, type: " << t << std::endl;
100 #endif
101  handle(event);
102  }
103 }
104 
106 {
107  keyRepeat = enable;
108  if (keyRepeat) {
109  SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
110  SDL_DEFAULT_REPEAT_INTERVAL);
111  } else {
112  SDL_EnableKeyRepeat(0, 0);
113  }
114 }
115 
116 
117 void InputEventGenerator::setNewOsdControlButtonState(
118  unsigned newState, const EventPtr& origEvent)
119 {
120  unsigned deltaState = osdControlButtonsState ^ newState;
121  for (unsigned i = OsdControlEvent::LEFT_BUTTON;
122  i <= OsdControlEvent::B_BUTTON; ++i) {
123  if (deltaState & (1 << i)) {
124  if (newState & (1 << i)) {
125  eventDistributor.distributeEvent(
126  make_shared<OsdControlReleaseEvent>(
127  i, origEvent));
128  } else {
129  eventDistributor.distributeEvent(
130  make_shared<OsdControlPressEvent>(
131  i, origEvent));
132  }
133  }
134  }
135  osdControlButtonsState = newState;
136 }
137 
138 void InputEventGenerator::triggerOsdControlEventsFromJoystickAxisMotion(
139  unsigned axis, int value, const EventPtr& origEvent)
140 {
141  unsigned neg_button, pos_button;
142  switch (axis) {
143  case 0:
144  neg_button = 1 << OsdControlEvent::LEFT_BUTTON;
145  pos_button = 1 << OsdControlEvent::RIGHT_BUTTON;
146  break; // axis 0
147  case 1:
148  neg_button = 1 << OsdControlEvent::UP_BUTTON;
149  pos_button = 1 << OsdControlEvent::DOWN_BUTTON;
150  break;
151  default:
152  // Ignore all other axis (3D joysticks and flight joysticks may
153  // have more than 2 axis)
154  return;
155  }
156 
157  if (value > 0) {
158  // release negative button, press positive button
159  setNewOsdControlButtonState(
160  (osdControlButtonsState | neg_button) & ~pos_button,
161  origEvent);
162  } else if (value < 0) {
163  // press negative button, release positive button
164  setNewOsdControlButtonState(
165  (osdControlButtonsState | pos_button) & ~neg_button,
166  origEvent);
167  } else {
168  // release both buttons
169  setNewOsdControlButtonState(
170  osdControlButtonsState | neg_button | pos_button,
171  origEvent);
172  }
173 }
174 
175 void InputEventGenerator::triggerOsdControlEventsFromJoystickHat(
176  int value, const EventPtr& origEvent)
177 {
178  unsigned dir = 0;
179  if (!(value & SDL_HAT_UP )) dir |= 1 << OsdControlEvent::UP_BUTTON;
180  if (!(value & SDL_HAT_DOWN )) dir |= 1 << OsdControlEvent::DOWN_BUTTON;
181  if (!(value & SDL_HAT_LEFT )) dir |= 1 << OsdControlEvent::LEFT_BUTTON;
182  if (!(value & SDL_HAT_RIGHT)) dir |= 1 << OsdControlEvent::RIGHT_BUTTON;
183  unsigned ab = osdControlButtonsState & ((1 << OsdControlEvent::A_BUTTON) |
185  setNewOsdControlButtonState(ab | dir, origEvent);
186 }
187 
188 void InputEventGenerator::osdControlChangeButton(
189  bool up, unsigned changedButtonMask, const EventPtr& origEvent)
190 {
191  auto newButtonState = up
192  ? osdControlButtonsState | changedButtonMask
193  : osdControlButtonsState & ~changedButtonMask;
194  setNewOsdControlButtonState(newButtonState, origEvent);
195 }
196 
197 void InputEventGenerator::triggerOsdControlEventsFromJoystickButtonEvent(
198  unsigned button, bool up, const EventPtr& origEvent)
199 {
200  osdControlChangeButton(
201  up,
202  ((button & 1) ? (1 << OsdControlEvent::B_BUTTON)
203  : (1 << OsdControlEvent::A_BUTTON)),
204  origEvent);
205 }
206 
207 void InputEventGenerator::triggerOsdControlEventsFromKeyEvent(
208  Keys::KeyCode keyCode, bool up, const EventPtr& origEvent)
209 {
210  keyCode = static_cast<Keys::KeyCode>(keyCode & Keys::K_MASK);
211  if (keyCode == Keys::K_LEFT) {
212  osdControlChangeButton(up, 1 << OsdControlEvent::LEFT_BUTTON,
213  origEvent);
214  } else if (keyCode == Keys::K_RIGHT) {
215  osdControlChangeButton(up, 1 << OsdControlEvent::RIGHT_BUTTON,
216  origEvent);
217  } else if (keyCode == Keys::K_UP) {
218  osdControlChangeButton(up, 1 << OsdControlEvent::UP_BUTTON,
219  origEvent);
220  } else if (keyCode == Keys::K_DOWN) {
221  osdControlChangeButton(up, 1 << OsdControlEvent::DOWN_BUTTON,
222  origEvent);
223  } else if (keyCode == Keys::K_SPACE || keyCode == Keys::K_RETURN) {
224  osdControlChangeButton(up, 1 << OsdControlEvent::A_BUTTON,
225  origEvent);
226  } else if (keyCode == Keys::K_ESCAPE) {
227  osdControlChangeButton(up, 1 << OsdControlEvent::B_BUTTON,
228  origEvent);
229  }
230 }
231 
232 void InputEventGenerator::handle(const SDL_Event& evt)
233 {
234  EventPtr event;
235  switch (evt.type) {
236  case SDL_KEYUP:
237  // Virtual joystick of SDL Android port does not have joystick
238  // buttons. It has however up to 6 virtual buttons that can be
239  // mapped to SDL keyboard events. Two of these virtual buttons
240  // will be mapped to keys SDLK_WORLD_93 and 94 and are
241  // interpeted here as joystick buttons (respectively button 0
242  // and 1).
243  if (PLATFORM_ANDROID && evt.key.keysym.sym == SDLK_WORLD_93) {
244  event = make_shared<JoystickButtonUpEvent>(0, 0);
245  triggerOsdControlEventsFromJoystickButtonEvent(
246  0, true, event);
247  androidButtonA = false;
248  } else if (PLATFORM_ANDROID && evt.key.keysym.sym == SDLK_WORLD_94) {
249  event = make_shared<JoystickButtonUpEvent>(0, 1);
250  triggerOsdControlEventsFromJoystickButtonEvent(
251  1, true, event);
252  androidButtonB = false;
253  } else {
254  auto keyCode = Keys::getCode(
255  evt.key.keysym.sym, evt.key.keysym.mod,
256  evt.key.keysym.scancode, true);
257  event = make_shared<KeyUpEvent>(
258  keyCode, evt.key.keysym.unicode);
259  triggerOsdControlEventsFromKeyEvent(keyCode, true, event);
260  }
261  break;
262  case SDL_KEYDOWN:
263  if (PLATFORM_ANDROID && evt.key.keysym.sym == SDLK_WORLD_93) {
264  event = make_shared<JoystickButtonDownEvent>(0, 0);
265  triggerOsdControlEventsFromJoystickButtonEvent(
266  0, false, event);
267  androidButtonA = true;
268  } else if (PLATFORM_ANDROID && evt.key.keysym.sym == SDLK_WORLD_94) {
269  event = make_shared<JoystickButtonDownEvent>(0, 1);
270  triggerOsdControlEventsFromJoystickButtonEvent(
271  1, false, event);
272  androidButtonB = true;
273  } else {
274  auto keyCode = Keys::getCode(
275  evt.key.keysym.sym, evt.key.keysym.mod,
276  evt.key.keysym.scancode, false);
277  event = make_shared<KeyDownEvent>(
278  keyCode, evt.key.keysym.unicode);
279  triggerOsdControlEventsFromKeyEvent(keyCode, false, event);
280  }
281  break;
282 
283  case SDL_MOUSEBUTTONUP:
284  event = make_shared<MouseButtonUpEvent>(evt.button.button);
285  break;
286  case SDL_MOUSEBUTTONDOWN:
287  event = make_shared<MouseButtonDownEvent>(evt.button.button);
288  break;
289  case SDL_MOUSEMOTION:
290  event = make_shared<MouseMotionEvent>(
291  evt.motion.xrel, evt.motion.yrel,
292  evt.motion.x, evt.motion.y);
293  break;
294 
295  case SDL_JOYBUTTONUP:
296  event = make_shared<JoystickButtonUpEvent>(
297  evt.jbutton.which, evt.jbutton.button);
298  triggerOsdControlEventsFromJoystickButtonEvent(
299  evt.jbutton.button, true, event);
300  break;
301  case SDL_JOYBUTTONDOWN:
302  event = make_shared<JoystickButtonDownEvent>(
303  evt.jbutton.which, evt.jbutton.button);
304  triggerOsdControlEventsFromJoystickButtonEvent(
305  evt.jbutton.button, false, event);
306  break;
307  case SDL_JOYAXISMOTION: {
308  auto& setting = globalSettings.getJoyDeadzoneSetting(evt.jaxis.which);
309  int threshold = (setting.getInt() * 32768) / 100;
310  auto value = (evt.jaxis.value < -threshold) ? evt.jaxis.value
311  : (evt.jaxis.value > threshold) ? evt.jaxis.value
312  : 0;
313  event = make_shared<JoystickAxisMotionEvent>(
314  evt.jaxis.which, evt.jaxis.axis, value);
315  triggerOsdControlEventsFromJoystickAxisMotion(
316  evt.jaxis.axis, value, event);
317  break;
318  }
319  case SDL_JOYHATMOTION:
320  event = make_shared<JoystickHatEvent>(
321  evt.jhat.which, evt.jhat.hat, evt.jhat.value);
322  triggerOsdControlEventsFromJoystickHat(evt.jhat.value, event);
323  break;
324 
325  case SDL_ACTIVEEVENT:
326  event = make_shared<FocusEvent>(evt.active.gain != 0);
327  break;
328 
329  case SDL_VIDEORESIZE:
330  event = make_shared<ResizeEvent>(evt.resize.w, evt.resize.h);
331  break;
332 
333  case SDL_VIDEOEXPOSE:
334  event = make_shared<SimpleEvent>(OPENMSX_EXPOSE_EVENT);
335  break;
336 
337  case SDL_QUIT:
338  event = make_shared<QuitEvent>();
339  break;
340 
341  default:
342  break;
343  }
344 
345 #if 0
346  if (event) {
347  std::cerr << "SDL event converted to: " + event->toString() << std::endl;
348  } else {
349  std::cerr << "SDL event was of unknown type, not converted to an openMSX event" << std::endl;
350  }
351 #endif
352 
353  if (event) eventDistributor.distributeEvent(event);
354 }
355 
356 
357 void InputEventGenerator::update(const Setting& setting)
358 {
359  assert(&setting == &grabInput); (void)setting;
360  escapeGrabState = ESCAPE_GRAB_WAIT_CMD;
361  setGrabInput(grabInput.getBoolean());
362 }
363 
364 int InputEventGenerator::signalEvent(const std::shared_ptr<const Event>& event)
365 {
366  auto& focusEvent = checked_cast<const FocusEvent&>(*event);
367  switch (escapeGrabState) {
368  case ESCAPE_GRAB_WAIT_CMD:
369  // nothing
370  break;
371  case ESCAPE_GRAB_WAIT_LOST:
372  if (!focusEvent.getGain()) {
373  escapeGrabState = ESCAPE_GRAB_WAIT_GAIN;
374  }
375  break;
376  case ESCAPE_GRAB_WAIT_GAIN:
377  if (focusEvent.getGain()) {
378  escapeGrabState = ESCAPE_GRAB_WAIT_CMD;
379  }
380  setGrabInput(true);
381  break;
382  default:
383  UNREACHABLE;
384  }
385  return 0;
386 }
387 
388 void InputEventGenerator::setGrabInput(bool grab)
389 {
390  // Note that this setting is also changed in VisibleSurface constructor
391  // because for Mac we want to enable it in fullscreen.
392  // It's not worth it to get that exactly right here, because here
393  // we don't have easy access to renderer settings and it may only
394  // go wrong if you explicitly change grab input at full screen (on Mac)
395  SDL_WM_GrabInput(grab ? SDL_GRAB_ON : SDL_GRAB_OFF);
396 }
397 
398 
399 // Wrap SDL joystick button functions to handle the 'fake' android joystick
400 // buttons. The method InputEventGenerator::handle() already takes care of fake
401 // events for the andoid joystick buttons, these two wrappers handle the direct
402 // joystick button state queries.
403 int InputEventGenerator::joystickNumButtons(SDL_Joystick* joystick)
404 {
405  if (PLATFORM_ANDROID) {
406  return 2;
407  } else {
408  return SDL_JoystickNumButtons(joystick);
409  }
410 }
411 bool InputEventGenerator::joystickGetButton(SDL_Joystick* joystick, int button)
412 {
413  if (PLATFORM_ANDROID) {
414  switch (button) {
415  case 0: return androidButtonA;
416  case 1: return androidButtonB;
417  default: UNREACHABLE; return false;
418  }
419  } else {
420  return SDL_JoystickGetButton(joystick, button) != 0;
421  }
422 }
423 
424 
425 // class EscapeGrabCmd
426 
427 InputEventGenerator::EscapeGrabCmd::EscapeGrabCmd(
428  CommandController& commandController_)
429  : Command(commandController_, "escape_grab")
430 {
431 }
432 
433 void InputEventGenerator::EscapeGrabCmd::execute(
434  array_ref<TclObject> /*tokens*/, TclObject& /*result*/)
435 {
436  auto& inputEventGenerator = OUTER(InputEventGenerator, escapeGrabCmd);
437  if (inputEventGenerator.grabInput.getBoolean()) {
438  inputEventGenerator.escapeGrabState =
439  InputEventGenerator::ESCAPE_GRAB_WAIT_LOST;
440  inputEventGenerator.setGrabInput(false);
441  }
442 }
443 
444 string InputEventGenerator::EscapeGrabCmd::help(
445  const vector<string>& /*tokens*/) const
446 {
447  return "Temporarily release input grab.";
448 }
449 
450 } // namespace openmsx
IntegerSetting & getJoyDeadzoneSetting(int i)
void registerEventListener(EventType type, EventListener &listener, Priority priority=OTHER)
Registers a given object to receive certain events.
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
void wait()
Wait for event(s) and handle it.
void distributeEvent(const EventPtr &event)
Schedule the given event for delivery.
void reinit()
This functions shouldn&#39;t be needed, but in the SDL library input and video or closely coupled (sigh)...
KeyCode
Constants that identify keys and key modifiers.
Definition: Keys.hh:26
#define PLATFORM_ANDROID
Definition: build-info.hh:18
void attach(Observer< T > &observer)
Definition: Subject.hh:52
static int joystickNumButtons(SDL_Joystick *joystick)
Normally the following two functions simply delegate to SDL_JoystickNumButtons() and SDL_JoystickGetB...
This class implements a subset of the proposal for std::array_ref (proposed for the next c++ standard...
Definition: array_ref.hh:19
KeyCode getCode(string_ref name)
Translate key name to key code.
Definition: Keys.cc:288
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
Send when (part of) the openMSX window gets exposed, and thus should be repainted.
Definition: Event.hh:60
void setKeyRepeat(bool enable)
Enable or disable keyboard event repeats.
static bool joystickGetButton(SDL_Joystick *joystick, int button)
void detach(Observer< T > &observer)
Definition: Subject.hh:58
#define OUTER(type, member)
Definition: outer.hh:38
This class contains settings that are used by several other class (including some singletons)...
InputEventGenerator(const InputEventGenerator &)=delete
#define UNREACHABLE
Definition: unreachable.hh:35