WinXP/Java6
I had to flip two fullscreen rows to make fullscreen-mode run, setFullscreenWindow must be called before setDisplayMode method. Then I added commandlines to make testing easier.
c:\test> java -cp test.jar GameLoop “fullscreen=true”
I have LCD 60fps screen and was able to see a tearing on both modes. FPS counter actually reported 60-63 frames on both windowed and fullscreen modes. It’s strange to have tearing even this simple and low-cpu intensive animation.
package gameloop;
import java.util.*;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferStrategy;
import java.awt.DisplayMode; // for full-screen mode
/**
* A basic game loop.
*
* @author Mario
*/
public class GameLoop implements KeyListener {
/*
* The display window
*/
Frame mainFrame;
/*
* Saves the time in nanos at which the last frame was drawn
*/
long lastFrameDrawn;
/*
* The frame rate cap. Set to 60 fps here as a default.
*/
long fpsLimit = 60;
/*
* Stores the frames per second.
*/
long fps;
/*
* Saves the last time we looked at the clock.
*/
long lastTime;
/*
* Saves a count of the number of frames drawn per 1 sec interval.
*/
long frameCounter;
/*
* Saves the elapsed time in nanos.
*/
long elapsedTime;
/*
* A rectangle to use as a basic game object to move
* around on screen.
*/
Rectangle2D rect;
/**
* Create a new GameLoop that will use the specified GraphicsDevice.
*
* @param device
*/
public GameLoop(Map<String,String> args, GraphicsDevice device) {
try {
// Setup the frame
GraphicsConfiguration gc = device.getDefaultConfiguration();
mainFrame = new Frame(gc);
mainFrame.setUndecorated(true);
mainFrame.setIgnoreRepaint(true);
mainFrame.setVisible(true);
mainFrame.setSize(640, 480);
mainFrame.setLocationRelativeTo(null);
mainFrame.createBufferStrategy(2);
mainFrame.addKeyListener(this);
// Uncomment this code if you want to see the game loop run full
// screen. Running in full screen will enable the buffer strategy's
// vertical retrace lock which should result in smoother animation.
if ("true".equalsIgnoreCase(args.get("fullscreen"))) {
device.setFullScreenWindow(mainFrame);
device.setDisplayMode(new DisplayMode(640, 480, 8, DisplayMode.REFRESH_RATE_UNKNOWN));
}
// Cache the buffer strategy and create a rectangle to move
BufferStrategy bufferStrategy = mainFrame.getBufferStrategy();
rect = new Rectangle2D.Float(0,100,64,64);
// Main loop
while(true) {
long time = System.nanoTime();
elapsedTime = System.nanoTime() - time;
updateWorld(elapsedTime);
// Draw
Graphics g = bufferStrategy.getDrawGraphics();
drawScreen(g);
g.dispose();
// Flip the buffer
if( ! bufferStrategy.contentsLost() )
bufferStrategy.show();
// Synchronise with the display hardware. Note that on
// Windows Vista this method may cause your screen to flash.
// If that bothers you, just comment it out.
Toolkit.getDefaultToolkit().sync();
yield();
calculateFramesPerSecond();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
device.setFullScreenWindow(null);
}
}
private void updateWorld(long elapsedTime) {
//double xMov = 0.001 * elapsedTime;
double xMov = 2.0;
rect.setRect(rect.getX() + xMov, 100, 64, 64);
if( rect.getX() > mainFrame.getWidth() )
rect.setRect(-rect.getWidth(), 100, 64, 64);
}
private void drawScreen(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(0, 0, mainFrame.getWidth(), mainFrame.getHeight());
g.setColor(Color.WHITE);
g.drawString("FPS: "+ fps, 0, 17);
g.setColor(Color.RED);
g.fillRect((int)rect.getX(), (int)rect.getY(), (int)rect.getWidth(), (int)rect.getHeight());
}
protected void yield() throws InterruptedException {
long delayTime = 1000000000L/fpsLimit - (System.nanoTime() - lastFrameDrawn);
if(delayTime > 0) Thread.sleep(delayTime/1000000);
lastFrameDrawn = System.nanoTime();
}
private void calculateFramesPerSecond() {
long time = System.nanoTime();
if( time - lastTime >= 1000000000L ) {
fps = frameCounter;
lastTime = time;
frameCounter = 0;
}
frameCounter++;
}
public void keyPressed(KeyEvent e) {
if( e.getKeyCode() == KeyEvent.VK_ESCAPE ) {
System.exit(0);
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public static void main(String[] args) {
try {
Map<String,String> mapArgs = parseArguments(args);
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice device = env.getDefaultScreenDevice();
new GameLoop(mapArgs, device);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Parse commandline arguments, each parameter is a name-value pair.
* Example: java.exe MyApp "key1=value1" "key2=value2"
*/
private static Map<String,String> parseArguments(String[] args) {
Map<String,String> mapArgs = new HashMap<String,String>();
for(int idx=0; idx < args.length; idx++) {
String val = args[idx];
int delimIdx = val.indexOf('=');
if (delimIdx < 0) {
mapArgs.put(val, null);
} else if (delimIdx == 0) {
mapArgs.put("", val.substring(1));
} else {
mapArgs.put(
val.substring(0, delimIdx).trim(),
val.substring(delimIdx+1)
);
}
}
return mapArgs;
}
}
(me still looking for a good mainloop for windowed mode gfx apps)

