Adobe AIR Non-Transparent Window Bugs Part 3: Native Controls Showing Up

February 9th, 2010 by Steven Sacks

In my previous post, I showed how sometimes Windows would draw native controls on top of your non-transparent windows, as pictured here:

Windows XP

However, I could not figure out a sure way to reproduce it. Now I have.

Here's how you do it:

1. Make a new AIR application in Flex Builder.

2. In your-app.xml, set systemChrome to none and visible to true.

3. Prevent MINIMIZE and MAXIMIZE in your WindowedApplication:

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
    <mx:Script>
        <![CDATA[
            private function init():void
            {
             nativeWindow.addEventListener(NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGING, onDisplayStateChanging);
            }
            private function onDisplayStateChanging(event:NativeWindowDisplayStateEvent):void
            {
                if (event.afterDisplayState == NativeWindowDisplayState.MINIMIZED || event.afterDisplayState == NativeWindowDisplayState.MAXIMIZED)
                {
                    event.preventDefault();
                }
            }
        ]]>
    </mx:Script>
</mx:WindowedApplication>

4. Launch the app.

5. Click twice on the taskbar button (should do nothing).

6. Right-click on the taskbar button. Windows native system controls appear.

This is just a reliable way to make it happen every time. You don't have to do this exact thing to make them show up, they show up on their own randomly. I hope this example makes it easy for the AIR team to debug this issue.

I've exported the sample project with the above code. Click here to download.

Posted in AIR, Adobe, Bugs having 3 comments »

Adobe AIR Non-Transparent Window Bugs Part 2: Maximize

February 4th, 2010 by Steven Sacks

Maximized AIR windows on Windows XP are positioned at -4,-4 and the width and height add 8 pixels to compensate. On Windows 7, this is doubled, so the position is -8,-8 and the width and height add 16 pixels to compensate. This works fine with transparent AIR windows.

Note: This behavior is not documented, and I hope they add it to the AIR documentation.

However, when you maximize a non-transparent non-system chrome window in AIR, the window is drawn 1 pixel in on all edges, so you can see the desktop as a 1 pixel border around the window. Additionally, the window is positioned at 1,1 instead of -4,-4 in Windows XP and -8,-8 in Windows 7, and the window is cropped to 1 pixel less than the visible bounds on width and height, which means 10 pixels are being cropped in Windows XP and 18 pixels are being cropped in Windows 7.

Here's a screenshot of what the window top and bottom look like when normal:
Normal AIR Window
Normal AIR Window Bottom

And this is what it looks like when it's maximized (I cropped out the middle to fit the blog):
Maximized AIR Window
Maximized AIR Window Bottom

As you can see from the maximized image, the window is incorrectly rendered. There are two primary issues.

The NativeWindowBoundsEvent says it's positioned at -4,-4 / -8,-8, but it's actually positioned at 1,1.

The NativeWindowBoundsEvent width and height are correct at +8 for Windows XP and +16 for Windows 7. But, because the window is cropped at 2 pixels less width and height than the window is drawn at, on Windows XP the window is cropped by 10 pixels, and on Windows 7 the window is cropped by 18 pixels.

