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#include <sstream>
19#include <map>
20
21#include "Logger.hxx"
22
23#include "Base.hxx"
24#include "Console.hxx"
25#include "FrameBuffer.hxx"
26#include "FSNode.hxx"
27#include "OSystem.hxx"
28#include "Joystick.hxx"
29#include "Paddles.hxx"
30#include "PointingDevice.hxx"
31#include "PropsSet.hxx"
32#include "Settings.hxx"
33#include "Sound.hxx"
34#include "StateManager.hxx"
35#include "RewindManager.hxx"
36#include "TimerManager.hxx"
37#include "Switches.hxx"
38#include "M6532.hxx"
39#include "MouseControl.hxx"
40#include "PNGLibrary.hxx"
41#include "TIASurface.hxx"
42
43#include "EventHandler.hxx"
44
45#ifdef CHEATCODE_SUPPORT
46 #include "Cheat.hxx"
47 #include "CheatManager.hxx"
48#endif
49#ifdef DEBUGGER_SUPPORT
50 #include "Debugger.hxx"
51#endif
52#ifdef GUI_SUPPORT
53 #include "Menu.hxx"
54 #include "CommandMenu.hxx"
55 #include "DialogContainer.hxx"
56 #include "Launcher.hxx"
57 #include "TimeMachine.hxx"
58 #include "FileListWidget.hxx"
59 #include "ScrollBarWidget.hxx"
60#endif
61
62// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
63EventHandler::EventHandler(OSystem& osystem)
64 : myOSystem(osystem),
65 myOverlay(nullptr),
66 myState(EventHandlerState::NONE),
67 myAllowAllDirectionsFlag(false),
68 myFryingFlag(false),
69 mySkipMouseMotion(true),
70 myIs7800(false)
71{
72 // Create keyboard handler (to handle all physical keyboard functionality)
73 myPKeyHandler = make_unique<PhysicalKeyboardHandler>(osystem, *this);
74
75 // Create joystick handler (to handle all physical joystick functionality)
76 myPJoyHandler = make_unique<PhysicalJoystickHandler>(osystem, *this);
77
78 // Erase the 'combo' array
79 for(int i = 0; i < COMBO_SIZE; ++i)
80 for(int j = 0; j < EVENTS_PER_COMBO; ++j)
81 myComboTable[i][j] = Event::NoType;
82}
83
84// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
85EventHandler::~EventHandler()
86{
87}
88
89// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
90void EventHandler::initialize()
91{
92 // Make sure the event/action mappings are correctly set,
93 // and fill the ActionList structure with valid values
94 setComboMap();
95 setActionMappings(EventMode::kEmulationMode);
96 setActionMappings(EventMode::kMenuMode);
97
98 Joystick::setDeadZone(myOSystem.settings().getInt("joydeadzone"));
99 Paddles::setDejitterBase(myOSystem.settings().getInt("dejitter.base"));
100 Paddles::setDejitterDiff(myOSystem.settings().getInt("dejitter.diff"));
101 Paddles::setDigitalSensitivity(myOSystem.settings().getInt("dsense"));
102 Paddles::setMouseSensitivity(myOSystem.settings().getInt("msense"));
103 PointingDevice::setSensitivity(myOSystem.settings().getInt("tsense"));
104
105#ifdef GUI_SUPPORT
106 // Set quick select delay when typing characters in listwidgets
107 FileListWidget::setQuickSelectDelay(myOSystem.settings().getInt("listdelay"));
108
109 // Set number of lines a mousewheel will scroll
110 ScrollBarWidget::setWheelLines(myOSystem.settings().getInt("mwheel"));
111
112 // Mouse double click
113 DialogContainer::setDoubleClickDelay(myOSystem.settings().getInt("mdouble"));
114
115 // Input delay
116 DialogContainer::setControllerDelay(myOSystem.settings().getInt("inpDelay"));
117
118 // Input rate
119 DialogContainer::setControllerRate(myOSystem.settings().getInt("inpRate"));
120#endif
121
122 // Integer to string conversions (for HEX) use upper or lower-case
123 Common::Base::setHexUppercase(myOSystem.settings().getBool("dbg.uhex"));
124
125 // Default phosphor blend
126 Properties::setDefault(PropType::Display_PPBlend,
127 myOSystem.settings().getString("tv.phosblend"));
128}
129
130// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
131void EventHandler::reset(EventHandlerState state)
132{
133 setState(state);
134 myOSystem.state().reset();
135#ifdef PNG_SUPPORT
136 myOSystem.png().setContinuousSnapInterval(0);
137#endif
138 myFryingFlag = false;
139
140 // Reset events almost immediately after starting emulation mode
141 // We wait a little while (0.5s), since 'hold' events may be present,
142 // and we want time for the ROM to process them
143 if(state == EventHandlerState::EMULATION)
144 myOSystem.timer().setTimeout([&ev = myEvent]() { ev.clear(); }, 500);
145 // Toggle 7800 mode
146 set7800Mode();
147}
148
149// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
150void EventHandler::addPhysicalJoystick(PhysicalJoystickPtr joy)
151{
152#ifdef JOYSTICK_SUPPORT
153 int ID = myPJoyHandler->add(joy);
154 if(ID < 0)
155 return;
156
157 setActionMappings(EventMode::kEmulationMode);
158 setActionMappings(EventMode::kMenuMode);
159
160 ostringstream buf;
161 buf << "Added joystick " << ID << ":" << endl
162 << " " << joy->about() << endl;
163 Logger::info(buf.str());
164#endif
165}
166
167// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
168void EventHandler::removePhysicalJoystick(int id)
169{
170#ifdef JOYSTICK_SUPPORT
171 myPJoyHandler->remove(id);
172#endif
173}
174
175// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
176void EventHandler::mapStelladaptors(const string& saport)
177{
178#ifdef JOYSTICK_SUPPORT
179 myPJoyHandler->mapStelladaptors(saport);
180#endif
181}
182
183// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
184void EventHandler::toggleSAPortOrder()
185{
186#ifdef JOYSTICK_SUPPORT
187 const string& saport = myOSystem.settings().getString("saport");
188 if(saport == "lr")
189 {
190 mapStelladaptors("rl");
191 myOSystem.frameBuffer().showMessage("Stelladaptor ports right/left");
192 }
193 else
194 {
195 mapStelladaptors("lr");
196 myOSystem.frameBuffer().showMessage("Stelladaptor ports left/right");
197 }
198#endif
199}
200
201// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
202void EventHandler::set7800Mode()
203{
204 if(myOSystem.hasConsole())
205 myIs7800 = myOSystem.console().switches().check7800Mode(myOSystem.settings());
206 else
207 myIs7800 = false;
208}
209
210// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
211void EventHandler::handleMouseControl()
212{
213 if(myMouseControl)
214 myOSystem.frameBuffer().showMessage(myMouseControl->next());
215}
216
217// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
218void EventHandler::poll(uInt64 time)
219{
220 // Process events from the underlying hardware
221 pollEvent();
222
223 // Update controllers and console switches, and in general all other things
224 // related to emulation
225 if(myState == EventHandlerState::EMULATION)
226 {
227 myOSystem.console().riot().update();
228
229 // Now check if the StateManager should be saving or loading state
230 // (for rewind and/or movies
231 if(myOSystem.state().mode() != StateManager::Mode::Off)
232 myOSystem.state().update();
233
234 #ifdef CHEATCODE_SUPPORT
235 for(auto& cheat: myOSystem.cheat().perFrame())
236 cheat->evaluate();
237 #endif
238
239 #ifdef PNG_SUPPORT
240 // Handle continuous snapshots
241 if(myOSystem.png().continuousSnapEnabled())
242 myOSystem.png().updateTime(time);
243 #endif
244 }
245 else if(myOverlay)
246 {
247 #ifdef GUI_SUPPORT
248 // Update the current dialog container at regular intervals
249 // Used to implement continuous events
250 myOverlay->updateTime(time);
251 #endif
252 }
253
254 // Turn off all mouse-related items; if they haven't been taken care of
255 // in the previous ::update() methods, they're now invalid
256 myEvent.set(Event::MouseAxisXValue, 0);
257 myEvent.set(Event::MouseAxisYValue, 0);
258}
259
260// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
261void EventHandler::handleTextEvent(char text)
262{
263#ifdef GUI_SUPPORT
264 // Text events are only used in GUI mode
265 if(myOverlay)
266 myOverlay->handleTextEvent(text);
267#endif
268}
269
270// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
271void EventHandler::handleMouseMotionEvent(int x, int y, int xrel, int yrel)
272{
273 // Determine which mode we're in, then send the event to the appropriate place
274 if(myState == EventHandlerState::EMULATION)
275 {
276 if(!mySkipMouseMotion)
277 {
278 myEvent.set(Event::MouseAxisXValue, xrel);
279 myEvent.set(Event::MouseAxisYValue, yrel);
280 }
281 mySkipMouseMotion = false;
282 }
283#ifdef GUI_SUPPORT
284 else if(myOverlay)
285 myOverlay->handleMouseMotionEvent(x, y);
286#endif
287}
288
289// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
290void EventHandler::handleMouseButtonEvent(MouseButton b, bool pressed,
291 int x, int y)
292{
293 // Determine which mode we're in, then send the event to the appropriate place
294 if(myState == EventHandlerState::EMULATION)
295 {
296 switch(b)
297 {
298 case MouseButton::LEFT:
299 myEvent.set(Event::MouseButtonLeftValue, int(pressed));
300 break;
301 case MouseButton::RIGHT:
302 myEvent.set(Event::MouseButtonRightValue, int(pressed));
303 break;
304 default:
305 return;
306 }
307 }
308#ifdef GUI_SUPPORT
309 else if(myOverlay)
310 myOverlay->handleMouseButtonEvent(b, pressed, x, y);
311#endif
312}
313
314// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
315void EventHandler::handleSystemEvent(SystemEvent e, int, int)
316{
317 switch(e)
318 {
319 case SystemEvent::WINDOW_EXPOSED:
320 case SystemEvent::WINDOW_RESIZED:
321 myOSystem.frameBuffer().update(true); // force full update
322 break;
323#ifdef BSPF_UNIX
324 case SystemEvent::WINDOW_FOCUS_GAINED:
325 // Used to handle Alt-x key combos; sometimes the key associated with
326 // Alt gets 'stuck' and is passed to the core for processing
327 if(myPKeyHandler->altKeyCount() > 0)
328 myPKeyHandler->altKeyCount() = 2;
329 break;
330#endif
331#if 0
332 case SystemEvent::WINDOW_MINIMIZED:
333 if(myState == EventHandlerState::EMULATION) enterMenuMode(EventHandlerState::OPTIONSMENU);
334 break;
335#endif
336 default: // handle other events as testing requires
337 // cerr << "handleSystemEvent: " << e << endl;
338 break;
339 }
340}
341
342// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
343void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated)
344{
345 // Take care of special events that aren't part of the emulation core
346 // or need to be preprocessed before passing them on
347 bool pressed = (value != 0);
348
349 switch(event)
350 {
351 ////////////////////////////////////////////////////////////////////////
352 // If enabled, make sure 'impossible' joystick directions aren't allowed
353 case Event::JoystickZeroUp:
354 if(!myAllowAllDirectionsFlag && pressed)
355 myEvent.set(Event::JoystickZeroDown, 0);
356 break;
357
358 case Event::JoystickZeroDown:
359 if(!myAllowAllDirectionsFlag && pressed)
360 myEvent.set(Event::JoystickZeroUp, 0);
361 break;
362
363 case Event::JoystickZeroLeft:
364 if(!myAllowAllDirectionsFlag && pressed)
365 myEvent.set(Event::JoystickZeroRight, 0);
366 break;
367
368 case Event::JoystickZeroRight:
369 if(!myAllowAllDirectionsFlag && pressed)
370 myEvent.set(Event::JoystickZeroLeft, 0);
371 break;
372
373 case Event::JoystickOneUp:
374 if(!myAllowAllDirectionsFlag && pressed)
375 myEvent.set(Event::JoystickOneDown, 0);
376 break;
377
378 case Event::JoystickOneDown:
379 if(!myAllowAllDirectionsFlag && pressed)
380 myEvent.set(Event::JoystickOneUp, 0);
381 break;
382
383 case Event::JoystickOneLeft:
384 if(!myAllowAllDirectionsFlag && pressed)
385 myEvent.set(Event::JoystickOneRight, 0);
386 break;
387
388 case Event::JoystickOneRight:
389 if(!myAllowAllDirectionsFlag && pressed)
390 myEvent.set(Event::JoystickOneLeft, 0);
391 break;
392 ////////////////////////////////////////////////////////////////////////
393
394 case Event::Fry:
395 if (!repeated) myFryingFlag = pressed;
396 return;
397
398 case Event::ReloadConsole:
399 if (pressed && !repeated) myOSystem.reloadConsole();
400 return;
401
402 case Event::VolumeDecrease:
403 if(pressed) myOSystem.sound().adjustVolume(-1);
404 return;
405
406 case Event::VolumeIncrease:
407 if(pressed) myOSystem.sound().adjustVolume(+1);
408 return;
409
410 case Event::SoundToggle:
411 if(pressed && !repeated) myOSystem.sound().toggleMute();
412 return;
413
414 case Event::VidmodeDecrease:
415 if(pressed) myOSystem.frameBuffer().changeVidMode(-1);
416 return;
417
418 case Event::VidmodeIncrease:
419 if(pressed) myOSystem.frameBuffer().changeVidMode(+1);
420 return;
421
422 case Event::ToggleFullScreen:
423 if (pressed && !repeated) myOSystem.frameBuffer().toggleFullscreen();
424 return;
425
426 case Event::DecreaseOverscan:
427 if (pressed) myOSystem.frameBuffer().changeOverscan(-1);
428 return;
429
430 case Event::IncreaseOverScan:
431 if (pressed) myOSystem.frameBuffer().changeOverscan(1);
432 return;
433
434 case Event::VidmodeStd:
435 if (pressed && !repeated) myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::Preset::OFF);
436 return;
437
438 case Event::VidmodeRGB:
439 if (pressed && !repeated) myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::Preset::RGB);
440 return;
441
442 case Event::VidmodeSVideo:
443 if (pressed && !repeated) myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::Preset::SVIDEO);
444 return;
445
446 case Event::VidModeComposite:
447 if (pressed && !repeated) myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::Preset::COMPOSITE);
448 return;
449
450 case Event::VidModeBad:
451 if (pressed && !repeated) myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::Preset::BAD);
452 return;
453
454 case Event::VidModeCustom:
455 if (pressed && !repeated) myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::Preset::CUSTOM);
456 return;
457
458 case Event::ScanlinesDecrease:
459 if (pressed) myOSystem.frameBuffer().tiaSurface().setScanlineIntensity(-2);
460 return;
461
462 case Event::ScanlinesIncrease:
463 if (pressed) myOSystem.frameBuffer().tiaSurface().setScanlineIntensity(+2);
464 return;
465
466 case Event::PreviousAttribute:
467 if (pressed)
468 {
469 myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::Preset::CUSTOM);
470 myOSystem.frameBuffer().showMessage(
471 myOSystem.frameBuffer().tiaSurface().ntsc().setPreviousAdjustable());
472 }
473 return;
474
475 case Event::NextAttribute:
476 if (pressed)
477 {
478 myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::Preset::CUSTOM);
479 myOSystem.frameBuffer().showMessage(
480 myOSystem.frameBuffer().tiaSurface().ntsc().setNextAdjustable());
481 }
482 return;
483
484 case Event::DecreaseAttribute:
485 if (pressed)
486 {
487 myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::Preset::CUSTOM);
488 myOSystem.frameBuffer().showMessage(
489 myOSystem.frameBuffer().tiaSurface().ntsc().decreaseAdjustable());
490 }
491 return;
492
493 case Event::IncreaseAttribute:
494 if (pressed)
495 {
496 myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::Preset::CUSTOM);
497 myOSystem.frameBuffer().showMessage(
498 myOSystem.frameBuffer().tiaSurface().ntsc().increaseAdjustable());
499 }
500 return;
501
502 case Event::DecreasePhosphor:
503 if (pressed) myOSystem.console().changePhosphor(-1);
504 return;
505
506 case Event::IncreasePhosphor:
507 if (pressed) myOSystem.console().changePhosphor(1);
508 return;
509
510 case Event::TogglePhosphor:
511 if (pressed && !repeated) myOSystem.console().togglePhosphor();
512 return;
513
514 case Event::ToggleColorLoss:
515 if (pressed && !repeated) myOSystem.console().toggleColorLoss();
516 return;
517
518 case Event::TogglePalette:
519 if (pressed && !repeated) myOSystem.console().togglePalette();
520 return;
521
522 case Event::ToggleJitter:
523 if (pressed && !repeated) myOSystem.console().toggleJitter();
524 return;
525
526 case Event::ToggleFrameStats:
527 if (pressed) myOSystem.frameBuffer().toggleFrameStats();
528 return;
529
530 case Event::ToggleTimeMachine:
531 if (pressed && !repeated) myOSystem.state().toggleTimeMachine();
532 return;
533
534 #ifdef PNG_SUPPORT
535 case Event::ToggleContSnapshots:
536 if (pressed && !repeated) myOSystem.png().toggleContinuousSnapshots(false);
537 return;
538
539 case Event::ToggleContSnapshotsFrame:
540 if (pressed && !repeated) myOSystem.png().toggleContinuousSnapshots(true);
541 return;
542 #endif
543
544 case Event::HandleMouseControl:
545 if (pressed && !repeated) handleMouseControl();
546 return;
547
548 case Event::ToggleSAPortOrder:
549 if (pressed && !repeated) toggleSAPortOrder();
550 return;
551
552 case Event::DecreaseFormat:
553 if (pressed) myOSystem.console().toggleFormat(-1);
554 return;
555
556 case Event::IncreaseFormat:
557 if (pressed) myOSystem.console().toggleFormat(1);
558 return;
559
560 case Event::ToggleGrabMouse:
561 if (pressed && !repeated && !myOSystem.frameBuffer().fullScreen())
562 {
563 myOSystem.frameBuffer().toggleGrabMouse();
564 myOSystem.frameBuffer().showMessage(myOSystem.frameBuffer().grabMouseEnabled()
565 ? "Grab mouse enabled" : "Grab mouse disabled");
566 }
567 return;
568
569 case Event::ToggleP0Collision:
570 if (pressed && !repeated) myOSystem.console().toggleP0Collision();
571 return;
572
573 case Event::ToggleP0Bit:
574 if (pressed && !repeated) myOSystem.console().toggleP0Bit();
575 return;
576
577 case Event::ToggleP1Collision:
578 if (pressed && !repeated) myOSystem.console().toggleP1Collision();
579 return;
580
581 case Event::ToggleP1Bit:
582 if (pressed && !repeated) myOSystem.console().toggleP1Bit();
583 return;
584
585 case Event::ToggleM0Collision:
586 if (pressed && !repeated) myOSystem.console().toggleM0Collision();
587 return;
588
589 case Event::ToggleM0Bit:
590 if (pressed && !repeated) myOSystem.console().toggleM0Bit();
591 return;
592
593 case Event::ToggleM1Collision:
594 if (pressed && !repeated) myOSystem.console().toggleM1Collision();
595 return;
596
597 case Event::ToggleM1Bit:
598 if (pressed && !repeated) myOSystem.console().toggleM1Bit();
599 return;
600
601 case Event::ToggleBLCollision:
602 if (pressed && !repeated) myOSystem.console().toggleBLCollision();
603 return;
604
605 case Event::ToggleBLBit:
606 if (pressed) myOSystem.console().toggleBLBit();
607 return;
608
609 case Event::TogglePFCollision:
610 if (pressed && !repeated) myOSystem.console().togglePFCollision();
611 return;
612
613 case Event::TogglePFBit:
614 if (pressed && !repeated) myOSystem.console().togglePFBit();
615 return;
616
617 case Event::ToggleFixedColors:
618 if (pressed) myOSystem.console().toggleFixedColors();
619 return;
620
621 case Event::ToggleCollisions:
622 if (pressed && !repeated) myOSystem.console().toggleCollisions();
623 return;
624
625 case Event::ToggleBits:
626 if (pressed && !repeated) myOSystem.console().toggleBits();
627 return;
628
629 case Event::SaveState:
630 if(pressed && !repeated) myOSystem.state().saveState();
631 return;
632
633 case Event::SaveAllStates:
634 if (pressed && !repeated)
635 myOSystem.frameBuffer().showMessage(myOSystem.state().rewindManager().saveAllStates());
636 return;
637
638 case Event::ChangeState:
639 if(pressed) myOSystem.state().changeState();
640 return;
641
642 case Event::ToggleAutoSlot:
643 if (pressed) myOSystem.state().toggleAutoSlot();
644 return;
645
646 case Event::LoadState:
647 if(pressed && !repeated) myOSystem.state().loadState();
648 return;
649
650 case Event::LoadAllStates:
651 if (pressed && !repeated)
652 myOSystem.frameBuffer().showMessage(myOSystem.state().rewindManager().loadAllStates());
653 return;
654
655 case Event::RewindPause:
656 if (pressed) myOSystem.state().rewindStates();
657 if (myState == EventHandlerState::EMULATION)
658 setState(EventHandlerState::PAUSE);
659 return;
660
661 case Event::UnwindPause:
662 if (pressed) myOSystem.state().unwindStates();
663 if (myState == EventHandlerState::EMULATION)
664 setState(EventHandlerState::PAUSE);
665 return;
666
667 case Event::Rewind1Menu:
668 if (pressed) enterTimeMachineMenuMode(1, false);
669 return;
670
671 case Event::Rewind10Menu:
672 if (pressed) enterTimeMachineMenuMode(10, false);
673 return;
674
675 case Event::RewindAllMenu:
676 if (pressed) enterTimeMachineMenuMode(1000, false);
677 return;
678
679 case Event::Unwind1Menu:
680 if (pressed) enterTimeMachineMenuMode(1, true);
681 return;
682
683 case Event::Unwind10Menu:
684 if (pressed) enterTimeMachineMenuMode(10, true);
685 return;
686
687 case Event::UnwindAllMenu:
688 if (pressed) enterTimeMachineMenuMode(1000, true);
689 return;
690
691 case Event::TakeSnapshot:
692 if(pressed && !repeated) myOSystem.frameBuffer().tiaSurface().saveSnapShot();
693 return;
694
695 case Event::ExitMode:
696 // Special handling for Escape key
697 // Basically, exit whichever mode we're currently in
698 switch (myState)
699 {
700 case EventHandlerState::PAUSE:
701 if (pressed && !repeated) changeStateByEvent(Event::TogglePauseMode);
702 return;
703
704 case EventHandlerState::CMDMENU:
705 if (pressed && !repeated) changeStateByEvent(Event::CmdMenuMode);
706 return;
707
708 case EventHandlerState::TIMEMACHINE:
709 if (pressed && !repeated) changeStateByEvent(Event::TimeMachineMode);
710 return;
711
712 // this event is called when exiting a ROM from the debugger, so it acts like pressing ESC in emulation
713 case EventHandlerState::DEBUGGER:
714 case EventHandlerState::EMULATION:
715 if (pressed && !repeated)
716 {
717 exitEmulation();
718 // Go back to the launcher, or immediately quit
719 if (myOSystem.settings().getBool("exitlauncher") ||
720 myOSystem.launcherUsed())
721 myOSystem.createLauncher();
722 else
723 handleEvent(Event::Quit);
724 }
725 return;
726
727 default:
728 return;
729 }
730
731 case Event::Quit:
732 if(pressed && !repeated)
733 {
734 saveKeyMapping();
735 saveJoyMapping();
736 if (myState != EventHandlerState::LAUNCHER)
737 exitEmulation();
738 myOSystem.quit();
739 }
740 return;
741
742 case Event::StartPauseMode:
743 if (pressed && !repeated && myState == EventHandlerState::EMULATION)
744 setState(EventHandlerState::PAUSE);
745 return;
746
747 ////////////////////////////////////////////////////////////////////////
748 // A combo event is simply multiple calls to handleEvent, once for
749 // each event it contains
750 case Event::Combo1:
751 case Event::Combo2:
752 case Event::Combo3:
753 case Event::Combo4:
754 case Event::Combo5:
755 case Event::Combo6:
756 case Event::Combo7:
757 case Event::Combo8:
758 case Event::Combo9:
759 case Event::Combo10:
760 case Event::Combo11:
761 case Event::Combo12:
762 case Event::Combo13:
763 case Event::Combo14:
764 case Event::Combo15:
765 case Event::Combo16:
766 for(int i = 0, combo = event - Event::Combo1; i < EVENTS_PER_COMBO; ++i)
767 if(myComboTable[combo][i] != Event::NoType)
768 handleEvent(myComboTable[combo][i], pressed, repeated);
769 return;
770 ////////////////////////////////////////////////////////////////////////
771
772 ////////////////////////////////////////////////////////////////////////
773 // Events which relate to switches()
774 case Event::ConsoleColor:
775 if(pressed && !repeated)
776 {
777 myEvent.set(Event::ConsoleBlackWhite, 0);
778 myEvent.set(Event::ConsoleColor, 1);
779 myOSystem.frameBuffer().showMessage(myIs7800 ? "Pause released" : "Color Mode");
780 myOSystem.console().switches().update();
781 }
782 return;
783 case Event::ConsoleBlackWhite:
784 if(pressed && !repeated)
785 {
786 myEvent.set(Event::ConsoleBlackWhite, 1);
787 myEvent.set(Event::ConsoleColor, 0);
788 myOSystem.frameBuffer().showMessage(myIs7800 ? "Pause pushed" : "B/W Mode");
789 myOSystem.console().switches().update();
790 }
791 return;
792 case Event::ConsoleColorToggle:
793 if(pressed && !repeated)
794 {
795 if(myOSystem.console().switches().tvColor())
796 {
797 myEvent.set(Event::ConsoleBlackWhite, 1);
798 myEvent.set(Event::ConsoleColor, 0);
799 myOSystem.frameBuffer().showMessage(myIs7800 ? "Pause pushed" : "B/W Mode");
800 }
801 else
802 {
803 myEvent.set(Event::ConsoleBlackWhite, 0);
804 myEvent.set(Event::ConsoleColor, 1);
805 myOSystem.frameBuffer().showMessage(myIs7800 ? "Pause released" : "Color Mode");
806 }
807 myOSystem.console().switches().update();
808 }
809 return;
810
811 case Event::Console7800Pause:
812 if(pressed && !repeated)
813 {
814 myEvent.set(Event::ConsoleBlackWhite, 0);
815 myEvent.set(Event::ConsoleColor, 0);
816 if (myIs7800)
817 myOSystem.frameBuffer().showMessage("Pause pressed");
818 myOSystem.console().switches().update();
819 }
820 return;
821
822 case Event::ConsoleLeftDiffA:
823 if(pressed && !repeated)
824 {
825 myEvent.set(Event::ConsoleLeftDiffA, 1);
826 myEvent.set(Event::ConsoleLeftDiffB, 0);
827 myOSystem.frameBuffer().showMessage(GUI::LEFT_DIFFICULTY + " A");
828 myOSystem.console().switches().update();
829 }
830 return;
831 case Event::ConsoleLeftDiffB:
832 if(pressed && !repeated)
833 {
834 myEvent.set(Event::ConsoleLeftDiffA, 0);
835 myEvent.set(Event::ConsoleLeftDiffB, 1);
836 myOSystem.frameBuffer().showMessage(GUI::LEFT_DIFFICULTY + " B");
837 myOSystem.console().switches().update();
838 }
839 return;
840 case Event::ConsoleLeftDiffToggle:
841 if(pressed && !repeated)
842 {
843 if(myOSystem.console().switches().leftDifficultyA())
844 {
845 myEvent.set(Event::ConsoleLeftDiffA, 0);
846 myEvent.set(Event::ConsoleLeftDiffB, 1);
847 myOSystem.frameBuffer().showMessage(GUI::LEFT_DIFFICULTY + " B");
848 }
849 else
850 {
851 myEvent.set(Event::ConsoleLeftDiffA, 1);
852 myEvent.set(Event::ConsoleLeftDiffB, 0);
853 myOSystem.frameBuffer().showMessage(GUI::LEFT_DIFFICULTY + " A");
854 }
855 myOSystem.console().switches().update();
856 }
857 return;
858
859 case Event::ConsoleRightDiffA:
860 if(pressed && !repeated)
861 {
862 myEvent.set(Event::ConsoleRightDiffA, 1);
863 myEvent.set(Event::ConsoleRightDiffB, 0);
864 myOSystem.frameBuffer().showMessage(GUI::RIGHT_DIFFICULTY + " A");
865 myOSystem.console().switches().update();
866 }
867 return;
868 case Event::ConsoleRightDiffB:
869 if(pressed && !repeated)
870 {
871 myEvent.set(Event::ConsoleRightDiffA, 0);
872 myEvent.set(Event::ConsoleRightDiffB, 1);
873 myOSystem.frameBuffer().showMessage(GUI::RIGHT_DIFFICULTY + " B");
874 myOSystem.console().switches().update();
875 }
876 return;
877 case Event::ConsoleRightDiffToggle:
878 if(pressed && !repeated)
879 {
880 if(myOSystem.console().switches().rightDifficultyA())
881 {
882 myEvent.set(Event::ConsoleRightDiffA, 0);
883 myEvent.set(Event::ConsoleRightDiffB, 1);
884 myOSystem.frameBuffer().showMessage(GUI::RIGHT_DIFFICULTY + " B");
885 }
886 else
887 {
888 myEvent.set(Event::ConsoleRightDiffA, 1);
889 myEvent.set(Event::ConsoleRightDiffB, 0);
890 myOSystem.frameBuffer().showMessage(GUI::RIGHT_DIFFICULTY + " A");
891 }
892 myOSystem.console().switches().update();
893 }
894 return;
895 ////////////////////////////////////////////////////////////////////////
896
897 case Event::NoType: // Ignore unmapped events
898 return;
899
900 default:
901 break;
902 }
903
904 // Otherwise, pass it to the emulation core
905 if (!repeated)
906 myEvent.set(event, value);
907}
908
909// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
910void EventHandler::handleConsoleStartupEvents()
911{
912 if(myOSystem.settings().getBool("holdreset"))
913 handleEvent(Event::ConsoleReset);
914
915 if(myOSystem.settings().getBool("holdselect"))
916 handleEvent(Event::ConsoleSelect);
917
918 const string& holdjoy0 = myOSystem.settings().getString("holdjoy0");
919
920 if(BSPF::containsIgnoreCase(holdjoy0, "U"))
921 handleEvent(Event::JoystickZeroUp);
922 if(BSPF::containsIgnoreCase(holdjoy0, "D"))
923 handleEvent(Event::JoystickZeroDown);
924 if(BSPF::containsIgnoreCase(holdjoy0, "L"))
925 handleEvent(Event::JoystickZeroLeft);
926 if(BSPF::containsIgnoreCase(holdjoy0, "R"))
927 handleEvent(Event::JoystickZeroRight);
928 if(BSPF::containsIgnoreCase(holdjoy0, "F"))
929 handleEvent(Event::JoystickZeroFire);
930
931 const string& holdjoy1 = myOSystem.settings().getString("holdjoy1");
932 if(BSPF::containsIgnoreCase(holdjoy1, "U"))
933 handleEvent(Event::JoystickOneUp);
934 if(BSPF::containsIgnoreCase(holdjoy1, "D"))
935 handleEvent(Event::JoystickOneDown);
936 if(BSPF::containsIgnoreCase(holdjoy1, "L"))
937 handleEvent(Event::JoystickOneLeft);
938 if(BSPF::containsIgnoreCase(holdjoy1, "R"))
939 handleEvent(Event::JoystickOneRight);
940 if(BSPF::containsIgnoreCase(holdjoy1, "F"))
941 handleEvent(Event::JoystickOneFire);
942}
943
944// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
945bool EventHandler::changeStateByEvent(Event::Type type)
946{
947 bool handled = true;
948
949 switch(type)
950 {
951 case Event::TogglePauseMode:
952 if(myState == EventHandlerState::EMULATION)
953 setState(EventHandlerState::PAUSE);
954 else if(myState == EventHandlerState::PAUSE)
955 setState(EventHandlerState::EMULATION);
956 else
957 handled = false;
958 break;
959
960 case Event::OptionsMenuMode:
961 if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE)
962 enterMenuMode(EventHandlerState::OPTIONSMENU);
963 else
964 handled = false;
965 break;
966
967 case Event::CmdMenuMode:
968 if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE)
969 enterMenuMode(EventHandlerState::CMDMENU);
970 else if(myState == EventHandlerState::CMDMENU && !myOSystem.settings().getBool("minimal_ui"))
971 // The extra check for "minimal_ui" allows mapping e.g. right joystick fire
972 // to open the command dialog and navigate there using that fire button
973 leaveMenuMode();
974 else
975 handled = false;
976 break;
977
978 case Event::TimeMachineMode:
979 if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE)
980 enterTimeMachineMenuMode(0, false);
981 else if(myState == EventHandlerState::TIMEMACHINE)
982 leaveMenuMode();
983 else
984 handled = false;
985 break;
986
987 case Event::DebuggerMode:
988 #ifdef DEBUGGER_SUPPORT
989 if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE
990 || myState == EventHandlerState::TIMEMACHINE)
991 enterDebugMode();
992 else if(myState == EventHandlerState::DEBUGGER && myOSystem.debugger().canExit())
993 leaveDebugMode();
994 else
995 handled = false;
996 #endif
997 break;
998
999 default:
1000 handled = false;
1001 }
1002
1003 return handled;
1004}
1005
1006// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1007void EventHandler::setActionMappings(EventMode mode)
1008{
1009 int listsize = 0;
1010 ActionList* list = nullptr;
1011
1012 switch(mode)
1013 {
1014 case EventMode::kEmulationMode:
1015 listsize = EMUL_ACTIONLIST_SIZE;
1016 list = ourEmulActionList;
1017 break;
1018 case EventMode::kMenuMode:
1019 listsize = MENU_ACTIONLIST_SIZE;
1020 list = ourMenuActionList;
1021 break;
1022 default:
1023 return;
1024 }
1025
1026 // Fill the ActionList with the current key and joystick mappings
1027 for(int i = 0; i < listsize; ++i)
1028 {
1029 Event::Type event = list[i].event;
1030 list[i].key = "None";
1031 string key = myPKeyHandler->getMappingDesc(event, mode);
1032
1033#ifdef JOYSTICK_SUPPORT
1034 string joydesc = myPJoyHandler->getMappingDesc(event, mode);
1035 if(joydesc != "")
1036 {
1037 if(key != "")
1038 key += ", ";
1039 key += joydesc;
1040 }
1041#endif
1042
1043 if(key != "")
1044 list[i].key = key;
1045 }
1046}
1047
1048// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1049void EventHandler::setComboMap()
1050{
1051 // Since istringstream swallows whitespace, we have to make the
1052 // delimiters be spaces
1053 string list = myOSystem.settings().getString("combomap");
1054 replace(list.begin(), list.end(), ':', ' ');
1055 istringstream buf(list);
1056 Int32 version = myOSystem.settings().getInt("event_ver");
1057
1058 // Erase the 'combo' array
1059 auto ERASE_ALL = [&]() {
1060 for(int i = 0; i < COMBO_SIZE; ++i)
1061 for(int j = 0; j < EVENTS_PER_COMBO; ++j)
1062 myComboTable[i][j] = Event::NoType;
1063 };
1064
1065 // Compare if event list version has changed so that combo maps became invalid
1066 if(version != Event::VERSION || !buf.good())
1067 ERASE_ALL();
1068 else
1069 {
1070 // Get combo count, which should be the first int in the list
1071 // If it isn't, then we treat the entire list as invalid
1072 string key;
1073 buf >> key;
1074 if(atoi(key.c_str()) == COMBO_SIZE)
1075 {
1076 // Fill the combomap table with events for as long as they exist
1077 int combocount = 0;
1078 while(buf >> key && combocount < COMBO_SIZE)
1079 {
1080 // Each event in a comboevent is separated by a comma
1081 replace(key.begin(), key.end(), ',', ' ');
1082 istringstream buf2(key);
1083
1084 int eventcount = 0;
1085 while(buf2 >> key && eventcount < EVENTS_PER_COMBO)
1086 {
1087 myComboTable[combocount][eventcount] = Event::Type(atoi(key.c_str()));
1088 ++eventcount;
1089 }
1090 ++combocount;
1091 }
1092 }
1093 else
1094 ERASE_ALL();
1095 }
1096
1097 saveComboMapping();
1098}
1099
1100// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1101void EventHandler::removePhysicalJoystickFromDatabase(const string& name)
1102{
1103#ifdef JOYSTICK_SUPPORT
1104 myPJoyHandler->remove(name);
1105#endif
1106}
1107
1108// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1109bool EventHandler::addKeyMapping(Event::Type event, EventMode mode, StellaKey key, StellaMod mod)
1110{
1111 bool mapped = myPKeyHandler->addMapping(event, mode, key, mod);
1112 if(mapped)
1113 setActionMappings(mode);
1114
1115 return mapped;
1116}
1117
1118// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1119bool EventHandler::addJoyMapping(Event::Type event, EventMode mode,
1120 int stick, int button, JoyAxis axis, JoyDir adir,
1121 bool updateMenus)
1122{
1123#ifdef JOYSTICK_SUPPORT
1124 bool mapped = myPJoyHandler->addJoyMapping(event, mode, stick, button, axis, adir);
1125 if (mapped && updateMenus)
1126 setActionMappings(mode);
1127
1128 return mapped;
1129#else
1130 return false;
1131#endif
1132}
1133
1134// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1135bool EventHandler::addJoyHatMapping(Event::Type event, EventMode mode,
1136 int stick, int button, int hat, JoyHatDir dir,
1137 bool updateMenus)
1138{
1139#ifdef JOYSTICK_SUPPORT
1140 bool mapped = myPJoyHandler->addJoyHatMapping(event, mode, stick, button, hat, dir);
1141 if (mapped && updateMenus)
1142 setActionMappings(mode);
1143
1144 return mapped;
1145#else
1146 return false;
1147#endif
1148}
1149
1150// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1151void EventHandler::eraseMapping(Event::Type event, EventMode mode)
1152{
1153 // Erase the KeyEvent array
1154 myPKeyHandler->eraseMapping(event, mode);
1155
1156#ifdef JOYSTICK_SUPPORT
1157 // Erase the joystick mapping arrays
1158 myPJoyHandler->eraseMapping(event, mode);
1159#endif
1160
1161 setActionMappings(mode);
1162}
1163
1164// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1165void EventHandler::setDefaultMapping(Event::Type event, EventMode mode)
1166{
1167 setDefaultKeymap(event, mode);
1168 setDefaultJoymap(event, mode);
1169}
1170
1171// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1172void EventHandler::setDefaultKeymap(Event::Type event, EventMode mode)
1173{
1174 myPKeyHandler->setDefaultMapping(event, mode);
1175 setActionMappings(mode);
1176}
1177
1178// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1179void EventHandler::setDefaultJoymap(Event::Type event, EventMode mode)
1180{
1181#ifdef JOYSTICK_SUPPORT
1182 myPJoyHandler->setDefaultMapping(event, mode);
1183 setActionMappings(mode);
1184#endif
1185}
1186
1187// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1188void EventHandler::saveKeyMapping()
1189{
1190 myPKeyHandler->saveMapping();
1191}
1192
1193// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1194void EventHandler::saveJoyMapping()
1195{
1196#ifdef JOYSTICK_SUPPORT
1197 myPJoyHandler->saveMapping();
1198#endif
1199}
1200
1201// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1202void EventHandler::saveComboMapping()
1203{
1204 // Iterate through the combomap table and create a colon-separated list
1205 // For each combo event, create a comma-separated list of its events
1206 // Prepend the event count, so we can check it on next load
1207 ostringstream buf;
1208 buf << COMBO_SIZE;
1209 for(int i = 0; i < COMBO_SIZE; ++i)
1210 {
1211 buf << ":" << myComboTable[i][0];
1212 for(int j = 1; j < EVENTS_PER_COMBO; ++j)
1213 buf << "," << myComboTable[i][j];
1214 }
1215 myOSystem.settings().setValue("combomap", buf.str());
1216}
1217
1218// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1219StringList EventHandler::getActionList(EventMode mode) const
1220{
1221 StringList l;
1222 switch(mode)
1223 {
1224 case EventMode::kEmulationMode:
1225 for(uInt32 i = 0; i < EMUL_ACTIONLIST_SIZE; ++i)
1226 l.push_back(EventHandler::ourEmulActionList[i].action);
1227 break;
1228 case EventMode::kMenuMode:
1229 for(uInt32 i = 0; i < MENU_ACTIONLIST_SIZE; ++i)
1230 l.push_back(EventHandler::ourMenuActionList[i].action);
1231 break;
1232 default:
1233 break;
1234 }
1235 return l;
1236}
1237
1238// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1239StringList EventHandler::getActionList(Event::Group group) const
1240{
1241 StringList l;
1242
1243 switch(group)
1244 {
1245 case Event::Group::Menu:
1246 return getActionList(EventMode::kMenuMode);
1247
1248 case Event::Group::Emulation:
1249 return getActionList(EventMode::kEmulationMode);
1250
1251 case Event::Group::Misc:
1252 return getActionList(MiscEvents);
1253
1254 case Event::Group::AudioVideo:
1255 return getActionList(AudioVideoEvents);
1256
1257 case Event::Group::States:
1258 return getActionList(StateEvents);
1259
1260 case Event::Group::Console:
1261 return getActionList(ConsoleEvents);
1262
1263 case Event::Group::Joystick:
1264 return getActionList(JoystickEvents);
1265
1266 case Event::Group::Paddles:
1267 return getActionList(PaddlesEvents);
1268
1269 case Event::Group::Keyboard:
1270 return getActionList(KeyboardEvents);
1271
1272 case Event::Group::Debug:
1273 return getActionList(DebugEvents);
1274
1275 case Event::Group::Combo:
1276 return getActionList(ComboEvents);
1277
1278 default:
1279 return l; // ToDo
1280 }
1281}
1282
1283// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1284StringList EventHandler::getActionList(const Event::EventSet& events, EventMode mode) const
1285{
1286 StringList l;
1287
1288 switch(mode)
1289 {
1290 case EventMode::kMenuMode:
1291 for(uInt32 i = 0; i < MENU_ACTIONLIST_SIZE; ++i)
1292 for(const auto& event : events)
1293 if(EventHandler::ourMenuActionList[i].event == event)
1294 {
1295 l.push_back(EventHandler::ourMenuActionList[i].action);
1296 break;
1297 }
1298 break;
1299
1300 default:
1301 for(uInt32 i = 0; i < EMUL_ACTIONLIST_SIZE; ++i)
1302 for(const auto& event : events)
1303 if(EventHandler::ourEmulActionList[i].event == event)
1304 {
1305 l.push_back(EventHandler::ourEmulActionList[i].action);
1306 break;
1307 }
1308 }
1309 return l;
1310}
1311
1312// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1313VariantList EventHandler::getComboList(EventMode /**/) const
1314{
1315 // For now, this only works in emulation mode
1316 VariantList l;
1317 ostringstream buf;
1318
1319 VarList::push_back(l, "None", "-1");
1320 for(uInt32 i = 0; i < EMUL_ACTIONLIST_SIZE; ++i)
1321 {
1322 Event::Type event = EventHandler::ourEmulActionList[i].event;
1323 // exclude combos events
1324 if(!(event >= Event::Combo1 && event <= Event::Combo16))
1325 {
1326 buf << i;
1327 VarList::push_back(l, EventHandler::ourEmulActionList[i].action, buf.str());
1328 buf.str("");
1329 }
1330 }
1331 return l;
1332}
1333
1334// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1335StringList EventHandler::getComboListForEvent(Event::Type event) const
1336{
1337 StringList l;
1338 ostringstream buf;
1339 if(event >= Event::Combo1 && event <= Event::Combo16)
1340 {
1341 int combo = event - Event::Combo1;
1342 for(uInt32 i = 0; i < EVENTS_PER_COMBO; ++i)
1343 {
1344 Event::Type e = myComboTable[combo][i];
1345 for(uInt32 j = 0; j < EMUL_ACTIONLIST_SIZE; ++j)
1346 {
1347 if(EventHandler::ourEmulActionList[j].event == e)
1348 {
1349 buf << j;
1350 l.push_back(buf.str());
1351 buf.str("");
1352 }
1353 }
1354 // Make sure entries are 1-to-1, using '-1' to indicate Event::NoType
1355 if(i == l.size())
1356 l.push_back("-1");
1357 }
1358 }
1359 return l;
1360}
1361
1362// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1363void EventHandler::setComboListForEvent(Event::Type event, const StringList& events)
1364{
1365 if(event >= Event::Combo1 && event <= Event::Combo16)
1366 {
1367 assert(events.size() == 8);
1368 int combo = event - Event::Combo1;
1369 for(int i = 0; i < 8; ++i)
1370 {
1371 int idx = atoi(events[i].c_str());
1372 if(idx >= 0 && idx < EMUL_ACTIONLIST_SIZE)
1373 myComboTable[combo][i] = EventHandler::ourEmulActionList[idx].event;
1374 else
1375 myComboTable[combo][i] = Event::NoType;
1376 }
1377 saveComboMapping();
1378 }
1379}
1380
1381// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1382int EventHandler::getEmulActionListIndex(int idx, const Event::EventSet& events) const
1383{
1384 // idx = index into intersection set of 'events' and 'ourEmulActionList'
1385 // ordered by 'ourEmulActionList'!
1386 Event::Type event = Event::NoType;
1387
1388 for(uInt32 i = 0; i < EMUL_ACTIONLIST_SIZE; ++i)
1389 {
1390 for(const auto& item : events)
1391 if(EventHandler::ourEmulActionList[i].event == item)
1392 {
1393 idx--;
1394 if(idx < 0)
1395 event = item;
1396 break;
1397 }
1398 if(idx < 0)
1399 break;
1400 }
1401
1402 for(uInt32 i = 0; i < EMUL_ACTIONLIST_SIZE; ++i)
1403 if(EventHandler::ourEmulActionList[i].event == event)
1404 return i;
1405
1406 return -1;
1407}
1408
1409// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1410int EventHandler::getActionListIndex(int idx, Event::Group group) const
1411{
1412 switch(group)
1413 {
1414 case Event::Group::Menu:
1415 return idx;
1416
1417 case Event::Group::Emulation:
1418 return idx;
1419
1420 case Event::Group::Misc:
1421 return getEmulActionListIndex(idx, MiscEvents);
1422
1423 case Event::Group::AudioVideo:
1424 return getEmulActionListIndex(idx, AudioVideoEvents);
1425
1426 case Event::Group::States:
1427 return getEmulActionListIndex(idx, StateEvents);
1428
1429 case Event::Group::Console:
1430 return getEmulActionListIndex(idx, ConsoleEvents);
1431
1432 case Event::Group::Joystick:
1433 return getEmulActionListIndex(idx, JoystickEvents);
1434
1435 case Event::Group::Paddles:
1436 return getEmulActionListIndex(idx, PaddlesEvents);
1437
1438 case Event::Group::Keyboard:
1439 return getEmulActionListIndex(idx, KeyboardEvents);
1440
1441 case Event::Group::Debug:
1442 return getEmulActionListIndex(idx, DebugEvents);
1443
1444 case Event::Group::Combo:
1445 return getEmulActionListIndex(idx, ComboEvents);
1446
1447 default:
1448 return -1;
1449 }
1450}
1451
1452// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1453Event::Type EventHandler::eventAtIndex(int idx, Event::Group group) const
1454{
1455 int index = getActionListIndex(idx, group);
1456
1457 switch(group)
1458 {
1459 case Event::Group::Menu:
1460 if(index < 0 || index >= MENU_ACTIONLIST_SIZE)
1461 return Event::NoType;
1462 else
1463 return ourMenuActionList[index].event;
1464
1465 default:
1466 if(index < 0 || index >= EMUL_ACTIONLIST_SIZE)
1467 return Event::NoType;
1468 else
1469 return ourEmulActionList[index].event;
1470 }
1471}
1472
1473// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1474string EventHandler::actionAtIndex(int idx, Event::Group group) const
1475{
1476 int index = getActionListIndex(idx, group);
1477
1478 switch(group)
1479 {
1480 case Event::Group::Menu:
1481 if(index < 0 || index >= MENU_ACTIONLIST_SIZE)
1482 return EmptyString;
1483 else
1484 return ourMenuActionList[index].action;
1485
1486 default:
1487 if(index < 0 || index >= EMUL_ACTIONLIST_SIZE)
1488 return EmptyString;
1489 else
1490 return ourEmulActionList[index].action;
1491 }
1492}
1493
1494// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1495string EventHandler::keyAtIndex(int idx, Event::Group group) const
1496{
1497 int index = getActionListIndex(idx, group);
1498
1499 switch(group)
1500 {
1501 case Event::Group::Menu:
1502 if(index < 0 || index >= MENU_ACTIONLIST_SIZE)
1503 return EmptyString;
1504 else
1505 return ourMenuActionList[index].key;
1506
1507 default:
1508 if(index < 0 || index >= EMUL_ACTIONLIST_SIZE)
1509 return EmptyString;
1510 else
1511 return ourEmulActionList[index].key;
1512 }
1513}
1514
1515// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1516void EventHandler::setMouseControllerMode(const string& enable)
1517{
1518 if(myOSystem.hasConsole())
1519 {
1520 bool usemouse = false;
1521 if(BSPF::equalsIgnoreCase(enable, "always"))
1522 usemouse = true;
1523 else if(BSPF::equalsIgnoreCase(enable, "never"))
1524 usemouse = false;
1525 else // 'analog'
1526 {
1527 usemouse = myOSystem.console().leftController().isAnalog() ||
1528 myOSystem.console().rightController().isAnalog();
1529 }
1530
1531 const string& control = usemouse ?
1532 myOSystem.console().properties().get(PropType::Controller_MouseAxis) : "none";
1533
1534 myMouseControl = make_unique<MouseControl>(myOSystem.console(), control);
1535 myMouseControl->next(); // set first available mode
1536 }
1537}
1538
1539// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1540void EventHandler::enterMenuMode(EventHandlerState state)
1541{
1542#ifdef GUI_SUPPORT
1543 setState(state);
1544 myOverlay->reStack();
1545 myOSystem.sound().mute(true);
1546#endif
1547}
1548
1549// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1550void EventHandler::leaveMenuMode()
1551{
1552#ifdef GUI_SUPPORT
1553 setState(EventHandlerState::EMULATION);
1554 myOSystem.sound().mute(false);
1555#endif
1556}
1557
1558// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1559bool EventHandler::enterDebugMode()
1560{
1561#ifdef DEBUGGER_SUPPORT
1562 if(myState == EventHandlerState::DEBUGGER || !myOSystem.hasConsole())
1563 return false;
1564
1565 // Make sure debugger starts in a consistent state
1566 // This absolutely *has* to come before we actually change to debugger
1567 // mode, since it takes care of locking the debugger state, which will
1568 // probably be modified below
1569 myOSystem.debugger().setStartState();
1570 setState(EventHandlerState::DEBUGGER);
1571
1572 FBInitStatus fbstatus = myOSystem.createFrameBuffer();
1573 if(fbstatus != FBInitStatus::Success)
1574 {
1575 myOSystem.debugger().setQuitState();
1576 setState(EventHandlerState::EMULATION);
1577 if(fbstatus == FBInitStatus::FailTooLarge)
1578 myOSystem.frameBuffer().showMessage("Debugger window too large for screen",
1579 MessagePosition::BottomCenter, true);
1580 return false;
1581 }
1582 myOverlay->reStack();
1583 myOSystem.sound().mute(true);
1584#else
1585 myOSystem.frameBuffer().showMessage("Debugger support not included",
1586 MessagePosition::BottomCenter, true);
1587#endif
1588
1589 return true;
1590}
1591
1592// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1593void EventHandler::leaveDebugMode()
1594{
1595#ifdef DEBUGGER_SUPPORT
1596 // paranoia: this should never happen:
1597 if(myState != EventHandlerState::DEBUGGER)
1598 return;
1599
1600 // Make sure debugger quits in a consistent state
1601 myOSystem.debugger().setQuitState();
1602
1603 setState(EventHandlerState::EMULATION);
1604 myOSystem.createFrameBuffer();
1605 myOSystem.sound().mute(false);
1606#endif
1607}
1608
1609// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1610void EventHandler::enterTimeMachineMenuMode(uInt32 numWinds, bool unwind)
1611{
1612#ifdef GUI_SUPPORT
1613 // add one extra state if we are in Time Machine mode
1614 // TODO: maybe remove this state if we leave the menu at this new state
1615 myOSystem.state().addExtraState("enter Time Machine dialog"); // force new state
1616
1617 if(numWinds)
1618 // hande winds and display wind message (numWinds != 0) in time machine dialog
1619 myOSystem.timeMachine().setEnterWinds(unwind ? numWinds : -numWinds);
1620
1621 enterMenuMode(EventHandlerState::TIMEMACHINE);
1622#endif
1623}
1624
1625// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1626void EventHandler::setState(EventHandlerState state)
1627{
1628 myState = state;
1629
1630 // Normally, the usage of modifier keys is determined by 'modcombo'
1631 // For certain ROMs it may be forced off, whatever the setting
1632 myPKeyHandler->useModKeys() = myOSystem.settings().getBool("modcombo");
1633
1634 // Only enable text input in GUI modes, since in emulation mode the
1635 // keyboard acts as one large joystick with many (single) buttons
1636 myOverlay = nullptr;
1637 switch(myState)
1638 {
1639 case EventHandlerState::EMULATION:
1640 myOSystem.sound().mute(false);
1641 enableTextEvents(false);
1642 break;
1643
1644 case EventHandlerState::PAUSE:
1645 myOSystem.sound().mute(true);
1646 enableTextEvents(false);
1647 break;
1648
1649 #ifdef GUI_SUPPORT
1650 case EventHandlerState::OPTIONSMENU:
1651 myOverlay = &myOSystem.menu();
1652 enableTextEvents(true);
1653 break;
1654
1655 case EventHandlerState::CMDMENU:
1656 myOverlay = &myOSystem.commandMenu();
1657 enableTextEvents(true);
1658 break;
1659
1660 case EventHandlerState::TIMEMACHINE:
1661 myOSystem.timeMachine().requestResize();
1662 myOverlay = &myOSystem.timeMachine();
1663 enableTextEvents(true);
1664 break;
1665
1666 case EventHandlerState::LAUNCHER:
1667 myOverlay = &myOSystem.launcher();
1668 enableTextEvents(true);
1669 break;
1670 #endif
1671
1672 #ifdef DEBUGGER_SUPPORT
1673 case EventHandlerState::DEBUGGER:
1674 myOverlay = &myOSystem.debugger();
1675 enableTextEvents(true);
1676 break;
1677 #endif
1678
1679 case EventHandlerState::NONE:
1680 default:
1681 break;
1682 }
1683
1684 // Inform various subsystems about the new state
1685 myOSystem.stateChanged(myState);
1686 myOSystem.frameBuffer().stateChanged(myState);
1687 myOSystem.frameBuffer().setCursorState();
1688 if(myOSystem.hasConsole())
1689 myOSystem.console().stateChanged(myState);
1690
1691 // Sometimes an extraneous mouse motion event is generated
1692 // after a state change, which should be supressed
1693 mySkipMouseMotion = true;
1694
1695 // Erase any previously set events, since a state change implies
1696 // that old events are now invalid
1697 myEvent.clear();
1698}
1699
1700// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1701void EventHandler::exitEmulation()
1702{
1703 // TODO: confirm message
1704 string saveOnExit = myOSystem.settings().getString("saveonexit");
1705
1706 if (saveOnExit == "all")
1707 handleEvent(Event::SaveAllStates);
1708 else if (saveOnExit == "current")
1709 handleEvent(Event::SaveState);
1710}
1711
1712// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1713EventHandler::ActionList EventHandler::ourEmulActionList[EMUL_ACTIONLIST_SIZE] = {
1714 { Event::Quit, "Quit", "" },
1715 { Event::ReloadConsole, "Reload current ROM/load next game", "" },
1716 { Event::ExitMode, "Exit current Stella menu/mode", "" },
1717 { Event::OptionsMenuMode, "Enter Options menu UI", "" },
1718 { Event::CmdMenuMode, "Toggle Commands menu UI", "" },
1719 { Event::TogglePauseMode, "Toggle Pause mode", "" },
1720 { Event::StartPauseMode, "Start Pause mode", "" },
1721 { Event::Fry, "Fry cartridge", "" },
1722 { Event::DebuggerMode, "Toggle Debugger mode", "" },
1723
1724 { Event::ConsoleSelect, "Select", "" },
1725 { Event::ConsoleReset, "Reset", "" },
1726 { Event::ConsoleColor, "Color TV", "" },
1727 { Event::ConsoleBlackWhite, "Black & White TV", "" },
1728 { Event::ConsoleColorToggle, "Toggle Color / B&W TV", "" },
1729 { Event::Console7800Pause, "7800 Pause Key", "" },
1730 { Event::ConsoleLeftDiffA, "P0 Difficulty A", "" },
1731 { Event::ConsoleLeftDiffB, "P0 Difficulty B", "" },
1732 { Event::ConsoleLeftDiffToggle, "P0 Toggle Difficulty", "" },
1733 { Event::ConsoleRightDiffA, "P1 Difficulty A", "" },
1734 { Event::ConsoleRightDiffB, "P1 Difficulty B", "" },
1735 { Event::ConsoleRightDiffToggle, "P1 Toggle Difficulty", "" },
1736 { Event::SaveState, "Save state", "" },
1737 { Event::ChangeState, "Change state slot", "" },
1738 { Event::ToggleAutoSlot, "Toggle automatic state slot change", "" },
1739 { Event::LoadState, "Load state", "" },
1740#ifdef PNG_SUPPORT
1741 { Event::TakeSnapshot, "Snapshot", "" },
1742 { Event::ToggleContSnapshots, "Save continuous snapsh. (as defined)", "" },
1743 { Event::ToggleContSnapshotsFrame,"Save continuous snapsh. (every frame)", "" },
1744#endif
1745
1746 { Event::JoystickZeroUp, "P0 Joystick Up", "" },
1747 { Event::JoystickZeroDown, "P0 Joystick Down", "" },
1748 { Event::JoystickZeroLeft, "P0 Joystick Left", "" },
1749 { Event::JoystickZeroRight, "P0 Joystick Right", "" },
1750 { Event::JoystickZeroFire, "P0 Joystick Fire", "" },
1751 { Event::JoystickZeroFire5, "P0 Booster Top Booster Button", "" },
1752 { Event::JoystickZeroFire9, "P0 Booster Handle Grip Trigger", "" },
1753
1754 { Event::JoystickOneUp, "P1 Joystick Up", "" },
1755 { Event::JoystickOneDown, "P1 Joystick Down", "" },
1756 { Event::JoystickOneLeft, "P1 Joystick Left", "" },
1757 { Event::JoystickOneRight, "P1 Joystick Right", "" },
1758 { Event::JoystickOneFire, "P1 Joystick Fire", "" },
1759 { Event::JoystickOneFire5, "P1 Booster Top Booster Button", "" },
1760 { Event::JoystickOneFire9, "P1 Booster Handle Grip Trigger", "" },
1761
1762 { Event::PaddleZeroAnalog, "Paddle 0 Analog", "" },
1763 { Event::PaddleZeroIncrease, "Paddle 0 Turn Left", "" },
1764 { Event::PaddleZeroDecrease, "Paddle 0 Turn Right", "" },
1765 { Event::PaddleZeroFire, "Paddle 0 Fire", "" },
1766
1767 { Event::PaddleOneAnalog, "Paddle 1 Analog", "" },
1768 { Event::PaddleOneIncrease, "Paddle 1 Turn Left", "" },
1769 { Event::PaddleOneDecrease, "Paddle 1 Turn Right", "" },
1770 { Event::PaddleOneFire, "Paddle 1 Fire", "" },
1771
1772 { Event::PaddleTwoAnalog, "Paddle 2 Analog", "" },
1773 { Event::PaddleTwoIncrease, "Paddle 2 Turn Left", "" },
1774 { Event::PaddleTwoDecrease, "Paddle 2 Turn Right", "" },
1775 { Event::PaddleTwoFire, "Paddle 2 Fire", "" },
1776
1777 { Event::PaddleThreeAnalog, "Paddle 3 Analog", "" },
1778 { Event::PaddleThreeIncrease, "Paddle 3 Turn Left", "" },
1779 { Event::PaddleThreeDecrease, "Paddle 3 Turn Right", "" },
1780 { Event::PaddleThreeFire, "Paddle 3 Fire", "" },
1781
1782 { Event::KeyboardZero1, "P0 Keyboard 1", "" },
1783 { Event::KeyboardZero2, "P0 Keyboard 2", "" },
1784 { Event::KeyboardZero3, "P0 Keyboard 3", "" },
1785 { Event::KeyboardZero4, "P0 Keyboard 4", "" },
1786 { Event::KeyboardZero5, "P0 Keyboard 5", "" },
1787 { Event::KeyboardZero6, "P0 Keyboard 6", "" },
1788 { Event::KeyboardZero7, "P0 Keyboard 7", "" },
1789 { Event::KeyboardZero8, "P0 Keyboard 8", "" },
1790 { Event::KeyboardZero9, "P0 Keyboard 9", "" },
1791 { Event::KeyboardZeroStar, "P0 Keyboard *", "" },
1792 { Event::KeyboardZero0, "P0 Keyboard 0", "" },
1793 { Event::KeyboardZeroPound, "P0 Keyboard #", "" },
1794
1795 { Event::KeyboardOne1, "P1 Keyboard 1", "" },
1796 { Event::KeyboardOne2, "P1 Keyboard 2", "" },
1797 { Event::KeyboardOne3, "P1 Keyboard 3", "" },
1798 { Event::KeyboardOne4, "P1 Keyboard 4", "" },
1799 { Event::KeyboardOne5, "P1 Keyboard 5", "" },
1800 { Event::KeyboardOne6, "P1 Keyboard 6", "" },
1801 { Event::KeyboardOne7, "P1 Keyboard 7", "" },
1802 { Event::KeyboardOne8, "P1 Keyboard 8", "" },
1803 { Event::KeyboardOne9, "P1 Keyboard 9", "" },
1804 { Event::KeyboardOneStar, "P1 Keyboard *", "" },
1805 { Event::KeyboardOne0, "P1 Keyboard 0", "" },
1806 { Event::KeyboardOnePound, "P1 Keyboard #", "" },
1807 // Video
1808 { Event::VidmodeDecrease, "Previous zoom level", "" },
1809 { Event::VidmodeIncrease, "Next zoom level", "" },
1810 { Event::ToggleFullScreen, "Toggle fullscreen", "" },
1811 { Event::DecreaseOverscan, "Decrease overscan in fullscreen mode", "" },
1812 { Event::IncreaseOverScan, "Increase overscan in fullscreen mode", "" },
1813 { Event::DecreaseFormat, "Decrease display format", "" },
1814 { Event::IncreaseFormat, "Increase display format", "" },
1815 { Event::TogglePalette, "Switch palette (Standard/Z26/User)", "" },
1816
1817 // TV effects:
1818 { Event::VidmodeStd, "Disable TV effects", "" },
1819 { Event::VidmodeRGB, "Select 'RGB' preset", "" },
1820 { Event::VidmodeSVideo, "Select 'S-Video' preset", "" },
1821 { Event::VidModeComposite, "Select 'Composite' preset", "" },
1822 { Event::VidModeBad, "Select 'Badly adjusted' preset", "" },
1823 { Event::VidModeCustom, "Select 'Custom' preset", "" },
1824 { Event::PreviousAttribute, "Select previous 'Custom' attribute", "" },
1825 { Event::NextAttribute, "Select next 'Custom' attribute", "" },
1826 { Event::DecreaseAttribute, "Decrease selected 'Custom' attribute", "" },
1827 { Event::IncreaseAttribute, "Increase selected 'Custom' attribute", "" },
1828 { Event::TogglePhosphor, "Toggle 'phosphor' effect", "" },
1829 { Event::DecreasePhosphor, "Decrease 'phosphor' blend", "" },
1830 { Event::IncreasePhosphor, "Increase 'phosphor' blend", "" },
1831 { Event::ScanlinesDecrease, "Decrease scanlines", "" },
1832 { Event::ScanlinesIncrease, "Increase scanlines", "" },
1833 // Developer keys:
1834 { Event::ToggleFrameStats, "Toggle frame stats", "" },
1835 { Event::ToggleP0Bit, "Toggle TIA Player0 object", "" },
1836 { Event::ToggleP0Collision, "Toggle TIA Player0 collisions", "" },
1837 { Event::ToggleP1Bit, "Toggle TIA Player1 object", "" },
1838 { Event::ToggleP1Collision, "Toggle TIA Player1 collisions", "" },
1839 { Event::ToggleM0Bit, "Toggle TIA Missile0 object", "" },
1840 { Event::ToggleM0Collision, "Toggle TIA Missile0 collisions", "" },
1841 { Event::ToggleM1Bit, "Toggle TIA Missile1 object", "" },
1842 { Event::ToggleM1Collision, "Toggle TIA Missile1 collisions", "" },
1843 { Event::ToggleBLBit, "Toggle TIA Ball object", "" },
1844 { Event::ToggleBLCollision, "Toggle TIA Ball collisions", "" },
1845 { Event::TogglePFBit, "Toggle TIA Playfield object", "" },
1846 { Event::TogglePFCollision, "Toggle TIA Playfield collisions", "" },
1847 { Event::ToggleBits, "Toggle all TIA objects", "" },
1848 { Event::ToggleCollisions, "Toggle all TIA collisions", "" },
1849 { Event::ToggleFixedColors, "Toggle TIA 'Fixed Debug Colors' mode", "" },
1850 { Event::ToggleColorLoss, "Toggle PAL color-loss effect", "" },
1851 { Event::ToggleJitter, "Toggle TV 'Jitter' effect", "" },
1852 // Other keys:
1853 { Event::SoundToggle, "Toggle sound", "" },
1854 { Event::VolumeDecrease, "Decrease volume", "" },
1855 { Event::VolumeIncrease, "Increase volume", "" },
1856
1857 { Event::HandleMouseControl, "Switch mouse emulation modes", "" },
1858 { Event::ToggleGrabMouse, "Toggle grab mouse", "" },
1859 { Event::ToggleSAPortOrder, "Swap Stelladaptor port ordering", "" },
1860
1861 { Event::SaveAllStates, "Save all TM states of current game", "" },
1862 { Event::LoadAllStates, "Load saved TM states for current game", "" },
1863 { Event::ToggleTimeMachine, "Toggle 'Time Machine' mode", "" },
1864 { Event::TimeMachineMode, "Toggle 'Time Machine' UI", "" },
1865 { Event::RewindPause, "Rewind one state & enter Pause mode", "" },
1866 { Event::Rewind1Menu, "Rewind one state & enter TM UI", "" },
1867 { Event::Rewind10Menu, "Rewind 10 states & enter TM UI", "" },
1868 { Event::RewindAllMenu, "Rewind all states & enter TM UI", "" },
1869 { Event::UnwindPause, "Unwind one state & enter Pause mode", "" },
1870 { Event::Unwind1Menu, "Unwind one state & enter TM UI", "" },
1871 { Event::Unwind10Menu, "Unwind 10 states & enter TM UI", "" },
1872 { Event::UnwindAllMenu, "Unwind all states & enter TM UI", "" },
1873
1874 { Event::Combo1, "Combo 1", "" },
1875 { Event::Combo2, "Combo 2", "" },
1876 { Event::Combo3, "Combo 3", "" },
1877 { Event::Combo4, "Combo 4", "" },
1878 { Event::Combo5, "Combo 5", "" },
1879 { Event::Combo6, "Combo 6", "" },
1880 { Event::Combo7, "Combo 7", "" },
1881 { Event::Combo8, "Combo 8", "" },
1882 { Event::Combo9, "Combo 9", "" },
1883 { Event::Combo10, "Combo 10", "" },
1884 { Event::Combo11, "Combo 11", "" },
1885 { Event::Combo12, "Combo 12", "" },
1886 { Event::Combo13, "Combo 13", "" },
1887 { Event::Combo14, "Combo 14", "" },
1888 { Event::Combo15, "Combo 15", "" },
1889 { Event::Combo16, "Combo 16", "" }
1890};
1891
1892// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1893EventHandler::ActionList EventHandler::ourMenuActionList[MENU_ACTIONLIST_SIZE] = {
1894 { Event::UIUp, "Move Up", "" },
1895 { Event::UIDown, "Move Down", "" },
1896 { Event::UILeft, "Move Left", "" },
1897 { Event::UIRight, "Move Right", "" },
1898
1899 { Event::UIHome, "Home", "" },
1900 { Event::UIEnd, "End", "" },
1901 { Event::UIPgUp, "Page Up", "" },
1902 { Event::UIPgDown, "Page Down", "" },
1903
1904 { Event::UIOK, "OK", "" },
1905 { Event::UICancel, "Cancel", "" },
1906 { Event::UISelect, "Select item", "" },
1907
1908 { Event::UINavPrev, "Previous object", "" },
1909 { Event::UINavNext, "Next object", "" },
1910 { Event::UITabPrev, "Previous tab", "" },
1911 { Event::UITabNext, "Next tab", "" },
1912
1913 { Event::UIPrevDir, "Parent directory", "" },
1914 { Event::ToggleFullScreen, "Toggle fullscreen", "" },
1915 { Event::Quit, "Quit", "" }
1916};
1917
1918// Event groups
1919// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1920const Event::EventSet EventHandler::MiscEvents = {
1921 Event::Quit, Event::ReloadConsole, Event::Fry, Event::StartPauseMode,
1922 Event::TogglePauseMode, Event::OptionsMenuMode, Event::CmdMenuMode, Event::ExitMode,
1923 Event::TakeSnapshot, Event::ToggleContSnapshots, Event::ToggleContSnapshotsFrame,
1924 // Event::MouseAxisXValue, Event::MouseAxisYValue,
1925 // Event::MouseButtonLeftValue, Event::MouseButtonRightValue,
1926 Event::HandleMouseControl, Event::ToggleGrabMouse,
1927 Event::ToggleSAPortOrder,
1928};
1929
1930// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1931const Event::EventSet EventHandler::AudioVideoEvents = {
1932 Event::VolumeDecrease, Event::VolumeIncrease, Event::SoundToggle,
1933 Event::VidmodeDecrease, Event::VidmodeIncrease,
1934 Event::ToggleFullScreen,
1935 Event::VidmodeStd, Event::VidmodeRGB, Event::VidmodeSVideo, Event::VidModeComposite, Event::VidModeBad, Event::VidModeCustom,
1936 Event::PreviousAttribute, Event::NextAttribute, Event::DecreaseAttribute, Event::IncreaseAttribute,
1937 Event::ScanlinesDecrease, Event::ScanlinesIncrease,
1938 Event::DecreasePhosphor, Event::IncreasePhosphor, Event::TogglePhosphor,
1939 Event::DecreaseFormat, Event::IncreaseFormat,
1940 Event::DecreaseOverscan, Event::IncreaseOverScan,
1941 Event::TogglePalette,
1942};
1943
1944// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1945const Event::EventSet EventHandler::StateEvents = {
1946 Event::ChangeState, Event::LoadState, Event::SaveState, Event::TimeMachineMode,
1947 Event::RewindPause, Event::UnwindPause, Event::ToggleTimeMachine,
1948 Event::Rewind1Menu, Event::Rewind10Menu, Event::RewindAllMenu,
1949 Event::Unwind1Menu, Event::Unwind10Menu, Event::UnwindAllMenu,
1950 Event::SaveAllStates, Event::LoadAllStates, Event::ToggleAutoSlot,
1951};
1952
1953// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1954const Event::EventSet EventHandler::ConsoleEvents = {
1955 Event::ConsoleColor, Event::ConsoleBlackWhite,
1956 Event::ConsoleLeftDiffA, Event::ConsoleLeftDiffB,
1957 Event::ConsoleRightDiffA, Event::ConsoleRightDiffB,
1958 Event::ConsoleSelect, Event::ConsoleReset,
1959 Event::ConsoleLeftDiffToggle, Event::ConsoleRightDiffToggle, Event::ConsoleColorToggle,
1960 Event::Console7800Pause,
1961};
1962
1963// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1964const Event::EventSet EventHandler::JoystickEvents = {
1965 Event::JoystickZeroUp, Event::JoystickZeroDown, Event::JoystickZeroLeft, Event::JoystickZeroRight,
1966 Event::JoystickZeroFire, Event::JoystickZeroFire5, Event::JoystickZeroFire9,
1967 Event::JoystickOneUp, Event::JoystickOneDown, Event::JoystickOneLeft, Event::JoystickOneRight,
1968 Event::JoystickOneFire, Event::JoystickOneFire5, Event::JoystickOneFire9,
1969};
1970
1971// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1972const Event::EventSet EventHandler::PaddlesEvents = {
1973 Event::PaddleZeroDecrease, Event::PaddleZeroIncrease, Event::PaddleZeroAnalog, Event::PaddleZeroFire,
1974 Event::PaddleOneDecrease, Event::PaddleOneIncrease, Event::PaddleOneAnalog, Event::PaddleOneFire,
1975 Event::PaddleTwoDecrease, Event::PaddleTwoIncrease, Event::PaddleTwoAnalog, Event::PaddleTwoFire,
1976 Event::PaddleThreeDecrease, Event::PaddleThreeIncrease, Event::PaddleThreeAnalog, Event::PaddleThreeFire,
1977};
1978
1979const Event::EventSet EventHandler::KeyboardEvents = {
1980 Event::KeyboardZero1, Event::KeyboardZero2, Event::KeyboardZero3,
1981 Event::KeyboardZero4, Event::KeyboardZero5, Event::KeyboardZero6,
1982 Event::KeyboardZero7, Event::KeyboardZero8, Event::KeyboardZero9,
1983 Event::KeyboardZeroStar, Event::KeyboardZero0, Event::KeyboardZeroPound,
1984
1985 Event::KeyboardOne1, Event::KeyboardOne2, Event::KeyboardOne3,
1986 Event::KeyboardOne4, Event::KeyboardOne5, Event::KeyboardOne6,
1987 Event::KeyboardOne7, Event::KeyboardOne8, Event::KeyboardOne9,
1988 Event::KeyboardOneStar, Event::KeyboardOne0, Event::KeyboardOnePound,
1989};
1990
1991// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1992const Event::EventSet EventHandler::ComboEvents = {
1993 Event::Combo1, Event::Combo2, Event::Combo3, Event::Combo4, Event::Combo5, Event::Combo6, Event::Combo7, Event::Combo8,
1994 Event::Combo9, Event::Combo10, Event::Combo11, Event::Combo12, Event::Combo13, Event::Combo14, Event::Combo15, Event::Combo16,
1995};
1996
1997// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1998const Event::EventSet EventHandler::DebugEvents = {
1999 Event::DebuggerMode,
2000 Event::ToggleFrameStats,
2001 Event::ToggleP0Collision, Event::ToggleP0Bit, Event::ToggleP1Collision, Event::ToggleP1Bit,
2002 Event::ToggleM0Collision, Event::ToggleM0Bit, Event::ToggleM1Collision, Event::ToggleM1Bit,
2003 Event::ToggleBLCollision, Event::ToggleBLBit, Event::TogglePFCollision, Event::TogglePFBit,
2004 Event::ToggleCollisions, Event::ToggleBits, Event::ToggleFixedColors,
2005 Event::ToggleColorLoss,
2006 Event::ToggleJitter,
2007};
2008