Okay, finally figured this out, so putting the bits here for anyone else who is trying to solve this problem.
In straight Flash (no Starling invoked), you can simply switch between windowed and fullscreen by using
stage.displayState = StageDisplayState.FULL_SCREEN_INTERACTIVE; or
stage.displayState = StageDisplayState.NORMAL; If you've previously set the following:
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.SHOW_ALL;
Then Flash AIR will just autoscale your app for you.
However, once you invoke Starling, this no longer works. Here's how I got around it.
First, in the app.xml, I made sure I had the following settings:
Obviously, you'll set your size to whatever is appropriate for you. Then in the startup class for the app itself, I only define the framerate and background color for the SWF, not the app size:
[SWF(frameRate= 30, backgroundColor="#000000")]
Set the stage.align and stage.scaleMode, as above, then you need to do a few calculations. Somewhere in code, capture your default windowed screen dimensions in some static variables (I use WIDTH and HEIGHT set to 1280 and 720 respectively), and then in your main class onAddedToStage handler (you'll need to have an event listener for Event.ADDED_TO_STAGE in your constructor), create and then call this method to cache away the upscale ratio to a private member variable:
private function calculateScreenRatio():void
var ratioX:Number = Capabilities.screenResolutionX / WIDTH;
var ratioY:Number = Capabilities.screenResolutionY / HEIGHT;
m_fsRatio = Math.min(ratioX,ratioY);
Great, now you have the proper scaling ratio for your fullscreen mode. Now you need to do three more things: 1) Setup Starling with the proper default stage size, 2) Handle Resize Events in Starling, and 3) Handle Resize Events for your classic stage.
First, setting up Starling:
// create our Starling instance
var viewRect:Rectangle = new Rectangle(0,0, WIDTH, HEIGHT);
mStarling = new Starling( StarlingRootSprite, stage, viewRect);
// set anti-aliasing (higher the better quality but slower performance)
mStarling.antiAliasing = 0; // Pixel Game, no AA!
Now in your StarlingRootSprite class, have a handler for the Starling resize event (not the classic Flash event):
and your onResize method should look like:
private function onResize(resizeEvent:ResizeEvent, size:Point):void
new Rectangle(0, 0, stage.stageWidth, stage.stageHeight),
new Rectangle(0, 0, size.x, size.y),
That will properly resize Starling to fullscreen or windowed on a resize. Now we just need to handle the classic Flash stage, which won't resize automatically for us. Create another event listener for the classic Flash resize event, probably also in your onAddedToStage method for the base app class:
And then make an onResize method like this:
private function onResize(e:Event):void
if(stage.displayState == StageDisplayState.NORMAL)
this.scaleY = this.scaleX = 1;
this.scaleY = this.scaleX = m_fsRatio;
You're done. Now both Flash and Starling will resize properly when you set stage.displayState to swap between windowed and fullscreen. The advantage here is that if the user pressed ESC in fullscreen mode, both event handlers will capture the event and properly resize down to Windowed mode. Also, your Starling and Flash displays will be properly sized and not missing any segments in fullscreen mode.