IBM Skip to main content
Search for:   within 
      Search help  
     IBM home  |  Products & services  |  Support & downloads   |  My account

developerWorks > Java technology
developerWorks
Magic with Merlin: Exercise complete control over your graphics display
Discuss71 KBe-mail it!
Contents:
Change display modes
Use fullscreen windows
Fullscreen rendering
A working example
First the screen, then the known universe!
Resources
About the author
Rate this article
Related content:
Magic with Merlin series
Introduction to the Retained Graphic Object API
Porter-Duff rules!
Subscriptions:
dW newsletters
dW Subscription
(CDs and downloads)
Fullscreen Exclusive Mode API lets you change display modes and draw directly to video RAM

Level: Introductory

John Zukowski (mailto:jaz@zukowski.net?cc=&subject=Exercise complete control over your graphics display)
President, JZ Ventures, Inc.
29 April 2003

Column iconGame developers and computer speed nuts alike will love Merlin's new Fullscreen Exclusive Mode (FEM) API, with which you can write directly to video memory with complete control over the graphics display. In this latest Magic with Merlin installment, Java programming expert John Zukowski introduces the power of this new Merlin feature. Share your thoughts on this article with the author and other readers in the accompanying discussion forum. (You can also click Discuss at the top or bottom of the article to access the forum.)

Do you enjoy upsetting people by making your programs do annoying things? If you answered "yes," this month's tip is sure to be a pleaser. With J2SE 1.4, your Java programs can now change video modes and seize absolute control of the screen. You need not let anyone else play nicely; you literally can possess total control. For this god-like power, thank the new Fullscreen Exclusive Mode (FEM) API.

Even if you answered "no" and you don't get your kicks by annoying others, you'll find the FEM API has much to offer. The FEM API delivers complete control of the display by writing directly to video RAM -- perfect for game development, although other uses abound. Some programs, for instance, simply look and work better with a certain size screen. Read on to discover your inner control freak.

Change display modes
Let's begin by examining the FEM API's java.awt.DislayMode class, which wraps the screen dimensions and refresh rate for a particular display mode. The modes supported are specific to a system's hardware support.

To find out a specific system's supported modes, ask the GraphicsEnvironment. From that environment you get the default screen device, a GraphicDevice, from which you get the display modes, as seen in Listing 1:

Listing 1. Look up display modes

    GraphicsEnvironment graphicsEnvironment =
      GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice graphicsDevice =
      graphicsEnvironment.getDefaultScreenDevice();
    DisplayMode displayModes[] =
      graphicsDevice.getDisplayModes();

You can also discover the current display mode with the getDisplayMode() method, as shown in Listing 2:

Listing 2. Get the current display mode
    DisplayMode originalDisplayMode =
      graphicsDevice.getDisplayMode();

Changing modes proves relatively easy, but you must first ask if the involved graphics device supports changing with the GraphicsDevice's isDisplayChangeSupported() method.

Once that's known, to change modes use the setDisplayMode() method, passing in the new mode. The display-mode change typically occurs in a try/finally block such that the finally block resets code to the original mode. While that process is not absolutely required, it ensures a safe mode upon the program's completion. Listing 3 shows a typical pattern for changing display modes:

Listing 3. Change modes

    GraphicsDevice graphicsDevice = ...
    DisplayMode originalDisplayMode = graphicsDevice.getDisplayMode();
    DisplayMode newDisplayMode = ...
    try {
      if (graphicsDevice.isDisplayChangeSupported()) {
        graphicsDevice.setDisplayMode(newDisplayMode);
      }
    } finally {
      graphicsDevice.setDisplayMode(originalDisplayMode);
    }

Use fullscreen windows
With the FEM API, entering fullscreen windows is a snap: just pass a window to the GraphicsDevice's setFullScreenWindow() method, as shown in Listing 4. When you want to return to nonfullscreen mode, pass null into the method. Of course, first check whether the GraphicsDevice supports fullscreen mode by employing the isFullScreenSupported() method.

Listing 4. Enter fullscreen mode

    GraphicsDevice graphicsDevice = ...
    Window window = ...
    try {
      if (graphicsDevice.isFullScreenSupported()) {
        graphicsDevice.setFullScreenWindow(window);
      }
    } finally {
      graphicsDevice.setFullScreenWindow(null);
    }

