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