Applet stays white after Pressing Cntr+Alt+Del -> Esc

Hi there,

I’ve developed an applet which suffers from a strange problem only on the windows platform.
When I press Cntr+Alt+Del the windows-“system”-dialog appears and after pressing “Esc” it disappears and the normal background appears again, however where the applet was in the browser I only see a white rectangle.

Since my game only redraws parts of the screen at user interaction, I can see how some parts paint themself when I click on the applet.
If I resize the browser it repaints itself and everything is fine again.
I am using a VolatileImage as backbuffer but I do painting in a do-while loop checking wether contents have been lost and wether the image is still valid, re-creating it when it became invalid. When using a “normal” offscreen-image created by Component.createImage(int, int) I am experience the same problem, however it often can’t be reproduced.
If it happend once it does not mean that it’ll happen the next time, however if it happens the user will think the application crashed.

I’ve seen this behaviour in three different computers, one with firefox browser and two with IE6, two with java-1.4.2 and one with java-5.0_u7.

Any ideas where this could come from?

Thank you in advance, lg Clemens

Use a focus listener on the Applet component to detect when the applet regains focus & then repaint your VolatileImage. When you resize the browser repaint() is effectively being called on your applet, so the above does the same thing & should fix it. Ofcourse the applet will still be white until you click on it to focus it.

Thanks for the suggestion.

However thats a workarround, I am curious where the problem comes from. Has anybody except me seen this behaviour?
Is it a bug of the java-plugin?

Thanks, lg Clemens

I don’t think it’s a bug so much as a feature.

When a multi-tasking operating system switches to a different process, it stops processing the other processes. I don’t think there’s anything you can do about it except fix it when you get the focus back.

This isnt a scheduling fault - scheduling happens in very small time frames, so even if my process would get no CPU for 500ms it should paint its surface after beeing told by the operating system.

As it looks for me VolatileImages loose their content but don’t tell anybody, thats why the contentsLost() loop does not loop till everything is drawn correctly. Since the backbuffer-image is reused all over the time this also explains why it is completly white first and then gets populatred more and more.

Anyhow, seems nobody is able to give me an useful answer :frowning:

lg Clemens

What release was this with? I remember us fixing a few issues with VIs losing surfaces
and not repainting correctly afterwards in mustang.

Could you please try mustang?

Thanks,
Dmitri

Thanks a lot for your reply - I tried it with 1.3.1, 1.4.2, 1.5.0_u7 and mustang b93 and it happened everywhere except 1.3.1.
An interesting result is that this bug does not happen as soon as something is drawn in XOR mode.

I really guess its a bug in my drawing code, I must have broken something. All the VI handling is done through reflection since the code is 1.1 compatible, I attached the drawing code below, the class is not complete I just included whats really related to this topic.
It would be great if someone could give it a short look.

Thanks in advance, lg Clemens


import java.awt.*;

import java.lang.reflect.*;



public class LwPaintManImpl extends LwPaintManager

{

	protected Dimension MAX_BUFFER_SIZE;

	protected Image buffer;

	protected boolean isBuffered;



	static boolean isAccerlated = false;

	static Class volatileClass;

	static Method createVImage;

	static Method getGraphicsConfiguration;

	static Method volatileValidate;

	static Method volatileContentsLost;

	static Object[] emptyParaArray =

	{};

	static int IMAGE_INCOMPATIBLE;



	static

	{

		try

		{

			Class[] createVIParams =

			{ Integer.TYPE, Integer.TYPE };

			Class[] emptyParams =

			{};

			Class[] validateParams =

			{ Class.forName("java.awt.GraphicsConfiguration") };



			volatileClass = Class.forName("java.awt.image.VolatileImage");



			IMAGE_INCOMPATIBLE = volatileClass.getField("IMAGE_INCOMPATIBLE").getInt(null);

			System.out.println(IMAGE_INCOMPATIBLE);



			createVImage = java.awt.Component.class.getMethod("createVolatileImage", createVIParams);

			getGraphicsConfiguration = java.awt.Component.class.getMethod("getGraphicsConfiguration", emptyParams);

			volatileValidate = volatileClass.getMethod("validate", validateParams);

			volatileContentsLost = volatileClass.getMethod("contentsLost", emptyParams);



			isAccerlated = true;

			System.out.println("Accerlated!");

		} catch (Exception ex)

		{

			ex.printStackTrace();

			isAccerlated = false;

		}

	}



