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 | |
30 | static constexpr char CTRL_DELIM = '^'; |
31 | |
32 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
33 | PhysicalJoystickHandler::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
59 | int 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
133 | bool 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
169 | bool 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
181 | void 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) |
240 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
268 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
316 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
324 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
360 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
405 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
417 | void PhysicalJoystickHandler::enableMappings(const Event::EventSet events, EventMode mode) |
418 | { |
419 | for (const auto& event : events) |
420 | enableMapping(event, mode); |
421 | } |
422 | |
423 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
424 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
439 | EventMode 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
460 | bool PhysicalJoystickHandler::isJoystickEvent(const Event::Type event) const |
461 | { |
462 | return LeftJoystickEvents.find(event) != LeftJoystickEvents.end() |
463 | || RightJoystickEvents.find(event) != RightJoystickEvents.end(); |
464 | } |
465 | |
466 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
467 | bool PhysicalJoystickHandler::isPaddleEvent(const Event::Type event) const |
468 | { |
469 | return LeftPaddlesEvents.find(event) != LeftPaddlesEvents.end() |
470 | || RightPaddlesEvents.find(event) != RightPaddlesEvents.end(); |
471 | } |
472 | |
473 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
474 | bool PhysicalJoystickHandler::isKeypadEvent(const Event::Type event) const |
475 | { |
476 | return LeftKeypadEvents.find(event) != LeftKeypadEvents.end() |
477 | || RightKeypadEvents.find(event) != RightKeypadEvents.end(); |
478 | } |
479 | |
480 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
481 | bool PhysicalJoystickHandler::isCommonEvent(const Event::Type event) const |
482 | { |
483 | return !(isJoystickEvent(event) || isPaddleEvent(event) || isKeypadEvent(event)); |
484 | } |
485 | |
486 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
487 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
516 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
532 | string 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
557 | bool 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
588 | bool 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
606 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
678 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
701 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
746 | VariantList 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
756 | ostream& 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
774 | PhysicalJoystickHandler::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
794 | PhysicalJoystickHandler::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
814 | PhysicalJoystickHandler::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
828 | PhysicalJoystickHandler::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
842 | PhysicalJoystickHandler::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
858 | PhysicalJoystickHandler::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
874 | PhysicalJoystickHandler::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
887 | PhysicalJoystickHandler::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 | |