Apple MFi gamepad support?

Any chance this could happen in the future soon? I have many people ask me to support this in my games.

It’s been addes a few days ago to SDL so I’m pretty sure it will soon make its way to openfl too :wink:

Awesome! I’ll be waiting for this. :smile: Any ETA on it?

We’re on a somewhat older development version of SDL. When trying the latest, I found that iOS was not running at retina resolution. When I tried the HIGHDPI hint, it ran at retina resolution, but our rendering was only in one corner of the screen. It then reset when rotating.

Either there’s a bug that crept into SDL here, or this is an area where we should (?) be using the library a little differently to work on iOS. If we can figure this one out, we can update to a new development version and include MFi support :slight_smile:

On a side note I’m merging my changes for tvOS to the default repository of SDL. If they get accepted we could pull all that stuff over.

But first we have to make sure that Retina is working fine. How are you testing it @singmajesty ? Do you have a test project for it?

You can use a development copy of Lime, then do the following:

cd lime/project/lib/sdl
git checkout master

Then you should be able to do a clean rebuild for iOS (as SDL changes tend to require a clean)

lime rebuild ios -clean

Try targeting iOS, if it works as it did for me, it will not be retina.

If you want to try the HIGHDPI flag, you can edit “lime/project/src/backend/sdl/SDLWindow.cpp”

Line 28 looks like this:

//sdlFlags |= SDL_WINDOW_ALLOW_HIGHDPI;

Try uncommenting that flag, rebuild (shouldn’t need to -clean this time) and try again. It was showing at retina resolution, but only the bottom-left corner for me, then when I rotated I think it went back to not being retina again

The SDL code for Lime is in the “backend/sdl” section of the code, shouldn’t be complicated to understand if you try making changes, just rebuild after you make changes

@singmajesty have you checked that we’re actually using the correct function to retrieve the screen size? SDL_GetWindowSize() is returning resolution independent points, which I think is the one we should use.
SDL_GL_GetDrawableSize() and SDL_GetRendererOutputSize() both return the size in pixels instead.

Here’s the 2.0.4 changelog for iOS… maybe we missed something in there?

iOS:
* Added support for iOS 8
* Added support for MFi game controllers
* Added support for the hint SDL_HINT_ACCELEROMETER_AS_JOYSTICK
* Added sRGB OpenGL ES context support on iOS 7+
* Added native resolution support for the iPhone 6 Plus
* Added support for SDL_DisableScreenSaver(), SDL_EnableScreenSaver() and the hint SDL_HINT_VIDEO_ALLOW_SCREENSAVER
* The SDL_WINDOW_ALLOW_HIGHDPI window flag now enables high-dpi support, and SDL_GL_GetDrawableSize() or SDL_GetRendererOutputSize() gets the window resolution in pixels
* SDL_GetWindowSize() and display mode sizes are in the "DPI-independent points" coordinate space rather than pixels (matches OS X behavior)
* SDL_SysWMinfo now contains the OpenGL ES framebuffer and color renderbuffer objects used by the window's active GLES view
* Fixed various rotation and orientation issues
* Fixed memory leaks

I did some more tests. Apparently the stage size is correct and the rotation, too… It’s just the image that is deceiving as it’s bigger than the screen in portrait :smile:

Portrait on iPhone 6: Main.hx:42: Stage size: 375x667
Landscape on iPhone 6: Main.hx:42: Stage size: 667x375

If you agree with me that Retina is working correctly, we should definitely wait for my tvOS changes to be merged in SDL’s main repo and update to the latest to get both tvOS and MFI support.

Check the images (save them on your pc as they’ve got a white background):

The code I used to test is the following:

package;


import openfl.display.Bitmap;
import openfl.display.BitmapData;
import openfl.display.Sprite;
import openfl.display.Graphics;
import openfl.display.CapsStyle;
import openfl.display.JointStyle;
import openfl.display.LineScaleMode;
import openfl.Assets;
import openfl.events.Event;


class Main extends Sprite {
	
	var bitmap:Bitmap;
	
	public function new () {
		
		super ();


		bitmap = new Bitmap (Assets.getBitmapData ("assets/openfl.png"));
		trace(bitmap);
		trace(bitmap.width);
		trace(bitmap.height);
		addChild (bitmap);
		
		bitmap.x = (stage.stageWidth - bitmap.width) / 2;
		bitmap.y = (stage.stageHeight - bitmap.height) / 2;

		this.addEventListener(Event.ENTER_FRAME, update);
		/*this.graphics.lineStyle(10, 0xFF0000, 1, LineScaleMode.NORMAL, CapsStyle.NONE, JointStyle.ROUND, 10);
		this.graphics.beginFill(0x00FF00, 1);
		this.graphics.drawRect(0, 0, 500, 500);
		this.graphics.endFill();*/
		
	}

