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 auto button = [&] {
156 using enum OsdControlEvent::Button;
157 if (buttonName == "LEFT") {
158 return LEFT;
159 } else if (buttonName == "RIGHT") {
160 return RIGHT;
161 } else if (buttonName == "UP") {
162 return UP;
163 } else if (buttonName == "DOWN") {
164 return DOWN;
165 } else if (buttonName == "A") {
166 return A;
167 } else if (buttonName == "B") {
168 return B;
169 } else {
170 throw CommandException(
171 "Invalid OSDcontrol event, invalid button name: ",
172 buttonName);
173 }
174 }();
175 auto buttonAction = str.getListIndex(interp, 2).getString();
176 if (buttonAction == "RELEASE") {
177 return OsdControlReleaseEvent(button);
178 } else if (buttonAction == "PRESS") {
179 return OsdControlPressEvent(button);
180 }
181 }
182 throw CommandException("Invalid OSDcontrol event: ", str.getString());
183}
184
185[[nodiscard]] static Event parseJoystickEvent(const TclObject& str, Interpreter& interp)
186{
187 auto len = str.getListLength(interp);
188 if (len >= 2) {
189 auto comp0 = str.getListIndex(interp, 0).getString(); // joyN
190 auto comp1 = str.getListIndex(interp, 1).getString();
191
192 if (len == 2) {
193 using enum EventType;
194 if (comp1.starts_with("button")) {
195 return GroupEvent(
196 std::initializer_list<EventType>{JOY_BUTTON_UP, JOY_BUTTON_DOWN},
197 makeTclList("joy", "button"));
198 } else if (comp1.starts_with("axis")) {
199 return GroupEvent(
200 std::initializer_list<EventType>{JOY_AXIS_MOTION},
201 makeTclList("joy", "axis"));
202 } else if (comp1.starts_with("hat")) {
203 return GroupEvent(
204 std::initializer_list<EventType>{JOY_HAT},
205 makeTclList("joy", "hat"));
206 }
207 } else if (len == 3) {
208 auto comp2 = str.getListIndex(interp, 2);
209 if (auto j = StringOp::stringToBase<10, unsigned>(comp0.substr(3))) {
210 unsigned joystick = *j - 1;
211 if (comp1.starts_with("button")) {
212 if (auto button = StringOp::stringToBase<10, unsigned>(comp1.substr(6))) {
213 SDL_Event evt;
214 evt.jbutton = SDL_JoyButtonEvent{};
215 auto& e = evt.jbutton;
216
217 e.timestamp = SDL_GetTicks();
218 e.which = joystick;
219 e.button = narrow_cast<uint8_t>(*button);
220 if (upDown(comp2.getString())) {
221 e.type = SDL_JOYBUTTONUP;
222 e.state = SDL_RELEASED;
223 return JoystickButtonUpEvent(evt);
224 } else {
225 e.type = SDL_JOYBUTTONDOWN;
226 e.state = SDL_PRESSED;
227 return JoystickButtonDownEvent(evt);
228 }
229 }
230 } else if (comp1.starts_with("axis")) {
231 if (auto axis = StringOp::stringToBase<10, unsigned>(comp1.substr(4))) {
232 SDL_Event evt;
233 evt.jaxis = SDL_JoyAxisEvent{};
234 auto& e = evt.jaxis;
235
236 e.type = SDL_JOYAXISMOTION;
237 e.timestamp = SDL_GetTicks();
238 e.which = joystick;
239 e.axis = narrow_cast<uint8_t>(*axis);
240 e.value = narrow_cast<int16_t>(str.getListIndex(interp, 2).getInt(interp));
241 return JoystickAxisMotionEvent(evt);
242 }
243 } else if (comp1.starts_with("hat")) {
244 if (auto hat = StringOp::stringToBase<10, unsigned>(comp1.substr(3))) {
245 auto valueStr = str.getListIndex(interp, 2).getString();
246 int value = [&] {
247 if (valueStr == "up") return SDL_HAT_UP;
248 else if (valueStr == "right") return SDL_HAT_RIGHT;
249 else if (valueStr == "down") return SDL_HAT_DOWN;
250 else if (valueStr == "left") return SDL_HAT_LEFT;
251 else if (valueStr == "rightup") return SDL_HAT_RIGHTUP;
252 else if (valueStr == "rightdown") return SDL_HAT_RIGHTDOWN;
253 else if (valueStr == "leftup") return SDL_HAT_LEFTUP;
254 else if (valueStr == "leftdown") return SDL_HAT_LEFTDOWN;
255 else if (valueStr == "center") return SDL_HAT_CENTERED;
256 else {
257 throw CommandException("Invalid hat value: ", valueStr);
258 }
259 }();
260 SDL_Event evt;
261 evt.jhat = SDL_JoyHatEvent{};
262 auto& e = evt.jhat;
263
264 e.type = SDL_JOYHATMOTION;
265 e.timestamp = SDL_GetTicks();
266 e.which = joystick;
267 e.hat = narrow_cast<uint8_t>(*hat);
268 e.value = narrow_cast<uint8_t>(value);
269 return JoystickHatEvent(evt);
270 }
271 }
272 }
273 }
274 }
275 throw CommandException("Invalid joystick event: ", str.getString());
276}
277
278[[nodiscard]] static Event parseFocusEvent(const TclObject& str, Interpreter& interp)
279{
280 if (str.getListLength(interp) != 2) {
281 throw CommandException("Invalid focus event: ", str.getString());
282 }
283 bool gained = str.getListIndex(interp, 1).getBoolean(interp);
284
285 SDL_Event evt;
286 evt.window = SDL_WindowEvent{};
287 auto& e = evt.window;
288
289 e.type = SDL_WINDOWEVENT;
290 e.timestamp = SDL_GetTicks();
291 e.windowID = WindowEvent::getMainWindowId();
292 e.event = gained ? SDL_WINDOWEVENT_FOCUS_GAINED : SDL_WINDOWEVENT_FOCUS_LOST;
293 return WindowEvent(evt);
294}
295
296[[nodiscard]] static Event parseFileDropEvent(const TclObject& str, Interpreter& interp)
297{
298 if (str.getListLength(interp) != 1) {
299 throw CommandException("Invalid filedrop event: ", str.getString());
300 }
301 return GroupEvent(
302 std::initializer_list<EventType>{EventType::FILE_DROP},
303 makeTclList("filename"));
304}
305
306[[nodiscard]] static Event parseQuitEvent(const TclObject& str, Interpreter& interp)
307{
308 if (str.getListLength(interp) != 1) {
309 throw CommandException("Invalid quit event: ", str.getString());
310 }
311 return QuitEvent();
312}
313
315{
316 if (str.getListLength(interp) == 0) {
317 throw CommandException("Invalid event: ", str.getString());
318 }
319 auto type = str.getListIndex(interp, 0).getString();
320 if (type == "keyb") {
321 return parseKeyEvent(str, interp);
322 } else if (type == "mouse") {
323 return parseMouseEvent(str, interp);
324 } else if (type.starts_with("joy")) {
325 return parseJoystickEvent(str, interp);
326 } else if (type == "focus") {
327 return parseFocusEvent(str, interp);
328 } else if (type == "filedrop") {
329 return parseFileDropEvent(str, interp);
330 } else if (type == "quit") {
331 return parseQuitEvent(str, interp);
332 } else if (type == "command") {
333 SDL_Event evt;
334 evt.key = SDL_KeyboardEvent{};
335 evt.key.type = SDL_KEYUP;
336 evt.key.state = SDL_RELEASED;
337 return KeyUpEvent(evt); // dummy event, for bw compat
338 //return parseCommandEvent(str);
339 } else if (type == "OSDcontrol") {
340 return parseOsdControlEvent(str, interp);
341 } else {
342 // fall back
343 return parseKeyEvent(str.getString(), 0);
344 }
345}
346Event createInputEvent(std::string_view str, Interpreter& interp)
347{
348 return createInputEvent(TclObject(str), interp);
349}
350
351} // 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:454
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:445
std::array< const EDStorage, 4 > A
TclObject makeTclList(Args &&... args)
Definition TclObject.hh:293
static std::optional< SDLKey > fromString(std::string_view name)
Definition SDLKey.cc:119