OXYGENHUB
◆ main openmsx / src / events / Event.hh
17.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#ifndef EVENT_HH
2
#define EVENT_HH
3
4
#include "JoystickId.hh"
5
#include "RenderSettings.hh"
6
#include "SDLKey.hh"
7
#include "StringStorage.hh"
8
#include "TclObject.hh"
9
10
#include "static_vector.hh"
11
#include "stl.hh"
12
13
#include <cassert>
14
#include <cstdint>
15
#include <string>
16
#include <utility>
17
#include <variant>
18
19
#include <SDL.h>
20
21
namespace openmsx {
22
23
class CliConnection;
24
enum class EventType : uint8_t;
25
26
// --- The actual event classes (the Event class refers to one of these) ---
27
28
class EventBase {};
29
30
class SdlEvent : public EventBase
31
{
32
public:
33
	explicit SdlEvent(const SDL_Event& e) : evt(e) {}
34
	[[nodiscard]] const SDL_Event& getSdlEvent() const { return evt; }
35
	[[nodiscard]] const SDL_CommonEvent& getCommonSdlEvent() const { return evt.common; }
36
37
protected:
38
	SDL_Event evt;
39
};
40
41
42
class KeyEvent : public SdlEvent
43
{
44
public:
45
	using SdlEvent::SdlEvent;
46
47
	[[nodiscard]] bool         getRepeat()    const { return evt.key.repeat; }
48
	[[nodiscard]] SDL_Keycode  getKeyCode()   const { return evt.key.keysym.sym; }
49
	[[nodiscard]] SDL_Scancode getScanCode()  const { return evt.key.keysym.scancode; }
50
	[[nodiscard]] uint16_t     getModifiers() const { return evt.key.keysym.mod; }
51
	[[nodiscard]] uint32_t     getUnicode()   const {
52
		// HACK: repurposed 'unused' field as 'unicode' field
53
		return evt.key.keysym.unused;
54
	}
55
	[[nodiscard]] SDLKey getKey() const { return {.sym = evt.key.keysym, .down = evt.type == SDL_KEYDOWN}; }
56
};
57
58
class KeyUpEvent final : public KeyEvent
59
{
60
public:
61
	using KeyEvent::KeyEvent;
62
63
	[[nodiscard]] static KeyUpEvent create(SDL_Keycode code, SDL_Keymod mod = KMOD_NONE) {
64
		SDL_Event evt;
65
		evt.key = SDL_KeyboardEvent{};
66
		auto& e = evt.key;
67
68
		e.type = SDL_KEYUP;
69
		e.timestamp = SDL_GetTicks();
70
		e.state = SDL_RELEASED;
71
		e.keysym.sym = code;
72
		e.keysym.mod = mod;
73
		return KeyUpEvent(evt);
74
	}
75
};
76
77
class KeyDownEvent final : public KeyEvent
78
{
79
public:
80
	using KeyEvent::KeyEvent;
81
82
	[[nodiscard]] static KeyDownEvent create(SDL_Keycode code, SDL_Keymod mod = KMOD_NONE) {
83
		SDL_Event evt;
84
		evt.key = SDL_KeyboardEvent{};
85
		auto& e = evt.key;
86
87
		e.type = SDL_KEYDOWN;
88
		e.timestamp = SDL_GetTicks();
89
		e.state = SDL_PRESSED;
90
		e.keysym.sym = code;
91
		e.keysym.mod = mod;
92
		return KeyDownEvent(evt);
93
	}
94
	[[nodiscard]] static KeyDownEvent create(uint32_t timestamp, unsigned unicode) {
95
		SDL_Event evt;
96
		evt.key = SDL_KeyboardEvent{};
97
		auto& e = evt.key;
98
99
		e.type = SDL_KEYDOWN;
100
		e.timestamp = timestamp;
101
		e.state = SDL_PRESSED;
102
		e.keysym.sym = SDLK_UNKNOWN;
103
		e.keysym.mod = KMOD_NONE;
104
		e.keysym.unused = unicode;
105
		return KeyDownEvent(evt);
106
	}
107
};
108
109
110
class MouseButtonEvent : public SdlEvent
111
{
112
public:
113
	using SdlEvent::SdlEvent;
114
115
	[[nodiscard]] auto getButton() const { return evt.button.button; }
116
};
117
118
class MouseButtonUpEvent final : public MouseButtonEvent
119
{
120
public:
121
	using MouseButtonEvent::MouseButtonEvent;
122
};
123
124
class MouseButtonDownEvent final : public MouseButtonEvent
125
{
126
public:
127
	using MouseButtonEvent::MouseButtonEvent;
128
};
129
130
class MouseWheelEvent final : public SdlEvent
131
{
132
public:
133
	using SdlEvent::SdlEvent;
134
135
	[[nodiscard]] int getX() const { return normalize(evt.wheel.x); }
136
	[[nodiscard]] int getY() const { return normalize(evt.wheel.y); }
137
138
private:
139
	[[nodiscard]] int normalize(int x) const {
140
		return evt.wheel.direction == SDL_MOUSEWHEEL_FLIPPED ? -x : x;
141
	}
142
};
143
144
class MouseMotionEvent final : public SdlEvent
145
{
146
public:
147
	using SdlEvent::SdlEvent;
148
149
	[[nodiscard]] int getX() const    { return evt.motion.xrel; }
150
	[[nodiscard]] int getY() const    { return evt.motion.yrel; }
151
	[[nodiscard]] int getAbsX() const { return evt.motion.x; }
152
	[[nodiscard]] int getAbsY() const { return evt.motion.y; }
153
};
154
155
156
class JoystickEvent : public SdlEvent
157
{
158
public:
159
	using SdlEvent::SdlEvent;
160
161
	[[nodiscard]] JoystickId getJoystick() const {
162
		// SDL joystick instance ID has already been translated to an openMSX JoystickID
163
		return JoystickId(evt.jbutton.which);
164
	}
165
};
166
167
class JoystickButtonEvent : public JoystickEvent
168
{
169
public:
170
	using JoystickEvent::JoystickEvent;
171
172
	[[nodiscard]] unsigned getButton() const { return evt.jbutton.button; }
173
};
174
175
class JoystickButtonUpEvent final : public JoystickButtonEvent
176
{
177
public:
178
	using JoystickButtonEvent::JoystickButtonEvent;
179
};
180
181
class JoystickButtonDownEvent final : public JoystickButtonEvent
182
{
183
public:
184
	using JoystickButtonEvent::JoystickButtonEvent;
185
};
186
187
class JoystickAxisMotionEvent final : public JoystickEvent
188
{
189
public:
190
	static constexpr uint8_t X_AXIS = 0;
191
	static constexpr uint8_t Y_AXIS = 1;
192
193
	using JoystickEvent::JoystickEvent;
194
195
	[[nodiscard]] uint8_t getAxis() const { return evt.jaxis.axis; }
196
	[[nodiscard]] int16_t getValue() const { return evt.jaxis.value; }
197
};
198
199
class JoystickHatEvent final : public JoystickEvent
200
{
201
public:
202
	using JoystickEvent::JoystickEvent;
203
204
	[[nodiscard]] uint8_t getHat()   const { return evt.jhat.hat; }
205
	[[nodiscard]] uint8_t getValue() const { return evt.jhat.value; }
206
};
207
208
209
class WindowEvent : public SdlEvent
210
{
211
public:
212
	using SdlEvent::SdlEvent;
213
214
	[[nodiscard]] const SDL_WindowEvent& getSdlWindowEvent() const { return evt.window; }
215
	[[nodiscard]] bool isMainWindow() const { return isMainWindow(evt.window.windowID); }
216
217
public:
218
	static void setMainWindowId(uint32_t id) { mainWindowId = id; }
219
	static uint32_t getMainWindowId() { return mainWindowId; }
220
	[[nodiscard]] static bool isMainWindow(unsigned id) { return id == mainWindowId; }
221
private:
222
	static inline uint32_t mainWindowId = unsigned(-1);
223
};
224
225
226
class TextEvent : public SdlEvent
227
{
228
public:
229
	using SdlEvent::SdlEvent;
230
};
231
232
233
class FileDropEvent final : public EventBase
234
{
235
public:
236
	explicit FileDropEvent(std::string_view fileName_)
237
		: fileName(allocate_c_string(fileName_)) {}
238
	FileDropEvent(const FileDropEvent&) { assert(false); }
239
	FileDropEvent& operator=(const FileDropEvent&) { assert(false); return *this; }
240
	FileDropEvent(FileDropEvent&&) = default;
241
	FileDropEvent& operator=(FileDropEvent&&) = default;
242
	~FileDropEvent() = default;
243
244
	[[nodiscard]] zstring_view getFileName() const { return fileName.get(); }
245
246
private:
247
	StringStorage fileName;
248
};
249
250
251
class QuitEvent final : public EventBase {};
252
253
254
/** OSD events are triggered by other events. They aggregate keyboard and
255
 * joystick events into one set of events that can be used to e.g. control
256
 * OSD elements.
257
 */
258
class OsdControlEvent : public EventBase
259
{
260
public:
261
	enum class Button : uint8_t {LEFT, RIGHT, UP, DOWN, A, B, NUM};
262
	[[nodiscard]] Button getButton() const { return button; }
263
264
protected:
265
	explicit OsdControlEvent(Button button_)
266
		: button(button_) {}
267
268
private:
269
	Button button;
270
};
271
272
class OsdControlReleaseEvent final : public OsdControlEvent
273
{
274
public:
275
	explicit OsdControlReleaseEvent(Button button_)
276
		: OsdControlEvent(button_) {}
277
};
278
279
class OsdControlPressEvent final : public OsdControlEvent
280
{
281
public:
282
	explicit OsdControlPressEvent(Button button_)
283
		: OsdControlEvent(button_) {}
284
};
285
286
287
class GroupEvent final : public EventBase
288
{
289
public:
290
	GroupEvent(std::initializer_list<EventType> typesToMatch_, TclObject tclListComponents_)
291
		: typesToMatch(typesToMatch_)
292
		, tclListComponents(std::move(tclListComponents_)) {}
293
294
	[[nodiscard]] const auto& getTypesToMatch() const { return typesToMatch; }
295
	[[nodiscard]] const auto& getTclListComponents() const { return tclListComponents; }
296
297
private:
298
	static_vector<EventType, 3> typesToMatch;
299
	TclObject tclListComponents;
300
};
301
302
/**
303
 * This event is send when a device (v99x8, v9990, video9000, laserdisc)
304
 * reaches the end of a frame. This event has info on:
305
 *  - which device generated the event
306
 *  - which video layer was active
307
 *  - was the frame actually rendered or not (frameskip)
308
 * Note that even if a frame was rendered (not skipped) it may not (need to) be
309
 * displayed because the corresponding video layer is not active. Or also even
310
 * if the corresponding video layer for a device is not active, the rendered
311
 * frame may still be displayed as part of a superimposed video layer.
312
 */
313
class FinishFrameEvent final : public EventBase
314
{
315
public:
316
	FinishFrameEvent(int thisSource_, int selectedSource_,
317
	                 bool skipped_)
318
		: thisSource(thisSource_), selectedSource(selectedSource_)
319
		, skipped(skipped_)
320
	{
321
	}
322
323
	[[nodiscard]] int getSource()         const { return thisSource; }
324
	[[nodiscard]] int getSelectedSource() const { return selectedSource; }
325
	[[nodiscard]] bool isSkipped() const { return skipped; }
326
	[[nodiscard]] bool needRender() const { return !skipped && (thisSource == selectedSource); }
327
328
private:
329
	int thisSource;
330
	int selectedSource;
331
	bool skipped;
332
};
333
334
/** Command received on CliComm connection. */
335
class CliCommandEvent final : public EventBase
336
{
337
public:
338
	CliCommandEvent(std::string_view command_, const CliConnection* id_)
339
		: command(allocate_c_string(command_)), id(id_) {}
340
	CliCommandEvent(const CliCommandEvent&) { assert(false); }
341
	CliCommandEvent& operator=(const CliCommandEvent&) { assert(false); return *this; }
342
	CliCommandEvent(CliCommandEvent&&) = default;
343
	CliCommandEvent& operator=(CliCommandEvent&&) = default;
344
	~CliCommandEvent() = default;
345
346
	[[nodiscard]] zstring_view getCommand() const { return command.get(); }
347
	[[nodiscard]] const CliConnection* getId() const { return id; }
348
349
private:
350
	StringStorage command;
351
	const CliConnection* id;
352
};
353
354
class ImGuiActiveEvent final : public EventBase
355
{
356
public:
357
	explicit ImGuiActiveEvent(bool active_)
358
		: active(active_) {}
359
	[[nodiscard]] bool getActive() const { return active; }
360
361
private:
362
	bool active;
363
};
364
365
class SwitchRendererEvent final : public EventBase {
366
public:
367
	explicit SwitchRendererEvent(RenderSettings::RendererID id_)
368
		: id(id_) {}
369
	[[nodiscard]] auto getRenderer() const { return id; }
370
371
private:
372
	RenderSettings::RendererID id;
373
374
};
375
376
// Events that don't need additional data
377
class SimpleEvent : public EventBase {};
378
379
/** Sent when the MSX resets or powers up. */
380
class BootEvent                  final : public SimpleEvent {};
381
382
/** Sent when a FINISH_FRAME caused a redraw of the screen.
383
  * So in other words send when a FINISH_FRAME event was send
384
  * and the frame was not skipped and the event came from the active video
385
  * source. */
386
class FrameDrawnEvent            final : public SimpleEvent {};
387
388
class BreakEvent                 final : public SimpleEvent {};
389
class ContinueEvent              final : public SimpleEvent {};
390
391
/** Used to schedule 'taking reverse snapshots' between Z80 instructions. */
392
class TakeReverseSnapshotEvent   final : public SimpleEvent {};
393
394
/** Send when an after-EmuTime command should be executed. */
395
class AfterTimedEvent            final : public SimpleEvent {};
396
397
/** Send when a (new) machine configuration is loaded. */
398
class MachineLoadedEvent         final : public SimpleEvent {};
399
400
/** Send when a machine is (de)activated.
401
  * This events is specific per machine. */
402
class MachineActivatedEvent      final : public SimpleEvent {};
403
class MachineDeactivatedEvent    final : public SimpleEvent {};
404
405
class MidiInReaderEvent          final : public SimpleEvent {};
406
class MidiInWindowsEvent         final : public SimpleEvent {};
407
class MidiInCoreMidiEvent        final : public SimpleEvent {};
408
class MidiInCoreMidiVirtualEvent final : public SimpleEvent {};
409
class MidiInALSAEvent            final : public SimpleEvent {};
410
class Rs232TesterEvent           final : public SimpleEvent {};
411
class Rs232NetEvent              final : public SimpleEvent {};
412
class Rs232RawEvent              final : public SimpleEvent {};
413
class ImGuiDelayedActionEvent    final : public SimpleEvent {};
414
415
/** Send when changed host keyboard layout. */
416
class KeyMapChangedEvent          final : public SimpleEvent {};
417
418
// --- Put all (non-abstract) Event classes into a std::variant ---
419
420
using Event = std::variant<
421
	KeyUpEvent,
422
	KeyDownEvent,
423
	MouseMotionEvent,
424
	MouseButtonUpEvent,
425
	MouseButtonDownEvent,
426
	MouseWheelEvent,
427
	JoystickAxisMotionEvent,
428
	JoystickHatEvent,
429
	JoystickButtonUpEvent,
430
	JoystickButtonDownEvent,
431
	OsdControlReleaseEvent,
432
	OsdControlPressEvent,
433
	WindowEvent,
434
	TextEvent,
435
	FileDropEvent,
436
	QuitEvent,
437
	FinishFrameEvent,
438
	CliCommandEvent,
439
	GroupEvent,
440
	BootEvent,
441
	FrameDrawnEvent,
442
	BreakEvent,
443
	ContinueEvent,
444
	SwitchRendererEvent,
445
	TakeReverseSnapshotEvent,
446
	AfterTimedEvent,
447
	MachineLoadedEvent,
448
	MachineActivatedEvent,
449
	MachineDeactivatedEvent,
450
	MidiInReaderEvent,
451
	MidiInWindowsEvent,
452
	MidiInCoreMidiEvent,
453
	MidiInCoreMidiVirtualEvent,
454
	MidiInALSAEvent,
455
	Rs232TesterEvent,
456
	Rs232NetEvent,
457
	Rs232RawEvent,
458
	ImGuiDelayedActionEvent,
459
	ImGuiActiveEvent,
460
	KeyMapChangedEvent
461
>;
462
463
template<typename T>
464
inline constexpr uint8_t event_index = get_index<T, Event>::value;
465
466
467
// --- Define an enum for all concrete event types. ---
468
// Use the numeric value from the corresponding index in the Event.
469
enum class EventType : uint8_t
470
{
471
	KEY_UP                   = event_index<KeyUpEvent>,
472
	KEY_DOWN                 = event_index<KeyDownEvent>,
473
	MOUSE_MOTION             = event_index<MouseMotionEvent>,
474
	MOUSE_BUTTON_UP          = event_index<MouseButtonUpEvent>,
475
	MOUSE_BUTTON_DOWN        = event_index<MouseButtonDownEvent>,
476
	MOUSE_WHEEL              = event_index<MouseWheelEvent>,
477
	JOY_AXIS_MOTION          = event_index<JoystickAxisMotionEvent>,
478
	JOY_HAT                  = event_index<JoystickHatEvent>,
479
	JOY_BUTTON_UP            = event_index<JoystickButtonUpEvent>,
480
	JOY_BUTTON_DOWN          = event_index<JoystickButtonDownEvent>,
481
	OSD_CONTROL_RELEASE      = event_index<OsdControlReleaseEvent>,
482
	OSD_CONTROL_PRESS        = event_index<OsdControlPressEvent>,
483
	WINDOW                   = event_index<WindowEvent>,
484
	TEXT                     = event_index<TextEvent>,
485
	FILE_DROP                = event_index<FileDropEvent>,
486
	QUIT                     = event_index<QuitEvent>,
487
	GROUP                    = event_index<GroupEvent>,
488
	BOOT                     = event_index<BootEvent>,
489
	FINISH_FRAME             = event_index<FinishFrameEvent>,
490
	FRAME_DRAWN              = event_index<FrameDrawnEvent>,
491
	BREAK                    = event_index<BreakEvent>,
492
	CONTINUE                 = event_index<ContinueEvent>,
493
	SWITCH_RENDERER          = event_index<SwitchRendererEvent>,
494
	TAKE_REVERSE_SNAPSHOT    = event_index<TakeReverseSnapshotEvent>,
495
	CLICOMMAND               = event_index<CliCommandEvent>,
496
	AFTER_TIMED              = event_index<AfterTimedEvent>,
497
	MACHINE_LOADED           = event_index<MachineLoadedEvent>,
498
	MACHINE_ACTIVATED        = event_index<MachineActivatedEvent>,
499
	MACHINE_DEACTIVATED      = event_index<MachineDeactivatedEvent>,
500
	MIDI_IN_READER           = event_index<MidiInReaderEvent>,
501
	MIDI_IN_WINDOWS          = event_index<MidiInWindowsEvent>,
502
	MIDI_IN_COREMIDI         = event_index<MidiInCoreMidiEvent>,
503
	MIDI_IN_COREMIDI_VIRTUAL = event_index<MidiInCoreMidiVirtualEvent>,
504
	MIDI_IN_ALSA             = event_index<MidiInALSAEvent>,
505
	RS232_TESTER             = event_index<Rs232TesterEvent>,
506
	RS232_NET                = event_index<Rs232NetEvent>,
507
	RS232_RAW                = event_index<Rs232RawEvent>,
508
	IMGUI_DELAYED_ACTION     = event_index<ImGuiDelayedActionEvent>,
509
	IMGUI_ACTIVE             = event_index<ImGuiActiveEvent>,
510
	KEYMAP_CHANGED           = event_index<KeyMapChangedEvent>,
511
512
	NUM_EVENT_TYPES // must be last
513
};
514
515
516
// --- Event class, free functions ---
517
518
[[nodiscard]] EventType getType(const Event& event);
519
520
/** Get a string representation of this event. */
521
[[nodiscard]] std::string toString(const Event& event);
522
523
/** Similar to toString(), but retains the structure of the event. */
524
[[nodiscard]] TclObject toTclList(const Event& event);
525
526
[[nodiscard]] bool operator==(const Event& x, const Event& y);
527
528
/** Does this event 'match' the given event. Normally an event
529
  * only matches itself (as defined by operator==). But e.g.
530
  * MouseMotionGroupEvent matches any MouseMotionEvent. */
531
[[nodiscard]] bool matches(const Event& self, const Event& other);
532
533
534
// --- Event class implementation, free functions ---
535
536
inline EventType getType(const Event& event)
537
{
538
	return EventType(event.index());
539
}
540
541
// Similar to std::get() and std::get_if()
542
template<typename T>
543
struct GetIfEventHelper { // standard std::get_if() behavior
544
	const T* operator()(const Event& event) {
545
		return std::get_if<T>(&event);
546
	}
547
};
548
template<>
549
struct GetIfEventHelper<SdlEvent> { // extension for base-classes
550
	[[nodiscard]] const SdlEvent* operator()(const Event& event) const {
551
		const auto& var = event;
552
		switch (EventType(var.index())) {
553
		using enum EventType;
554
		case KEY_UP:            return &std::get<KeyUpEvent>(var);
555
		case KEY_DOWN:          return &std::get<KeyDownEvent>(var);
556
		case MOUSE_BUTTON_UP:   return &std::get<MouseButtonUpEvent>(var);
557
		case MOUSE_BUTTON_DOWN: return &std::get<MouseButtonDownEvent>(var);
558
		case MOUSE_WHEEL:       return &std::get<MouseWheelEvent>(var);
559
		case MOUSE_MOTION:      return &std::get<MouseMotionEvent>(var);
560
		case JOY_BUTTON_UP:     return &std::get<JoystickButtonUpEvent>(var);
561
		case JOY_BUTTON_DOWN:   return &std::get<JoystickButtonDownEvent>(var);
562
		case JOY_AXIS_MOTION:   return &std::get<JoystickAxisMotionEvent>(var);
563
		case JOY_HAT:           return &std::get<JoystickHatEvent>(var);
564
		case WINDOW:            return &std::get<WindowEvent>(var);
565
		case TEXT:              return &std::get<TextEvent>(var);
566
		default: return nullptr;
567
		}
568
	}
569
};
570
template<>
571
struct GetIfEventHelper<KeyEvent> {
572
	[[nodiscard]] const KeyEvent* operator()(const Event& event) const {
573
		const auto& var = event;
574
		switch (EventType(var.index())) {
575
		using enum EventType;
576
		case KEY_UP:   return &std::get<KeyUpEvent>(var);
577
		case KEY_DOWN: return &std::get<KeyDownEvent>(var);
578
		default: return nullptr;
579
		}
580
	}
581
};
582
template<>
583
struct GetIfEventHelper<JoystickEvent> {
584
	[[nodiscard]] const JoystickEvent* operator()(const Event& event) const {
585
		const auto& var = event;
586
		switch (EventType(var.index())) {
587
		using enum EventType;
588
		case JOY_BUTTON_UP:   return &std::get<JoystickButtonUpEvent>(var);
589
		case JOY_BUTTON_DOWN: return &std::get<JoystickButtonDownEvent>(var);
590
		case JOY_AXIS_MOTION: return &std::get<JoystickAxisMotionEvent>(var);
591
		case JOY_HAT:         return &std::get<JoystickHatEvent>(var);
592
		default: return nullptr;
593
		}
594
	}
595
};
596
template<typename T>
597
const T* get_event_if(const Event& event)
598
{
599
	GetIfEventHelper<T> helper;
600
	return helper(event);
601
}
602
template<typename T>
603
const T& get_event(const Event& event)
604
{
605
	const T* t = get_event_if<T>(event);
606
	assert(t);
607
	return *t;
608
}
609
610
} // namespace openmsx
611
612
#endif