1 | //============================================================================ |
2 | // |
3 | // SSSS tt lll lll |
4 | // SS SS tt ll ll |
5 | // SS tttttt eeee ll ll aaaa |
6 | // SSSS tt ee ee ll ll aa |
7 | // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" |
8 | // SS SS tt ee ll ll aa aa |
9 | // SSSS ttt eeeee llll llll aaaaa |
10 | // |
11 | // Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony |
12 | // and the Stella Team |
13 | // |
14 | // See the file "License.txt" for information on usage and redistribution of |
15 | // this file, and for a DISCLAIMER OF ALL WARRANTIES. |
16 | //============================================================================ |
17 | |
18 | #ifndef EVENTHANDLER_HXX |
19 | #define EVENTHANDLER_HXX |
20 | |
21 | #include <map> |
22 | |
23 | class Console; |
24 | class OSystem; |
25 | class MouseControl; |
26 | class DialogContainer; |
27 | class PhysicalJoystick; |
28 | |
29 | #include "Event.hxx" |
30 | #include "EventHandlerConstants.hxx" |
31 | #include "Control.hxx" |
32 | #include "StellaKeys.hxx" |
33 | #include "PKeyboardHandler.hxx" |
34 | #include "PJoystickHandler.hxx" |
35 | #include "Variant.hxx" |
36 | #include "bspf.hxx" |
37 | |
38 | /** |
39 | This class takes care of event remapping and dispatching for the |
40 | Stella core, as well as keeping track of the current 'mode'. |
41 | |
42 | The frontend will send translated events here, and the handler will |
43 | check to see what the current 'mode' is. |
44 | |
45 | If in emulation mode, events received from the frontend are remapped and |
46 | sent to the emulation core. If in menu mode, the events are sent |
47 | unchanged to the menu class, where (among other things) changing key |
48 | mapping can take place. |
49 | |
50 | @author Stephen Anthony, Thomas Jentzsch |
51 | */ |
52 | class EventHandler |
53 | { |
54 | public: |
55 | /** |
56 | Create a new event handler object |
57 | */ |
58 | EventHandler(OSystem& osystem); |
59 | virtual ~EventHandler(); |
60 | |
61 | /** |
62 | Returns the event object associated with this handler class. |
63 | |
64 | @return The event object |
65 | */ |
66 | const Event& event() const { return myEvent; } |
67 | |
68 | /** |
69 | Initialize state of this eventhandler. |
70 | */ |
71 | void initialize(); |
72 | |
73 | /** |
74 | Maps the given Stelladaptor/2600-daptor(s) to specified ports on a real 2600. |
75 | |
76 | @param saport How to map the ports ('lr' or 'rl') |
77 | */ |
78 | void mapStelladaptors(const string& saport); |
79 | |
80 | /** |
81 | Swaps the ordering of Stelladaptor/2600-daptor(s) devices. |
82 | */ |
83 | void toggleSAPortOrder(); |
84 | |
85 | /** |
86 | Toggle whether the console is in 2600 or 7800 mode. |
87 | Note that for now, this only affects whether the 7800 pause button is |
88 | supported; there is no further emulation of the 7800 itself. |
89 | */ |
90 | void set7800Mode(); |
91 | |
92 | /** |
93 | Collects and dispatches any pending events. This method should be |
94 | called regularly (at X times per second, where X is the game framerate). |
95 | |
96 | @param time The current time in microseconds. |
97 | */ |
98 | void poll(uInt64 time); |
99 | |
100 | /** |
101 | Get/set the current state of the EventHandler |
102 | |
103 | @return The EventHandlerState type |
104 | */ |
105 | EventHandlerState state() const { return myState; } |
106 | void setState(EventHandlerState state); |
107 | |
108 | /** |
109 | Resets the state machine of the EventHandler to the defaults |
110 | |
111 | @param state The current state to set |
112 | */ |
113 | void reset(EventHandlerState state); |
114 | |
115 | /** |
116 | This method indicates that the system should terminate. |
117 | */ |
118 | void quit() { handleEvent(Event::Quit); } |
119 | |
120 | /** |
121 | Sets the mouse axes and buttons to act as the controller specified in |
122 | the ROM properties, otherwise disable mouse control completely |
123 | |
124 | @param enable Whether to use the mouse to emulate controllers |
125 | Currently, this will be one of the following values: |
126 | 'always', 'analog', 'never' |
127 | */ |
128 | void setMouseControllerMode(const string& enable); |
129 | |
130 | void enterMenuMode(EventHandlerState state); |
131 | void leaveMenuMode(); |
132 | bool enterDebugMode(); |
133 | void leaveDebugMode(); |
134 | void enterTimeMachineMenuMode(uInt32 numWinds, bool unwind); |
135 | |
136 | /** |
137 | Send an event directly to the event handler. |
138 | These events cannot be remapped. |
139 | |
140 | @param type The event |
141 | @param value The value to use for the event |
142 | @param repeated Repeated key (true) or first press/release (false) |
143 | */ |
144 | void handleEvent(Event::Type type, Int32 value = 1, bool repeated = false); |
145 | |
146 | /** |
147 | Handle events that must be processed each time a new console is |
148 | created. Typically, these are events set by commandline arguments. |
149 | */ |
150 | void handleConsoleStartupEvents(); |
151 | |
152 | bool frying() const { return myFryingFlag; } |
153 | |
154 | StringList getActionList(Event::Group group) const; |
155 | VariantList getComboList(EventMode mode) const; |
156 | |
157 | /** Used to access the list of events assigned to a specific combo event. */ |
158 | StringList getComboListForEvent(Event::Type event) const; |
159 | void setComboListForEvent(Event::Type event, const StringList& events); |
160 | |
161 | /** Convert keys and physical joystick events into Stella events. */ |
162 | Event::Type eventForKey(EventMode mode, StellaKey key, StellaMod mod) const { |
163 | return myPKeyHandler->eventForKey(mode, key, mod); |
164 | } |
165 | Event::Type eventForJoyAxis(EventMode mode, int stick, JoyAxis axis, JoyDir adir, int button) const { |
166 | return myPJoyHandler->eventForAxis(mode, stick, axis, adir, button); |
167 | } |
168 | Event::Type eventForJoyButton(EventMode mode, int stick, int button) const { |
169 | return myPJoyHandler->eventForButton(mode, stick, button); |
170 | } |
171 | Event::Type eventForJoyHat(EventMode mode, int stick, int hat, JoyHatDir hdir, int button) const { |
172 | return myPJoyHandler->eventForHat(mode, stick, hat, hdir, button); |
173 | } |
174 | |
175 | /** Get description of given event and mode. */ |
176 | string getMappingDesc(Event::Type event, EventMode mode) const { |
177 | return myPKeyHandler->getMappingDesc(event, mode); |
178 | } |
179 | |
180 | Event::Type eventAtIndex(int idx, Event::Group group) const; |
181 | string actionAtIndex(int idx, Event::Group group) const; |
182 | string keyAtIndex(int idx, Event::Group group) const; |
183 | |
184 | /** |
185 | Bind a key to an event/action and regenerate the mapping array(s). |
186 | |
187 | @param event The event we are remapping |
188 | @param mode The mode where this event is active |
189 | @param key The key to bind to this event |
190 | @param mod The modifier to bind to this event |
191 | */ |
192 | bool addKeyMapping(Event::Type event, EventMode mode, StellaKey key, StellaMod mod); |
193 | |
194 | /** |
195 | Enable controller specific keyboard event mappings. |
196 | */ |
197 | void defineKeyControllerMappings(const Controller::Type type, Controller::Jack port) { |
198 | myPKeyHandler->defineControllerMappings(type, port); |
199 | } |
200 | |
201 | /** |
202 | Enable emulation keyboard event mappings. |
203 | */ |
204 | void enableEmulationKeyMappings() { |
205 | myPKeyHandler->enableEmulationMappings(); |
206 | } |
207 | |
208 | /** |
209 | Bind a physical joystick axis direction to an event/action and regenerate |
210 | the mapping array(s). The axis can be combined with a button. The button |
211 | can also be mapped without an axis. |
212 | |
213 | @param event The event we are remapping |
214 | @param mode The mode where this event is active |
215 | @param stick The joystick number |
216 | @param button The joystick button |
217 | @param axis The joystick axis |
218 | @param adir The given axis |
219 | @param updateMenus Whether to update the action mappings (normally |
220 | we want to do this, unless there are a batch of |
221 | 'adds', in which case it's delayed until the end |
222 | */ |
223 | bool addJoyMapping(Event::Type event, EventMode mode, int stick, |
224 | int button, JoyAxis axis = JoyAxis::NONE, JoyDir adir = JoyDir::NONE, |
225 | bool = true); |
226 | |
227 | /** |
228 | Bind a physical joystick hat direction to an event/action and regenerate |
229 | the mapping array(s). The hat can be combined with a button. |
230 | |
231 | @param event The event we are remapping |
232 | @param mode The mode where this event is active |
233 | @param stick The joystick number |
234 | @param button The joystick button |
235 | @param hat The joystick hat |
236 | @param dir The value on the given hat |
237 | @param updateMenus Whether to update the action mappings (normally |
238 | we want to do this, unless there are a batch of |
239 | 'adds', in which case it's delayed until the end |
240 | */ |
241 | bool addJoyHatMapping(Event::Type event, EventMode mode, int stick, |
242 | int button, int hat, JoyHatDir dir, |
243 | bool = true); |
244 | |
245 | /** |
246 | Enable controller specific keyboard event mappings. |
247 | */ |
248 | void defineJoyControllerMappings(const Controller::Type type, Controller::Jack port) { |
249 | myPJoyHandler->defineControllerMappings(type, port); |
250 | } |
251 | |
252 | /** |
253 | Enable emulation keyboard event mappings. |
254 | */ |
255 | void enableEmulationJoyMappings() { |
256 | myPJoyHandler->enableEmulationMappings(); |
257 | } |
258 | |
259 | /** |
260 | Erase the specified mapping. |
261 | |
262 | @param event The event for which we erase all mappings |
263 | @param mode The mode where this event is active |
264 | */ |
265 | void eraseMapping(Event::Type event, EventMode mode); |
266 | |
267 | /** |
268 | Resets the event mappings to default values. |
269 | |
270 | @param event The event which to (re)set (Event::NoType resets all) |
271 | @param mode The mode for which the defaults are set |
272 | */ |
273 | void setDefaultMapping(Event::Type event, EventMode mode); |
274 | |
275 | /** |
276 | Sets the combo event mappings to those in the 'combomap' setting |
277 | */ |
278 | void setComboMap(); |
279 | |
280 | /** |
281 | Joystick emulates 'impossible' directions (ie, left & right |
282 | at the same time). |
283 | |
284 | @param allow Whether or not to allow impossible directions |
285 | */ |
286 | void allowAllDirections(bool allow) { myAllowAllDirectionsFlag = allow; } |
287 | |
288 | /** |
289 | Changes to a new state based on the current state and the given event. |
290 | |
291 | @param type The event |
292 | @return True if the state changed, else false |
293 | */ |
294 | bool changeStateByEvent(Event::Type type); |
295 | |
296 | /** |
297 | Get the current overlay in use. The overlay won't always exist, |
298 | so we should test if it's available. |
299 | |
300 | @return The overlay object |
301 | */ |
302 | DialogContainer& overlay() const { return *myOverlay; } |
303 | bool hasOverlay() const { return myOverlay != nullptr; } |
304 | |
305 | /** |
306 | Return a list of all physical joysticks currently in the internal database |
307 | (first part of variant) and its internal ID (second part of variant). |
308 | */ |
309 | VariantList physicalJoystickDatabase() const { |
310 | return myPJoyHandler->database(); |
311 | } |
312 | |
313 | /** |
314 | Remove the physical joystick identified by 'name' from the joystick |
315 | database, only if it is not currently active. |
316 | */ |
317 | void removePhysicalJoystickFromDatabase(const string& name); |
318 | |
319 | /** |
320 | Enable/disable text events (distinct from single-key events). |
321 | */ |
322 | virtual void enableTextEvents(bool enable) = 0; |
323 | |
324 | /** |
325 | Handle changing mouse modes. |
326 | */ |
327 | void handleMouseControl(); |
328 | |
329 | void saveKeyMapping(); |
330 | void saveJoyMapping(); |
331 | |
332 | void exitEmulation(); |
333 | |
334 | protected: |
335 | // Global OSystem object |
336 | OSystem& myOSystem; |
337 | |
338 | /** |
339 | Methods which are called by derived classes to handle specific types |
340 | of input. |
341 | */ |
342 | void handleTextEvent(char text); |
343 | void handleMouseMotionEvent(int x, int y, int xrel, int yrel); |
344 | void handleMouseButtonEvent(MouseButton b, bool pressed, int x, int y); |
345 | void handleKeyEvent(StellaKey key, StellaMod mod, bool pressed, bool repeated) { |
346 | myPKeyHandler->handleEvent(key, mod, pressed, repeated); |
347 | } |
348 | void handleJoyBtnEvent(int stick, int button, bool pressed) { |
349 | myPJoyHandler->handleBtnEvent(stick, button, pressed); |
350 | } |
351 | void handleJoyAxisEvent(int stick, int axis, int value) { |
352 | myPJoyHandler->handleAxisEvent(stick, axis, value); |
353 | } |
354 | void handleJoyHatEvent(int stick, int hat, int value) { |
355 | myPJoyHandler->handleHatEvent(stick, hat, value); |
356 | } |
357 | |
358 | /** |
359 | Collects and dispatches any pending events. |
360 | */ |
361 | virtual void pollEvent() = 0; |
362 | |
363 | // Other events that can be received from the underlying event handler |
364 | enum class SystemEvent { |
365 | WINDOW_SHOWN, |
366 | WINDOW_HIDDEN, |
367 | WINDOW_EXPOSED, |
368 | WINDOW_MOVED, |
369 | WINDOW_RESIZED, |
370 | WINDOW_MINIMIZED, |
371 | WINDOW_MAXIMIZED, |
372 | WINDOW_RESTORED, |
373 | WINDOW_ENTER, |
374 | WINDOW_LEAVE, |
375 | WINDOW_FOCUS_GAINED, |
376 | WINDOW_FOCUS_LOST |
377 | }; |
378 | void handleSystemEvent(SystemEvent e, int data1 = 0, int data2 = 0); |
379 | |
380 | /** |
381 | Add the given joystick to the list of physical joysticks available to the handler. |
382 | */ |
383 | void addPhysicalJoystick(PhysicalJoystickPtr stick); |
384 | |
385 | /** |
386 | Remove physical joystick at the current index. |
387 | */ |
388 | void removePhysicalJoystick(int index); |
389 | |
390 | private: |
391 | static constexpr Int32 |
392 | COMBO_SIZE = 16, |
393 | EVENTS_PER_COMBO = 8, |
394 | #ifdef PNG_SUPPORT |
395 | PNG_SIZE = 2, |
396 | #else |
397 | PNG_SIZE = 0, |
398 | #endif |
399 | EMUL_ACTIONLIST_SIZE = 139 + PNG_SIZE + COMBO_SIZE, |
400 | MENU_ACTIONLIST_SIZE = 18 |
401 | ; |
402 | |
403 | // Define event groups |
404 | static const Event::EventSet MiscEvents; |
405 | static const Event::EventSet AudioVideoEvents; |
406 | static const Event::EventSet StateEvents; |
407 | static const Event::EventSet ConsoleEvents; |
408 | static const Event::EventSet JoystickEvents; |
409 | static const Event::EventSet PaddlesEvents; |
410 | static const Event::EventSet KeyboardEvents; |
411 | static const Event::EventSet ComboEvents; |
412 | static const Event::EventSet DebugEvents; |
413 | |
414 | /** |
415 | The following methods take care of assigning action mappings. |
416 | */ |
417 | void setActionMappings(EventMode mode); |
418 | void setDefaultKeymap(Event::Type, EventMode mode); |
419 | void setDefaultJoymap(Event::Type, EventMode mode); |
420 | void saveComboMapping(); |
421 | |
422 | StringList getActionList(EventMode mode) const; |
423 | StringList getActionList(const Event::EventSet& events, EventMode mode = EventMode::kEmulationMode) const; |
424 | // returns the action array index of the index in the provided group |
425 | int getEmulActionListIndex(int idx, const Event::EventSet& events) const; |
426 | int getActionListIndex(int idx, Event::Group group) const; |
427 | |
428 | private: |
429 | // Structure used for action menu items |
430 | struct ActionList { |
431 | Event::Type event; |
432 | string action; |
433 | string key; |
434 | }; |
435 | |
436 | // Global Event object |
437 | Event myEvent; |
438 | |
439 | // Indicates current overlay object |
440 | DialogContainer* myOverlay; |
441 | |
442 | // Handler for all keyboard-related events |
443 | unique_ptr<PhysicalKeyboardHandler> myPKeyHandler; |
444 | |
445 | // Handler for all joystick addition/removal/mapping |
446 | unique_ptr<PhysicalJoystickHandler> myPJoyHandler; |
447 | |
448 | // MouseControl object, which takes care of switching the mouse between |
449 | // all possible controller modes |
450 | unique_ptr<MouseControl> myMouseControl; |
451 | |
452 | // The event(s) assigned to each combination event |
453 | Event::Type myComboTable[COMBO_SIZE][EVENTS_PER_COMBO]; |
454 | |
455 | // Indicates the current state of the system (ie, which mode is current) |
456 | EventHandlerState myState; |
457 | |
458 | // Indicates whether the virtual joystick emulates 'impossible' directions |
459 | bool myAllowAllDirectionsFlag; |
460 | |
461 | // Indicates whether or not we're in frying mode |
462 | bool myFryingFlag; |
463 | |
464 | // Sometimes an extraneous mouse motion event occurs after a video |
465 | // state change; we detect when this happens and discard the event |
466 | bool mySkipMouseMotion; |
467 | |
468 | // Whether the currently enabled console is emulating certain aspects |
469 | // of the 7800 (for now, only the switches are notified) |
470 | bool myIs7800; |
471 | |
472 | // Holds static strings for the remap menu (emulation and menu events) |
473 | static ActionList ourEmulActionList[EMUL_ACTIONLIST_SIZE]; |
474 | static ActionList ourMenuActionList[MENU_ACTIONLIST_SIZE]; |
475 | |
476 | // Following constructors and assignment operators not supported |
477 | EventHandler() = delete; |
478 | EventHandler(const EventHandler&) = delete; |
479 | EventHandler(EventHandler&&) = delete; |
480 | EventHandler& operator=(const EventHandler&) = delete; |
481 | EventHandler& operator=(EventHandler&&) = delete; |
482 | }; |
483 | |
484 | #endif |
485 | |