Here are screenshots where you can see the 1 pixel border on Windows XP and Windows 7. I made the desktop bright red (#FF0000) so you can clearly see the borders. There's also another bug with Windows XP that I'll go into below.

Windows XP
Windows XP

Windows 7
Windows 7

If you look at the Windows XP one, you can see that native window controls are being drawn (this happens in Windows 7, as well). This happens sometimes when you maximize from a normal state on a non-transparent window. There's nothing reliable that causes it, but it does happen sometimes. This is the first time I've been able to take a screenshot of it. What's weird is rolling over the graphics in the window hides the controls but you can still click on them. What's weirder is that if you click on the native control to restore the window (go back to normal), AIR doesn't fire a state change event!

There's one more issue with non-transparent AIR windows. It's the same bug that the CS4 suite of software has when the windows are maximized. When you have two monitors and your CS4 application is maximized (like Flash or Photoshop) and you drag a window across the right edge of the application, you see ugly ghosting of the window you're dragging.

Windows XP Edge Bug

See all that ugly blue? That was me dragging the FlexBuilder window around. The blue is the color of the title bar of the native window. Any window will cause that, though. On AIR, the problem is even worse because that 1 pixel border on the top and bottom also suffer from the ghosting.

I don't know why, but CS4 and AIR both have the same exact issue.

Here's what it looks like in Flash CS4 and Photoshop CS4:

Flash CS4
Windows XP Edge Bug

Photoshop CS4
Windows XP Edge Bug

The ghosting goes away if you change state, like minimizing then restoring or clicking the restore button. This only happens when the window is maximized.

Please, please, please fix this in AIR (and CS5)!

Added to Adobe Bugs on Feb 8th

Update: Workaround

While there's no way to deal with the 1 pixel border or the ugly ghosting that happens, I have figured out a straightforward workaround for the resize. Because you cannot move Mac windows to negative y positions (AFAIK), this if statement will only happen on windows.

private function onResize(event:NativeWindowBoundsEvent):void
{
	var offset:int;
	if ((event.afterBounds.x == -4 && event.afterBounds.y == -4) || (event.afterBounds.x == -8 && event.afterBounds.y == -8)) offset = event.afterBounds.x * -2;
	width = event.afterBounds.width - offset;
	height = event.afterBounds.height - offset;
}

Posted in AIR, Adobe, Bugs having 5 comments »

Adobe AIR has bad redraw bugs on Windows

January 27th, 2010 by Steven Sacks

In Adobe AIR, I recently had to change from using a transparent window to a non-transparent one, and I've never really worked with non-transparent windows without system chrome. Doing so has created a very painful situation due to AIR's redraw bugs on Windows when using non-transparent windows.

Transparency performance on Windows is bad, but it's not Adobe's fault. Windows has bad transparent window performance. However, on Windows, there are major redraw issues with AIR on a non-transparent window, and none of these issues are present in non-AIR applications.

When you minimize a window in AIR, it resizes the application to 160×31, and moves it to -32000, -32000. I can understand the move, but the resize doesn't make sense to me. Especially considering the minSize of the Window is much larger than that.

Because of the resize to 160×31, when you restore the app from a minimized state it resizes the application from that small size back to the size it was before you minimized. On transparent windows it resizes before you can see it, which is why I never saw this before. But, on a non-transparent window, you can actually see your application redraw from 160×31 to the size it was before you minimized. This looks terrible.

What happens whenever you change the window state from Minimized to Normal or from Normal to Maximized, or Maximized to Normal, the move happens first, then, about 100ms later (though getTimer() claims they happen in the same millisecond), the resize happens. This is an clearly visible to the user, and really doesn't look good.

Additionally, on this specific project, when AIR resizes my window to 160×31, it causes all kinds of negative issues with elements that are not meant to draw to that small of a dimension (I set the minSize of the window for a reason). Because of the delay in resizing after the move as outlined above, these issues are visible for a moment when you restore from a minimized state, but sometimes, elements inside an HTMLLoader do not resize correctly unless I resize the application by 1 pixel after restoring.

The best I can do is in the RESIZE listeners check to see if the afterBounds is 160×31 and ignore it if so. So, I do the check for 160×31 in my onResize listener, ignoring the resize if those are the afterBounds values, and that mitigates the issue except for a couple important exceptions.

First, you still see the two step redraw occur, except that since the application isn't resized smaller, it just looks like the application is masked for that split second. The second issue is that I have an HTMLLoader playing a swf with wmode="window" inside it and what happens on redraw is that even though the application is "masked" by the smaller window size (since I'm ignoring the resize), the swf in the HTMLLoader is visible beyond the bounds of the NativeWindow 160×31 size until the resize occurs after the move. This isn't the worst thing in the world, but it's not that great either. It's definitely better than the visual consequences of redrawing the application to 160×31.

Another example of this move-wait-resize behavior is when you maximize a non-transparent AIR window. First, it moves the window to -4,-4 (-8,-8 on Windows 7) and then resizes the window to fill the screen. Because of the delay, you see maximize happen in two steps. You see the window jump up to the top left corner first and then you see it resize to fill the screen. This doesn't happen with transparent windows and it doesn't happen with any other application on Windows.

It's really disappointing that something so basic is so buggy. I don't like that AIR resizes the window to 160×31 when you minimize, and I really wish they could move and resize non-transparent windows at the same time since other applications on Windows work that way.

As it is, I'm stuck trying to work around these issues and mitigate them as much as possible. I'm still hoping our commercial application will be a shining example of the kinds of things you can make with AIR, despite AIR's flaws. Most of the bad rap AIR gets is undeserved since most of the time, it is developers not knowing how to manage memory and other such nonsense. But in cases like this, it's something fundamental and specific to how the window draws on the most common target platform, Windows, and is completely out of my control.

