iOS 7 Game Controller Support #244

alexcash opened this Issue Oct 20, 2013 · 12 comments


None yet

5 participants


With iOS 7, Apple has provided an API for Game controller support. It'd be really cool to have it in Ejecta, and I know that would be quite an undertaking. However, I'd like to get the conversation started.

On the Obj-C side, it looks like you can either read the values of each input at any time you'd like, or you can receive callbacks when the state of an input changes. I'm not sure which would be preferable on the JS side, but I do know that games must respond to an pause callback from the controller.

This would require us going back to Xcode 5 and iOS 7 base SDK. It might also require a bit of fancy footwork to detect existence of the API and provide good messaging back to the JS side.

I'd be very interested in helping out with a pull request if we can agree on a clean API model.


HTML5 has a Gamepad API, so if we do this in Ejecta, I think it should use the same API (or something very close). See:

Somebody wants to sponsor a Logitech Powershell or the Moga Ace? :)


Closing for lack of interest.

@phoboslab phoboslab closed this Feb 1, 2014
bnolan commented Jan 15, 2015

I'm not saying this should be reopened, just saying I'm interested in it. And yeah, it should match the html5 gamepad API.


Any chance this request gets some new life now that Ejecta and tvOS are a thing?


Well, my offer still stands. Sponsor a GamePad (and a new AppleTV maybe? :)) and I'll do it.


Let's figure out how to do this. I don't have an issue sponsoring a controller.


Just ordered.


Should be here on Tuesday. I'll probably find some time over the next weekend to implement it.

If you want to contribute:

Bitcoin: 1m8mc4p98VjhGiLWoY6ri8Rz7no6MkfgD

Thanks :)

@phoboslab phoboslab reopened this Nov 8, 2015
@phoboslab phoboslab closed this in 448720d Nov 11, 2015

This supports Gamepads on iOS and tvOS. It closely follows the W3C Gamepad API Spec - including the "standard" mapping.

On tvOS, the TV Remote is mapped to the appropriate buttons, the touchpad is mapped to an analog stick and(!) a D-Pad. The one addition to the W3C spec is the gamepad.exitOnMenuPress flag. See here for the fascinating details about why this flag exists.

Example, as shown in this Demo Video:

// Setup canvas with retina res
var canvas = document.getElementById('canvas');
canvas.width = window.innerWidth * window.devicePixelRatio;
canvas.height = window.innerHeight * window.devicePixelRatio; = window.innerWidth + "px"; = window.innerHeight + "px";

var ctx = canvas.getContext("2d");

// The gamepad we draw below is about 1280px wide. We just
// scale the ctx so that it fills the actual screen width

var scale = canvas.width / 1280;
ctx.scale(scale, scale);
ctx.font = '32px Helvetica';
ctx.fillStyle = '#fff';
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;

var drawButton = function(button, x, y) {
    ctx.fillRect(x - 24, y + 24 - 48 * button.value, 48, 48 * button.value );

    ctx.strokeStyle = button.pressed ? '#0f0' : '#ccc';
    ctx.strokeRect(x - 24, y - 24, 48, 48);
    ctx.strokeStyle = '#fff';

var drawAnalogStick = function( axisX, axisY, x, y ) {
    ctx.arc(x, y, 64, 0, 2*Math.PI);

    ctx.arc(x + axisX*48, y + axisY*48, 16, 0, 2*Math.PI);

    ctx.clearRect(0, 0, canvas.width/scale, canvas.height/scale);

    // Always use 2nd gamepad for this visualization if it's present. On an
    // Apple TV when the TV Remote and a Game Controller is connected, the Remote
    // will be the first entry in the gamepads array; we want a dedicated
    // Game Controller if there is one.

    var gamepads = navigator.getGamepads();
    var gamepad = gamepads[1] || gamepads[0];

    if( !gamepad ) {
        ctx.fillText('No Gamepads connected', 32, 32);

    ctx.fillText('Using Gamepad: #' + gamepad.index + ' ('')', 32, 32);

    // Button Mappings according to

    drawButton(gamepad.buttons[0], 928, 354); // A
    drawButton(gamepad.buttons[1], 994, 288); // B
    drawButton(gamepad.buttons[2], 862, 288); // X
    drawButton(gamepad.buttons[3], 928, 224); // Y

    drawButton(gamepad.buttons[4], 412, 96); // L1
    drawButton(gamepad.buttons[5], 868, 96); // R1
    drawButton(gamepad.buttons[6], 288, 96); // L2
    drawButton(gamepad.buttons[7], 994, 96); // R2

    drawButton(gamepad.buttons[12], 352, 224); // Up
    drawButton(gamepad.buttons[13], 352, 354); // Down
    drawButton(gamepad.buttons[14], 288, 288); // Left
    drawButton(gamepad.buttons[15], 416, 288); // Right

    drawButton(gamepad.buttons[16], 640, 196); // Menu

    drawAnalogStick(gamepad.axes[0], gamepad.axes[1], 540, 416); // left stick
    drawAnalogStick(gamepad.axes[2], gamepad.axes[3], 736, 416); // right stick

    // You can control whether the MENU button exits your game. Apple will reject your
    // App if it does not. For Gamepads, the B button can also act as a MENU button.
    // However, MENU should only exit the App the in certain cases.

    // The expected behavior is this:
    // > Pauses/resumes gameplay. Returns to previous screen, exits to main game menu,
    // > and/or exits to Apple TV Home screen.
    // ~

    // In any case, judging from other games, you're probably safe to set 'exitOnMenuPress'
    // to false during gameplay and only set it to true when you're on the Title Screen of
    // your game.

    gamepad.exitOnMenuPress = true;
}, 16);
degenet commented Nov 13, 2015

Hi @phoboslab!

Awesome job with the gamepad and tvOS integration. I'm having one issue with the gamepad.exitOnMenuPress and the apple tv remote though. After the following algorithm, setting it to false still exits the game.

To Reproduce:

  1. Run and set gamepad.exitOnMenuPress = false;
  2. Press the Menu button (works as expected :)
  3. Set gamepad.exitOnMenuPress = true and press the Menu button to exit the app.
  4. Reopen the app, and set the gamepad.exitOnMenuPress = false.
  5. Press the Menu button (it exits the app :/ )

I've set breakpoints in XCode to check for the value of exitOnMenuPress inside EJJavaScriptView.m and it is correct every time. For some reason, I believe pressesBegan is still being called in super.

Any ideas?


Weird. Maybe pressesEnded also exits the App under certain conditions!? Can you try adding the following method in EJJavaScriptView.m and see if it makes a difference?

-(void)pressesEnded:(NSSet*)presses withEvent:(UIPressesEvent *)event {
    if( exitOnMenuPress && ((UIPress *)presses.anyObject).type == UIPressTypeMenu ) {
        return [super pressesEnded:presses withEvent:event];

Just did a quick test and this seems to indeed fix the issue. It's weird that it reacts on pressesBegan one time and pressesEnded on another.

Thanks for reporting!

degenet commented Nov 14, 2015

Seems like adding the pressesEnded handler does the trick. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment