Why does this simple Java program work correctly under Windows but not Fedora 10?

I have written the following little framework for a game I was hoping to write for Linux. This works fine with the same version of the JDK on Windows - a black screened window with a frames-per-second readout on the top left, but this fails to work under Fedora 10. I have installed Sun’s official JDK and all I get is the Swing window which is the default grey and nothing else.

Little help?

Game.java


public class Game {
        private static Universe theGame = null;

        public static void main(String[] args) {
                theGame = new Universe();
        }
}

Universe.java


import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.Random;
import javax.swing.JFrame;

public class Universe extends Canvas {
	private JFrame theFrame;
	private BufferStrategy bufferStrategy;
	private BufferedImage bufferedImage;
	private GraphicsEnvironment graphicsEnvironment;
	private GraphicsDevice graphicsDevice;
	private GraphicsConfiguration graphicsConfiguration;
	private Graphics graphics;
	private Graphics2D graphics2D;
	private Color colorBackground;
	private Random random;
        private static final int SCREEN_WIDTH = 640;
        private static final int SCREEN_HEIGHT = 480;
	private static final int SCREEN_DEPTH = 32;
	private static int fps;
	private static int frames;
	private static long totalTime;
	private static long currentTime;
	private static long lastTime;
	private boolean isRunning = false;
	private Font theFont = new Font("Courier New", Font.PLAIN, 12);
	private static boolean isFullScreen = false;

	public Universe() {
		initUniverse();
	}

	private void initUniverse() {
		theFrame = new JFrame();
		theFrame.setIgnoreRepaint(true);
		//theFrame.setCursor(getToolkit().createCustomCursor(new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB),new Point(0,0),null));
		graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
		graphicsDevice = graphicsEnvironment.getDefaultScreenDevice();
		graphicsConfiguration = graphicsDevice.getDefaultConfiguration();
		bufferedImage = graphicsConfiguration.createCompatibleImage(Universe.SCREEN_WIDTH, Universe.SCREEN_HEIGHT);
		if (!isFullScreen) {
	                theFrame.add(this);
                	theFrame.pack();
                	theFrame.setVisible(true);                 theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                	theFrame.add(this);
                	theFrame.pack();
                	theFrame.setVisible(true);
			theFrame.setSize(Universe.SCREEN_WIDTH, Universe.SCREEN_HEIGHT);
		} else {
			theFrame.setUndecorated(true);
			theFrame.addKeyListener(new KeyAdapter() {
                        public void keyPressed(KeyEvent ke) {
                                if(ke.getKeyCode() == KeyEvent.VK_ESCAPE )
                                        isRunning = false;
                                }
                        });
			graphicsDevice.setFullScreenWindow(theFrame);
			if(graphicsDevice.isDisplayChangeSupported()) {
  				graphicsDevice.setDisplayMode(new DisplayMode(Universe.SCREEN_WIDTH, Universe.SCREEN_HEIGHT, Universe.SCREEN_DEPTH, DisplayMode.REFRESH_RATE_UNKNOWN));

			}
		}
		theFrame.createBufferStrategy(2);
                bufferStrategy = theFrame.getBufferStrategy();
		colorBackground = Color.BLACK;
		random = new Random();
		setupFPS();
		gameLoop();
	}

	private void setupFPS() {
		Universe.fps = 0;
		Universe.frames = 0;
		Universe.totalTime = 0;
		Universe.currentTime = 0;
		Universe.lastTime = 0;
	}

	private void gameLoop() {
		isRunning = true;
		Universe.currentTime = System.currentTimeMillis();
		while(isRunning) {
			try {
				Universe.lastTime = Universe.currentTime;
				Universe.currentTime = System.currentTimeMillis();
				Universe.totalTime += Universe.currentTime - Universe.lastTime;
				if (Universe.totalTime > 1000) {
					Universe.totalTime -= 1000;
					Universe.fps = Universe.frames;
					Universe.frames = 0;
				}
				++Universe.frames;
				clear();
				updateInput();
				updateLogic();
				updateRender();
				displayFPS();
				flip();
			} catch (Exception e) {
				System.out.println("ERROR:" + e.getMessage());
			} finally {
				cleanUp();
			}
		}
		isRunning = false;
		shutdownGame();
		shutDownProgram();
	}

