Here's how I envision the system working:
Every controller gets mapped to a 360 controller. If the controller is missing buttons, they can be emulated using gestures. Then, this standardized controller is mapped to individual consoles' controllers. Using the 360 controller as an intermediate simplifies the process of mapping X controllers to Y consoles; unknown controllers only need to be mapped once to support all consoles. I also plan to allow console mappings to be overridden on an individual basis (eventually).
The gesture recognition actually takes place in two levels: once to map raw driver elements (buttons, hats, axes) to physical 360 elements (buttons, triggers, hats, analog sticks, accelerometers), and once to map physical 360 elements to higher-level gestures (spinning analog sticks, accelerometer motions, etc).
Here's an example: Let's say joystick.xml allows multi-presses, and pressing Start + Select exits the game via
PlayerControl(Stop). However, the controller is N64 and only has a start button and no select. If select is virtually mapped to a multi-press gesture (let's say A + B), then the game can be exited by pressing A + B + Start. Kodi won't see A or B being pressed - the lower level turns this into a Select before Kodi can look up the gesture in the joystick.xml keymap. Kodi sees Select + Start being pressed, and turns this into an "Exit-game" command.
Another example: If select is mapped to double-pressing A, then the game can be exited by pressing A then quickly A + Start. Because the double-press emits a Select, both A presses are absorbed by the lower-level and Kodi doesn't detect any A presses - only Select.
Does this two-level gesture detection scheme make sense? I think it's necessary to separate the databases into controller->360 mapping and 360->console mapping.
If anyone is willing to put on their thinking caps, I'd like to identify problems while I'm still close to the conceptual phase so I don't have to re-write thousands of lines of code during testing. Here's some thoughts:
- In the above example, Kodi sees Start + Select instead of A + B + Start. If the configuration utility displays "Start + Select", that will be very confusing as the N64 has no select. This can be fixed by exposing the lower-level button mapping to the configuration utility so it can properly display "A + B + Start" instead.
- If Select is mapped to holding Start or double-pressing Start, then the game can't be exiting using Start + Select, obviously.
- Response times will be slightly delayed for overloaded buttons (and only overloaded buttons). If double-pressing Start maps to Select, then "Start" can't be emitted until we're sure the user isn't attempting a Select. Double-press timeout is 500ms ATM, so if the user taps/holds Start, we won't know that they want Start instead of Select until the timeout lapses.
Tapping example: At t=0, Start is pressed. At t=100ms, Start is released. At t=501ms, Select is no longer possible, so a "Start pressed" is emitted. Also at t=501ms, a "Start released" is emitted. To Kodi and emulators, it will look like Start is held for 0ms instead of 100ms, but any action registered to Start will still be fired (albeit 501ms late).
Holding example: At t=0, Start is pressed. At t=1,000ms, Start is released (held for 1s). The "Start pressed" event is emitted at t=501ms and the "Start released" event is emitted at t=1,000ms. To Kodi and emulators, it will look like Start is held for 499ms instead of 1,000ms and the Start action will be fired 501ms late. However, if "Hold Start for 1,000ms" is mapped to an action in joystick.xml, Kodi is smart enough to emit the action at t=1,000ms instead of t=1,501ms.
- I don't think the response-time delays caused by the lower-level mapping is a problem. Most games use start to open a menu or pause, and opening a menu 1/2 second late is a small price to pay for a free Select button. I expect users will overload delay-tolerant buttons instead of primary gameplay ones like A and B.
- Multi-press buttons must be pressed within 80ms (tentatively) of each other. Therefore, if response time is an issue, use a multi-press gesture instead of a hold/double-press. In this case, overloaded (and only overloaded) actions will be fired only 80ms late.
- Things might get confusing if a combination of lower-level holds/double-presses are combined with higher-level holds/double-presses. ATM no holds/double-presses are recognized in the higher level.