	public function update(event:Event) {
		trace("Stage size: "+ stage.stageWidth + "x" + stage.stageHeight);
		bitmap.x = (stage.stageWidth - bitmap.width) / 2;
		bitmap.y = (stage.stageHeight - bitmap.height) / 2;
	}
	
	
}

MFI support seems to partially work with the latest SDL on tvOS. I haven’t tested it on iOS yet, though.
It looks like the axes are being correctly mapped but the buttons aren’t working.

@singmajesty where does the mapping of the gamepads live in Lime?

So in order to fix this, we need to use drawable size, with the HIGH_DPI flag, and it should work?

SDL finds the joysticks, and if it has device mappings (see lime/ui/Window for a list of mappings we’re adding) it enables use as a gamepad. For this reason, though (the gap when a device does not appear as a gamepad yet) I’m debating if Lime should expose joystick events in order to give us more transparent access

Apparently that’s all that’s needed.

Meh… forget about it, I was looking at the cpp header instead of the Haxe code :stuck_out_tongue:

It’s weird. I added the definitions for the MFI gamepads.
There are no changes to the behavior of Lime, though. Before and after I am still seeing the Apple TV Remote behave just like it’s being recognized as a gamepad but only for the x/y axis. Buttons aren’t working (but they do in SDL).
And if I put a trace() in the onGamepadConnect function of Application.hx in Lime, I see nothing being traced.

Is it enough to just run lime rebuild tools and then lime rebuild tvos and in my project openfl update tvos ?

The SDL mappings should give control over what buttons/axes connect to what gamepad controls, so button ID 0 == button A, etc

That’s exactly what I’d have expected. But I cannot understand why the gamepad is not even being added to the list of controllers… but the movement up/down in my test menu does work. Any idea what might be wrong?

I just saw your pull request. Why does it add ARC to iOS builds? Won’t this break compatibility with iOS extensions and such?

There’s some documentation about the mapping format here:

https://wiki.libsdl.org/SDL_GameControllerAddMapping

Perhaps it is the wrong mapping, or it is correct and working, but firing different button values than you expected?

Because SDL expects iOS to be built with ARC enabled, otherwise it won’t build at all. Enabling ARC only breaks compatibility with very old versions of XCode. And you shouldn’t even be using that anyway unless you still live in 2008 :wink:

Jokes apart, no it doesn’t break anything. All the projects you create with XCode have ARC enabled by default.

The first problem was that, for some reason, if I change a .hx file and I do not run lime rebuild [target] with the -clean flag, it doesn’t rebuild that target. I wasn’t aware of that.

Now I can see that the controller is being added but still I don’t get the button presses. Right now I’m using HaxeFlixel and doing a simple check:

	if (FlxG.gamepads.anyButton())

The good part is that I get this trace:

Application.hx:257: onGamepadConnect
Application.hx:258: Gamepad
FlxGamepadManager.hx:415: GameInputDevice
FlxGamepadManager.hx:420: id: 0
FlxGamepadManager.hx:421: name: MFi Apple TV Remote
FlxGamepadManager.hx:422: model: XBox360

The bad part is that it’s being recognized as an XBox360 controller.

Even if it’s recognized as an X360 controller, it should still see the buttons, as the mapping is pretty much the same (aka no real mapping for MFi controllers)

lime rebuild only affects the C++ code, so most users don’t need this, and when you work from the source, it’s when the C++ is updated. When you do something like changing SDL versions, it does require a rebuild -clean because of how SDL is constructed

That’s good, so can you do anything with it, or you still can’t get buttons going?

BTW, thank you for the help about the drawable size change! I just got it working here with the latest SDL, I had to handle not only the window creation, but when we check for width/height and when we handle window resize events :smile:

It’s super that you’ve got the Retina stuff working!

I still don’t get the buttons. They do work in plain SDL, though. I created a test project to run the actual standard SDL game controller test on the Apple TV and it shows the buttons being pressed. So, at least on the SDL side, I’m sure it’s working.

That means that sonething is happening in Lime, but I’m not sure where to look at.

On the Lime side, you should be able to do something like this:

lime.ui.Gamepad.onConnect.add (function (gamepad) {
    
    gamepad.onButtonDown.add (function (button) {
        
        trace ("Pressed " + button);
        
    });
    
});

(this will work with an OpenFL project as well so long as you don’t use -Dlegacy)

1 Like

I think that HaxeFlixel wraps the OpenFL implementation of something similar (you surely know better than me). Tomorrow I’m going to try your code in a new empty Lime project and I’ll let you know if I get the button press. Cheers!