	protected/* C#override */void paintDesktop(Graphics g, LwDesktop d)

	{

		if (isAccerlated)

		{

			paintDesktopAccerlated(g, d);

		} else

		{

			paintDesktopUnAccerlated(g, d);

		}

	}



	protected void paintDesktopAccerlated(Graphics g, LwDesktop d)

	{

		try

		{

			int w = d.getWidth(), h = d.getHeight();

			if (isBuffered && w <= MAX_BUFFER_SIZE.width && h <= MAX_BUFFER_SIZE.height)

			{

				Component nat = (Component) d.getNCanvas();

				Object gconf = getGraphicsConfiguration.invoke(nat, emptyParaArray);



				if (buffer == null || w > buffer.getWidth(null) || h > buffer.getHeight(null))

				{

					Integer[] createVIParams =

					{ new Integer(w), new Integer(h) };

					buffer = (Image) createVImage.invoke(nat, createVIParams);

				}



				synchronized (LOCKER)

				{

					Rectangle da = g.getClipBounds();

					Graphics gg = null;

					try

					{

						do

						{

							Object[] validateParams =

							{ gconf };

							int valCode = ((Integer) volatileValidate.invoke(buffer, validateParams)).intValue();

							if (valCode == IMAGE_INCOMPATIBLE)

							{

								Integer[] createVIParams =

								{ new Integer(w), new Integer(h) };

								buffer = (Image) createVImage.invoke(nat, createVIParams);

							}



							int xx = da.x + da.width, yy = da.y + da.height;

							if (LwToolkit.desktops > 1 || d.getDA().width >= 0)

							{

								gg = buffer.getGraphics();

								gg.clearRect(da.x, da.y, da.width, da.height);

								gg.setClip(da.x, da.y, da.width, da.height);

								super.paintDesktop(gg, d);

							}

							g.drawImage(buffer, da.x, da.y, xx, yy, da.x, da.y, xx, yy, null);

						} while (((Boolean) volatileContentsLost.invoke(buffer, emptyParaArray)).booleanValue());

					} finally

					{

						if (gg != null)

							gg.dispose();

					}

				}

			} else

				super.paintDesktop(g, d);

		} catch (Exception ex)

		{

			ex.printStackTrace();

		}

	}

}



I don’t see anything wrong with your code.
The graphcis that’s passed to the rendering method, is this
simply a screen graphcis context, or BufferStrategy’s?
If it’s the latter, make sure you call BufferStrategy.getDrawGraphics()
is called on every frame (that is, don’t cache it).

Could you install fastdebug binaries for mustang and run with J2D_TRACE_LEVEL=4 and
collect output? You might want to add a sleep somewhere in the loop to slow it down
so that you don’t have too much output.

It looks like as if we don’t notice that the buffer is lost.
The reason XOR helps is that we try to lock the DirectDraw surface
and discover that it’s lost, which trigger revalidation.

This may be a workaround for now: render a 1x1 translucent image
to the graphics.

Thanks,
Dmitri

Hello again,

Thanks a lot for all your patience. Since I am far away from beeing in a position where I could help myself I am really glad you listen to me :slight_smile:

Seems I’ve been wrong. On one old test machine I used (celeron 600, ATI graphic card) the XOR trick helped but on another machine I just see the XOR-part rendered, and everything else is white.

Its s screen gpahics created on an java.awt.Panel.

Yes of course, I’ll try them on monday. Since my applet is commercial I can’t send it to you, however should I try to create a small demonstration applet which uses the same code?
The Toolkit I use is GPL, therefor browsing a bit should be fairly easy.

