openMSX
InputEventFactory.cc
Go to the documentation of this file.
1 #include "InputEventFactory.hh"
2 #include "Event.hh"
3 #include "CommandException.hh"
4 #include "Keys.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 keyCode = Keys::getCode(str);
15  if (keyCode == Keys::K_NONE) {
16  throw CommandException("Invalid keycode: ", str);
17  }
18  if (keyCode & Keys::KD_RELEASE) {
19  return Event::create<KeyUpEvent>(keyCode);
20  } else {
21  return Event::create<KeyDownEvent>(keyCode, unicode);
22  }
23 }
24 
25 [[nodiscard]] static Event parseKeyEvent(const TclObject& str, Interpreter& interp)
26 {
27  auto len = str.getListLength(interp);
28  if (len == 1) {
29  return Event::create<GroupEvent>(
30  std::initializer_list<EventType>{EventType::KEY_UP, EventType::KEY_DOWN},
31  makeTclList("keyb"));
32  } else if (len == 2) {
33  auto comp1 = str.getListIndex(interp, 1).getString();
34  return parseKeyEvent(comp1, 0);
35  } else if (len == 3) {
36  auto comp1 = str.getListIndex(interp, 1).getString();
37  auto comp2 = str.getListIndex(interp, 2).getString();
38  if (StringOp::startsWith(comp2, "unicode")) {
39  if (auto u = StringOp::stringToBase<10, unsigned>(comp2.substr(7))) {
40  return parseKeyEvent(comp1, *u);
41  }
42  }
43  }
44  throw CommandException("Invalid keyboard event: ", str.getString());
45 }
46 
47 [[nodiscard]] static bool upDown(std::string_view str)
48 {
49  if (str == "up") {
50  return true;
51  } else if (str == "down") {
52  return false;
53  }
54  throw CommandException(
55  "Invalid direction (expected 'up' or 'down'): ", str);
56 }
57 
58 [[nodiscard]] static Event parseMouseEvent(const TclObject& str, Interpreter& interp)
59 {
60  auto len = str.getListLength(interp);
61  if (len >= 2) {
62  auto comp1 = str.getListIndex(interp, 1).getString();
63  if (comp1 == "motion") {
64  if (len == 2) {
65  return Event::create<GroupEvent>(
66  std::initializer_list<EventType>{EventType::MOUSE_MOTION},
67  makeTclList("mouse", comp1));
68  } else if (len == one_of(4u, 6u)) {
69  int absX = 0, absY = 0;
70  if (len == 6) {
71  absX = str.getListIndex(interp, 4).getInt(interp);
72  absY = str.getListIndex(interp, 5).getInt(interp);
73  } else {
74  // for bw-compat also allow events without absX,absY
75  }
76  return Event::create<MouseMotionEvent>(
77  str.getListIndex(interp, 2).getInt(interp),
78  str.getListIndex(interp, 3).getInt(interp),
79  absX, absY);
80  }
81  } else if (StringOp::startsWith(comp1, "button")) {
82  if (len == 2) {
83  return Event::create<GroupEvent>(
84  std::initializer_list<EventType>{EventType::MOUSE_BUTTON_UP, EventType::MOUSE_BUTTON_DOWN},
85  makeTclList("mouse", "button"));
86  } else if (len == 3) {
87  if (auto button = StringOp::stringToBase<10, unsigned>(comp1.substr(6))) {
88  if (upDown(str.getListIndex(interp, 2).getString())) {
89  return Event::create<MouseButtonUpEvent> (*button);
90  } else {
91  return Event::create<MouseButtonDownEvent>(*button);
92  }
93  }
94  }
95  } else if (comp1 == "wheel") {
96  if (len == 2) {
97  return Event::create<GroupEvent>(
98  std::initializer_list<EventType>{EventType::MOUSE_WHEEL},
99  makeTclList("mouse", comp1));
100  } else if (len == 4) {
101  return Event::create<MouseWheelEvent>(
102  str.getListIndex(interp, 2).getInt(interp),
103  str.getListIndex(interp, 3).getInt(interp));
104  }
105  }
106  }
107  throw CommandException("Invalid mouse event: ", str.getString());
108 }
109 
110 [[nodiscard]] static Event parseOsdControlEvent(const TclObject& str, Interpreter& interp)
111 {
112  if (str.getListLength(interp) == 3) {
113  auto buttonName = str.getListIndex(interp, 1).getString();
114  unsigned button = [&] {
115  if (buttonName == "LEFT") {
117  } else if (buttonName == "RIGHT") {
119  } else if (buttonName == "UP") {
121  } else if (buttonName == "DOWN") {
123  } else if (buttonName == "A") {
125  } else if (buttonName == "B") {
127  } else {
128  throw CommandException(
129  "Invalid OSDcontrol event, invalid button name: ",
130  buttonName);
131  }
132  }();
133  auto buttonAction = str.getListIndex(interp, 2).getString();
134  if (buttonAction == "RELEASE") {
135  return Event::create<OsdControlReleaseEvent>(button, Event{});
136  } else if (buttonAction == "PRESS") {
137  return Event::create<OsdControlPressEvent> (button, Event{});
138  }
139  }
140  throw CommandException("Invalid OSDcontrol event: ", str.getString());
141 }
142 
143 [[nodiscard]] static Event parseJoystickEvent(const TclObject& str, Interpreter& interp)
144 {
145  auto len = str.getListLength(interp);
146  if (len >= 2) {
147  auto comp0 = str.getListIndex(interp, 0).getString(); // joyN
148  auto comp1 = str.getListIndex(interp, 1).getString();
149 
150  if (len == 2) {
151  if (StringOp::startsWith(comp1, "button")) {
152  return Event::create<GroupEvent>(
153  std::initializer_list<EventType>{EventType::JOY_BUTTON_UP, EventType::JOY_BUTTON_DOWN},
154  makeTclList("joy", "button"));
155  } else if (StringOp::startsWith(comp1, "axis")) {
156  return Event::create<GroupEvent>(
157  std::initializer_list<EventType>{EventType::JOY_AXIS_MOTION},
158  makeTclList("joy", "axis"));
159  } else if (StringOp::startsWith(comp1, "hat")) {
160  return Event::create<GroupEvent>(
161  std::initializer_list<EventType>{EventType::JOY_HAT},
162  makeTclList("joy", "hat"));
163  }
164  } else if (len == 3) {
165  auto comp2 = str.getListIndex(interp, 2);
166  if (auto j = StringOp::stringToBase<10, unsigned>(comp0.substr(3))) {
167  unsigned joystick = *j - 1;
168  if (StringOp::startsWith(comp1, "button")) {
169  if (auto button = StringOp::stringToBase<10, unsigned>(comp1.substr(6))) {
170  if (upDown(comp2.getString())) {
171  return Event::create<JoystickButtonUpEvent> (joystick, *button);
172  } else {
173  return Event::create<JoystickButtonDownEvent>(joystick, *button);
174  }
175  }
176  } else if (StringOp::startsWith(comp1, "axis")) {
177  if (auto axis = StringOp::stringToBase<10, unsigned>(comp1.substr(4))) {
178  int value = str.getListIndex(interp, 2).getInt(interp);
179  return Event::create<JoystickAxisMotionEvent>(joystick, *axis, value);
180  }
181  } else if (StringOp::startsWith(comp1, "hat")) {
182  if (auto hat = StringOp::stringToBase<10, unsigned>(comp1.substr(3))) {
183  auto valueStr = str.getListIndex(interp, 2).getString();
184  int value = [&] {
185  if (valueStr == "up") return SDL_HAT_UP;
186  else if (valueStr == "right") return SDL_HAT_RIGHT;
187  else if (valueStr == "down") return SDL_HAT_DOWN;
188  else if (valueStr == "left") return SDL_HAT_LEFT;
189  else if (valueStr == "rightup") return SDL_HAT_RIGHTUP;
190  else if (valueStr == "rightdown") return SDL_HAT_RIGHTDOWN;
191  else if (valueStr == "leftup") return SDL_HAT_LEFTUP;
192  else if (valueStr == "leftdown") return SDL_HAT_LEFTDOWN;
193  else if (valueStr == "center") return SDL_HAT_CENTERED;
194  else {
195  throw CommandException("Invalid hat value: ", valueStr);
196  }
197  }();
198  return Event::create<JoystickHatEvent>(joystick, *hat, value);
199  }
200  }
201  }
202  }
203  }
204  throw CommandException("Invalid joystick event: ", str.getString());
205 }
206 
207 [[nodiscard]] static Event parseFocusEvent(const TclObject& str, Interpreter& interp)
208 {
209  if (str.getListLength(interp) != 2) {
210  throw CommandException("Invalid focus event: ", str.getString());
211  }
212  return Event::create<FocusEvent>(str.getListIndex(interp, 1).getBoolean(interp));
213 }
214 
215 [[nodiscard]] static Event parseFileDropEvent(const TclObject& str, Interpreter& interp)
216 {
217  if (str.getListLength(interp) != 1) {
218  throw CommandException("Invalid filedrop event: ", str.getString());
219  }
220  return Event::create<GroupEvent>(
221  std::initializer_list<EventType>{EventType::FILEDROP},
222  makeTclList("filename"));
223 }
224 
225 [[nodiscard]] static Event parseResizeEvent(const TclObject& str, Interpreter& interp)
226 {
227  if (str.getListLength(interp) != 3) {
228  throw CommandException("Invalid resize event: ", str.getString());
229  }
230  return Event::create<ResizeEvent>(
231  str.getListIndex(interp, 1).getInt(interp),
232  str.getListIndex(interp, 2).getInt(interp));
233 }
234 
235 [[nodiscard]] static Event parseQuitEvent(const TclObject& str, Interpreter& interp)
236 {
237  if (str.getListLength(interp) != 1) {
238  throw CommandException("Invalid quit event: ", str.getString());
239  }
240  return Event::create<QuitEvent>();
241 }
242 
244 {
245  if (str.getListLength(interp) == 0) {
246  throw CommandException("Invalid event: ", str.getString());
247  }
248  auto type = str.getListIndex(interp, 0).getString();
249  if (type == "keyb") {
250  return parseKeyEvent(str, interp);
251  } else if (type == "mouse") {
252  return parseMouseEvent(str, interp);
253  } else if (StringOp::startsWith(type, "joy")) {
254  return parseJoystickEvent(str, interp);
255  } else if (type == "focus") {
256  return parseFocusEvent(str, interp);
257  } else if (type == "filedrop") {
258  return parseFileDropEvent(str, interp);
259  } else if (type == "resize") {
260  return parseResizeEvent(str, interp);
261  } else if (type == "quit") {
262  return parseQuitEvent(str, interp);
263  } else if (type == "command") {
264  return Event::create<KeyUpEvent>(Keys::K_UNKNOWN); // dummy event, for bw compat
265  //return parseCommandEvent(str);
266  } else if (type == "OSDcontrol") {
267  return parseOsdControlEvent(str, interp);
268  } else {
269  // fall back
270  return parseKeyEvent(type, 0);
271  }
272 }
273 Event createInputEvent(std::string_view str, Interpreter& interp)
274 {
275  return createInputEvent(TclObject(str), interp);
276 }
277 
278 } // namespace openmsx::InputEventFactory
Definition: one_of.hh:7
unsigned getListLength(Interpreter &interp) const
Definition: TclObject.cc:125
TclObject getListIndex(Interpreter &interp, unsigned index) const
Definition: TclObject.cc:143
zstring_view getString() const
Definition: TclObject.cc:111
bool startsWith(string_view total, string_view part)
Definition: StringOp.cc:29
Event createInputEvent(const TclObject &str, Interpreter &interp)
@ KD_RELEASE
Definition: Keys.hh:212
@ K_UNKNOWN
Definition: Keys.hh:30
KeyCode getCode(string_view name)
Translate key name to key code.
Definition: Keys.cc:340
TclObject makeTclList(Args &&... args)
Definition: TclObject.hh:290