To illustrate everything you've learned so far, Listing 5 contains a complete example. The code in Listing 5 gets the display modes, picks one at random to change to, then displays a fullscreen window with the text "Hello, World!" The new display mode's characteristics display so you can see the specific screen size, refresh rate, and bit depth.

Listing 5. Mode changing example

import java.awt.*;
import javax.swing.*;
import java.util.Random;

public class DisplayModes {

  public static void main(String args[]) {
    GraphicsEnvironment graphicsEnvironment =
      GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice graphicsDevice =
      graphicsEnvironment.getDefaultScreenDevice();
    DisplayMode displayModes[] = graphicsDevice.getDisplayModes();
    DisplayMode originalDisplayMode = graphicsDevice.getDisplayMode();
    JWindow window = new JWindow() {
      public void paint(Graphics g) {
        g.setColor(Color.blue);
        g.drawString("Hello, World!", 50, 50);
      }
    };
    try {
      if (graphicsDevice.isFullScreenSupported()) {
        graphicsDevice.setFullScreenWindow(window);
      }
      Random random = new Random();
      int mode = random.nextInt(displayModes.length);
      DisplayMode displayMode = displayModes[mode];
      System.out.println(displayMode.getWidth() + "x" + 
        displayMode.getHeight() + " \t" + displayMode.getRefreshRate() + 
        " / " + displayMode.getBitDepth());
      if (graphicsDevice.isDisplayChangeSupported()) {
        graphicsDevice.setDisplayMode(displayMode);
      }
      Thread.sleep(1000);
    } catch (InterruptedException e) {
    } finally {
      graphicsDevice.setDisplayMode(originalDisplayMode);
      graphicsDevice.setFullScreenWindow(null);
    }
    System.exit(0);
  }
}

Fullscreen rendering
Notice in Listing 5 that the window used for the fullscreen includes a perfectly functional paint() method. However, because the window is in fullscreen mode, the overhead required for the paint() method, how the method deals with clipping, and other associated display-handling behaviors are unnecessary. Indeed, the standard passive rendering proves overkill, slowing fullscreen application down. You needn't deal with such tasks as overlapping or resizing a window, for instance. Instead, take a more active approach and create a tight loop that handles the rendering itself.

If you're familiar with double buffering, you know it manages two Image objects in memory and swaps between them based upon which possesses the current display information. While one Image displays, you draw to the other and swap Image objects between each scene.

Graphics cards employ a similar concept, but instead of working with actual Java Image objects, they swap memory pages. The display changes when you swap memory pages so you need not copy an Image object from program memory to video memory; you merely change video pointers and the display changes. While the double-buffering concept still exists, instead of writing to an Image in program space, you draw directly to video memory space.

The BufferStrategy class masks which of the two aforementioned double-buffering methodologies is used and lets you take advantage of any hardware-based buffering the system offers. To create a BufferStrategy, tell the system the number of buffers you desire with the createBufferStrategy() method and swap between buffers with the getDrawGraphics() method, which returns the next buffer to work with. Conceptually that's all there is to it, but, as Listing 6 shows, the working code requires a bit more effort:

Listing 6. Use a BufferStrategy

  JFrame frame = ...
  frame.createBufferStrategy(2); // Number of buffers to have
  BufferStrategy bufferStrategy = frame.getBufferStrategy();
  while (!done()) {  // Some condition to end
    Graphics g = null;
    try {
      g = bufferStrategy.getDrawGraphics();
      drawNextScene(g); // Method to draw to 
    } finally {
      // Free resources
      if (g != null) {
        g.dispose();
      }
    }
    bufferStrategy.show();
  }

When using a BufferStrategy, you can't assume the last item you drew to the buffer remains valid; you must ask with the contentsLost() method. If lost, you must redraw the entire buffer. Otherwise, you need draw only what's changed since its last usage.

In addition to buffer support with BufferStrategy, the BufferCapabilities class lets you discover what capabilities -- such as full screen mode -- a strategy supports with the getCapabilities() method.

A working example
Putting all the pieces together produces the example program in Listing 7. Because of my limited artistic skills, don't look for a sophisticated game. You will, however, find a complete working example employing a BufferStrategy and fullscreen drawing modes. The example program purposely does not synchronize buffers by remembering what wasn't drawn to the current buffer -- letting you see multiple buffers at work more clearly. The program does randomly draw 100 rectangles to the screen, with a 1/10th-of-a-second delay between draws.

