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 "FileOperations.hh"
8 #include "checked_cast.hh"
9 #include "one_of.hh"
10 #include "outer.hh"
11 #include "unreachable.hh"
12 #include "utf8_unchecked.hh"
13 #include "build-info.hh"
14 #include <cassert>
15 #include <memory>
16 
17 using std::string;
18 using std::vector;
19 using std::make_shared;
20 
21 namespace openmsx {
22 
24  EventDistributor& eventDistributor_,
25  GlobalSettings& globalSettings_)
26  : eventDistributor(eventDistributor_)
27  , globalSettings(globalSettings_)
28  , grabInput(
29  commandController, "grabinput",
30  "This setting controls if openMSX takes over mouse and keyboard input",
31  false, Setting::DONT_SAVE)
32  , escapeGrabCmd(commandController)
33  , escapeGrabState(ESCAPE_GRAB_WAIT_CMD)
34 {
35  setGrabInput(grabInput.getBoolean());
36  eventDistributor.registerEventListener(OPENMSX_FOCUS_EVENT, *this);
37 
38  osdControlButtonsState = unsigned(~0); // 0 is pressed, 1 is released
39 
40 #ifndef SDL_JOYSTICK_DISABLED
41  SDL_JoystickEventState(SDL_ENABLE); // joysticks generate events
42 #endif
43 }
44 
46 {
47  eventDistributor.unregisterEventListener(OPENMSX_FOCUS_EVENT, *this);
48 }
49 
51 {
52  // SDL bug workaround
53  if (!SDL_WasInit(SDL_INIT_VIDEO)) {
54  SDL_Delay(100);
55  }
56 
57  if (SDL_WaitEvent(nullptr)) {
58  poll();
59  }
60 }
61 
63 {
64  // Heuristic to emulate the old SDL1 behavior:
65  //
66  // SDL1 had a unicode field on each KEYDOWN event. In SDL2 that
67  // information is moved to the (new) SDL_TEXTINPUT events.
68  //
69  // Though our MSX keyboard emulation code needs to relate KEYDOWN
70  // events with the associated unicode. We try to mimic this by the
71  // following heuristic:
72  // When two successive events in a single batch (so, the same
73  // invocation of poll()) are KEYDOWN followed by TEXTINPUT, then copy
74  // the unicode (of the first character) of the TEXT event to the
75  // KEYDOWN event.
76  // Implementing this requires a lookahead of 1 event. So the code below
77  // deals with a 'current' and a 'previous' event, and keeps track of
78  // whether the previous event is still pending (not yet processed).
79  //
80  // In a previous version we also added the constraint that these two
81  // consecutive events must have the same timestamp, but that had mixed
82  // results:
83  // - on Linux it worked fine
84  // - on Windows the timestamps sometimes did not match
85  // - on Mac the timestamps mostly did not match
86  // So we removed this constraint.
87  //
88  // We also split SDL_TEXTINPUT events into (possibly) multiple KEYDOWN
89  // events because a single event type makes it easier to handle higher
90  // priority listeners that can block the event for lower priority
91  // listener (console > hotkey > msx).
92 
93  SDL_Event event1, event2;
94  auto* prev = &event1;
95  auto* curr = &event2;
96  bool pending = false;
97 
98  while (SDL_PollEvent(curr)) {
99  if (pending) {
100  pending = false;
101  if ((prev->type == SDL_KEYDOWN) && (curr->type == SDL_TEXTINPUT)) {
102  const char* utf8 = curr->text.text;
103  auto unicode = utf8::unchecked::next(utf8);
104  handleKeyDown(prev->key, unicode);
105  if (unicode) { // possibly there are more characters
106  handleText(utf8);
107  }
108  continue;
109  } else {
110  handle(*prev);
111  }
112  }
113  if (curr->type == SDL_KEYDOWN) {
114  pending = true;
115  std::swap(curr, prev);
116  } else {
117  handle(*curr);
118  }
119  }
120  if (pending) {
121  handle(*prev);
122  }
123 }
124 
125 void InputEventGenerator::setNewOsdControlButtonState(
126  unsigned newState, const EventPtr& origEvent)
127 {
128  unsigned deltaState = osdControlButtonsState ^ newState;
129  for (unsigned i = OsdControlEvent::LEFT_BUTTON;
130  i <= OsdControlEvent::B_BUTTON; ++i) {
131  if (deltaState & (1 << i)) {
132  if (newState & (1 << i)) {
133  eventDistributor.distributeEvent(
134  make_shared<OsdControlReleaseEvent>(
135  i, origEvent));
136  } else {
137  eventDistributor.distributeEvent(
138  make_shared<OsdControlPressEvent>(
139  i, origEvent));
140  }
141  }
142  }
143  osdControlButtonsState = newState;
144 }
145 
146 void InputEventGenerator::triggerOsdControlEventsFromJoystickAxisMotion(
147  unsigned axis, int value, const EventPtr& origEvent)
148 {
149  auto [neg_button, pos_button] = [&] {
150  switch (axis) {
151  case 0:
152  return std::pair{1u << OsdControlEvent::LEFT_BUTTON,
154  case 1:
155  return std::pair{1u << OsdControlEvent::UP_BUTTON,
157  default:
158  // Ignore all other axis (3D joysticks and flight joysticks may
159  // have more than 2 axis)
160  return std::pair{0u, 0u};
161  }
162  }();
163 
164  if (value > 0) {
165  // release negative button, press positive button
166  setNewOsdControlButtonState(
167  (osdControlButtonsState | neg_button) & ~pos_button,
168  origEvent);
169  } else if (value < 0) {
170  // press negative button, release positive button
171  setNewOsdControlButtonState(
172  (osdControlButtonsState | pos_button) & ~neg_button,
173  origEvent);
174  } else {
175  // release both buttons
176  setNewOsdControlButtonState(
177  osdControlButtonsState | neg_button | pos_button,
178  origEvent);
179  }
180 }
181 
182 void InputEventGenerator::triggerOsdControlEventsFromJoystickHat(
183  int value, const EventPtr& origEvent)
184 {
185  unsigned dir = 0;
186  if (!(value & SDL_HAT_UP )) dir |= 1 << OsdControlEvent::UP_BUTTON;
187  if (!(value & SDL_HAT_DOWN )) dir |= 1 << OsdControlEvent::DOWN_BUTTON;
188  if (!(value & SDL_HAT_LEFT )) dir |= 1 << OsdControlEvent::LEFT_BUTTON;
189  if (!(value & SDL_HAT_RIGHT)) dir |= 1 << OsdControlEvent::RIGHT_BUTTON;
190  unsigned ab = osdControlButtonsState & ((1 << OsdControlEvent::A_BUTTON) |
192  setNewOsdControlButtonState(ab | dir, origEvent);
193 }
194 
195 void InputEventGenerator::osdControlChangeButton(
196  bool up, unsigned changedButtonMask, const EventPtr& origEvent)
197 {
198  auto newButtonState = up
199  ? osdControlButtonsState | changedButtonMask
200  : osdControlButtonsState & ~changedButtonMask;
201  setNewOsdControlButtonState(newButtonState, origEvent);
202 }
203 
204 void InputEventGenerator::triggerOsdControlEventsFromJoystickButtonEvent(
205  unsigned button, bool up, const EventPtr& origEvent)
206 {
207  osdControlChangeButton(
208  up,
209  ((button & 1) ? (1 << OsdControlEvent::B_BUTTON)
210  : (1 << OsdControlEvent::A_BUTTON)),
211  origEvent);
212 }
213 
214 void InputEventGenerator::triggerOsdControlEventsFromKeyEvent(
215  Keys::KeyCode keyCode, bool up, bool repeat, const EventPtr& origEvent)
216 {
217  unsigned buttonMask = [&] {
218  switch (static_cast<Keys::KeyCode>(keyCode & Keys::K_MASK)) {
219  case Keys::K_LEFT: return 1 << OsdControlEvent::LEFT_BUTTON;
221  case Keys::K_UP: return 1 << OsdControlEvent::UP_BUTTON;
222  case Keys::K_DOWN: return 1 << OsdControlEvent::DOWN_BUTTON;
223  case Keys::K_SPACE: return 1 << OsdControlEvent::A_BUTTON;
224  case Keys::K_RETURN: return 1 << OsdControlEvent::A_BUTTON;
225  case Keys::K_ESCAPE: return 1 << OsdControlEvent::B_BUTTON;
226  default: return 0;
227  }
228  }();
229  if (buttonMask) {
230  if (repeat) {
231  osdControlChangeButton(!up, buttonMask, origEvent);
232  }
233  osdControlChangeButton(up, buttonMask, origEvent);
234  }
235 }
236 
237 static Uint16 normalizeModifier(SDL_Keycode sym, Uint16 mod)
238 {
239  // Apparently modifier-keys also have the corresponding
240  // modifier attribute set. See here for a discussion:
241  // https://github.com/openMSX/openMSX/issues/1202
242  // As a solution, on pure modifier keys, we now clear the
243  // modifier attributes.
244  return (sym == one_of(SDLK_LCTRL, SDLK_LSHIFT, SDLK_LALT, SDLK_LGUI,
245  SDLK_RCTRL, SDLK_RSHIFT, SDLK_RALT, SDLK_RGUI,
246  SDLK_MODE))
247  ? 0
248  : mod;
249 }
250 
251 void InputEventGenerator::handleKeyDown(const SDL_KeyboardEvent& key, uint32_t unicode)
252 {
253  EventPtr event;
254  /*if (PLATFORM_ANDROID && evt.key.keysym.sym == SDLK_WORLD_93) {
255  event = make_shared<JoystickButtonDownEvent>(0, 0);
256  triggerOsdControlEventsFromJoystickButtonEvent(
257  0, false, event);
258  androidButtonA = true;
259  } else if (PLATFORM_ANDROID && evt.key.keysym.sym == SDLK_WORLD_94) {
260  event = make_shared<JoystickButtonDownEvent>(0, 1);
261  triggerOsdControlEventsFromJoystickButtonEvent(
262  1, false, event);
263  androidButtonB = true;
264  } else*/ {
265  auto mod = normalizeModifier(key.keysym.sym, key.keysym.mod);
266  auto [keyCode, scanCode] = Keys::getCodes(
267  key.keysym.sym, mod, key.keysym.scancode, false);
268  event = make_shared<KeyDownEvent>(keyCode, scanCode, unicode);
269  triggerOsdControlEventsFromKeyEvent(keyCode, false, key.repeat, event);
270  }
271  eventDistributor.distributeEvent(event);
272 }
273 
274 void InputEventGenerator::handleText(const char* utf8)
275 {
276  while (true) {
277  auto unicode = utf8::unchecked::next(utf8);
278  if (unicode == 0) return;
279  eventDistributor.distributeEvent(
280  make_shared<KeyDownEvent>(Keys::K_NONE, unicode));
281  }
282 }
283 
284 void InputEventGenerator::handle(const SDL_Event& evt)
285 {
286  EventPtr event;
287  switch (evt.type) {
288  case SDL_KEYUP:
289  // Virtual joystick of SDL Android port does not have joystick
290  // buttons. It has however up to 6 virtual buttons that can be
291  // mapped to SDL keyboard events. Two of these virtual buttons
292  // will be mapped to keys SDLK_WORLD_93 and 94 and are
293  // interpreted here as joystick buttons (respectively button 0
294  // and 1).
295  // TODO Android code should be rewritten for SDL2
296  /*if (PLATFORM_ANDROID && evt.key.keysym.sym == SDLK_WORLD_93) {
297  event = make_shared<JoystickButtonUpEvent>(0, 0);
298  triggerOsdControlEventsFromJoystickButtonEvent(
299  0, true, event);
300  androidButtonA = false;
301  } else if (PLATFORM_ANDROID && evt.key.keysym.sym == SDLK_WORLD_94) {
302  event = make_shared<JoystickButtonUpEvent>(0, 1);
303  triggerOsdControlEventsFromJoystickButtonEvent(
304  1, true, event);
305  androidButtonB = false;
306  } else*/ {
307  auto mod = normalizeModifier(evt.key.keysym.sym, evt.key.keysym.mod);
308  auto [keyCode, scanCode] = Keys::getCodes(
309  evt.key.keysym.sym, mod, evt.key.keysym.scancode, true);
310  event = make_shared<KeyUpEvent>(keyCode, scanCode);
311  bool repeat = false;
312  triggerOsdControlEventsFromKeyEvent(keyCode, true, repeat, event);
313  }
314  break;
315  case SDL_KEYDOWN:
316  handleKeyDown(evt.key, 0);
317  break;
318 
319  case SDL_MOUSEBUTTONUP:
320  event = make_shared<MouseButtonUpEvent>(evt.button.button);
321  break;
322  case SDL_MOUSEBUTTONDOWN:
323  event = make_shared<MouseButtonDownEvent>(evt.button.button);
324  break;
325  case SDL_MOUSEWHEEL: {
326  int x = evt.wheel.x;
327  int y = evt.wheel.y;
328  if (evt.wheel.direction == SDL_MOUSEWHEEL_FLIPPED)
329  {
330  x = -x;
331  y = -y;
332  }
333  event = make_shared<MouseWheelEvent>(x, y);
334  break;
335  }
336  case SDL_MOUSEMOTION:
337  event = make_shared<MouseMotionEvent>(
338  evt.motion.xrel, evt.motion.yrel,
339  evt.motion.x, evt.motion.y);
340  break;
341 
342  case SDL_JOYBUTTONUP:
343  event = make_shared<JoystickButtonUpEvent>(
344  evt.jbutton.which, evt.jbutton.button);
345  triggerOsdControlEventsFromJoystickButtonEvent(
346  evt.jbutton.button, true, event);
347  break;
348  case SDL_JOYBUTTONDOWN:
349  event = make_shared<JoystickButtonDownEvent>(
350  evt.jbutton.which, evt.jbutton.button);
351  triggerOsdControlEventsFromJoystickButtonEvent(
352  evt.jbutton.button, false, event);
353  break;
354  case SDL_JOYAXISMOTION: {
355  auto& setting = globalSettings.getJoyDeadzoneSetting(evt.jaxis.which);
356  int threshold = (setting.getInt() * 32768) / 100;
357  auto value = (evt.jaxis.value < -threshold) ? evt.jaxis.value
358  : (evt.jaxis.value > threshold) ? evt.jaxis.value
359  : 0;
360  event = make_shared<JoystickAxisMotionEvent>(
361  evt.jaxis.which, evt.jaxis.axis, value);
362  triggerOsdControlEventsFromJoystickAxisMotion(
363  evt.jaxis.axis, value, event);
364  break;
365  }
366  case SDL_JOYHATMOTION:
367  event = make_shared<JoystickHatEvent>(
368  evt.jhat.which, evt.jhat.hat, evt.jhat.value);
369  triggerOsdControlEventsFromJoystickHat(evt.jhat.value, event);
370  break;
371 
372  case SDL_TEXTINPUT:
373  handleText(evt.text.text);
374  break;
375 
376  case SDL_WINDOWEVENT:
377  switch (evt.window.event) {
378  case SDL_WINDOWEVENT_FOCUS_GAINED:
379  event = make_shared<FocusEvent>(true);
380  break;
381  case SDL_WINDOWEVENT_FOCUS_LOST:
382  event = make_shared<FocusEvent>(false);
383  break;
384  case SDL_WINDOWEVENT_RESIZED:
385  event = make_shared<ResizeEvent>(
386  evt.window.data1, evt.window.data2);
387  break;
388  case SDL_WINDOWEVENT_EXPOSED:
389  event = make_shared<SimpleEvent>(OPENMSX_EXPOSE_EVENT);
390  break;
391  default:
392  break;
393  }
394  break;
395 
396  case SDL_DROPFILE:
397  event = make_shared<FileDropEvent>(FileOperations::getConventionalPath(evt.drop.file));
398  SDL_free(evt.drop.file);
399  break;
400 
401  case SDL_QUIT:
402  event = make_shared<QuitEvent>();
403  break;
404 
405  default:
406  break;
407  }
408 
409 #if 0
410  if (event) {
411  std::cerr << "SDL event converted to: " << event->toString() << '\n';
412  } else {
413  std::cerr << "SDL event was of unknown type, not converted to an openMSX event\n";
414  }
415 #endif
416 
417  if (event) eventDistributor.distributeEvent(event);
418 }
419 
420 
422 {
423  escapeGrabState = ESCAPE_GRAB_WAIT_CMD;
424  setGrabInput(grab);
425 }
426 
427 int InputEventGenerator::signalEvent(const std::shared_ptr<const Event>& event)
428 {
429  const auto& focusEvent = checked_cast<const FocusEvent&>(*event);
430  switch (escapeGrabState) {
431  case ESCAPE_GRAB_WAIT_CMD:
432  // nothing
433  break;
434  case ESCAPE_GRAB_WAIT_LOST:
435  if (!focusEvent.getGain()) {
436  escapeGrabState = ESCAPE_GRAB_WAIT_GAIN;
437  }
438  break;
439  case ESCAPE_GRAB_WAIT_GAIN:
440  if (focusEvent.getGain()) {
441  escapeGrabState = ESCAPE_GRAB_WAIT_CMD;
442  }
443  setGrabInput(true);
444  break;
445  default:
446  UNREACHABLE;
447  }
448  return 0;
449 }
450 
451 void InputEventGenerator::setGrabInput(bool grab)
452 {
453  SDL_SetRelativeMouseMode(grab ? SDL_TRUE : SDL_FALSE);
454 
455  // TODO is this still the correct place in SDL2
456  // TODO get the SDL_window
457  //SDL_Window* window = ...;
458  //SDL_SetWindowGrab(window, grab ? SDL_TRUE : SDL_FALSE);
459 }
460 
461 
462 // Wrap SDL joystick button functions to handle the 'fake' android joystick
463 // buttons. The method InputEventGenerator::handle() already takes care of fake
464 // events for the andoid joystick buttons, these two wrappers handle the direct
465 // joystick button state queries.
466 int InputEventGenerator::joystickNumButtons(SDL_Joystick* joystick)
467 {
468  if (PLATFORM_ANDROID) {
469  return 2;
470  } else {
471  return SDL_JoystickNumButtons(joystick);
472  }
473 }
474 bool InputEventGenerator::joystickGetButton(SDL_Joystick* joystick, int button)
475 {
476  if (PLATFORM_ANDROID) {
477  switch (button) {
478  case 0: return androidButtonA;
479  case 1: return androidButtonB;
480  default: UNREACHABLE; return false;
481  }
482  } else {
483  return SDL_JoystickGetButton(joystick, button) != 0;
484  }
485 }
486 
487 
488 // class EscapeGrabCmd
489 
490 InputEventGenerator::EscapeGrabCmd::EscapeGrabCmd(
491  CommandController& commandController_)
492  : Command(commandController_, "escape_grab")
493 {
494 }
495 
496 void InputEventGenerator::EscapeGrabCmd::execute(
497  span<const TclObject> /*tokens*/, TclObject& /*result*/)
498 {
499  auto& inputEventGenerator = OUTER(InputEventGenerator, escapeGrabCmd);
500  if (inputEventGenerator.grabInput.getBoolean()) {
501  inputEventGenerator.escapeGrabState =
502  InputEventGenerator::ESCAPE_GRAB_WAIT_LOST;
503  inputEventGenerator.setGrabInput(false);
504  }
505 }
506 
507 string InputEventGenerator::EscapeGrabCmd::help(
508  const vector<string>& /*tokens*/) const
509 {
510  return "Temporarily release input grab.";
511 }
512 
513 } // namespace openmsx
one_of.hh
openmsx::OsdControlEvent::DOWN_BUTTON
@ DOWN_BUTTON
Definition: InputEvents.hh:271
openmsx::Keys::K_ESCAPE
@ K_ESCAPE
Definition: Keys.hh:36
openmsx::OsdControlEvent::RIGHT_BUTTON
@ RIGHT_BUTTON
Definition: InputEvents.hh:271
openmsx::Keys::getCodes
std::pair< KeyCode, KeyCode > getCodes(SDL_Keycode sdlkeycode, Uint16 mod, SDL_Scancode sdlscancode, bool release)
Translate SDL_Keycode/SDL_Scancode into openMSX key/scan Keycode's.
Definition: Keys.cc:369
IntegerSetting.hh
openmsx::InputEventGenerator::poll
void poll()
Definition: InputEventGenerator.cc:62
utf8
Definition: utf8_checked.hh:37
openmsx::CommandController
Definition: CommandController.hh:19
utf8::unchecked::next
uint32_t next(octet_iterator &it)
Definition: utf8_unchecked.hh:65
openmsx::InputEventGenerator::joystickGetButton
static bool joystickGetButton(SDL_Joystick *joystick, int button)
Definition: InputEventGenerator.cc:474
openmsx::Keys::K_RIGHT
@ K_RIGHT
Definition: Keys.hh:126
openmsx::Setting
Definition: Setting.hh:127
openmsx::EventDistributor
Definition: EventDistributor.hh:17
repeat
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
Definition: xrange.hh:170
openmsx::Keys::K_SPACE
@ K_SPACE
Definition: Keys.hh:37
span
Definition: span.hh:126
openmsx::InputEventGenerator::InputEventGenerator
InputEventGenerator(const InputEventGenerator &)=delete
PLATFORM_ANDROID
#define PLATFORM_ANDROID
Definition: build-info.hh:17
OUTER
#define OUTER(type, member)
Definition: outer.hh:41
openmsx::Keys::K_NONE
@ K_NONE
Definition: Keys.hh:29
openmsx::InputEventGenerator::wait
void wait()
Wait for event(s) and handle it.
Definition: InputEventGenerator.cc:50
UNREACHABLE
#define UNREACHABLE
Definition: unreachable.hh:38
openmsx::Keys::K_DOWN
@ K_DOWN
Definition: Keys.hh:125
utf8_unchecked.hh
openmsx::InputEventGenerator::joystickNumButtons
static int joystickNumButtons(SDL_Joystick *joystick)
Normally the following two functions simply delegate to SDL_JoystickNumButtons() and SDL_JoystickGetB...
Definition: InputEventGenerator.cc:466
one_of
Definition: one_of.hh:7
EventDistributor.hh
InputEvents.hh
openmsx::InputEventGenerator::~InputEventGenerator
~InputEventGenerator()
Definition: InputEventGenerator.cc:45
build-info.hh
Keys.hh
GlobalSettings.hh
openmsx::InputEventFactory::EventPtr
std::shared_ptr< const Event > EventPtr
Definition: InputEventFactory.hh:15
FileOperations.hh
outer.hh
openmsx::GlobalSettings::getJoyDeadzoneSetting
IntegerSetting & getJoyDeadzoneSetting(int i)
Definition: GlobalSettings.hh:51
openmsx::x
constexpr KeyMatrixPosition x
Keyboard bindings.
Definition: Keyboard.cc:1414
checked_cast.hh
openmsx::OPENMSX_EXPOSE_EVENT
@ OPENMSX_EXPOSE_EVENT
Send when (part of) the openMSX window gets exposed, and thus should be repainted.
Definition: Event.hh:69
openmsx::OPENMSX_FOCUS_EVENT
@ OPENMSX_FOCUS_EVENT
Definition: Event.hh:29
openmsx::GlobalSettings
This class contains settings that are used by several other class (including some singletons).
Definition: GlobalSettings.hh:25
openmsx::Keys::K_UP
@ K_UP
Definition: Keys.hh:124
openmsx::OsdControlEvent::UP_BUTTON
@ UP_BUTTON
Definition: InputEvents.hh:271
openmsx::Keys::K_LEFT
@ K_LEFT
Definition: Keys.hh:127
openmsx::OsdControlEvent::LEFT_BUTTON
@ LEFT_BUTTON
Definition: InputEvents.hh:271
openmsx::FileOperations::getConventionalPath
const std::string & getConventionalPath(const std::string &path)
Returns the path in conventional path-delimiter.
Definition: FileOperations.hh:155
openmsx::Keys::K_RETURN
@ K_RETURN
Definition: Keys.hh:34
openmsx::Command
Definition: Command.hh:41
openmsx::InputEventGenerator::updateGrab
void updateGrab(bool grab)
Must be called when 'grabinput' or 'fullscreen' setting changes.
Definition: InputEventGenerator.cc:421
openmsx::OsdControlEvent::A_BUTTON
@ A_BUTTON
Definition: InputEvents.hh:272
InputEventGenerator.hh
openmsx::OsdControlEvent::B_BUTTON
@ B_BUTTON
Definition: InputEvents.hh:272
openmsx::Keys::KeyCode
KeyCode
Constants that identify keys and key modifiers.
Definition: Keys.hh:26
openmsx::EventDistributor::registerEventListener
void registerEventListener(EventType type, EventListener &listener, Priority priority=OTHER)
Registers a given object to receive certain events.
Definition: EventDistributor.cc:23
unreachable.hh
openmsx::EventDistributor::unregisterEventListener
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
Definition: EventDistributor.cc:35
openmsx
This file implemented 3 utility functions:
Definition: Autofire.cc:5
openmsx::Keys::K_MASK
@ K_MASK
Definition: Keys.hh:27
openmsx::BooleanSetting::getBoolean
bool getBoolean() const noexcept
Definition: BooleanSetting.hh:17
openmsx::EventDistributor::distributeEvent
void distributeEvent(const EventPtr &event)
Schedule the given event for delivery.
Definition: EventDistributor.cc:44