openMSX
InputEventFactory.cc
Go to the documentation of this file.
2#include "Event.hh"
3#include "CommandException.hh"
4#include "SDLKey.hh"
5#include "StringOp.hh"
6#include "TclObject.hh"
7#include "one_of.hh"
8#include <SDL.h>
9
11
12[[nodiscard]] static Event parseKeyEvent(std::string_view str, uint32_t unicode)
13{
14 auto key = SDLKey::fromString(str);
15 if (!key) {
16 throw CommandException("Invalid keycode: ", str);
17 }
18
19 SDL_Event evt;
20 evt.key = SDL_KeyboardEvent{};
21 auto& e = evt.key;
22
23 e.timestamp = SDL_GetTicks();
24 e.keysym = key->sym;
25 e.keysym.unused = unicode;
26 if (key->down) {
27 e.type = SDL_KEYDOWN;
28 e.state = SDL_PRESSED;
29 return KeyDownEvent(evt);
30 } else {
31 e.type = SDL_KEYUP;
32 e.state = SDL_RELEASED;
33 return KeyUpEvent(evt);
34 }
35}
36
37[[nodiscard]] static Event parseKeyEvent(const TclObject& str, Interpreter& interp)
38{
39 auto len = str.getListLength(interp);
40 if (len == 1) {
41 return GroupEvent(
42 std::initializer_list<EventType>{EventType::KEY_UP, EventType::KEY_DOWN},
43 makeTclList("keyb"));
44 } else if (len == 2) {
45 auto comp1 = str.getListIndex(interp, 1).getString();
46 return parseKeyEvent(comp1, 0);
47 } else if (len == 3) {
48 auto comp1 = str.getListIndex(interp, 1).getString();
49 auto comp2 = str.getListIndex(interp, 2).getString();
50 if (comp2.starts_with("unicode")) {
51 if (auto u = StringOp::stringToBase<10, unsigned>(comp2.substr(7))) {
52 return parseKeyEvent(comp1, *u);
53 }
54 }
55 }
56 throw CommandException("Invalid keyboard event: ", str.getString());
57}
58
59[[nodiscard]] static bool upDown(std::string_view str)
60{
61 if (str == "up") {
62 return true;
63 } else if (str == "down") {
64 return false;
65 }
66 throw CommandException(
67 "Invalid direction (expected 'up' or 'down'): ", str);
68}
69
70[[nodiscard]] static Event parseMouseEvent(const TclObject& str, Interpreter& interp)
71{
72 auto len = str.getListLength(interp);
73 if (len >= 2) {
74 auto comp1 = str.getListIndex(interp, 1).getString();
75 if (comp1 == "motion") {
76 if (len == 2) {
77 return GroupEvent(
78 std::initializer_list<EventType>{EventType::MOUSE_MOTION},
79 makeTclList("mouse", comp1));
80 } else if (len == one_of(4u, 6u)) {
81 int absX = 0, absY = 0;
82 if (len == 6) {
83 absX = str.getListIndex(interp, 4).getInt(interp);
84 absY = str.getListIndex(interp, 5).getInt(interp);
85 } else {
86 // for bw-compat also allow events without absX,absY
87 }
88
89 SDL_Event evt;
90 evt.motion = SDL_MouseMotionEvent{};
91 auto& e = evt.motion;
92
93 e.type = SDL_MOUSEMOTION;
94 e.timestamp = SDL_GetTicks();
95 e.x = absX;
96 e.y = absY;
97 e.xrel = str.getListIndex(interp, 2).getInt(interp);
98 e.yrel = str.getListIndex(interp, 3).getInt(interp);
99 return MouseMotionEvent(evt);
100 }
101 } else if (comp1.starts_with("button")) {
102 if (len == 2) {
103 return GroupEvent(
104 std::initializer_list<EventType>{EventType::MOUSE_BUTTON_UP, EventType::MOUSE_BUTTON_DOWN},
105 makeTclList("mouse", "button"));
106 } else if (len == 3) {
107 if (auto button = StringOp::stringToBase<10, unsigned>(comp1.substr(6))) {
108 SDL_Event evt;
109 evt.button = SDL_MouseButtonEvent{};
110 auto& e = evt.button;
111
112 e.timestamp = SDL_GetTicks();
113 e.button = narrow<uint8_t>(*button);
114 if (upDown(str.getListIndex(interp, 2).getString())) {
115 e.type = SDL_MOUSEBUTTONUP;
116 e.state = SDL_RELEASED;
117 return MouseButtonUpEvent(evt);
118 } else {
119 e.type = SDL_MOUSEBUTTONDOWN;
120 e.state = SDL_PRESSED;
121 return MouseButtonDownEvent(evt);
122 }
123 }
124 }
125 } else if (comp1 == "wheel") {
126 if (len == 2) {
127 return GroupEvent(
128 std::initializer_list<EventType>{EventType::MOUSE_WHEEL},
129 makeTclList("mouse", comp1));
130 } else if (len == 4) {
131 SDL_Event evt;
132 evt.wheel = SDL_MouseWheelEvent{};
133 auto& e = evt.wheel;
134
135 e.type = SDL_MOUSEWHEEL;
136 e.timestamp = SDL_GetTicks();
137 e.direction = SDL_MOUSEWHEEL_NORMAL;
138 e.x = str.getListIndex(interp, 2).getInt(interp);
139 e.y = str.getListIndex(interp, 3).getInt(interp);
140 #if (SDL_VERSION_ATLEAST(2, 0, 18))
141 e.preciseX = narrow_cast<float>(e.x);
142 e.preciseY = narrow_cast<float>(e.y);
143 #endif
144 return MouseWheelEvent(evt);
145 }
146 }
147 }
148 throw CommandException("Invalid mouse event: ", str.getString());
149}
150
151[[nodiscard]] static Event parseOsdControlEvent(const TclObject& str, Interpreter& interp)
152{
153 if (str.getListLength(interp) == 3) {
154 auto buttonName = str.getListIndex(interp, 1).getString();
155 unsigned button = [&] {
156 if (buttonName == "LEFT") {
158 } else if (buttonName == "RIGHT") {
160 } else if (buttonName == "UP") {
162 } else if (buttonName == "DOWN") {
164 } else if (buttonName == "A") {
166 } else if (buttonName == "B") {
168 } else {
169 throw CommandException(
170 "Invalid OSDcontrol event, invalid button name: ",
171 buttonName);
172 }
173 }();
174 auto buttonAction = str.getListIndex(interp, 2).getString();
175 if (buttonAction == "RELEASE") {
176 return OsdControlReleaseEvent(button);
177 } else if (buttonAction == "PRESS") {
178 return OsdControlPressEvent(button);
179 }
180 }
181 throw CommandException("Invalid OSDcontrol event: ", str.getString());
182}
183
184[[nodiscard]] static Event parseJoystickEvent(const TclObject& str, Interpreter& interp)
185{
186 auto len = str.getListLength(interp);
187 if (len >= 2) {
188 auto comp0 = str.getListIndex(interp, 0).getString(); // joyN
189 auto comp1 = str.getListIndex(interp, 1).getString();
190
191 if (len == 2) {
192 using enum EventType;
193 if (comp1.starts_with("button")) {
194 return GroupEvent(
195 std::initializer_list<EventType>{JOY_BUTTON_UP, JOY_BUTTON_DOWN},
196 makeTclList("joy", "button"));
197 } else if (comp1.starts_with("axis")) {
198 return GroupEvent(
199 std::initializer_list<EventType>{JOY_AXIS_MOTION},
200 makeTclList("joy", "axis"));
201 } else if (comp1.starts_with("hat")) {
202 return GroupEvent(
203 std::initializer_list<EventType>{JOY_HAT},
204 makeTclList("joy", "hat"));
205 }
206 } else if (len == 3) {
207 auto comp2 = str.getListIndex(interp, 2);
208 if (auto j = StringOp::stringToBase<10, unsigned>(comp0.substr(3))) {
209 unsigned joystick = *j - 1;
210 if (comp1.starts_with("button")) {
211 if (auto button = StringOp::stringToBase<10, unsigned>(comp1.substr(6))) {
212 SDL_Event evt;
213 evt.jbutton = SDL_JoyButtonEvent{};
214 auto& e = evt.jbutton;
215
216 e.timestamp = SDL_GetTicks();
217 e.which = joystick;
218 e.button = narrow_cast<uint8_t>(*button);
219 if (upDown(comp2.getString())) {
220 e.type = SDL_JOYBUTTONUP;
221 e.state = SDL_RELEASED;
222 return JoystickButtonUpEvent(evt);
223 } else {
224 e.type = SDL_JOYBUTTONDOWN;
225 e.state = SDL_PRESSED;
226 return JoystickButtonDownEvent(evt);
227 }
228 }
229 } else if (comp1.starts_with("axis")) {
230 if (auto axis = StringOp::stringToBase<10, unsigned>(comp1.substr(4))) {
231 SDL_Event evt;
232 evt.jaxis = SDL_JoyAxisEvent{};
233 auto& e = evt.jaxis;
234
235 e.type = SDL_JOYAXISMOTION;
236 e.timestamp = SDL_GetTicks();
237 e.which = joystick;
238 e.axis = narrow_cast<uint8_t>(*axis);
239 e.value = narrow_cast<int16_t>(str.getListIndex(interp, 2).getInt(interp));
240 return JoystickAxisMotionEvent(evt);
241 }
242 } else if (comp1.starts_with("hat")) {
243 if (auto hat = StringOp::stringToBase<10, unsigned>(comp1.substr(3))) {
244 auto valueStr = str.getListIndex(interp, 2).getString();
245 int value = [&] {
246 if (valueStr == "up") return SDL_HAT_UP;
247 else if (valueStr == "right") return SDL_HAT_RIGHT;
248 else if (valueStr == "down") return SDL_HAT_DOWN;
249 else if (valueStr == "left") return SDL_HAT_LEFT;
250 else if (valueStr == "rightup") return SDL_HAT_RIGHTUP;
251 else if (valueStr == "rightdown") return SDL_HAT_RIGHTDOWN;
252 else if (valueStr == "leftup") return SDL_HAT_LEFTUP;
253 else if (valueStr == "leftdown") return SDL_HAT_LEFTDOWN;
254 else if (valueStr == "center") return SDL_HAT_CENTERED;
255 else {
256 throw CommandException("Invalid hat value: ", valueStr);
257 }
258 }();
259 SDL_Event evt;
260 evt.jhat = SDL_JoyHatEvent{};
261 auto& e = evt.jhat;
262
263 e.type = SDL_JOYHATMOTION;
264 e.timestamp = SDL_GetTicks();
265 e.which = joystick;
266 e.hat = narrow_cast<uint8_t>(*hat);
267 e.value = narrow_cast<uint8_t>(value);
268 return JoystickHatEvent(evt);
269 }
270 }
271 }
272 }
273 }
274 throw CommandException("Invalid joystick event: ", str.getString());
275}
276
277[[nodiscard]] static Event parseFocusEvent(const TclObject& str, Interpreter& interp)
278{
279 if (str.getListLength(interp) != 2) {
280 throw CommandException("Invalid focus event: ", str.getString());
281 }
282 bool gained = str.getListIndex(interp, 1).getBoolean(interp);
283
284 SDL_Event evt;
285 evt.window = SDL_WindowEvent{};
286 auto& e = evt.window;
287
288 e.type = SDL_WINDOWEVENT;
289 e.timestamp = SDL_GetTicks();
290 e.windowID = WindowEvent::getMainWindowId();
291 e.event = gained ? SDL_WINDOWEVENT_FOCUS_GAINED : SDL_WINDOWEVENT_FOCUS_LOST;
292 return WindowEvent(evt);
293}
294
295[[nodiscard]] static Event parseFileDropEvent(const TclObject& str, Interpreter& interp)
296{
297 if (str.getListLength(interp) != 1) {
298 throw CommandException("Invalid filedrop event: ", str.getString());
299 }
300 return GroupEvent(
301 std::initializer_list<EventType>{EventType::FILE_DROP},
302 makeTclList("filename"));
303}
304
305[[nodiscard]] static Event parseQuitEvent(const TclObject& str, Interpreter& interp)
306{
307 if (str.getListLength(interp) != 1) {
308 throw CommandException("Invalid quit event: ", str.getString());
309 }
310 return QuitEvent();
311}
312
314{
315 if (str.getListLength(interp) == 0) {
316 throw CommandException("Invalid event: ", str.getString());
317 }
318 auto type = str.getListIndex(interp, 0).getString();
319 if (type == "keyb") {
320 return parseKeyEvent(str, interp);
321 } else if (type == "mouse") {
322 return parseMouseEvent(str, interp);
323 } else if (type.starts_with("joy")) {
324 return parseJoystickEvent(str, interp);
325 } else if (type == "focus") {
326 return parseFocusEvent(str, interp);
327 } else if (type == "filedrop") {
328 return parseFileDropEvent(str, interp);
329 } else if (type == "quit") {
330 return parseQuitEvent(str, interp);
331 } else if (type == "command") {
332 SDL_Event evt;
333 evt.key = SDL_KeyboardEvent{};
334 evt.key.type = SDL_KEYUP;
335 evt.key.state = SDL_RELEASED;
336 return KeyUpEvent(evt); // dummy event, for bw compat
337 //return parseCommandEvent(str);
338 } else if (type == "OSDcontrol") {
339 return parseOsdControlEvent(str, interp);
340 } else {
341 // fall back
342 return parseKeyEvent(str.getString(), 0);
343 }
344}
345Event createInputEvent(std::string_view str, Interpreter& interp)
346{
347 return createInputEvent(TclObject(str), interp);
348}
349
350} // namespace openmsx::InputEventFactory
unsigned getListLength(Interpreter &interp) const
Definition TclObject.cc:155
TclObject getListIndex(Interpreter &interp, unsigned index) const
Definition TclObject.cc:173
zstring_view getString() const
Definition TclObject.cc:141
static uint32_t getMainWindowId()
Definition Event.hh:218
Event createInputEvent(const TclObject &str, Interpreter &interp)
EventType
Definition Event.hh:455
std::variant< KeyUpEvent, KeyDownEvent, MouseMotionEvent, MouseButtonUpEvent, MouseButtonDownEvent, MouseWheelEvent, JoystickAxisMotionEvent, JoystickHatEvent, JoystickButtonUpEvent, JoystickButtonDownEvent, OsdControlReleaseEvent, OsdControlPressEvent, WindowEvent, TextEvent, FileDropEvent, QuitEvent, FinishFrameEvent, CliCommandEvent, GroupEvent, BootEvent, FrameDrawnEvent, BreakEvent, SwitchRendererEvent, TakeReverseSnapshotEvent, AfterTimedEvent, MachineLoadedEvent, MachineActivatedEvent, MachineDeactivatedEvent, MidiInReaderEvent, MidiInWindowsEvent, MidiInCoreMidiEvent, MidiInCoreMidiVirtualEvent, MidiInALSAEvent, Rs232TesterEvent, Rs232NetEvent, ImGuiDelayedActionEvent, ImGuiActiveEvent > Event
Definition Event.hh:446
TclObject makeTclList(Args &&... args)
Definition TclObject.hh:293
static std::optional< SDLKey > fromString(std::string_view name)
Definition SDLKey.cc:119