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