All I can do is mitigate the issues as best I can, and write blog posts outlining the problems in the hops that the AIR engineering team will fix these issues in a future release.

Added to Adobe Bugs on Feb 8th

Posted in AIR, Bugs having 6 comments »

Gaia 3.2.0 Available Now

January 11th, 2010 by Steven Sacks

Gaia 3.2.0 is now available for download.

Gaia 3.2.0 updates to the latest 3rd party packages, including TweenLite, SWFAddress, and DeMonsterDebugger. In addition, I fixed a bug that was preventing page swfs from potentially not unloading correctly, and a bug with SoundGroup when using fadeTo() without a callback.

Posted in Gaia having 1 comment »

Flash Socket Class Does Not Wait For Flush

December 8th, 2009 by Steven Sacks

Update
Flash Player Team has confirmed the documentation is wrong. Data is sent as soon as you write to the Socket. In other words, flush() has no purpose.


Flash's Socket class documentation says the following:

flush() method
Flushes any accumulated data in the socket's output buffer. Data written by the write methods is not immediately transmitted; it is queued until the flush() method is called.

However, this is simply not true. A basic test proves the true behavior of the Socket class.

The moment you write to a connected socket, it sends the data and the server receives it. You do not need to call flush(), and, in fact, calling flush() has no effect as far as the server or Flash is concerned. The documentation is not just misleading, it's exactly the opposite of the real behavior.

The socket automatically sends as little as 1 byte. socket.writeByte(0) is immediately sent across the socket.

In direct opposition to the documentation for Socket, data is not queued, it is immediately transmitted across the socket. Calling flush() does nothing. The data is already sent. There is absolutely no difference from the server's perspective whether I call flush() or not, and I can write millions of bytes on a Socket and never call flush() and there is no negative effect as far as all testing I have conducted has been able to show.

Flash sends data across the Socket in 536 byte packets. Anything larger than 536 bytes gets split into separate packets. So, if you send 537 bytes, Flash sends two packets, one that's 536 bytes and one that's 1 byte.

The internal limit that Flash can send via Socket appears to be 524,744 bytes. If you set up your server to not read off the socket, 524,744 bytes is the point that Flash's socket becomes blocked. However, Flash does not let you know that the Socket is blocked or that there are any bytes sitting on the Socket unsent.

This behavior is exacerbated by the fact that there is no way to track progress on Socket sending, only receiving. There is no event that fires if you're blocked, and you have no way of knowing that the server did not receive your entire transmission unless you have some kind of expected response that you did not receive within a certain time limit, which is not something you can reliably measure considering latency and/or varying bandwidths, and the fact that you have no idea how much data the server has received.

If you send 1,000,000 bytes and set up your server to wait until you send 524,744 bytes to read it, Flash does not even need you to call flush() to send the remaining 475,256 bytes, it happens automatically. No flush() required, and you don't even know that the Socket was blocked.

It's worth pointing out that if the server has a receive buffer (we set it to 8040 bytes), Flash does not honor it when running in the IDE, running as a SWF in the debug player on the desktop, or in AIR. It only honors it when it's running in a browser (tested in Chrome, Firefox and IE on Windows). Regardless, Flash does not report that only 8040 bytes were sent, and, again, flush() was not called but the bytes were sent anyway.

The behavior is identical whether you use socket.writeByte() and write 1 byte to the socket at a time, or use writeBytes() and write all the bytes to the socket at once.

FileReference lets you track progress on a HTTP Socket POST of bytes. I don't know why they still have not given us progress on the Socket sending, and I also don't know why the Socket doesn't wait for you to tell it to upload, like the documentation says it does.

As far as any Actionscript code you can write is concerned, every single byte you write to a socket is sent instantaneously. I can watch my network and see bytes as they're being sent, but according to my code's behavior, I can transmit gigs of data across a socket instantly. I tell the Socket to send it, and the Socket basically behaves as if it's a synchronous event and every byte has been sent. Obviously, there is a good reason for this behavior (not locking up Flash for Socket transmissions), but there's no good reason to not inform me of the progress.

It's bad to assume that all the data on the Socket was sent instantly. It's also complicated by the fact that Flash doesn't wait for me to call flush() before sending data.

I would appreciate anyone from Adobe or anyone else who has more information on Flash's Socket behavior, and I'm also curious why the documentation for Socket has been incorrect for years now and nobody has pointed it out.

More importantly, how long until we can see progress of bytes sent on a Socket?