lg Clemens

Hello,

I created a small test-case which can be found at: http://web460.server3.webplus24.de/sltc.html
I was able to reproduce the problem by pressing cntr+alt+del using this test case, my boss also told me that this happend also while the browser was minimized and showed me some screen shots.

On my brothers computer pressing this combination directly opens the task manager (no idea why), but there I was able to reproduce it by first choosing “switch user” (I only know the german names, so translation is quite likely wrong) and the re-login into the same session using the same user.
It does not happen always and not on all computers, it seems if the browser window is full-screen its more likely to happen.

lg Clemens

Thanks, I’ll check it out.

Dmitri

thanks a lot, if you need sources or whatever please let me know.

Hi again,

any news about that? Should I open a bug report instead?

lg Clemens

I tried it a couple of times and could reproduce it by switching user indeed. Also I noticed 9 out of 10 times I tried I saw the applet window blank for a second and then repaint. It stayed blank like that only once but repainted again when I clicked it.

Are you using paint() to draw your screen (and call your other paintDesktop methods) by any chance?

I always use active rendering in my applets, from my expirience the way browsers call repaint() seems to be inconsistent.

If you aren’t doing this already, try to disable repaint events by calling setIgnoreRepaint on your component where available (using reflection too). And in your main loop just call the paintDesktop methods to draw your stuff to screen at the desired framerate (or only when certain events have occured that could overlap your applet screen).

Hope that helps!

Thijs

this is a desktop applet which is not GUI related - therefor “passive” rendering is a must.

Ok, maybe you could put up the source for that test applet? It’s kind of a blackbox this way.

Hi thijs,

Of course, I uploaded the sources to the locations listed later. I just did not upload it because nobody asked for it.
If you’ve questions don’t hestitate to ask, I am not an LwVCL expert but the project I worked the last 3 years on uses it … maybe I can help somehow.

The sources of the UI library used are at: http://web460.server3.webplus24.de/lwvcl_customized_45.zip
The source of the test applet: http://web460.server3.webplus24.de/SLTC.java
The test applet itself: http://web460.server3.webplus24.de/sltc.html

Thanks, lg Clemens

Okies, I’ll try to have a look at it when I have some spare time.

Though the fact that it’s hard to reproduce the problem (seems to happen only sometimes on my config) and you’re also report it happens on some configs (though the test conditions are the same?), makes me fear it’s a bug in sun’s implementation somewhere…

Sorry, it took me a while to get to it.

Here’s what I think is going on: after the display change event which
happens when you do ctrl+alt+del (or via any other way) the
contents of the volatile image are lost, and validate() returns
RESTORED, meaning that you need to restore the contents
of the image by re-rendering its contents.

But the code in LwPaintManImpl.paintDesktopAccelrted() doesn’t
respond to this return code, it continues
to render the restored (empty, thus white) VolatileImage.
So it renders this empty VI until something happens which
makes it to re-render it (like if you click at the button, or
resize the applet).

So I think that if you add

||(valCode == RESTORED)

to the condition check at line 223 it should work.

Take a look at the VolatileImage javadoc in JDK6, we’ve extended it
a bit and provided more samples:
http://download.java.net/jdk6/doc/api/java/awt/image/VolatileImage.html

Thanks,
Dmitri
Java2D Team

Uuhh this is awkward - of course you were right.
The reason why I never thought about this piece of code is that I took it from LwVCL and just added reflection to non-java-1.1 compatible places, so I thought the way painting is done is correct. The LwVCL stuff was already VolatileImag’ized.

However you were right of course, both cases IMAGE_RESTORED and IMAGE_INCOMPATIBLE) were not taken into account the right way since the logic still painted only the small area which it was asked to repaint.
I fixed this for both cases, tested it (and yes it worked unbelievable while the old one broke) and sent a patch to the author.

Thanks a lot for your patience, I already thought about rendering into BufferedImages cause this thing drove me crazy. Thank you many, many times :slight_smile:

lg Clemens