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