Listing 7. A working example

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

public class MultipleBuffers {

  public static void main(String args[]) {
    GraphicsEnvironment graphicsEnvironment =
      GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice graphicsDevice =
      graphicsEnvironment.getDefaultScreenDevice();
    DisplayMode displayModes[] = graphicsDevice.getDisplayModes();
    DisplayMode originalDisplayMode = graphicsDevice.getDisplayMode();
    JFrame frame = new JFrame();
    frame.setUndecorated(true);
    frame.setIgnoreRepaint(true);
    try {
      if (graphicsDevice.isFullScreenSupported()) {
        graphicsDevice.setFullScreenWindow(frame);
      }
      Random random = new Random();
      int mode = random.nextInt(displayModes.length);
      DisplayMode displayMode = displayModes[mode];
      System.out.println(displayMode.getWidth() + "x" + 
        displayMode.getHeight() + " \t" + displayMode.getRefreshRate() + 
        " / " + displayMode.getBitDepth());
      if (graphicsDevice.isDisplayChangeSupported()) {
        graphicsDevice.setDisplayMode(displayMode);
      }
      frame.createBufferStrategy(2);
      BufferStrategy bufferStrategy = frame.getBufferStrategy();
      int width = frame.getWidth();
      int height = frame.getHeight();
      for (int i=0; i<100; i++) {
        Graphics g = bufferStrategy.getDrawGraphics();
        g.setColor(new Color(random.nextInt()));
  g.fillRect(random.nextInt(width), 
      random.nextInt(height), 100, 100);
        bufferStrategy.show();
        g.dispose();
        Thread.sleep(100);
      }
    } catch (InterruptedException e) {
    } finally {
      graphicsDevice.setDisplayMode(originalDisplayMode);
      graphicsDevice.setFullScreenWindow(null);
    }
    System.exit(0);
  }
}

The example program would benefit from such enhancements as keeping the buffers synchronized or checking if the complete buffer must be redrawn when contents are lost. The former task requires remembering only the last rectangle drawn (and the color), while the latter requires remembering them all.

First the screen, then the known universe!
With the new Fullscreen Exclusive Mode API, Java development becomes a mainstream option for game development. The API replaces the need to work with platform-specific APIs like DirectX or OpenGL, instead relying on a standard API across all Java-enabled platforms. It's more than an aspiring programming despot could imagine.

Resources

  • Participate in the discussion forum on this article. (You can also click Discuss at the top or bottom of the article to access the forum.)

  • Peruse John Zukowski's Magic with Merlin column archive for J2SE 1.4 tips.

  • In "Introduction to the Retained Graphic Object API" (developerWorks, November 2001), Barry Feigenbaum describes the Retained Graphic Objects (RGO) API, which enables the creation of reusable graphic objects for the Java platform.

  • The AlphaComposite class, which previously supported only 8 of the 12 Porter-Duff rules for digital image compositing, now supports all 12. In this earlier installment of Magic with Merlin, Porter-Duff rules! (developerWorks, September 2001), John Zukowski describes all 12 rules and offers an interactive program to demonstrate how they operate.

  • For more background on the FEM API, read the New Fullscreen Exclusive Mode API documentation from Sun Microsystems.

  • Bugtraq Report # 4189326 started it all (free login required).

  • Learn more about the DisplayMode class.

  • You'll also need more detail on the GraphicsDevice class.

  • The Java Tutorial offers more information about the FEM API.

  • You'll find hundreds of articles about every aspect of Java programming in the developerWorks Java technology zone.

About the author
Photo of John ZukowskiJohn Zukowski conducts strategic Java consulting with JZ Ventures, Inc. and serves as the resident guru for a number of jGuru's community-driven Java FAQs. His latest books include Learn Java with JBuilder 6 from Apress and Mastering Java 2: J2SE 1.4 from Sybex. Contact John at mailto:jaz@zukowski.net?Subject=Magic with Merlin.


Discuss71 KBe-mail it!

What do you think of this document?
Killer! (5) Good stuff (4) So-so; not bad (3) Needs work (2) Lame! (1)

Send us your comments or click Discuss to share your comments with others.



developerWorks > Java technology
developerWorks
  About IBM  |  Privacy  |  Terms of use  |  Contact