Posted in AS3, Actionscript, Flash, Rants having 8 comments »

FITC Toronto: Gaia Framework for Adobe Flash video

September 26th, 2009 by Steven Sacks

The video from my Gaia Framework For Adobe Flash presentation at FITC Toronto this past April is now available on Adobe TV.

Enjoy!

Posted in FITC, Gaia, Presentations having 4 comments »

Gaia 3.1.7 Released

September 3rd, 2009 by Steven Sacks

Gaia 3.1.7 has a bunch of improvements.

You can now set a version for your site to force reloading of swfs. You can set domain in the site node for the entire site so you don't have to do it on a per page basis. The Pages class now has every page in the site.xml. setPreloaderDelay now works with the asset preloader. afterComplete no longer takes a hijack argument. The DeMonster Debugger class is now distributed with Gaia. SWFObject has been updated to 2.2. Thunderbolt has been updated to 2.2. The event metadata had incorrect class paths. Gaia header comments have been removed from scaffolded page classes. Finally, on Windows a FlashDevelop project file is automatically generated when you create a new project.

Posted in Gaia having no comments »

Adobe MAX 2009: Two Gaia Sessions!

September 2nd, 2009 by Steven Sacks

Adobe MAX 2009 is right around the corner and I'm extremely excited for it. Aside from the fantastic opportunity Adobe MAX provides to see the newest advances with the software we use to make a living, learn new skills at the labs and presentations, network, and hang out with old friends, this year I am happy to announce that I will be presenting two Gaia sessions at Adobe MAX 2009 in Los Angeles.

One is an Adobe MAX Lab and the other is a presentation for the FITC Unconference at MAX.

Here are the details:

Gaia Framework for Adobe Flash: Revolutionizing Your Workflow
Wednesday, October 7th – 11:00am – Room: 518 – BYOL Lab
I am teaching a hands-on lab that will introduce Gaia's pragmatic approach to developing with Flash and get your feet wet with real-world examples. In this 90 minute lab, we'll be covering the fundamentals of Gaia development and best practices. Sign up while there are still seats available. This is a BYOL (Bring Your Own Laptop) lab.

Advanced Tips and Tricks with the Gaia Framework
Monday, October 5th – 11:30am – FITC Unconference Room
I am also giving an advanced Gaia presentation at the FITC Unconference at MAX 2009. In this presentation, I will be digging in and demonstrating how to leverage some of Gaia's more advanced features, such as Event Hijacking, Sound Groups and Custom Assets. You'll learn how Gaia makes it easy to develop specialized transition control, mp3 players, render strict XHTML in Flash using Gaia's runtime font loading, SEO and StyleSheet assets, as well as how to create your own custom assets using Gaia's extensible architecture.

If you haven't purchased tickets, now is the time to do it because the early bird tickets are only available until September 18th. MAX is taking place in sunny Los Angeles, CA this year, and the attendance is expected to be bigger than ever!

If you've never gone to MAX and you're on the fence, you definitely should consider going. In addition to the conference during the day, it's an extremely fun time at night. The parties are a blast and you meet a ton of great people. If you've already been, well, you already know!

Can't wait to see you there!

Posted in Adobe, Flash, Gaia, MAX, Presentations having 4 comments »

De MonsterDebugger 2.0.4 now works with Gaia

August 27th, 2009 by Steven Sacks

The popular De MonsterDebugger now works with the Gaia Flash Framework (AS3).

Detailed instructions on how to integrate it with Gaia can be found in the De MonsterDebugger help panel.

The upcoming Gaia release will include full integration for this popular debugging tool!

Posted in AS3, Adobe, Flash, Gaia, Tips/Tricks having 1 comment »

Gaia Framework 3.1.6

July 19th, 2009 by Steven Sacks

Gaia 3.1.6 adds SEOAsset innerHTML support to AS2, a gettable index property for SoundGroup, and fixes a bug with the panel's update file bytes in xml feature.

Posted in Uncategorized having 1 comment »

About Steven Sacks

I am a professional Flash developer with over 13 years of programming experience. I have consulted for high-profile agencies and companies in San Francisco, Los Angeles, Atlanta and New York, and developed numerous award-winning websites and rich internet applications for clients including Adobe, Fox Sports, FX Networks, Anheuser-Busch, GE, DirecTV, ESPN, The Weather Channel, Home Depot, and Coca-Cola.

I am the author of the open-source Gaia Framework for Adobe Flash, which dramatically reduces development time and makes developing Flash sites much easier.