	private void shutDownProgram() {
		cleanUp();
		if (isFullScreen) {
			graphicsDevice.setFullScreenWindow(null);
		}
		System.exit(0);
	}

	private void shutdownGame() {
	}

	private void updateInput() {
	}

	private void updateLogic() {
	}

	private void updateRender() {
	}

    	private void displayFPS() {
		graphics2D.setColor(Color.WHITE);
		graphics2D.setFont(theFont);
                graphics2D.drawString(String.format("FPS: %s", Universe.fps), 20, 20);
	}

	private void clear() {
		graphics2D = bufferedImage.createGraphics();
       		graphics2D.setColor(colorBackground);
        	graphics2D.fillRect(0, 0, Universe.SCREEN_WIDTH, Universe.SCREEN_HEIGHT);
	}

	private void flip() {
		graphics = bufferStrategy.getDrawGraphics();
                graphics.drawImage(bufferedImage, 0, 0, null);
                if (!bufferStrategy.contentsLost()) {
                	bufferStrategy.show();
        	}
		Thread.yield();
	}

	private void cleanUp() {
		if (graphics != null) {
			graphics.dispose();
			graphics = null;
		}

		if (graphics2D != null) {
			graphics2D.dispose();
			graphics2D = null;
		}
	}

	public boolean isRunning() {
		return isRunning;
	}
}

The official Sun JDK is in the system path (/etc/profile) and I have compiled it using that and also uninstalled the OpenJDK stuff but to no avail. What the hell is going on? Any ideas? As I said, this code works perfectly under Windows with the same version of the JDK. I even ran “javac -version” and “java -version” to make sure no other Fedora-specific JDK was overriding the official one. But nothing helps. :frowning:

Construct your UI on the event dispatch thread with SwingUtilities.invokeLater

Can you explain that in a little more detail, please? Sorry!

Changes to the properties of a visible component must occur on the event dispatch thread. You are creating and changing the properties of your canvas outside of the EDT. As swpalmer points out, if you create your Universe object in the invokelater() method it will be run on the EDT. Google swing and thread safety.

Okay, so I have read and understand the principle, but still don’t see how this applies to my program as in the examples I’ve seen create JFrames and such in the main() method. So would I have to change my Universe object to include the SwingUtilties.invokeLater() method and move my initUniverse() method to there or can I include the SwingUtilities.invokeLater() method in my Game object and create my Universe instance there and if so, how will main() know to call the invokeLater() method and thus start the game?

Thanks for your patience.

Sometimes you can get away with it, but not always. It’s not even always consistent for the same program. The only safe thing to do is to construct it in the event thread.

You can create swing components off of the EDT. You just can’t modify them after they’ve been realized off of the EDT. The way your class is set up right now it realizes the canvas and modifies it in the constructor. If you wish to keep this functionality then you must create your class on the EDT with invokeLater. If you remove the initUniverse method from the constructor then you can create your universe object in the main method and then call initUniverse on the EDT with invokeLater.


public class Game {
        private static Universe theGame = null;

        public static void main(String[] args) {
                SwingUtilities.invokeLater( new Runnable() {
                        public void run() {
                                theGame = new Universe();
                        }
                });
        }
}

It is considered a bug to create Swing components off the EDT. It used to be considered okay (if they were un-“realized”), but things changed. You just aren’t allowed to do it at all now. The problem is that you don’t know what sort of listeners and things get called… For example, simply adding components to a Swing hierarchy will fire events… so the hierarchy must be built on the EDT.

Slap my ass and call me Shirley I didn’t know that had changed. Thanks for the heads up.