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 "Logger.hxx"
19#include "OSystem.hxx"
20#include "Console.hxx"
21#include "Joystick.hxx"
22#include "Settings.hxx"
23#include "EventHandler.hxx"
24#include "PJoystickHandler.hxx"
25
26#ifdef GUI_SUPPORT
27 #include "DialogContainer.hxx"
28#endif
29
30static constexpr char CTRL_DELIM = '^';
31
32// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
33PhysicalJoystickHandler::PhysicalJoystickHandler(
34 OSystem& system, EventHandler& handler)
35 : myOSystem(system),
36 myHandler(handler)
37{
38 Int32 version = myOSystem.settings().getInt("event_ver");
39 // Load previously saved joystick mapping (if any) from settings
40 istringstream buf(myOSystem.settings().getString("joymap"));
41 string joymap, joyname;
42
43 // First compare if event list version has changed, and disregard the entire mapping if true
44 getline(buf, joymap, CTRL_DELIM); // event list size, ignore
45 if(version == Event::VERSION)
46 {
47 // Otherwise, put each joystick mapping entry into the database
48 while(getline(buf, joymap, CTRL_DELIM))
49 {
50 istringstream namebuf(joymap);
51 getline(namebuf, joyname, PhysicalJoystick::MODE_DELIM);
52 if(joyname.length() != 0)
53 myDatabase.emplace(joyname, StickInfo(joymap));
54 }
55 }
56}
57
58// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
59int PhysicalJoystickHandler::add(PhysicalJoystickPtr stick)
60{
61 // Skip if we couldn't open it for any reason
62 if(stick->ID < 0)
63 return -1;
64
65 // Figure out what type of joystick this is
66 bool specialAdaptor = false;
67
68 if(BSPF::containsIgnoreCase(stick->name, "2600-daptor"))
69 {
70 specialAdaptor = true;
71 if(stick->numAxes == 4)
72 {
73 // TODO - detect controller type based on z-axis
74 stick->name = "2600-daptor D9";
75 }
76 else if(stick->numAxes == 3)
77 {
78 stick->name = "2600-daptor II";
79 }
80 else
81 stick->name = "2600-daptor";
82 }
83 else if(BSPF::containsIgnoreCase(stick->name, "Stelladaptor"))
84 {
85 stick->name = "Stelladaptor";
86 specialAdaptor = true;
87 }
88 else
89 {
90 // We need unique names for mappable devices
91 // For non-unique names that already have a database entry,
92 // we append ' #x', where 'x' increases consecutively
93 int count = 0;
94 for(const auto& i: myDatabase)
95 if(BSPF::startsWithIgnoreCase(i.first, stick->name) && i.second.joy)
96 ++count;
97
98 if(count > 0)
99 {
100 ostringstream name;
101 name << stick->name << " #" << count+1;
102 stick->name = name.str();
103 }
104 stick->type = PhysicalJoystick::JT_REGULAR;
105 }
106 // The stick *must* be inserted here, since it may be used below
107 mySticks[stick->ID] = stick;
108
109 // Map the stelladaptors we've found according to the specified ports
110 if(specialAdaptor)
111 mapStelladaptors(myOSystem.settings().getString("saport"));
112
113 // Add stick to database
114 auto it = myDatabase.find(stick->name);
115 if(it != myDatabase.end()) // already present
116 {
117 it->second.joy = stick;
118 stick->setMap(it->second.mapping);
119 enableEmulationMappings();
120 }
121 else // adding for the first time
122 {
123 StickInfo info("", stick);
124 myDatabase.emplace(stick->name, info);
125 setStickDefaultMapping(stick->ID, Event::NoType, EventMode::kEmulationMode, true);
126 setStickDefaultMapping(stick->ID, Event::NoType, EventMode::kMenuMode, true);
127 }
128
129 return stick->ID;
130}
131
132// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
133bool PhysicalJoystickHandler::remove(int id)
134{
135 // When a joystick is removed, we delete the actual joystick object but
136 // remember its mapping, since it will eventually be saved to settings
137
138 // Sticks that are removed must have initially been added
139 // So we use the 'active' joystick list to access them
140 try
141 {
142 PhysicalJoystickPtr stick = mySticks.at(id);
143
144 auto it = myDatabase.find(stick->name);
145 if(it != myDatabase.end() && it->second.joy == stick)
146 {
147 ostringstream buf;
148 buf << "Removed joystick " << mySticks[id]->ID << ":" << endl
149 << " " << mySticks[id]->about() << endl;
150 Logger::info(buf.str());
151
152 // Remove joystick, but remember mapping
153 it->second.mapping = stick->getMap();
154 it->second.joy = nullptr;
155 mySticks.erase(id);
156
157 return true;
158 }
159 }
160 catch(const std::out_of_range&)
161 {
162 // fall through to indicate remove failed
163 }
164
165 return false;
166}
167
168// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
169bool PhysicalJoystickHandler::remove(const string& name)
170{
171 auto it = myDatabase.find(name);
172 if(it != myDatabase.end() && it->second.joy == nullptr)
173 {
174 myDatabase.erase(it);
175 return true;
176 }
177 return false;
178}
179
180// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
181void PhysicalJoystickHandler::mapStelladaptors(const string& saport)
182{
183 // saport will have two values:
184 // 'lr' means treat first valid adaptor as left port, second as right port
185 // 'rl' means treat first valid adaptor as right port, second as left port
186 // We know there will be only two such devices (at most), since the logic
187 // in setupJoysticks take care of that
188 int saCount = 0;
189 int saOrder[NUM_PORTS] = { 1, 2 };
190 if(BSPF::equalsIgnoreCase(saport, "rl"))
191 {
192 saOrder[0] = 2; saOrder[1] = 1;
193 }
194
195 for(auto& stick: mySticks)
196 {
197 // remove previously added emulated ports
198 size_t pos = stick.second->name.find(" (emulates ");
199
200 if(pos != std::string::npos)
201 stick.second->name.erase(pos);
202
203 if(BSPF::startsWithIgnoreCase(stick.second->name, "Stelladaptor"))
204 {
205 if(saOrder[saCount] == 1)
206 {
207 stick.second->name += " (emulates left joystick port)";
208 stick.second->type = PhysicalJoystick::JT_STELLADAPTOR_LEFT;
209 }
210 else if(saOrder[saCount] == 2)
211 {
212 stick.second->name += " (emulates right joystick port)";
213 stick.second->type = PhysicalJoystick::JT_STELLADAPTOR_RIGHT;
214 }
215 saCount++;
216 }
217 else if(BSPF::startsWithIgnoreCase(stick.second->name, "2600-daptor"))
218 {
219 if(saOrder[saCount] == 1)
220 {
221 stick.second->name += " (emulates left joystick port)";
222 stick.second->type = PhysicalJoystick::JT_2600DAPTOR_LEFT;
223 }
224 else if(saOrder[saCount] == 2)
225 {
226 stick.second->name += " (emulates right joystick port)";
227 stick.second->type = PhysicalJoystick::JT_2600DAPTOR_RIGHT;
228 }
229 saCount++;
230 }
231 }
232 myOSystem.settings().setValue("saport", saport);
233}
234
235// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
236// Depending on parameters, this method does the following:
237// 1. update all events with default (event == Event::NoType, updateDefault == true)
238// 2. reset all events to default (event == Event::NoType, updateDefault == false)
239// 3. reset one event to default (event != Event::NoType)
240void PhysicalJoystickHandler::setDefaultAction(const PhysicalJoystickPtr& j,
241 EventMapping map, Event::Type event,
242 EventMode mode, bool updateDefaults)
243{
244 // If event is 'NoType', erase and reset all mappings
245 // Otherwise, only reset the given event
246 bool eraseAll = !updateDefaults && (event == Event::NoType);
247
248 if(updateDefaults)
249 {
250 // if there is no existing mapping for the event or
251 // the default mapping for the event is unused, set default key for event
252 if(j->joyMap.getEventMapping(map.event, mode).size() == 0 ||
253 !j->joyMap.check(mode, map.button, map.axis, map.adir, map.hat, map.hdir))
254 {
255 j->joyMap.add(map.event, mode, map.button, map.axis, map.adir, map.hat, map.hdir);
256 }
257 }
258 else if(eraseAll || map.event == event)
259 {
260 // TODO: allow for multiple defaults
261 //j->joyMap.eraseEvent(map.event, mode);
262 j->joyMap.add(map.event, mode, map.button, map.axis, map.adir, map.hat, map.hdir);
263 }
264
265}
266
267// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
268void PhysicalJoystickHandler::setStickDefaultMapping(int stick, Event::Type event,
269 EventMode mode, bool updateDefaults)
270{
271 const PhysicalJoystickPtr j = joy(stick);
272
273 if(j)
274 {
275 switch (mode)
276 {
277 case EventMode::kEmulationMode:
278 if((stick % 2) == 0) // even sticks
279 {
280 // put all controller events into their own mode's mappings
281 for (const auto& item : DefaultLeftJoystickMapping)
282 setDefaultAction(j, item, event, EventMode::kJoystickMode, updateDefaults);
283 for (const auto& item : DefaultLeftPaddlesMapping)
284 setDefaultAction(j, item, event, EventMode::kPaddlesMode, updateDefaults);
285 for (const auto& item : DefaultLeftKeypadMapping)
286 setDefaultAction(j, item, event, EventMode::kKeypadMode, updateDefaults);
287 }
288 else // odd sticks
289 {
290 // put all controller events into their own mode's mappings
291 for (const auto& item : DefaultRightJoystickMapping)
292 setDefaultAction(j, item, event, EventMode::kJoystickMode, updateDefaults);
293 for (const auto& item : DefaultRightPaddlesMapping)
294 setDefaultAction(j, item, event, EventMode::kPaddlesMode, updateDefaults);
295 for (const auto& item : DefaultRightKeypadMapping)
296 setDefaultAction(j, item, event, EventMode::kKeypadMode, updateDefaults);
297 }
298 for(const auto& item : DefaultCommonMapping)
299 setDefaultAction(j, item, event, EventMode::kCommonMode, updateDefaults);
300 // update running emulation mapping too
301 enableEmulationMappings();
302 break;
303
304 case EventMode::kMenuMode:
305 for (const auto& item : DefaultMenuMapping)
306 setDefaultAction(j, item, event, EventMode::kMenuMode, updateDefaults);
307 break;
308
309 default:
310 break;
311 }
312 }
313}
314
315// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
316void PhysicalJoystickHandler::setDefaultMapping(Event::Type event, EventMode mode)
317{
318 eraseMapping(event, mode);
319 for (auto& i : mySticks)
320 setStickDefaultMapping(i.first, event, mode);
321}
322
323// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
324void PhysicalJoystickHandler::defineControllerMappings(const Controller::Type type, Controller::Jack port)
325{
326 // determine controller events to use
327 switch(type)
328 {
329 case Controller::Type::Keyboard:
330 case Controller::Type::KidVid:
331 if(port == Controller::Jack::Left)
332 myLeftMode = EventMode::kKeypadMode;
333 else
334 myRightMode = EventMode::kKeypadMode;
335 break;
336
337 case Controller::Type::Paddles:
338 case Controller::Type::PaddlesIAxDr:
339 case Controller::Type::PaddlesIAxis:
340 if(port == Controller::Jack::Left)
341 myLeftMode = EventMode::kPaddlesMode;
342 else
343 myRightMode = EventMode::kPaddlesMode;
344 break;
345
346 case Controller::Type::CompuMate:
347 myLeftMode = myRightMode = EventMode::kCompuMateMode;
348 break;
349
350 default:
351 // let's use joystick then
352 if(port == Controller::Jack::Left)
353 myLeftMode = EventMode::kJoystickMode;
354 else
355 myRightMode = EventMode::kJoystickMode;
356 }
357}
358
359// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
360void PhysicalJoystickHandler::enableEmulationMappings()
361{
362 for (auto& stick : mySticks)
363 {
364 const PhysicalJoystickPtr j = stick.second;
365
366 // start from scratch and enable common mappings
367 j->joyMap.eraseMode(EventMode::kEmulationMode);
368 }
369
370 enableCommonMappings();
371
372 // enable right mode first, so that in case of mapping clashes the left controller has preference
373 switch (myRightMode)
374 {
375 case EventMode::kPaddlesMode:
376 enableMappings(RightPaddlesEvents, EventMode::kPaddlesMode);
377 break;
378
379 case EventMode::kKeypadMode:
380 enableMappings(RightKeypadEvents, EventMode::kKeypadMode);
381 break;
382
383 default:
384 enableMappings(RightJoystickEvents, EventMode::kJoystickMode);
385 break;
386 }
387
388 switch (myLeftMode)
389 {
390 case EventMode::kPaddlesMode:
391 enableMappings(LeftPaddlesEvents, EventMode::kPaddlesMode);
392 break;
393
394 case EventMode::kKeypadMode:
395 enableMappings(LeftKeypadEvents, EventMode::kKeypadMode);
396 break;
397
398 default:
399 enableMappings(LeftJoystickEvents, EventMode::kJoystickMode);
400 break;
401 }
402}
403
404// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
405void PhysicalJoystickHandler::enableCommonMappings()
406{
407 for (int i = Event::NoType + 1; i < Event::LastType; i++)
408 {
409 Event::Type event = static_cast<Event::Type>(i);
410
411 if(isCommonEvent(event))
412 enableMapping(event, EventMode::kCommonMode);
413 }
414}
415
416// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
417void PhysicalJoystickHandler::enableMappings(const Event::EventSet events, EventMode mode)
418{
419 for (const auto& event : events)
420 enableMapping(event, mode);
421}
422
423// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
424void PhysicalJoystickHandler::enableMapping(const Event::Type event, EventMode mode)
425{
426 // copy from controller mode into emulation mode
427 for (auto& stick : mySticks)
428 {
429 const PhysicalJoystickPtr j = stick.second;
430
431 JoyMap::JoyMappingArray joyMappings = j->joyMap.getEventMapping(event, mode);
432
433 for (const auto& mapping : joyMappings)
434 j->joyMap.add(event, EventMode::kEmulationMode, mapping.button, mapping.axis, mapping.adir);
435 }
436}
437
438// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
439EventMode PhysicalJoystickHandler::getEventMode(const Event::Type event, const EventMode mode) const
440{
441 if(mode == EventMode::kEmulationMode)
442 {
443 if(isJoystickEvent(event))
444 return EventMode::kJoystickMode;
445
446 if(isPaddleEvent(event))
447 return EventMode::kPaddlesMode;
448
449 if(isKeypadEvent(event))
450 return EventMode::kKeypadMode;
451
452 if(isCommonEvent(event))
453 return EventMode::kCommonMode;
454 }
455
456 return mode;
457}
458
459// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
460bool PhysicalJoystickHandler::isJoystickEvent(const Event::Type event) const
461{
462 return LeftJoystickEvents.find(event) != LeftJoystickEvents.end()
463 || RightJoystickEvents.find(event) != RightJoystickEvents.end();
464}
465
466// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
467bool PhysicalJoystickHandler::isPaddleEvent(const Event::Type event) const
468{
469 return LeftPaddlesEvents.find(event) != LeftPaddlesEvents.end()
470 || RightPaddlesEvents.find(event) != RightPaddlesEvents.end();
471}
472
473// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
474bool PhysicalJoystickHandler::isKeypadEvent(const Event::Type event) const
475{
476 return LeftKeypadEvents.find(event) != LeftKeypadEvents.end()
477 || RightKeypadEvents.find(event) != RightKeypadEvents.end();
478}
479
480// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
481bool PhysicalJoystickHandler::isCommonEvent(const Event::Type event) const
482{
483 return !(isJoystickEvent(event) || isPaddleEvent(event) || isKeypadEvent(event));
484}
485
486// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
487void PhysicalJoystickHandler::eraseMapping(Event::Type event, EventMode mode)
488{
489 // If event is 'NoType', erase and reset all mappings
490 // Otherwise, only reset the given event
491 if(event == Event::NoType)
492 {
493 for (auto& stick : mySticks)
494 {
495 stick.second->eraseMap(mode); // erase all events
496 if(mode == EventMode::kEmulationMode)
497 {
498 stick.second->eraseMap(EventMode::kCommonMode);
499 stick.second->eraseMap(EventMode::kJoystickMode);
500 stick.second->eraseMap(EventMode::kPaddlesMode);
501 stick.second->eraseMap(EventMode::kKeypadMode);
502 }
503 }
504 }
505 else
506 {
507 for (auto& stick : mySticks)
508 {
509 stick.second->eraseEvent(event, mode); // only reset the specific event
510 stick.second->eraseEvent(event, getEventMode(event, mode));
511 }
512 }
513}
514
515// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
516void PhysicalJoystickHandler::saveMapping()
517{
518 // Save the joystick mapping hash table, making sure to update it with
519 // any changes that have been made during the program run
520 ostringstream joybuf;
521
522 for(const auto& i: myDatabase)
523 {
524 const string& map = i.second.joy ? i.second.joy->getMap() : i.second.mapping;
525 if(map != "")
526 joybuf << CTRL_DELIM << map;
527 }
528 myOSystem.settings().setValue("joymap", joybuf.str());
529}
530
531// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
532string PhysicalJoystickHandler::getMappingDesc(Event::Type event, EventMode mode) const
533{
534 ostringstream buf;
535 EventMode evMode = getEventMode(event, mode);
536
537 for(const auto& s: mySticks)
538 {
539 uInt32 stick = s.first;
540 const PhysicalJoystickPtr j = s.second;
541
542 if(j)
543 {
544 //Joystick mapping / labeling
545 if(j->joyMap.getEventMapping(event, evMode).size())
546 {
547 if(buf.str() != "")
548 buf << ", ";
549 buf << j->joyMap.getEventMappingDesc(stick, event, evMode);
550 }
551 }
552 }
553 return buf.str();
554}
555
556// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
557bool PhysicalJoystickHandler::addJoyMapping(Event::Type event, EventMode mode, int stick,
558 int button, JoyAxis axis, JoyDir adir)
559{
560 const PhysicalJoystickPtr j = joy(stick);
561
562 if(j && event < Event::LastType &&
563 button >= JOY_CTRL_NONE && button < j->numButtons &&
564 axis >= JoyAxis::NONE && int(axis) < j->numAxes)
565 {
566 EventMode evMode = getEventMode(event, mode);
567
568 // This confusing code is because each axis has two associated values,
569 // but analog events only affect one of the axis.
570 if(Event::isAnalog(event))
571 {
572 j->joyMap.add(event, evMode, button, axis, JoyDir::ANALOG);
573 // update running emulation mapping too
574 j->joyMap.add(event, EventMode::kEmulationMode, button, axis, JoyDir::ANALOG);
575 }
576 else
577 {
578 j->joyMap.add(event, evMode, button, axis, adir);
579 // update running emulation mapping too
580 j->joyMap.add(event, EventMode::kEmulationMode, button, axis, adir);
581 }
582 return true;
583 }
584 return false;
585}
586
587// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
588bool PhysicalJoystickHandler::addJoyHatMapping(Event::Type event, EventMode mode, int stick,
589 int button, int hat, JoyHatDir hdir)
590{
591 const PhysicalJoystickPtr j = joy(stick);
592
593 if(j && event < Event::LastType &&
594 button >= JOY_CTRL_NONE && button < j->numButtons &&
595 hat >= 0 && hat < j->numHats && hdir != JoyHatDir::CENTER)
596 {
597 j->joyMap.add(event, getEventMode(event, mode), button, hat, hdir);
598 // update running emulation mapping too
599 j->joyMap.add(event, EventMode::kEmulationMode, button, hat, hdir);
600 return true;
601 }
602 return false;
603}
604
605// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
606void PhysicalJoystickHandler::handleAxisEvent(int stick, int axis, int value)
607{
608 const PhysicalJoystickPtr j = joy(stick);
609
610 if(j)
611 {
612 int button = j->buttonLast[stick];
613
614 if(myHandler.state() == EventHandlerState::EMULATION)
615 {
616 // Check for analog events, which are handled differently
617 // A value change lower than ~90% indicates analog input
618 if(abs(j->axisLastValue[axis] - value) < 30000)
619 {
620 Event::Type eventAxisAnalog = j->joyMap.get(EventMode::kEmulationMode, button, JoyAxis(axis), JoyDir::ANALOG);
621
622 myHandler.handleEvent(eventAxisAnalog, value);
623 }
624 else
625 {
626 // Otherwise, we assume the event is digital
627 // Every axis event has two associated values, negative and positive
628 Event::Type eventAxisNeg = j->joyMap.get(EventMode::kEmulationMode, button, JoyAxis(axis), JoyDir::NEG);
629 Event::Type eventAxisPos = j->joyMap.get(EventMode::kEmulationMode, button, JoyAxis(axis), JoyDir::POS);
630
631 if(value > Joystick::deadzone())
632 myHandler.handleEvent(eventAxisPos);
633 else if(value < -Joystick::deadzone())
634 myHandler.handleEvent(eventAxisNeg);
635 else
636 {
637 // Treat any deadzone value as zero
638 value = 0;
639
640 // Now filter out consecutive, similar values
641 // (only pass on the event if the state has changed)
642 if(j->axisLastValue[axis] != value)
643 {
644 // Turn off both events, since we don't know exactly which one
645 // was previously activated.
646 myHandler.handleEvent(eventAxisNeg, false);
647 myHandler.handleEvent(eventAxisPos, false);
648 }
649 }
650 }
651 j->axisLastValue[axis] = value;
652 }
653 else if(myHandler.hasOverlay())
654 {
655 // First, clamp the values to simulate digital input
656 // (the only thing that the underlying code understands)
657 if(value > Joystick::deadzone())
658 value = 32000;
659 else if(value < -Joystick::deadzone())
660 value = -32000;
661 else
662 value = 0;
663
664 // Now filter out consecutive, similar values
665 // (only pass on the event if the state has changed)
666 if(value != j->axisLastValue[axis])
667 {
668#ifdef GUI_SUPPORT
669 myHandler.overlay().handleJoyAxisEvent(stick, JoyAxis(axis), convertAxisValue(value), button);
670#endif
671 j->axisLastValue[axis] = value;
672 }
673 }
674 }
675}
676
677// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
678void PhysicalJoystickHandler::handleBtnEvent(int stick, int button, bool pressed)
679{
680 const PhysicalJoystickPtr j = joy(stick);
681
682 if(j)
683 {
684 j->buttonLast[stick] = pressed ? button : JOY_CTRL_NONE;
685
686 // Handle buttons which switch eventhandler state
687 if(pressed && myHandler.changeStateByEvent(j->joyMap.get(EventMode::kEmulationMode, button)))
688 return;
689
690 // Determine which mode we're in, then send the event to the appropriate place
691 if(myHandler.state() == EventHandlerState::EMULATION)
692 myHandler.handleEvent(j->joyMap.get(EventMode::kEmulationMode, button), pressed);
693#ifdef GUI_SUPPORT
694 else if(myHandler.hasOverlay())
695 myHandler.overlay().handleJoyBtnEvent(stick, button, pressed);
696#endif
697 }
698}
699
700// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
701void PhysicalJoystickHandler::handleHatEvent(int stick, int hat, int value)
702{
703 // Preprocess all hat events, converting to Stella JoyHatDir type
704 // Generate multiple equivalent hat events representing combined direction
705 // when we get a diagonal hat event
706
707 const PhysicalJoystickPtr j = joy(stick);
708
709 if(j)
710 {
711 const int button = j->buttonLast[stick];
712
713 if(myHandler.state() == EventHandlerState::EMULATION)
714 {
715 myHandler.handleEvent(j->joyMap.get(EventMode::kEmulationMode, button, hat, JoyHatDir::UP),
716 value & EVENT_HATUP_M);
717 myHandler.handleEvent(j->joyMap.get(EventMode::kEmulationMode, button, hat, JoyHatDir::RIGHT),
718 value & EVENT_HATRIGHT_M);
719 myHandler.handleEvent(j->joyMap.get(EventMode::kEmulationMode, button, hat, JoyHatDir::DOWN),
720 value & EVENT_HATDOWN_M);
721 myHandler.handleEvent(j->joyMap.get(EventMode::kEmulationMode, button, hat, JoyHatDir::LEFT),
722 value & EVENT_HATLEFT_M);
723 }
724#ifdef GUI_SUPPORT
725 else if(myHandler.hasOverlay())
726 {
727 if(value == EVENT_HATCENTER_M)
728 myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHatDir::CENTER, button);
729 else
730 {
731 if(value & EVENT_HATUP_M)
732 myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHatDir::UP, button);
733 if(value & EVENT_HATRIGHT_M)
734 myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHatDir::RIGHT, button);
735 if(value & EVENT_HATDOWN_M)
736 myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHatDir::DOWN, button);
737 if(value & EVENT_HATLEFT_M)
738 myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHatDir::LEFT, button);
739 }
740 }
741#endif
742 }
743}
744
745// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
746VariantList PhysicalJoystickHandler::database() const
747{
748 VariantList db;
749 for(const auto& i: myDatabase)
750 VarList::push_back(db, i.first, i.second.joy ? i.second.joy->ID : -1);
751
752 return db;
753}
754
755// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
756ostream& operator<<(ostream& os, const PhysicalJoystickHandler& jh)
757{
758 os << "---------------------------------------------------------" << endl
759 << "joy database:" << endl;
760 for(const auto& i: jh.myDatabase)
761 os << i.first << endl << i.second << endl << endl;
762
763 os << "---------------------" << endl
764 << "joy active:" << endl;
765 for(const auto& i: jh.mySticks)
766 os << i.first << ": " << *i.second << endl;
767 os << "---------------------------------------------------------"
768 << endl << endl << endl;
769
770 return os;
771}
772
773// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
774PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultLeftJoystickMapping = {
775 // Left joystick (assume buttons zero..two)
776 {Event::JoystickZeroFire, 0},
777 {Event::JoystickZeroFire5, 1},
778 {Event::JoystickZeroFire9, 2},
779 // Left joystick left/right directions
780 {Event::JoystickZeroLeft, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG},
781 {Event::JoystickZeroRight, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
782 // Left joystick up/down directions
783 {Event::JoystickZeroUp, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::NEG},
784 {Event::JoystickZeroDown, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::POS},
785 // Left joystick left/right directions (assume hat 0)
786 {Event::JoystickZeroLeft, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHatDir::LEFT},
787 {Event::JoystickZeroRight, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHatDir::RIGHT},
788 // Left joystick up/down directions (assume hat 0)
789 {Event::JoystickZeroUp, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHatDir::UP},
790 {Event::JoystickZeroDown, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHatDir::DOWN},
791};
792
793// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
794PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultRightJoystickMapping = {
795 // Right joystick (assume buttons zero..two)
796 {Event::JoystickOneFire, 0},
797 {Event::JoystickOneFire5, 1},
798 {Event::JoystickOneFire9, 2},
799 // Right joystick left/right directions
800 {Event::JoystickOneLeft, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG},
801 {Event::JoystickOneRight, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
802 // Right joystick up/down directions
803 {Event::JoystickOneUp, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::NEG},
804 {Event::JoystickOneDown, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::POS},
805 // Right joystick left/right directions (assume hat 0)
806 {Event::JoystickOneLeft, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHatDir::LEFT},
807 {Event::JoystickOneRight, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHatDir::RIGHT},
808 // Right joystick up/down directions (assume hat 0)
809 {Event::JoystickOneUp, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHatDir::UP},
810 {Event::JoystickOneDown, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHatDir::DOWN},
811};
812
813// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
814PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultLeftPaddlesMapping = {
815 {Event::PaddleZeroAnalog, JOY_CTRL_NONE, JoyAxis::X, JoyDir::ANALOG},
816 // Current code does NOT allow digital and anlog events on the same axis at the same time
817 //{Event::PaddleZeroDecrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
818 //{Event::PaddleZeroIncrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG},
819 {Event::PaddleZeroFire, 0},
820 {Event::PaddleOneAnalog, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::ANALOG},
821 // Current code does NOT allow digital and anlog events on the same axis at the same
822 //{Event::PaddleOneDecrease, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::POS},
823 //{Event::PaddleOneIncrease, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::NEG},
824 {Event::PaddleOneFire, 1},
825};
826
827// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
828PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultRightPaddlesMapping = {
829 {Event::PaddleTwoAnalog, JOY_CTRL_NONE, JoyAxis::X, JoyDir::ANALOG},
830 // Current code does NOT allow digital and anlog events on the same axis at the same
831 //{Event::PaddleTwoDecrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
832 //{Event::PaddleTwoIncrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG},
833 {Event::PaddleTwoFire, 0},
834 {Event::PaddleThreeAnalog, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::ANALOG},
835 // Current code does NOT allow digital and anlog events on the same axis at the same
836 //{Event::PaddleThreeDecrease,JOY_CTRL_NONE, JoyAxis::Y, JoyDir::POS},
837 //{Event::PaddleThreeIncrease,JOY_CTRL_NONE, JoyAxis::Y, JoyDir::NEG},
838 {Event::PaddleThreeFire, 1},
839};
840
841// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
842PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultLeftKeypadMapping = {
843 {Event::KeyboardZero1, 0},
844 {Event::KeyboardZero2, 1},
845 {Event::KeyboardZero3, 2},
846 {Event::KeyboardZero4, 3},
847 {Event::KeyboardZero5, 4},
848 {Event::KeyboardZero6, 5},
849 {Event::KeyboardZero7, 6},
850 {Event::KeyboardZero8, 7},
851 {Event::KeyboardZero9, 8},
852 {Event::KeyboardZeroStar, 9},
853 {Event::KeyboardZero0, 10},
854 {Event::KeyboardZeroPound, 11},
855};
856
857// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
858PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultRightKeypadMapping = {
859 {Event::KeyboardOne1, 0},
860 {Event::KeyboardOne2, 1},
861 {Event::KeyboardOne3, 2},
862 {Event::KeyboardOne4, 3},
863 {Event::KeyboardOne5, 4},
864 {Event::KeyboardOne6, 5},
865 {Event::KeyboardOne7, 6},
866 {Event::KeyboardOne8, 7},
867 {Event::KeyboardOne9, 8},
868 {Event::KeyboardOneStar, 9},
869 {Event::KeyboardOne0, 10},
870 {Event::KeyboardOnePound, 11},
871};
872
873// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
874PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultCommonMapping = {
875 // valid for all joysticks
876//#if defined(RETRON77)
877 {Event::CmdMenuMode, 2},
878 {Event::OptionsMenuMode, 3},
879 {Event::ExitMode, 4},
880 {Event::RewindPause, 5},
881 {Event::ConsoleSelect, 7},
882 {Event::ConsoleReset, 8},
883//#endif
884};
885
886// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
887PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultMenuMapping = {
888 // valid for all joysticks
889 {Event::UISelect, 0},
890 {Event::UIOK, 1},
891 {Event::UITabNext, 2},
892 {Event::UITabPrev, 3},
893 {Event::UICancel, 5},
894
895 {Event::UINavPrev, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG},
896 {Event::UITabPrev, 0, JoyAxis::X, JoyDir::NEG},
897 {Event::UINavNext, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
898 {Event::UITabNext, 0, JoyAxis::X, JoyDir::POS},
899 {Event::UIUp, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::NEG},
900 {Event::UIDown, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::POS},
901
902 {Event::UINavPrev, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHatDir::LEFT},
903 {Event::UINavNext, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHatDir::RIGHT},
904 {Event::UIUp, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHatDir::UP},
905 {Event::UIDown, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHatDir::DOWN},
906};
907