FileDocCategorySizeDatePackage
Display.javaAPI DocJ2ME MIDP 2.0112071Thu Nov 07 12:02:26 GMT 2002javax.microedition.lcdui

Display.java

/*
 * @(#)Display.java	1.192 02/10/15
 *
 * Copyright (c) 1999-2002 Sun Microsystems, Inc.  All rights reserved.
 * PROPRIETARY/CONFIDENTIAL
 * Use is subject to license terms.
 */

package javax.microedition.lcdui;

import java.util.*;
import java.io.*;

/* This is used to implement the communication between MIDlet and Display */
import javax.microedition.midlet.MIDlet;

import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.io.Connector;

import com.sun.midp.midlet.MIDletState;
import com.sun.midp.midlet.MIDletStateMap;
import com.sun.midp.midlet.MIDletSuite;
import com.sun.midp.midlet.Scheduler;

import com.sun.midp.io.j2me.storage.File;
import com.sun.midp.io.j2me.storage.RandomAccessStream;
import com.sun.midp.main.Configuration;
import com.sun.midp.lcdui.*;

import com.sun.midp.security.*;

/**
 * <code>Display</code> represents the manager of the display and
 * input devices of the
 * system. It includes methods for retrieving properties of the device and
 * for requesting that objects be displayed on the device. Other methods that
 * deal with device attributes are primarily used with {@link Canvas Canvas}
 * objects and are thus defined there instead of here. <p>
 *
 * There is exactly one instance of Display per {@link
 * javax.microedition.midlet.MIDlet MIDlet} and the application can get a
 * reference to that instance by calling the {@link
 * #getDisplay(javax.microedition.midlet.MIDlet) getDisplay()} method. The
 * application may call the <code>getDisplay()</code> method at any time
 * during course of
 * its execution. The <code>Display</code> object
 * returned by all calls to <code>getDisplay()</code> will remain the
 * same during this
 * time. <p>
 *
 * A typical application will perform the following actions in response to
 * calls to its <code>MIDlet</code> methods:
 * <UL>
 * <LI><STRONG>startApp</STRONG> - the application is moving from the
 * paused state to the active state.
 * Initialization of objects needed while the application is active should be
 * done.  The application may call
 * {@link #setCurrent(Displayable) setCurrent()} for the first screen if that
 * has not already been done. Note that <code>startApp()</code> can be
 * called several
 * times if <code>pauseApp()</code> has been called in between. This
 * means that one-time
 * initialization
 * should not take place here but instead should occur within the
 * <code>MIDlet's</code>
 * constructor.
 * </LI>
 * <LI><STRONG>pauseApp</STRONG> - the application may pause its threads.
 * Also, if it is
 * desirable to start with another screen when the application is re-activated,
 * the new screen should be set with <code>setCurrent()</code>.</LI>
 * <LI><STRONG>destroyApp</STRONG> - the application should free resources,
 * terminate threads, etc.
 * The behavior of method calls on user interface objects after
 * <code>destroyApp()</code> has returned is undefined. </li>
 * </UL>
 * <p>
 *
 * <P>The user interface objects that are shown on the display device are
 * contained within a {@link Displayable Displayable} object. At any time the
 * application may have at most one <code>Displayable</code> object
 * that it intends to be
 * shown on the display device and through which user interaction occurs.  This
 * <code>Displayable</code> is referred to as the <em>current</em>
 * <code>Displayable</code>. </p>
 *
 * <P>The <code>Display</code> class has a {@link
 * #setCurrent(Displayable) setCurrent()}
 * method for setting the current <code>Displayable</code> and a
 * {@link #getCurrent()
 * getCurrent()} method for retrieving the current
 * <code>Displayable</code>.  The
 * application has control over its current <code>Displayable</code>
 * and may call
 * <code>setCurrent()</code> at any time.  Typically, the application
 * will change the
 * current <code>Displayable</code> in response to some user action.
 * This is not always the
 * case, however.  Another thread may change the current
 * <code>Displayable</code> in
 * response to some other stimulus.  The current
 * <code>Displayable</code> will also be
 * changed when the timer for an {@link Alert Alert} elapses. </P>
 *
 * <p> The application's current <code>Displayable</code> may not
 * physically be drawn on the
 * screen, nor will user events (such as keystrokes) that occur necessarily be
 * directed to the current <code>Displayable</code>.  This may occur
 * because of the presence
 * of other <code>MIDlet</code> applications running simultaneously on
 * the same device. </p>
 *
 * <P>An application is said to be in the <em>foreground</em> if its current
 * <code>Displayable</code> is actually visible on the display device
 * and if user input
 * device events will be delivered to it. If the application is not in the
 * foreground, it lacks access to both the display and input devices, and it is
 * said to be in the <em>background</em>. The policy for allocation of these
 * devices to different <code>MIDlet</code> applications is outside
 * the scope of this
 * specification and is under the control of an external agent referred to as
 * the <em>application management software</em>. </p>
 *
 * <P>As mentioned above, the application still has a notion of its current
 * <code>Displayable</code> even if it is in the background. The
 * current <code>Displayable</code> is
 * significant, even for background applications, because the current
 * <code>Displayable</code> is always the one that will be shown the
 * next time the
 * application is brought into the foreground.  The application can determine
 * whether a <code>Displayable</code> is actually visible on the
 * display by calling {@link
 * Displayable#isShown isShown()}. In the case of <code>Canvas</code>,
 * the {@link
 * Canvas#showNotify() showNotify()} and {@link Canvas#hideNotify()
 * hideNotify()} methods are called when the <code>Canvas</code> is
 * made visible and is
 * hidden, respectively.</P>
 *
 * <P> Each <code>MIDlet</code> application has its own current
 * <code>Displayable</code>.  This means
 * that the {@link #getCurrent() getCurrent()} method returns the
 * <code>MIDlet's</code>
 * current <code>Displayable</code>, regardless of the
 * <code>MIDlet's</code> foreground/background
 * state.  For example, suppose a <code>MIDlet</code> running in the
 * foreground has current
 * <code>Displayable</code> <em>F</em>, and a <code>MIDlet</code>
 * running in the background has current
 * <code>Displayable</code> <em>B</em>.  When the foreground
 * <code>MIDlet</code> calls <code>getCurrent()</code>, it
 * will return <em>F</em>, and when the background <code>MIDlet</code>
 * calls <code>getCurrent()</code>, it
 * will return <em>B</em>.  Furthermore, if either <code>MIDlet</code>
 * changes its current
 * <code>Displayable</code> by calling <code>setCurrent()</code>, this
 * will not affect the any other
 * <code>MIDlet's</code> current <code>Displayable</code>. </p>
 *
 * <P>It is possible for <code>getCurrent()</code> to return
 * <code>null</code>. This may occur at startup
 * time, before the <code>MIDlet</code> application has called
 * <code>setCurrent()</code> on its first
 * screen.  The <code>getCurrent(</code>) method will never return a
 * reference to a
 * <code>Displayable</code> object that was not passed in a prior call
 * to <code>setCurrent()</code> call
 * by this <code>MIDlet</code>. </p>
 *
 * <a name="systemscreens"></a>
 * <h3>System Screens</h3>
 *
 * <P> Typically, the
 * current screen of the foreground <code>MIDlet</code> will be
 * visible on the display.
 * However, under certain circumstances, the system may create a screen that
 * temporarily obscures the application's current screen.  These screens are
 * referred to as <em>system screens.</em> This may occur if the system needs
 * to show a menu of commands or if the system requires the user to edit text
 * on a separate screen instead of within a text field inside a
 * <code>Form</code>.  Even
 * though the system screen obscures the application's screen, the notion of
 * the current screen does not change.  In particular, while a system screen is
 * visible, a call to <code>getCurrent()</code> will return the
 * application's current
 * screen, not the system screen.  The value returned by
 * <code>isShown()</code> is <code>false</code>
 * while the current <code>Displayable</code> is obscured by a system
 * screen. </p>
 *
 * <p> If system screen obscures a canvas, its
 * <code>hideNotify()</code> method is called.
 * When the system screen is removed, restoring the canvas, its
 * <code>showNotify()</code>
 * method and then its <code>paint()</code> method are called.  If the
 * system screen was used
 * by the user to issue a command, the <code>commandAction()</code>
 * method is called after
 * <code>showNotify()</code> is called. </p>
 *
 * <p>This class contains methods to retrieve the prevailing foreground and
 * background colors of the high-level user interface.  These methods are
 * useful for creating <CODE>CustomItem</CODE> objects that match the user
 * interface of other items and for creating user interfaces within
 * <CODE>Canvas</CODE> that match the user interface of the rest of the
 * system.  Implementations are not restricted to using foreground and
 * background colors in their user interfaces (for example, they might use
 * highlight and shadow colors for a beveling effect) but the colors returned
 * are those that match reasonably well with the implementation's color
 * scheme.  An application implementing a custom item should use the
 * background color to clear its region and then paint text and geometric
 * graphics (lines, arcs, rectangles) in the foreground color.</p>
 *
 * @since MIDP 1.0
 */

public class Display {

/*
 * ************* public member variables
 */

    /**
     * Image type for <code>List</code> element image.
     *
     * <P>The value of <code>LIST_ELEMENT</code> is <code>1</code>.</P>
     * 
     * @see #getBestImageWidth(int imageType)
     * @see #getBestImageHeight(int imageType)
     * @since MIDP 2.0
     */
    public static final int LIST_ELEMENT = 1;

    /**
     * Image type for <code>ChoiceGroup</code> element image.
     * 
     * <P>The value of <code>CHOICE_GROUP_ELEMENT</code> is <code>2</code>.</P>
     * 
     * @see #getBestImageWidth(int imageType)
     * @see #getBestImageHeight(int imageType)
     * @since MIDP 2.0
     */
    public static final int CHOICE_GROUP_ELEMENT = 2;

    /**
     * Image type for <code>Alert</code> image.
     * 
     * <P>The value of <code>ALERT</code> is <code>3</code>.</P>
     * 
     * @see #getBestImageWidth(int imageType)
     * @see #getBestImageHeight(int imageType)
     * @since MIDP 2.0
     */
    public static final int ALERT = 3;

    /**
     * A color specifier for use with <code>getColor</code>.
     * <code>COLOR_BACKGROUND</code> specifies the background color of
     * the screen.
     * The background color will always contrast with the foreground color.
     *
     * <p>
     * <code>COLOR_BACKGROUND</code> has the value <code>0</code>.
     *
     * @see #getColor
     * @since MIDP 2.0
     */
    public static final int COLOR_BACKGROUND = 0;

    /** 
     * A color specifier for use with <code>getColor</code>.
     * <code>COLOR_FOREGROUND</code> specifies the foreground color,
     * for text characters
     * and simple graphics on the screen.  Static text or user-editable
     * text should be drawn with the foreground color.  The foreground color
     * will always constrast with background color.
     *
     * <p> <code>COLOR_FOREGROUND</code> has the value <code>1</code>.
     *
     * @see #getColor 
     * @since MIDP 2.0 
     */
    public static final int COLOR_FOREGROUND = 1;

    /**
     * A color specifier for use with <code>getColor</code>.
     * <code>COLOR_HIGHLIGHTED_BACKGROUND</code> identifies the color for the
     * focus, or focus highlight, when it is drawn as a
     * filled in rectangle. The highlighted
     * background will always constrast with the highlighted foreground.
     *
     * <p>
     * <code>COLOR_HIGHLIGHTED_BACKGROUND</code> has the value <code>2</code>.
     *
     * @see #getColor
     * @since MIDP 2.0 
     */
    public static final int COLOR_HIGHLIGHTED_BACKGROUND = 2;

    /**
     * A color specifier for use with <code>getColor</code>.
     * <code>COLOR_HIGHLIGHTED_FOREGROUND</code> identifies the color for text
     * characters and simple graphics when they are highlighted. 
     * Highlighted
     * foreground is the color to be used to draw the highlighted text
     * and graphics against the highlighted background.
     * The highlighted foreground will always constrast with
     * the highlighted background.
     *
     * <p>
     * <code>COLOR_HIGHLIGHTED_FOREGROUND</code> has the value <code>3</code>.
     *
     * @see #getColor
     * @since MIDP 2.0 
     */
    public static final int COLOR_HIGHLIGHTED_FOREGROUND = 3;

    /**
     * A color specifier for use with <code>getColor</code>.
     * <code>COLOR_BORDER</code> identifies the color for boxes and borders
     * when the object is to be drawn in a
     * non-highlighted state.  The border color is intended to be used with 
     * the background color and will contrast with it.
     * The application should draw its borders using the stroke style returned 
     * by <code>getBorderStyle()</code>.
     *
     * <p> <code>COLOR_BORDER</code> has the value <code>4</code>.
     *
     * @see #getColor
     * @since MIDP 2.0 
     */
    public static final int COLOR_BORDER = 4;

    /**
     * A color specifier for use with <code>getColor</code>.
     * <code>COLOR_HIGHLIGHTED_BORDER</code>
     * identifies the color for boxes and borders when the object is to be
     * drawn in a highlighted state.  The highlighted border color is intended
     * to be used with the background color (not the highlighted background
     * color) and will contrast with it.  The application should draw its
     * borders using the stroke style returned <code>by getBorderStyle()</code>.
     *
     * <p> <code>COLOR_HIGHLIGHTED_BORDER</code> has the value <code>5</code>.
     *
     * @see #getColor
     * @since MIDP 2.0 
     */
    public static final int COLOR_HIGHLIGHTED_BORDER = 5;

/*
 * ************* protected member variables
 */

/*
 * ************* package private member variables
 */

    /** Static lock object for LCDUI package */
    static final Object LCDUILock = new Object();

    /** Static lock object for making calls into application code */
    static final Object calloutLock = new Object();

    /** horizontal width */
    static final int WIDTH;

    /** vertical height */
    static final int HEIGHT;
    
    /** height available to draw on in normal mode */
    static final int ADORNEDHEIGHT;

    /** background color for erasing */
    static final int ERASE_COLOR;

    /** border color for non-highlighted border */
    static final int BORDER_COLOR = 0x00AFAFAF; // light gray

    /** border color for highlighted border */
    static final int BORDER_H_COLOR = 0x00606060; // dark gray

    /** pixel depth of display. */
    static final int DISPLAY_DEPTH;

    /** true, if display supports color. */
    static final boolean DISPLAY_IS_COLOR;

    /** true, if the device has a pointing device. */
    static final boolean POINTER_SUPPORTED;

    /** true, if motion events are supported. */
    static final boolean MOTION_SUPPORTED;

    /** true, if repeating events are supported. */
    static final boolean REPEAT_SUPPORTED;

    /** true, if the display is double buffered. */
    static final boolean IS_DOUBLE_BUFFERED;

    /** Standard foreground color */
    static final int FG_COLOR;

    /** Standard background highlight color */
    static final int BG_H_COLOR;

    /** Standard foreground highlight color */
    static final int FG_H_COLOR;

    /** number of alpha levels supported */
    static final int ALPHA_LEVELS;

    /** keyCode for up arrow */
    static final int KEYCODE_UP;

    /** keyCode for down arrow */
    static final int KEYCODE_DOWN;

    /** keyCode for left arrow */
    static final int KEYCODE_LEFT;

    /** keyCode for right arrow */
    static final int KEYCODE_RIGHT;

    /** keyCode for select */
    static final int KEYCODE_SELECT;

/*
 * ************* private member variables
 */

    /** Display manager with private methods. */
    private static DisplayManagerImpl displayManagerImpl;
    /** Device Access manager. */
    private static DisplayDeviceAccess deviceAccess;
    /** event handler for this Display instance. */
    private static EventHandler eventHandler;

    /**  singleton Graphics object */
    private static final Graphics screenGraphics;

    /** display accessor helper class */
    private DisplayAccessor accessor;

    /** MIDlet for this display */
    private MIDlet midlet;

    /** current displayable instance */
    private Displayable current;

    /** true, if last setCurrent was non-null */
    private boolean wantsForeground;

    /** stores key code of the current key pressed at least once */
    // caters to the GameCanvas.getKeyStats()
    // latching behavior. This latched state is cleared
    // when the getKeyStats() is called.
    private int stickyKeyMask;  

    /** stores key code of the current key is currently down */
    // sets the key to 1 when the key
    // is currently down
    private int currentKeyMask;

    /** What gets the MIDlet level events. */
    private MIDletEventListener midletEventListener;

    /** true, if painting operations are suspended. */
    private boolean paintSuspended; // = false;

    /** true, if the Display is the foreground object. */
    private boolean hasForeground; //  = false;

    /** first queue of serialized repaint operations. */
    private static java.util.Vector queue1 = new java.util.Vector();

    /** second queue of serialized repaint operations. */
    private static java.util.Vector queue2 = new java.util.Vector();

    /** current active queue for serially repainted operations. */
    private static java.util.Vector currentQueue = queue1;

    /** This class has a different security domain than the MIDlet suite */
    private static SecurityToken classSecurityToken;

/*
 * ************* Static initializer, constructor
 */
    static {

        /* done this way because native access to static fields is hard */
        DeviceCaps c = new DeviceCaps();

        WIDTH               = c.width;
        HEIGHT              = c.height;
        ADORNEDHEIGHT       = c.adornedHeight;
        ERASE_COLOR         = c.eraseColor;
        DISPLAY_DEPTH       = c.displayDepth;
        DISPLAY_IS_COLOR    = c.displayIsColor;
        POINTER_SUPPORTED   = c.pointerSupported;
        MOTION_SUPPORTED    = c.motionSupported;
        REPEAT_SUPPORTED    = c.repeatSupported;
        IS_DOUBLE_BUFFERED  = c.isDoubleBuffered;
        FG_COLOR            = 0;
        BG_H_COLOR          = FG_COLOR;
        FG_H_COLOR          = ERASE_COLOR;

        Text.FG_COLOR       = FG_COLOR;
        Text.FG_H_COLOR     = FG_H_COLOR;

        ALPHA_LEVELS        = c.numAlphaLevels;

        KEYCODE_UP          = c.keyCodeUp;
        KEYCODE_DOWN        = c.keyCodeDown;
        KEYCODE_LEFT        = c.keyCodeLeft;
        KEYCODE_RIGHT       = c.keyCodeRight;
        KEYCODE_SELECT      = c.keyCodeSelect;

        c = null; // let the DeviceCaps instance be garbage collected

        /* Let com.sun.midp classes call in to this class. */
        displayManagerImpl = new DisplayManagerImpl();
        DisplayManagerFactory.SetDisplayManagerImpl(displayManagerImpl);
        deviceAccess = new DisplayDeviceAccess();
        eventHandler = getEventHandler();

        screenGraphics = Graphics.getGraphics(null);
    }


    /**
     * initializes the display with an accessor helper class.
     *
     * @param m MIDlet that owns this display, can be null
     */
    Display(MIDlet m) {
        midlet = m;
        accessor = new DisplayAccessor();
        drawTrustedIcon(false);
    }

/*
 * ************* public methods
 */

    /**
     * Gets the <code>Display</code> object that is unique to this
     * <code>MIDlet</code>.
     * @param m <code>MIDlet</code> of the application
     * @return the display object that application can use for its user
     * interface
     *
     * @throws NullPointerException if <code>m</code> is <code>null</code>
     */
    public static Display getDisplay(MIDlet m) {
        MIDletState ms;
        Display d;

        synchronized (LCDUILock) {
            // Find or create if necessary the Display
            ms = MIDletStateMap.getState(m);
            if (ms != null) {
                d = ms.getDisplay();
                if (d != null) {
                    return d;
                }
            }

            throw new
                IllegalStateException("No display created for given MIDlet");
        }
    }

    /**
     * Returns one of the colors from the high level user interface
     * color scheme, in the form <code>0x00RRGGBB</code> based on the
     * <code>colorSpecifier</code> passed in.
     *
     * @param colorSpecifier the predefined color specifier;
     *  must be one of
     *  {@link #COLOR_BACKGROUND},
     *  {@link #COLOR_FOREGROUND},
     *  {@link #COLOR_HIGHLIGHTED_BACKGROUND},
     *  {@link #COLOR_HIGHLIGHTED_FOREGROUND},
     *  {@link #COLOR_BORDER}, or
     *  {@link #COLOR_HIGHLIGHTED_BORDER}
     * @return color in the form of <code>0x00RRGGBB</code>
     * @throws IllegalArgumentException if <code>colorSpecifier</code>
     * is not a valid color specifier
     * @since MIDP 2.0 
     */
    public int getColor(int colorSpecifier) {
        switch (colorSpecifier) {
        case COLOR_BACKGROUND:
            return ERASE_COLOR;
        case COLOR_FOREGROUND:
            return FG_COLOR;
        case COLOR_HIGHLIGHTED_BACKGROUND:
            return BG_H_COLOR;
        case COLOR_HIGHLIGHTED_FOREGROUND:
            return FG_H_COLOR;
        // REMINDER: COLOR_BORDER color and
        // COLOR_HIGHLIGHTED_BORDER color will
        // be changed once HI input is avaiable.
        case COLOR_BORDER:
            return BORDER_COLOR;
        case COLOR_HIGHLIGHTED_BORDER:
            return BORDER_H_COLOR;
        default:
            throw new IllegalArgumentException();
        }
    }

    /**
     * Returns the stroke style used for border drawing
     * depending on the state of the component
     * (highlighted/non-highlighted). For example, on a monochrome
     * system, the border around a non-highlighted item might be
     * drawn with a <code>DOTTED</code> stroke style while the border around a
     * highlighted item might be drawn with a <code>SOLID</code> stroke style.
     *
     * @param highlighted <code>true</code> if the border style being
     * requested is for the
     * highlighted state, <code>false</code> if the border style being
     * requested is for the
     * non-highlighted state
     * @return {@link Graphics#DOTTED} or {@link Graphics#SOLID}
     * @since MIDP 2.0 
     */
    public int getBorderStyle(boolean highlighted) {
        return (highlighted == true ? Graphics.SOLID : Graphics.DOTTED);
    }

    /**
     * Gets information about color support of the device.
     * @return <code>true</code> if the display supports color, 
     * <code>false</code> otherwise
     */
    public boolean isColor() {
        return DISPLAY_IS_COLOR;
    }

    /**
     * Gets the number of colors (if <code>isColor()</code> is
     * <code>true</code>)
     * or graylevels (if <code>isColor()</code> is <code>false</code>)
     * that can be
     * represented on the device.<P>
     * Note that the number of colors for a black and white display is
     * <code>2</code>.
     * @return number of colors
     */
    public int numColors() {
        return (1 << DISPLAY_DEPTH);
    }

    /**
     * Gets the number of alpha transparency levels supported by this
     * implementation.  The minimum legal return value is
     * <code>2</code>, which indicates
     * support for full transparency and full opacity and no blending.  Return
     * values greater than <code>2</code> indicate that alpha blending
     * is supported.  For
     * further information, see <a href="Image.html#alpha">Alpha
     * Processing</a>.
     *
     * @return number of alpha levels supported
     * @since MIDP 2.0
     */
    public int numAlphaLevels() {
        return ALPHA_LEVELS;
    }

    /**
     * Gets the current <code>Displayable</code> object for this
     * <code>MIDlet</code>.  The
     * <code>Displayable</code> object returned may not actually be
     * visible on the display
     * if the <code>MIDlet</code> is running in the background, or if
     * the <code>Displayable</code> is
     * obscured by a system screen.  The {@link Displayable#isShown()
     * Displayable.isShown()} method may be called to determine whether the
     * <code>Displayable</code> is actually visible on the display. 
     *
     * <p> The value returned by <code>getCurrent()</code> may be
     * <code>null</code>. This
     * occurs after the application has been initialized but before the first
     * call to <code>setCurrent()</code>. </p>
     *
     * @return the <code>MIDlet's</code> current <code>Displayable</code> object
     * @see #setCurrent
     */
    public Displayable getCurrent() {
        return current;
    }

    /**
     * Requests that a different <code>Displayable</code> object be
     * made visible on the
     * display.  The change will typically not take effect immediately.  It
     * may be delayed so that it occurs between event delivery method
     * calls, although it is not guaranteed to occur before the next event
     * delivery method is called.  The <code>setCurrent()</code> method returns
     * immediately, without waiting for the change to take place.  Because of
     * this delay, a call to <code>getCurrent()</code> shortly after a
     * call to <code>setCurrent()</code>
     * is unlikely to return the value passed to <code>setCurrent()</code>.
     *
     * <p> Calls to <code>setCurrent()</code> are not queued.  A
     * delayed request made by a
     * <code>setCurrent()</code> call may be superseded by a subsequent call to
     * <code>setCurrent()</code>.  For example, if screen
     * <code>S1</code> is current, then </p>
     *
     * <TABLE BORDER="2">
     * <TR>
     * <TD ROWSPAN="1" COLSPAN="1">
     *    <pre><code>
     *     d.setCurrent(S2);
     *     d.setCurrent(S3);     </code></pre>
     * </TD>
     * </TR>
     * </TABLE>
     *
     * <p> may eventually result in <code>S3</code> being made
     * current, bypassing <code>S2</code>
     * entirely. </p>
     *
     * <p> When a <code>MIDlet</code> application is first started,
     * there is no current
     * <code>Displayable</code> object.  It is the responsibility of
     * the application to
     * ensure that a <code>Displayable</code> is visible and can
     * interact with the user at
     * all times.  Therefore, the application should always call
     * <code>setCurrent()</code>
     * as part of its initialization. </p>
     *
     * <p> The application may pass <code>null</code> as the argument to
     * <code>setCurrent()</code>.  This does not have the effect of
     * setting the current
     * <code>Displayable</code> to <code>null</code>; instead, the
     * current <code>Displayable</code>
     * remains unchanged.  However, the application management software may
     * interpret this call as a request from the application that it is
     * requesting to be placed into the background.  Similarly, if the
     * application is in the background, passing a non-null
     * reference to <code>setCurrent()</code> may be interpreted by
     * the application
     * management software as a request that the application is
     * requesting to be
     * brought to the foreground.  The request should be considered to be made
     * even if the current <code>Displayable</code> is passed to the
     * <code>setCurrent()</code>.  For
     * example, the code </p>
     * <TABLE BORDER="2">
     * <TR>
     * <TD ROWSPAN="1" COLSPAN="1">
     *    <pre><code>
     *   d.setCurrent(d.getCurrent());    </code></pre>
     * </TD>
     * </TR>
     * </TABLE>
     * <p> generally will have no effect other than requesting that the
     * application be brought to the foreground.  These are only requests,
     * and there is no requirement that the application management
     * software comply with these requests in a timely fashion if at all. </p>
     *
     * <p> If the <code>Displayable</code> passed to
     * <code>setCurrent()</code> is an {@link Alert
     * Alert}, the previously current <code>Displayable</code>, if
     * any, is restored after
     * the <code>Alert</code> has been dismissed.  If there is a
     * current <code>Displayable</code>, the
     * effect is as if <code>setCurrent(Alert, getCurrent())</code>
     * had been called.  Note
     * that this will result in an exception being thrown if the current
     * <code>Displayable</code> is already an alert.  If there is no
     * current <code>Displayable</code>
     * (which may occur at startup time) the implementation's previous state
     * will be restored after the <code>Alert</code> has been
     * dismissed.  The automatic
     * restoration of the previous <code>Displayable</code> or the
     * previous state occurs
     * only when the <code>Alert's</code> default listener is present
     * on the <code>Alert</code> when it
     * is dismissed.  See <a href="Alert.html#commands">Alert Commands and
     * Listeners</a> for details.</p>
     *
     * <p>To specify the
     * <code>Displayable</code> to be shown after an
     * <code>Alert</code> is dismissed, the application
     * should use the {@link #setCurrent(Alert,Displayable) setCurrent(Alert,
     * Displayable)} method.  If the application calls
     * <code>setCurrent()</code> while an
     * <code>Alert</code> is current, the <code>Alert</code> is
     * removed from the display and any timer
     * it may have set is cancelled. </p>
     *
     * <p> If the application calls <code>setCurrent()</code> while a
     * system screen is
     * active, the effect may be delayed until after the system screen is
     * dismissed.  The implementation may choose to interpret
     * <code>setCurrent()</code> in
     * such a situation as a request to cancel the effect of the system
     * screen, regardless of whether <code>setCurrent()</code> has
     * been delayed. </p>
     *
     * @param nextDisplayable the <code>Displayable</code> requested
     * to be made current;
     * <code>null</code> is allowed
     * @see #getCurrent
     */
    public void setCurrent(Displayable nextDisplayable) {
        synchronized (LCDUILock) {
            if (nextDisplayable instanceof Alert) {
                /*
                 * This implicitly goes back to the current screen.
                 *
                 * If there is a pending screen change, we take that
                 * into account also.  This probably acts according to
                 * the principle of least astonishment.  Also, it
                 * has the effect of preventing an implicit call of
                 * setCurrent(Alert, Alert) because nextScreen is
                 * never an alert.
                 *
                 * REMIND: This is subject to spec interpretation;
                 * the handling of setCurrent(Alert) is a weak area.
                 */

                // NOTE: Removed the copy of the Alert
                ((Alert)nextDisplayable).setReturnScreen(current);
            }

            setCurrentImpl(nextDisplayable);
        } // synchronized
    }

    /**
     * Requests that this <code>Alert</code> be made current, and that
     * <code>nextDisplayable</code> be
     * made current
     * after the <code>Alert</code> is dismissed.  This call returns
     * immediately regardless
     * of the <code>Alert's</code> timeout value or whether it is a
     * modal alert.  The
     * <code>nextDisplayable</code> must not be an <code>Alert</code>,
     * and it must not be <code>null</code>.
     *
     * <p>The automatic advance to <code>nextDisplayable</code> occurs only 
     * when the <code>Alert's</code> default listener is present on
     * the <code>Alert</code> when it
     * is dismissed.  See <a href="Alert.html#commands">Alert Commands and
     * Listeners</a> for details.</p>
     * 
     * <p> In other respects, this method behaves identically to
     * {@link #setCurrent(Displayable) setCurrent(Displayable)}. </p>
     *
     * @param alert the alert to be shown
     * @param nextDisplayable the <code>Displayable</code> to be
     * shown after this alert is  dismissed
     * 
     * @throws NullPointerException if alert or
     * <code>nextDisplayable</code> is <code>null</code>
     * @throws IllegalArgumentException if <code>nextDisplayable</code>
     * is an <code>Alert</code>
     * @see Alert
     * @see #getCurrent
     */
    public void setCurrent(Alert alert, Displayable nextDisplayable) {
        if ((alert == null) || (nextDisplayable == null)) {
            throw new NullPointerException();
        }

        if (nextDisplayable instanceof Alert) {
            throw new IllegalArgumentException();
        }

        synchronized (LCDUILock) {
            alert.setReturnScreen(nextDisplayable);
            setCurrentImpl(alert);
        }
    }

    /**
     * Requests that the <code>Displayable</code> that contains this
     * <code>Item</code> be made current,
     * scrolls the <code>Displayable</code> so that this
     * <code>Item</code> is visible, and possibly
     * assigns the focus to this <code>Item</code>.  The containing
     * <code>Displayable</code> is first
     * made current as if {@link #setCurrent(Displayable)
     * setCurrent(Displayable)} had been called.  When the containing
     * <code>Displayable</code> becomes current, or if it is already
     * current, it is
     * scrolled if necessary so that the requested <code>Item</code>
     * is made visible.
     * Then, if the implementation supports the notion of input focus, and if
     * the <code>Item</code> accepts the input focus, the input focus
     * is assigned to the
     * <code>Item</code>.
     *
     * <p>This method always returns immediately, without waiting for the
     * switching of the <code>Displayable</code>, the scrolling, and
     * the assignment of
     * input focus to take place.</p>
     *
     * <p>It is an error for the <code>Item</code> not to be contained
     * within a container.
     * It is also an error if the <code>Item</code> is contained
     * within an <code>Alert</code>.</p>
     *
     * @param item the item that should be made visible
     * @throws IllegalStateException if the item is not owned by a container
     * @throws IllegalStateException if the item is owned by an 
     * <code>Alert</code>
     * @throws NullPointerException if <code>item</code> is <code>null</code>
     * @since MIDP 2.0
     */
    public void setCurrentItem(Item item) {
        synchronized (LCDUILock) {

            Screen nextDisplayable = item.getOwner();
            if (nextDisplayable instanceof Form) {
                ((Form)nextDisplayable).setCurrentItem(item);
            }

            if (nextDisplayable == null) {
                throw new IllegalStateException();
            }

            setCurrentImpl(nextDisplayable);
        } // synchronized
    }

    /**
     * Causes the <code>Runnable</code> object <code>r</code> to have
     * its <code>run()</code> method
     * called later, serialized with the event stream, soon after completion of
     * the repaint cycle.  As noted in the
     * <a href="./package-summary.html#events">Event Handling</a>
     * section of the package summary,
     * the methods that deliver event notifications to the application
     * are all called serially. The call to <code>r.run()</code> will
     * be serialized along with
     * the event calls into the application. The <code>run()</code>
     * method will be called exactly once for each call to
     * <code>callSerially()</code>. Calls to <code>run()</code> will
     * occur in the order in which they were requested by calls to
     * <code>callSerially()</code>.
     *
     * <p> If the current <code>Displayable</code> is a <code>Canvas</code>
     * that has a repaint pending at the time of a call to
     * <code>callSerially()</code>, the <code>paint()</code> method of the
     * <code>Canvas</code> will be called and
     * will return, and a buffer switch will occur (if double buffering is in
     * effect), before the <code>run()</code> method of the
     * <code>Runnable</code> is called.
     * If the current <code>Displayable</code> contains one or more
     * <code>CustomItems</code> that have repaints pending at the time
     * of a call to <code>callSerially()</code>, the <code>paint()</code>
     * methods of the <code>CustomItems</code> will be called and will
     * return before the <code>run()</code> method of the
     * <code>Runnable</code> is called.
     * Calls to the
     * <code>run()</code> method will occur in a timely fashion, but
     * they are not guaranteed
     * to occur immediately after the repaint cycle finishes, or even before
     * the next event is delivered. </p>
     *
     * <p> The <code>callSerially()</code> method may be called from
     * any thread. The call to
     * the <code>run()</code> method will occur independently of the
     * call to <code>callSerially()</code>.
     * In particular, <code>callSerially()</code> will <em>never</em>
     * block waiting
     * for <code>r.run()</code>
     * to return. </p>
     *
     * <p> As with other callbacks, the call to <code>r.run()</code>
     * must return quickly. If
     * it is necessary to perform a long-running operation, it may be initiated
     * from within the <code>run()</code> method. The operation itself
     * should be performed
     * within another thread, allowing <code>run()</code> to return. </p>
     *
     * <p> The <code>callSerially()</code> facility may be used by
     * applications to run an
     * animation that is properly synchronized with the repaint cycle. A
     * typical application will set up a frame to be displayed and then call
     * <code>repaint()</code>.  The application must then wait until
     * the frame is actually
     * displayed, after which the setup for the next frame may occur.  The call
     * to <code>run()</code> notifies the application that the
     * previous frame has finished
     * painting.  The example below shows <code>callSerially()</code>
     * being used for this
     * purpose. </p>
     * <TABLE BORDER="2">
     * <TR>
     * <TD ROWSPAN="1" COLSPAN="1">
     *    <pre><code>
     *     class Animation extends Canvas
     *         implements Runnable {
     *
     *     // paint the current frame
     *     void paint(Graphics g) { ... }
     *
     *        Display display; // the display for the application
     *
     *        void paint(Graphics g) { ... } // paint the current frame
     *
     *        void startAnimation() {
     *            // set up initial frame
     *            repaint();
     *            display.callSerially(this);
     *        }
     *
     *        // called after previous repaint is finished
     *        void run() {
     *            if ( /* there are more frames */ ) {
     *                // set up the next frame
     *                repaint();
     *                display.callSerially(this);
     *            }
     *        }
     *     }    </code></pre>
     * </TD>
     * </TR>
     * </TABLE>
     * @param r instance of interface <code>Runnable</code> to be called
     */
    public void callSerially(Runnable r) {
        if (r == null) {
            throw new NullPointerException();
        }
        synchronized (LCDUILock) {
            currentQueue.addElement(r);
            eventHandler.scheduleCallSerially();
        }
    }

    /**
     * Requests a flashing effect for the device's backlight.  The flashing
     * effect is intended to be used to attract the user's attention or as a
     * special effect for games.  Examples of flashing are cycling the
     * backlight on and off or from dim to bright repeatedly.
     * The return value indicates if the flashing of the backlight
     * can be controlled by the application.
     *
     * <p>The flashing effect occurs for the requested duration, or it is
     * switched off if the requested duration is zero.  This method returns
     * immediately; that is, it must not block the caller while the flashing
     * effect is running.</p>
     *
     * <p>Calls to this method are honored only if the
     * <code>Display</code> is in the
     * foreground.  This method MUST perform no action
     * and return <CODE>false</CODE> if the
     * <code>Display</code> is in the background.
     *
     * <p>The device MAY limit or override the duration. For devices
     * that do not include a controllable backlight, calls to this
     * method return <CODE>false</CODE>.
     *
     * @param duration the number of milliseconds the backlight should be
     * flashed, or zero if the flashing should be stopped
     *
     * @return <CODE>true</CODE> if the backlight can be controlled
     *           by the application and this display is in the foreground,
     *          <CODE>false</CODE> otherwise
     *
     * @throws IllegalArgumentException if <code>duration</code> is negative
     *
     * @since MIDP 2.0
     */
    public boolean flashBacklight(int duration) {

        if (!hasForeground) {
            return false;
        }

        return deviceAccess.flashBacklight(duration);
    }
    
    /**
     * Requests operation of the device's vibrator.  The vibrator is
     * intended to be used to attract the user's attention or as a
     * special effect for games.  The return value indicates if the
     * vibrator can be controlled by the application.
     *
     * <p>This method switches on the vibrator for the requested
     * duration, or switches it off if the requested duration is zero.
     * If this method is called while the vibrator is still activated
     * from a previous call, the request is interpreted as setting a
     * new duration. It is not interpreted as adding additional time
     * to the original request. This method returns immediately; that
     * is, it must not block the caller while the vibrator is
     * running. </p>
     *
     * <p>Calls to this method are honored only if the
     * <code>Display</code> is in the foreground.  This method MUST
     * perform no action and return <CODE>false</CODE> if the
     * <code>Display</code> is in the background.</p>
     *
     * <p>The device MAY limit or override the duration.  For devices
     * that do not include a controllable vibrator, calls to this
     * method return <CODE>false</CODE>.</p>
     *
     * @param duration the number of milliseconds the vibrator should be run,
     * or zero if the vibrator should be turned off
     *
     * @return <CODE>true</CODE> if the vibrator can be controlled by the
     *           application and this display is in the foreground,
     *          <CODE>false</CODE> otherwise
     *
     * @throws IllegalArgumentException if <code>duration</code> is negative
     *
     * @since MIDP 2.0
     */
    public boolean vibrate(int duration) {

        if (!hasForeground) {
            return false;
        }

        if (duration < 0) {
            throw new IllegalArgumentException();
        }
        
        if (nVibrate(duration) <= 0) {
            return false;
        } else {
            return true;
        }

    }

    /**
     * Returns the best image width for a given image type.
     * The image type must be one of
     * {@link #LIST_ELEMENT},
     * {@link #CHOICE_GROUP_ELEMENT}, or
     * {@link #ALERT}.
     * 
     * @param imageType the image type
     * @return the best image width for the image type, may be zero if
     * there is no best size; must not be negative
     * @throws IllegalArgumentException if <code>imageType</code> is illegal
     * @since MIDP 2.0
     */
    public int getBestImageWidth(int imageType) {
        switch (imageType) {
        case LIST_ELEMENT:
        case CHOICE_GROUP_ELEMENT:
            return ChoiceGroup.PREFERRED_IMG_W;
        case ALERT:
            return Display.WIDTH;
        default:
            throw new IllegalArgumentException();
        }
    }

    /**
     * Returns the best image height for a given image type.
     * The image type must be one of
     * {@link #LIST_ELEMENT},
     * {@link #CHOICE_GROUP_ELEMENT}, or
     * {@link #ALERT}.
     * 
     * @param imageType the image type
     * @return the best image height for the image type, may be zero if
     * there is no best size; must not be negative
     * @throws IllegalArgumentException if <code>imageType</code> is illegal
     * @since MIDP 2.0
     */
    public int getBestImageHeight(int imageType) {
        switch (imageType) {
        case LIST_ELEMENT:
        case CHOICE_GROUP_ELEMENT:
            return ChoiceGroup.PREFERRED_IMG_H;
        case ALERT:
            // return max height ignoring title and ticker and
            // allow for 2 lines of Text to be visible without scrolling
            return Display.ADORNEDHEIGHT - 2 * Screen.CONTENT_HEIGHT;
        default:
            throw new IllegalArgumentException();
        }
    }


/*
 * ************* protected methods
 */

/*
 * ************* package private methods
 */

    /**
     * Set the current Displayable and notify the display manager if
     * the has current state has changed.
     *
     * @param nextDisplayable The next Displayable to display
     */
    void setCurrentImpl(Displayable nextDisplayable) {
        boolean previousWantsForeground = wantsForeground;

        /*
         * note: this method handles the setCurrent(null) then
         * setCurrent(getCurrent()) case
         */

        wantsForeground = nextDisplayable != null;

        if (wantsForeground) {
            if (nextDisplayable != current) {
                if (hasForeground) {
                    // If a call to setCurrent() is coming in while in a
                    // suspended state, notify the event handler to dismiss
                    // the system screen
                    if (paintSuspended) {
                        eventHandler.clearSystemScreen();
                        paintSuspended = false;
                    }

                    eventHandler.scheduleScreenChange(this, nextDisplayable);

                    /*
                     * have the foreground, no need to notify the display
                     * manager
                     */
                    return;
                }

                // set current before we notify the display manager
                current = nextDisplayable;
            }
        }

        if (wantsForeground != previousWantsForeground) {
            // only notify the display manager on a change
            displayManagerImpl.notifyWantsForeground(this.accessor,
                                                     wantsForeground);
        }
    }

    /**
     * Change the current screen to the given displayable.
     *
     * @param d The Displayable to make current
     */
    void screenChange(Displayable d) {
        synchronized (LCDUILock) {
            if (current == d && !paintSuspended) {
                return;
            } else if (paintSuspended) {
                // If we happen to be suspended, simply update our current
                // screen to be the new Displayable and return. When we are
                // resumed, registerNewCurrent() will be called with the
                // newly set screen
                current = d;
                return;
            }
        }

        // Its ok to ignore the foreground/paintSuspended state from here
        // on, the last thing registerNewCurrent() will do is try to do
        // a repaint(), which will not occur if the
        // foreground/paintSuspended status has changed in the meantime.
        registerNewCurrent(d, false);
    }

    /**
     * Called by Alert when a modal alert has exited.
     *
     * @param returnScreen The Displayable to return to after this
     *                     Alert is cleared.
     */
    void clearAlert(Displayable returnScreen) {
        eventHandler.scheduleScreenChange(this, returnScreen);
    }

    /**
     * play a sound.
     * @param t type of alert
     * @return true, if sound was played.
     */
    boolean playAlertSound(AlertType t) {

        if (!paintSuspended && hasForeground) {
            try {
                // SYNC NOTE: playAlertSound is a native method, no locking
                // necessary
                return playAlertSound(t.getType());
            } catch (Exception e) {}
        }

        return false;
    }

    /**
     * Schedule a Form invalidation based on the given Item.
     * The item may be null.
     *
     * @param item The Item which caused the invalidation
     */
    void invalidate(Item item) {
        eventHandler.scheduleInvalidate(item);
    }

    /**
     * Schedule the notificaton of an ItemStateListener due to
     * a change in the given item.
     *
     * @param item The Item which has changed
     */
    void itemStateChanged(Item item) {
        eventHandler.scheduleItemStateChanged(item);
    }

    /**
     * get the associated MIDlet from the current display.
     * @return MIDlet that is associated with the current Display.
     */
    MIDlet getMIDlet() {
        return midlet;
    }

    /**
     * Request a repaint for the given Displayable.  The rectangle to be
     * painted is in x,y,w,h.  If delay is greater than zero, the repaint
     * may be deferred until that interval (in milliseconds) has elapsed.
     *
     * If the given Displayable is not current, the request is ignored.
     * This is safe because whenever a Displayable becomes current, it gets
     * a full repaint anyway.
     *
     * The target Object is optional. It will be packaged along with the
     * repaint request and arrive later when the repaint is serviced in
     * the callPaint() routine - IF this paint request has not been
     * coalesced with other repaints.
     *
     * @param d displayable object to be drawn
     * @param x upper left corner x-coordinate
     * @param y upper left corner y-coordinate
     * @param w horizontal width
     * @param h vertical height
     * @param target an optional paint target
     */
    void repaintImpl(Displayable d, int x, int y, int w, int h,
                     Object target) {

        synchronized (LCDUILock) {
            if (paintSuspended || !hasForeground || d != current) {
                return;
            }
        }

        eventHandler.scheduleRepaint(x, y, w, h, target);
    }

    /**
     * Process any pending repaint requests immediately.
     *
     * @param d The Displayable which is requesting the repaint
     *          (Used to determine if the Displayable making the
     *           request is currently being shown)
     *
     * SYNC NOTE: this method performs its own locking of
     * LCDUILock.  Therefore, callers
     * must not hold any locks when they call this method.
     */
    void serviceRepaints(Displayable d) {

        synchronized (LCDUILock) {
            if (paintSuspended || !hasForeground || d != current) {
                return;
            }
        }

        eventHandler.serviceRepaints();
    }

    /**
     * Process the specified repaint request immediately.
     *
     * @param x1 The x origin of the paint bounds
     * @param y1 The y origin of the paint bounds
     * @param x2 The x coordinate of the lower right bounds
     * @param y2 The y coordinate of the lower right bounds
     * @param target The optional paint target
     *
     * SYNC NOTE: this method performs its own locking of
     * LCDUILock and calloutLock.  Therefore, callers
     * must not hold any locks when they call this method.
     */
    void repaint(int x1, int y1, int x2, int y2, Object target) {
        Displayable currentCopy = null;

        synchronized (LCDUILock) {
            if (paintSuspended || !hasForeground) {
                return;
            }
            currentCopy = current;
        }

        if (currentCopy == null) {
            return;
        }

        screenGraphics.reset(x1, y1, x2, y2);
        current.callPaint(screenGraphics, target);
        refresh(x1, y1, x2, y2);
    }

    /**
     * Return the key code that corresponds to the specified game
     * action on the device.  gameAction must be a defined game action
     * (Canvas.UP, Canvas.DOWN, Canvas.FIRE, etc.)
     * <B>Post-conditions:</B><BR> The key code of the key that
     * corresponds to the specified action is returned.  The return
     * value will be -1 if the game action is invalid or not supported
     * by the device.
     *
     * @param gameAction The game action to obtain the key code for.
     * @return the key code.
     */
    static int getKeyCode(int gameAction) {
        return eventHandler.getKeyCode(gameAction);
    }

    /**
     * get the current vertical scroll position.
     * @return the vertical scroll position.
     */
    int getVerticalScrollPosition() {
        // SYNC NOTE: No need to lock here because 'current'
        // can only be null once, at startup, so we don't care
        // if 'current' changes values, just that it isn't null
        if (current != null) {
            return current.getVerticalScrollPosition();
        } else {
            return 0;
        }
    }

    /**
     * get the current vertical scroll proportion.
     * @return the vertical scroll proportion.
     */
    int getVerticalScrollProportion() {
        // SYNC NOTE: No need to lock here because 'current'
        // can only be null once, at startup, so we don't care
        // if 'current' changes values, just that it isn't null
         if (current != null) {
            return current.getVerticalScrollProportion();
        } else {
            return 100;
        }
    }

    /**
     * Return the game action associated with the given key code on
     * the device.  keyCode must refer to a key that is mapped as a
     * game key on the device The game action of the key is returned.
     * The return value will be 0 if the key is not mapped to a game
     * action or not present on the device.
     *
     * @param keyCode the key code
     * @return the corresponding game action (UP, DOWN, LEFT, RIGHT, FIRE, etc.)
     */
    static int getGameAction(int keyCode) {
        return eventHandler.getGameAction(keyCode);
    }

    /**
     * returns 0 if keyCode is not a system key.  Otherwise, returns
     * one of the EventHandler.SYSTEM_KEY_ constants.
     * @param keyCode get the system equivalent key.
     * @return translated system key or zero if it is not a system key.
     */
    static int getSystemKey(int keyCode) {
        return eventHandler.getSystemKey(keyCode);
    }

    /**
     * Gets an informative key string for a key. The string returned
     * should resemble the text physically printed on the key. For
     * example, on a device with function keys F1 through F4, calling
     * this method on the keycode for the F1 key will return the
     * string "F1". A typical use for this string will be to compose
     * help text such as "Press F1 to proceed."
     *
     * <p>There is no direct mapping from game actions to key
     * names. To get the string name for a game action, the
     * application must call
     *
     * <p><code>getKeyName(getKeyCode(GAME_A))</code>
     * @param keyCode the key code being requested
     * @return a string name for the key, or null if no name is available
     */
    static String getKeyName(int keyCode) {
        return eventHandler.getKeyName(keyCode);
    }

    /**
     * Update the system's account of abstract commands
     *
     * SYNC NOTE: Calls to this method should be synchronized
     * on the LCDUILock
     */
    void updateCommandSet() {

        Command[] screenCommands = current.getCommands();
        int screenComCount = current.getCommandCount();
        for (int i = 0; i < screenComCount; i++) {
             screenCommands[i].setInternalID(i);
        }

        Item curItem = current.getCurrentItem();
        if (curItem == null) {
            eventHandler.updateCommandSet(null, 0,
                                          screenCommands, screenComCount);
        } else {
            Command[] itemCommands = curItem.getCommands();
            int itemComCount = curItem.getCommandCount();
            for (int i = 0; i < itemComCount; i++) {
                itemCommands[i].setInternalID(i + screenComCount);
            }
            eventHandler.updateCommandSet(itemCommands, itemComCount,
                                          screenCommands, screenComCount);
        }
    }

    /**
     * is the current display visible.
     * @param d displayble instance to check, if current and visible.
     * @return true, if the Display is visible and the object is current.
     */
    boolean isShown(Displayable d) {
        // SYNC NOTE: calls to isShown() should be synchronized on
        // the LCDUILock.
        return hasForeground
            && !paintSuspended
            && (current == d);
    }

    /**
     * This is a utility method used to handle any Throwables
     * caught while calling application code. The default
     * implementation will simply call printStackTrace() on
     * the Throwable. Note, the parameter must be non-null or
     * this method will generate a NullPointerException.
     *
     * @param t The Throwable caught during the call into
     *          application code.
     */
    static void handleThrowable(Throwable t) {
        t.printStackTrace();
    }

    /**
     * set the current vertical scroll position and proportion.
     * @param scrollPosition vertical scroll position.
     * @param scrollProportion vertical scroll proportion.
     */
    native void setVerticalScroll(int scrollPosition, int scrollProportion);

    /**
     * set the input, mode.
     * @param mode type of input to accept.
     */
    native void setInputMode(int mode);

    /**
     * Control the drawing of the trusted MIDlet
     * icon in native.
     * @param drawTrusted true if the icon should be drawn, 
     *                    false if it should not.
     */
    private native void drawTrustedIcon(boolean drawTrusted);

    /**
     * See if we're using the display's Graphics object. 
     * @param gfx Graphics object to compare to <code>screenGraphics</code>.
     * @return true if <code>gfx</code> equals <code>screenGraphics</code>,
     *         false otherwise.
     */
    static boolean isGraphicsDisplay(Graphics gfx) {
        return screenGraphics == gfx;
    }

/*
 * ************* private methods
 */

    /**
     *  get the class that handle I/O event stream for this display.
     * @return handle to the event handler for this display
     */
    private static EventHandler getEventHandler() {

        String n = Configuration.getProperty(
            "com.sun.midp.lcdui.eventHandler");

        try {
            return (EventHandler) (Class.forName(n)).newInstance();
        } catch (Exception e) { }

        if (Configuration.getProperty("microedition.configuration") != null) {
            try {
                return (EventHandler) (Class.forName(
                    "com.sun.midp.lcdui.AutomatedEventHandler")).newInstance();
            } catch (Exception e) { }

            try {
                return (EventHandler) (Class.forName(
                    "com.sun.midp.lcdui.DefaultEventHandler")).newInstance();
            } catch (Exception e) { }

            throw new Error("Unable to establish EventHandler");
        }

        try {
            return (EventHandler)
                (Class.forName(
                    "com.sun.midp.lcdui.AWTEventHandler")).newInstance();
        } catch (Exception e) { }

        throw new Error("Unable to establish EventHandler");
    }

    /**
     * Registers a new Displayable object to this Display. This means that
     * it is now the current Displayable object, eligible to receive input
     * events, and eligible to paint. If necessary, showNotify() is called
     * on this Displayable and hideNotify() is called on the Displayable
     * being replaced. This method is used to initialize a Displayable as
     * a result of either:
     *  - a SCREEN change timerEvent()
     *  - a call to resumePainting()
     *  - a change in foreground status of this Display (results in a call
     *    to resumePainting())
     *
     * @param newCurrent The Displayable to take effect as the new
     *                      "current" Displayable
     * @param fgChange  If True, then this call to registerNewCurrent() is a
     *                  result of this Display being moved to the foreground,
     *                  i.e. setForeground(true)
     */
    private void registerNewCurrent(Displayable newCurrent, boolean fgChange)
    {
        // If this Display is not in the foreground, simply record
        // the new Displayable and return. When this Display resumes
        // the foreground, this method will be called again.

        Displayable currentCopy = null;

        // SYNC NOTE: The implementation of callShowNotify() will
        // use LCDUILock to lock around its internal handling.
        // Canvas will override callShowNotify() to first call
        // super(), and then obtain a lock
        // on calloutLock around a call to the applications
        // showNotify() method.

        if (newCurrent != null) {
            newCurrent.callShowNotify(Display.this);
        }

        synchronized (LCDUILock) {
            if (fgChange) {
                hasForeground = true;

                if (newCurrent == null) {
                    /*
                     * At least clear the screen so the last display, which
                     * could be destroyed (like the Selector) is not still
                     * showing.
                     */
                    screenGraphics.reset(0, 0, WIDTH, HEIGHT);
                    screenGraphics.setColor(Display.ERASE_COLOR);
                    screenGraphics.fillRect(0, 0, WIDTH, HEIGHT);
                    refresh(0, 0, WIDTH, HEIGHT);
                    eventHandler.updateCommandSet(null, 0, null, 0);

                    /*
                     * Without a displayable, we can shortcircuit the rest
                     * of the call and simply return.
                     */
                    return;
                }
            }


            // This will suppress drags, repeats and ups until a
            // corresponding down is seen.
            accessor.sawPointerPress = accessor.sawKeyPress = false;

            // We re-set our suspended state to false.
            paintSuspended = false;

            // We make a copy of the current Displayable to call
            // hideNotify() when we're done
            currentCopy = current;

            current = newCurrent;

            // set mapping between GameCanvas and DisplayAccess
            // set Game key event flag based on value passed in
            // GameCanvas constructor.
            if (current instanceof GameCanvas) {
                GameMap.register(current, accessor);
                stickyKeyMask = currentKeyMask = 0;
            } else {
                // set the keymask to -1 when
                // the displayable is not a GameCanvas.
                stickyKeyMask = currentKeyMask = -1;
            }

            setVerticalScroll(
                current.getVerticalScrollPosition(),
                current.getVerticalScrollProportion());

            // Next, update the command set
                updateCommandSet();

        } // synchronized

        // SYNC NOTE: The implementation of callHideNotify()
        // will use LCDUILock to lock around its internal handling.
        // Canvas will override callHideNotify(), first calling
        // super(), and then obtaining calloutLock around a call
        // to the application's hideNotify() method.

        // NOTE: the reason we test for currentCopy != current is
        // for cases when the Display has interrupted its suspension
        // by a system screen to immediately return to its "current"
        // Displayable. In this case, currentCopy == current, and
        // we've just called showNotify() above (no need to call
        // hideNotify())

        if (currentCopy != null && currentCopy != current) {
            currentCopy.callHideNotify(Display.this);
        }
        repaint(0, 0, WIDTH, HEIGHT, null);
    } // registerNewCurrent()

    /**
     * run the serially repainted operations.
     */
    private void getCallSerially() {
        java.util.Vector q = null;

        synchronized (LCDUILock) {
            q = currentQueue;
            currentQueue = (q == queue1) ? queue2 : queue1;
        }

        // SYNC NOTE: we synch on calloutLock for the call into
        // application code
        synchronized (calloutLock) {
            for (int i = 0; i < q.size(); i++) {
                try {
                    Runnable r = (Runnable) q.elementAt(i);
                    r.run();
                } catch (Throwable thr) {
                    handleThrowable(thr);
                }
            }
        }

        q.removeAllElements();
    }

    /**
     * Invalidate the current Form possibly due to an item's request
     *
     * @param item the Item which caused the invalidation
     */
    private void callInvalidate(Item item) {
        Displayable currentCopy = current;

        if (currentCopy != null) {
            currentCopy.callInvalidate(item);
        }
    }

    /**
     * Notify an ItemStateChangeListener of a change in the given
     * item.
     *
     * @param item the Item which has changed
     */
    private void callItemStateChanged(Item item) {
        Displayable currentCopy = current;

        if (currentCopy != null) {
            currentCopy.callItemStateChanged(item);
        }
    }

    /**
     * Initializes the security token for this class, so it can
     * perform actions that a normal MIDlet Suite cannot.
     *
     * @param token security token for this class.
     */
    private static void initSecurityToken(SecurityToken token) {
        if (classSecurityToken != null) {
            return;
        }
        
        classSecurityToken = token;
    }

    /**
     * redraw a portiion of the display.
     *
     * @param x1 upper left corner x-coordinate
     * @param y1 upper left corner y-coordinate
     * @param x2 lower right corner x-coordinate
     * @param y2 lower right corner y-coordinat
     */
    private native void refresh(int x1, int y1, int x2, int y2);

    /**
     * play a sound.
     * @param alertType type of alert
     * @return true, if sound was played.
     */
    private native boolean playAlertSound(int alertType);

    /**
     * Plays the vibration.
     * @param dur the duration in milli seconds
     * @return if it's successful, return 1, othewise return 0
     *
     */
    native private int nVibrate(int dur);

    /**
     * ************* Inner Class, DisplayAccessor
     */

    /** This is nested inside Display so that it can see the private fields */
    class DisplayAccessor implements DisplayAccess {

        /**
         * display painting.
         */
        public void suspendPainting() {
            Displayable currentCopy = null;

            synchronized (Display.LCDUILock) {
                paintSuspended = true;
                currentCopy = current;
                if (current instanceof Screen) {
                    ((Screen)current).resetToTop = false;
                }
            }

            // SYNC NOTE: The implementation of callHideNotify()
            // will use LCDUILock to lock around its internal handling.
            // Canvas will override callHideNotify(), first calling
            // super(), and then obtaining calloutLock around a call
            // to the application's hideNotify() method.

            if (currentCopy != null) {
                currentCopy.callHideNotify(Display.this);
            }

            // We want to re-set the scroll indicators when we suspend so
            // that the overtaking screen doesn't have to do it
            setVerticalScroll(0, 100);
        }

        /**
         * resume paininting , if doing repaint operations.
         */
        public void resumePainting() {
            registerNewCurrent(current, false);
        }

        /**
         * Called from the event delivery system when a command is seen.
         * @param id The id of the command for listener notification
         *           (as is returned by Command.getID())
         */
        public void commandAction(int id) {
            Command commands[];
            CommandListener listener;
            ItemCommandListener itemListener = null;

            Command c = null;
            Displayable currentCopy = null;
            Item curItem = null;

            synchronized (LCDUILock) {
                if (current == null) {
                    return;
                }

                    // See if one of the screen commands was activated
                if (((listener = current.getCommandListener()) != null) &&
                        (commands = current.getCommands()) != null) {

                    for (int i = 0, nc = current.getCommandCount();
                            i < nc; i++) {
                        if (commands[i] != null && commands[i].getID() == id) {
                            c = commands[i];
                            currentCopy = current;
                            break;
                        }
                    }
                }

                if ((c == null) &&
                        ((curItem = current.getCurrentItem()) != null)) {

                    // See if one of the item commands was activated
                    if (((itemListener = curItem.getItemCommandListener())
                        != null) &&
                            (commands = curItem.getCommands()) != null) {

                        for (int i = 0, nc = curItem.getCommandCount();
                                i < nc; i++) {
                            if (commands[i] != null &&
                                    commands[i].getID() == id) {
                                c = commands[i];
                                break;
                            }
                        }
                    }
                }

                if (c == null) {
                    // This means that in the time it took to receive the
                    // Command event, that Command has since been removed
                    // from the current Displayable, or the Displayable from
                    // which that Command originated is no longer current
                    return;
                }

                // commit any pending user interface for the current 
                // displayable or item
                if (currentCopy != null) {
                    currentCopy.commitPendingInteraction();
                } else {
                    curItem.commitPendingInteraction();
                }
            } // synchronized

            // Protect from any unexpected application exceptions
            try {
                // SYNC NOTE: We release the lock on LCDUILock and acquire
                // calloutLock before calling into application code
                synchronized (calloutLock) {
                    if (currentCopy != null) {
                        // screen command was activated
                        listener.commandAction(c, currentCopy);
                    } else {
                        itemListener.commandAction(c, curItem);
                    }
                }
            } catch (Throwable thr) {
                handleThrowable(thr);
            }
        } // commandAction

        /**
         * Called from the event delivery loop when a pointer event is seen.
         * @param type kind of pointer event
         * @param x x-coordinate of pointer event
         * @param y y-coordinate of pointer event
         */
        public void pointerEvent(int type, int x, int y) {
            Displayable currentCopy = null;
            int eventType = -1;

            synchronized (LCDUILock) {
                if (current == null) {
                    return;
                }
                currentCopy = current;

                switch (type) {
                    case EventHandler.PRESSED:
                        sawPointerPress = true;
                        eventType = 0;
                        break;
                    case EventHandler.RELEASED:
                        if (sawPointerPress) {
                            eventType = 1;
                        }
                        break;
                    case EventHandler.DRAGGED:
                        if (sawPointerPress) {
                            eventType = 2;
                         }
                        break;
                }
            } // synchronized

            // SYNC NOTE: Since we may call into application code,
            // we do so outside of LCDUILock
            switch (eventType) {
                case -1:
                    return;
                case 0:
                    currentCopy.callPointerPressed(x, y);
                    break;
                case 1:
                    currentCopy.callPointerReleased(x, y);
                    break;
                case 2:
                    currentCopy.callPointerDragged(x, y);
                    break;
                default:
                    // this is an error
                    break;
            }
        } // pointerEvent()

        /**
         * Called from the event delivery loop when a key event is seen.
         * @param type kind of key event - pressed, release, repeated, typed
         * @param keyCode key code of entered key
         */
        public void keyEvent(int type, int keyCode) {
            Displayable currentCopy = null;
            int eventType = -1;


            synchronized (LCDUILock) {
                if (current == null) {
                    return;
                }
                currentCopy = current;

                switch (type) {
                    case EventHandler.PRESSED:
                        sawKeyPress = true;
                        eventType = 0;
                        break;
                    case EventHandler.RELEASED:
                        if (sawKeyPress) {
                            eventType = 1;
                        }
                        break;
                    case EventHandler.REPEATED:
                        if (sawKeyPress) {
                            eventType = 2;
                        }
                        break;
                    case EventHandler.TYPED:
                        eventType = 3;
                }
                // used later by getKeyMask()
                if (currentKeyMask > -1 && eventType != -1) {
                    if (eventType == 1) {
                        releaseKeyMask(keyCode);
                    } else {
                        // set the mask on key press, repeat or type.
                        // don't set the mask when a key was released.
                        setKeyMask(keyCode);
                    }
                }
            } // synchronized

            // SYNC NOTE: Since we may call into application code,
            // we do so outside of LCDUILock
            switch (eventType) {
                case -1:
                    return;
                case 0:
                    currentCopy.callKeyPressed(keyCode);
                    break;
                case 1:
                    currentCopy.callKeyReleased(keyCode);
                    break;
                case 2:
                    currentCopy.callKeyRepeated(keyCode);
                    break;
                case 3:
                    currentCopy.callKeyTyped((char)keyCode);
                    break;
                default:
                    // this is an error
                    break;
            }
        } // keyEvent()

        /**
         * Change the current screen to the given displayable.
         *
         * @param d The Displayable to make current
         */
        public void screenChange(Displayable d) {
            synchronized (LCDUILock) {
                if (current == d && !paintSuspended) {
                    return;
                } else if (paintSuspended || !hasForeground) {
                    // If we happen to be suspended, simply update our current
                    // screen to be the new Displayable and return. When we are
                    // resumed, registerNewCurrent() will be called with the
                    // newly set screen
                    current = d;
                    return;
                }
            }

            // Its ok to ignore the foreground/paintSuspended state from here
            // on, the last thing registerNewCurrent() will do is try to do
            // a repaint(), which will not occur if the
            // foreground/paintSuspended status has changed in the meantime.
            registerNewCurrent(d, false);
        }

        /**
         * Repaint the given area of the screen
         *
         * @param x1 The x origin of the paint bounds
         * @param y1 The y origin of the paint bounds
         * @param x2 The x coordinate of the lower right bounds
         * @param y2 The y coordinate of the lower right bounds
         * @param target The optional paint target
         */
        public void repaint(int x1, int y1, int x2, int y2, Object target) {
            // Its ok to ignore the foreground/paintSuspended state from here.
            // repaint() will not succeed if the foreground/paintSuspended
            // status has changed in the meantime.
            Display.this.repaint(x1, y1, x2, y2, target);
        }

        /**
         * Process any pending call serially objects
         */
        public void callSerially() {
            // This line is no longer necessary. The event handler will
            // already have processed any pending repaints before processing
            // the callSerially() event.
            // eventHandler.serviceRepaints();
            getCallSerially();
        }

        /**
         * Perform an invalidate if the current displayable is a Form
         *
         * @param src the Item which caused the invalidation (may be null)
         */
        public void callInvalidate(Item src) {
            Display.this.callInvalidate(src);
        }

        /**
         * Notify any ItemStateChangeListener that the given Item has changed
         *
         * @param src the Item which has changed
         */
        public void callItemStateChanged(Item src) {
            Display.this.callItemStateChanged(src);
        }

        /**
         * Called from the event delivery loop when an input method
         * event is seen.
         * @param str input text string
         */
        public void inputMethodEvent(String str) {
            TextBox textBoxCopy = null;

            synchronized (LCDUILock) {
                if (current instanceof TextBox) {
                    textBoxCopy = (TextBox) current;
                }
            }

            // SYNC NOTE: TextBox.insert() does its own locking so we
            // move the call outside of our lock using a local variable
            if (textBoxCopy != null) {
                textBoxCopy.insert(str, textBoxCopy.getCaretPosition());
            }
        }

        /**
         * get the flag indicating the most recently set
         *  Displayable was non-null.
         * @return true, if most recent displayable was non-null.
         */
        public boolean wantsForeground() {
            return wantsForeground;
        }

        /**
         * get a hnadle to the current display
         * @return current Display handle.
         */
        public Display getDisplay() {
            // return display;
            return Display.this;
        }

        /**
         * Notify the display of a change in its the foreground/background
         * status.
         *
         * @param hasForeground true if the Display should be put in
         * the foreground.
         */
        public void foregroundNotify(boolean hasForeground) {
            if (hasForeground && !Display.this.hasForeground) {
                if (Scheduler.getScheduler().isDispatchThread()) {
                    // NOTE: This will have to be removed when Display
                    // changes queued in the event handler
                    eventHandler.serviceRepaints();
                }
                registerNewCurrent(current, true);
            } else {
                Displayable currentCopy = null;

                synchronized (Display.LCDUILock) {
                    Display.this.hasForeground = false;
                    paintSuspended = true;
                    currentCopy = current;
                }

                // SYNC NOTE: The implementation of callHideNotify()
                // will use LCDUILock to lock around its internal handling.
                // Canvas will override callHideNotify(), first calling
                // super(), and then obtaining calloutLock around a call
                // to the application's hideNotify() method.

                if (currentCopy != null) {
                    currentCopy.callHideNotify(Display.this);
                }

                // We want to re-set the scroll indicator when we suspend so
                // that the overtaking screen doesn't have to do it
                setVerticalScroll(0, 100);

            }
        }

        /**
         * Called to get key mask of all the keys that were pressed.
         * @return keyMask  The key mask of all the keys that were pressed.
         */
        public int getKeyMask() {
            synchronized (LCDUILock) {
                // don't release currently pressed keys
                int savedMaskCopy = stickyKeyMask | currentKeyMask;
                stickyKeyMask = 0;
                return savedMaskCopy;
            }
        }

        /**
         * Flushes the entire off-screen buffer to the display.
         * @param d The Displayable 
         * @param offscreen_buffer The image buffer 
         * @param x The left edge of the region to be flushed
         * @param y The top edge of the region to be flushed
         * @param width The width of the region to be flushed
         * @param height The height of the region to be flushed
         */
        public void flush(Displayable d, Image offscreen_buffer,
                   int x, int y, int width, int height) {

            synchronized (LCDUILock) {
                if (paintSuspended || !hasForeground || d != current) {
                    return;
                }
        
                int x2 = x + width;
                int y2 = y + height;

                screenGraphics.reset(x, y, x2, y2);
                screenGraphics.drawImage(offscreen_buffer, 
                                         x, y, 
                                         Graphics.TOP|Graphics.LEFT);
                refresh(x, y, x2, y2);
            }
        }

        /**
         * Set the trusted icon for this Display. When ever this display is in
         * the foreground the given icon will be displayed in the area reserved 
         * for the trusted icon. Only callers with the internal MIDP 
         * permission can use this method.
         *
         * @param token security token of the call that has internal MIDP
         *              permission
         * @param drawTrusted true to draw the trusted icon
         */
        public void setTrustedIcon(SecurityToken token, boolean drawTrusted) {
            
            token.checkIfPermissionAllowed(Permissions.MIDP);
            
            drawTrustedIcon(drawTrusted);
        }

        /**
         * Called to set key mask of all the keys that were pressed.
         * @param keyCode The key code to set the key mask.
         */
        private void setKeyMask(int keyCode) {
            if (paintSuspended || !hasForeground) {
                return;
            } 

            // set the mask of keys pressed 
            switch (eventHandler.getGameAction(keyCode)) {
            case Canvas.UP:
                stickyKeyMask = stickyKeyMask | GameCanvas.UP_PRESSED;
                currentKeyMask = currentKeyMask | GameCanvas.UP_PRESSED;
                break;
            case Canvas.DOWN:
                stickyKeyMask = stickyKeyMask | GameCanvas.DOWN_PRESSED;
                currentKeyMask = currentKeyMask | GameCanvas.DOWN_PRESSED;
                break;
            case Canvas.LEFT:
                stickyKeyMask = stickyKeyMask | GameCanvas.LEFT_PRESSED;
                currentKeyMask = currentKeyMask | GameCanvas.LEFT_PRESSED;
                break;
            case Canvas.RIGHT:
                stickyKeyMask = stickyKeyMask | GameCanvas.RIGHT_PRESSED;
                currentKeyMask = currentKeyMask | GameCanvas.RIGHT_PRESSED;
                break;
            case Canvas.FIRE:
                stickyKeyMask = stickyKeyMask | GameCanvas.FIRE_PRESSED;
                currentKeyMask = currentKeyMask | GameCanvas.FIRE_PRESSED;
                break;
            case Canvas.GAME_A:
                stickyKeyMask = stickyKeyMask | GameCanvas.GAME_A_PRESSED;
                currentKeyMask = currentKeyMask | GameCanvas.GAME_A_PRESSED;
                break;
            case Canvas.GAME_B:
                stickyKeyMask = stickyKeyMask | GameCanvas.GAME_B_PRESSED;
                currentKeyMask = currentKeyMask | GameCanvas.GAME_B_PRESSED;
                break;
            case Canvas.GAME_C:
                stickyKeyMask = stickyKeyMask | GameCanvas.GAME_C_PRESSED;
                currentKeyMask = currentKeyMask | GameCanvas.GAME_C_PRESSED;
                break;
            case Canvas.GAME_D:
                stickyKeyMask = stickyKeyMask | GameCanvas.GAME_D_PRESSED;
                currentKeyMask = currentKeyMask | GameCanvas.GAME_D_PRESSED;
            }
        }

        /**
         * Called to release key mask of all the keys that were release.
         * @param keyCode The key code to release the key mask.
         */
        private void releaseKeyMask(int keyCode) {

            if (paintSuspended || !hasForeground) {
                currentKeyMask = 0;
                return;
            } 
            // set the mask of keys pressed 
            switch (eventHandler.getGameAction(keyCode)) {
            case Canvas.UP:
                currentKeyMask = currentKeyMask & ~ GameCanvas.UP_PRESSED;
                break;
            case Canvas.DOWN:
                currentKeyMask = currentKeyMask & ~ GameCanvas.DOWN_PRESSED;
                break;
            case Canvas.LEFT:
                currentKeyMask = currentKeyMask & ~ GameCanvas.LEFT_PRESSED;
                break;
            case Canvas.RIGHT:
                currentKeyMask = currentKeyMask & ~ GameCanvas.RIGHT_PRESSED;
                break;
            case Canvas.FIRE:
                currentKeyMask = currentKeyMask & ~ GameCanvas.FIRE_PRESSED;
                break;
            case Canvas.GAME_A:
                currentKeyMask = currentKeyMask & ~ GameCanvas.GAME_A_PRESSED;
                break;
            case Canvas.GAME_B:
                currentKeyMask = currentKeyMask & ~ GameCanvas.GAME_B_PRESSED;
                break;
            case Canvas.GAME_C:
                currentKeyMask = currentKeyMask & ~ GameCanvas.GAME_C_PRESSED;
                break;
            case Canvas.GAME_D:
                currentKeyMask = currentKeyMask & ~ GameCanvas.GAME_D_PRESSED;
            }
        }

        /** local handle for current Display. */
        // private Display display;

        // No events will be delivered while these are false
        // This is our attempt at avoiding spurious up events
        /** true, if a pointer press is in progress. */
        boolean sawPointerPress;
        /** true, if a key press is in progress. */
        boolean sawKeyPress;


    } // END DisplayAccessor Class

/*
 * ************* Inner Class, DisplayManager
 */

    /**
     * This real display manager. It will manage
     * the list of active displays, passing any events it gets from
     * the event handler to the foreground display. It is upto the
     * scheduler to activate displays and deactivate displays,
     * however the display manager can suspend a display and resume
     * a display in response to events, it can also tell the scheduler
     * to destroy an active midlet or all midlets.
     */
    private static class DisplayManagerImpl implements DisplayManager {

        /** What should get system events. */
        private Vector systemEventListeners = new Vector(5, 5);

        /** Active displays. */
        private Vector displays = new Vector(5, 5);

        /**
         * This object is created to avoid a lot null checks.
         * It will get the system events when there is no foreground.
         */
        private final DisplayAccess noForeground = new Display(null).accessor;

        /** What should get system events. */
        private DisplayAccess foreground = noForeground;

        /** A suspended MIDlet waiting to be resumed. */
        private DisplayAccess suspended;

        /** If true all MIDlet are suspended. */
        private boolean allSuspended;

        /** The display that got preempted. */
        private DisplayAccess preempted;

        /**
         * Add a listener for system events.
         *
         * @param l object to notify with system events such as shutdown
         */
        public void addSystemEventListener(SystemEventListener l) {
            systemEventListeners.addElement(l);
        }

        /**
         * Places the display of the given MIDlet in the list of active
         * displays
         * and registers an object to receive events on behalf of the MIDlet.
         *
         * @param l object to notify with MIDlet events such as pause.
         * @param m MIDlet to activate
         */
        public void activate(MIDletEventListener l, MIDlet m) {
            Display d = getDisplay(m);
            DisplayAccess da = d.accessor;

            synchronized (displays) {
                if (displays.indexOf(da) != -1) {
                    // do not allow duplicates
                    return;
                }

                d.midletEventListener = l;
                displays.addElement(da);

                notifyWantsForeground(da, da.wantsForeground());
            }
        }

        /**
         * Removes the display of the given MIDlet from the list of active
         * displays
         * and unregisters MIDlet's event listener.
         *
         * @param m MIDlet to deactivate
         */
        public void deactivate(MIDlet m) {
            DisplayAccess da = getDisplay(m).accessor;

            synchronized (displays) {
                if (da == suspended) {
                    suspended = null;
                    return;
                }

                displays.removeElement(da);

                // there are more 2 special cases, foreground and preempted

                if (da == foreground) {
                    eventHandler.clearSystemScreen();
                    notifyWantsForeground(da, false);
                    return;
                }

                if (da == preempted) {
                    preempted = noForeground;
                    return;
                }
            }
        }

        /**
         * Preempt the current displayable with
         * the given displayable until donePreempting is called.
         * The preemptor should stop preempting when a destroyMIDlet event
         * occurs. The event will have a null MIDlet parameter.
         *
         * @param token security token for the calling class
         * @param l object to notify with the destroy MIDlet event.
         * @param d displayable to show the user
         * @param waitForDisplay if true this method will wait if the
         *        screen is being preempted by another thread.
         *
         * @return an preempt token object to pass to donePreempting done if
         * prempt will happen, else null
         *
         * @exception SecurityException if the caller does not have permission
         *   the internal MIDP permission.
         * @exception InterruptedException if another thread interrupts the
         *   calling thread while this method is waiting to preempt the
         *   display.
         */
        public Object preemptDisplay(SecurityToken token,
                MIDletEventListener l, Displayable d, boolean waitForDisplay)
                throws InterruptedException {
            Display tempDisplay;

            token.checkIfPermissionAllowed(Permissions.MIDP);

            synchronized (displays) {
                if (preempted != null || allSuspended) {

                    if (!waitForDisplay) {
                        return null;
                    }

                    displays.wait();
                }
            
                tempDisplay = new Display(null);
                tempDisplay.setCurrent(d);
                tempDisplay.midletEventListener = l;

                foreground.foregroundNotify(false);
                preempted = foreground;
                foreground = tempDisplay.accessor;
                foreground.foregroundNotify(true);

                // reuse one of our private object as the preempt token
                return displays;
            }
        }

        /**
         * Display the displayable that was being displayed before
         * preemptDisplay was called.
         *
         * @param preemptToken the token returned from preemptDisplay
         */
        public void donePreempting(Object preemptToken) {
            // only trusted callers are given a private object
            if (preemptToken != displays) {
                return;
            }

            synchronized (displays) {
                foreground.foregroundNotify(false);

                if (!allSuspended) {
                    if (preempted.wantsForeground()) {
                        foreground = preempted;
                        foreground.foregroundNotify(true);
                    } else {
                        /*
                         * force notifyWantsForeground to search the list
                         * temp no
                         * current. Start with noForeground since the preempted
                         * current
                         * display already got a foregroundNotify(false)
                         * when preempted.
                         */
                        foreground = noForeground;
                        notifyWantsForeground(noForeground, false);
                    }

                    // Another thread may be waiting to preempt
                    displays.notify();
                }

                preempted = null;
            }
        }

        /**
         * Release the system event listener set during the call to
         * getDisplayManger.
         *
         * @param l object that was being notified with system events
         */
        public void releaseSystemEventListener(SystemEventListener l) {
            systemEventListeners.removeElement(l);
        }

        /**
         * Suspend Pause all to allow the system to use the
         * display. This will result in calling pauseApp() on the
         * all of the active MIDlets. A subsequent 'resumeAll()' call will
         * return the all of the paused midlets to active status.
         * <p>
         * Called by the system event handler within Display.
         */
        public void suspendAll() {
            synchronized (displays) {
                if (allSuspended) {
                    return;
                }

                allSuspended = true;

                foreground.foregroundNotify(false);

                for (int i = 0; i < displays.size(); i++) {
                    Display d =
                        ((DisplayAccess)displays.elementAt(i)).getDisplay();
                    d.midletEventListener.pauseMIDlet(d.getMIDlet());
                }

                if (suspended != null) {
                    displays.addElement(suspended);
                    suspended = null;
                }
            }
        }

        /**
         * Resume the currently suspended state. This is a result
         * of the underlying system returning control to MIDP.
         * Any previously paused foreground MIDlet will be restarted
         * and the Display will be refreshed.
         * <p>
         * Called by the system event handler within Display.
         * <p>
         */
        public void resumeAll() {
            synchronized (displays) {
                allSuspended = false;

                for (int i = 0; i < displays.size(); i++) {
                    DisplayAccess da = (DisplayAccess)displays.elementAt(i);
                    Display d = da.getDisplay();

                    d.midletEventListener.startMIDlet(d.getMIDlet());
                }

                if (foreground.wantsForeground()) {
                    foreground.foregroundNotify(true);
                    return;
                }

                /*
                 * force notifyWantsForeground to search the list
                 * temp no current. Start with noForeground since the previous
                 * foreground display already got a foregroundNotify(false)
                 * when suspened.
                 */
                foreground = noForeground;
                notifyWantsForeground(foreground, false);

                // Another thread may be waiting to preempt the display
                displays.notify();
            }
        }

        /**
         * Shutdown all running MIDlets and prepare the MIDP runtime
         * to exit completely.
         */
        public void shutdown() {
            SystemEventListener l;

            synchronized (systemEventListeners) {
                for (int i = 0; i < systemEventListeners.size(); i++) {
                     l = (SystemEventListener)
                             systemEventListeners.elementAt(i);
                     l.shutdown();
                }
            }
        }

        /**
         * Suspend the current foreground MIDlet and return to the
         * AMS or "selector" to possibly run another MIDlet in the
         * currently active suite. Currently, the RI does not support
         * running multiple MIDlets, but if it did, this system
         * callback would allow it.
         * <p>
         * Called by the system event handler within Display.
         */
        public void suspendCurrent() {
            Display d;

            synchronized (displays) {
                if (foreground == noForeground || suspended != null ||
                       allSuspended || preempted != null) {
                    return;
                }

                d = foreground.getDisplay();
                d.midletEventListener.pauseMIDlet(d.getMIDlet());
                suspended = foreground;
                deactivate(d.getMIDlet());
            }
        }
 
        /**
         * Resume the currently suspended state. This is a result
         * of the underlying system returning control to MIDP.
         * Any previously paused foreground MIDlet will be restarted
         * and the Display will be refreshed.
         * <p>
         * Called by the system event handler within Display.
         */
        public void resumePrevious() {
            Display d;

            synchronized (displays) {
                if (suspended == null) {
                    return;
                }

                d = suspended.getDisplay();

                d.midletEventListener.startMIDlet(d.getMIDlet());

                displays.addElement(suspended);

                if (suspended.wantsForeground()) {
                    foreground.foregroundNotify(false);
                    foreground = suspended;
                    foreground.foregroundNotify(true);
                }

                suspended = null;
            }
        }

        /**
         * Kill the current foreground MIDlet and return to the
         * AMS or "selector" to possibly run another MIDlet in the
         * currently active suite. This is a system callback which
         * allows a user to forcibly exit a running MIDlet in cases
         * where it is necessary (such as a rogue MIDlet which does
         * not provide exit capability).
         * <p>
         * Called by the system event handler within Display.
         */
        public void killCurrent() {
            Display d;

            synchronized (displays) {
                if (foreground == noForeground) {
                    // There is no current, all MIDlets are paused
                    return;
                }

                d = foreground.getDisplay();
                d.midletEventListener.destroyMIDlet(d.getMIDlet());
            }
        }

        /**
         * Called by event delivery when an  Command is fired.
         * The parameter is an index into the list of Commands that are
         * current, i.e. those associated with the visible Screen.
         * @param id The integer id of the Command that fired (as returned
         *           by Command.getID())
         */
        public void commandAction(int id) {
            foreground.commandAction(id);
        }

        /**
         * Called by event delivery when a pen event is processed.
         * The type is one of EventHandler.PRESSED, EventHandler.RELEASED,
         * or EventHandler.DRAGGED.
         * @param type The type of event (press, release or drag)
         * @param x The x coordinate of the location of the pen     
         * @param y The y coordinate of the location of the pen     
         */
        public void pointerEvent(int type, int x, int y) {
            foreground.pointerEvent(type, x, y);
        }

        /**
         * Called by event delivery when a key event is processed.
         * The type is one of EventHandler.PRESSED, EventHandler.RELEASED,
         * or EventHandler.REPEATED.
         * @param type The type of event (press, release or repeat)
         * @param keyCode The key code for the key that registered the event
         */
        public void keyEvent(int type, int keyCode) {
            foreground.keyEvent(type, keyCode);
        }
        
        /**
         * Called by event delivery when a screen change needs to occur
         *
         * @param parent parent Display of the Displayable
         * @param screen The Displayable to make current in the Display
         */
        public void screenChange(Display parent, Displayable screen) {
            parent.screenChange(screen);
        }

        /**
        * Initializes the security token for this class, so it can
        * perform actions that a normal MIDlet Suite cannot.
        *
        * @param token security token for this class.
        */
        public void initSecurityToken(SecurityToken token) {
            Display.initSecurityToken(token);
        }

        /**
         * Create a display and return its internal access object.
         *
         * @param token security token for the calling class
         * @param midlet MIDlet that will own this display
         *
         * @return new display's access object
         *
         * @exception SecurityException if the caller does not have permission
         *   the internal MIDP permission.
         */
        public DisplayAccess createDisplay(SecurityToken token,
                                           MIDlet midlet) {
            token.checkIfPermissionAllowed(Permissions.MIDP);

            return new Display(midlet).accessor;
        }

        /**
         * Get the Image of the trusted icon for this Display.
         * Only callers with the internal MIDP permission can use this method.
         *
         * @return an Image of the trusted icon.
         */
        public Image getTrustedMIDletIcon() {
            MIDletSuite suite = Scheduler.getScheduler().getMIDletSuite();

            if (suite != null) {
                suite.checkIfPermissionAllowed(Permissions.MIDP);
            }
            
            return ImmutableImage.createIcon("trustedmidlet_icon.png");
        }

        /**
         * Called by event delivery when a repaint should occur
         *
         * @param x1 The origin x coordinate of the repaint region
         * @param y1 The origin y coordinate of the repaint region
         * @param x2 The bounding x coordinate of the repaint region
         * @param y2 The bounding y coordinate of the repaint region
         * @param target The optional paint target
         */
        public void repaint(int x1, int y1, int x2, int y2, Object target) {
            foreground.repaint(x1, y1, x2, y2, target);
        }

        /**
         * Called by event delivery to batch process all pending serial
         * callbacks
         */
        public void callSerially() {
            foreground.callSerially();
        }

        /**
         * Called by event delivery to process a Form invalidation
         * possibly caused by a specific item
         *
         * @param item the Item which caused the invalidation
         */
        public void callInvalidate(Item item) {
            foreground.callInvalidate(item);
        }

        /**
         * Called by event delivery to process an Item state change
         *
         * @param item the Item which has changed state
         */
        public void callItemStateChanged(Item item) {
            foreground.callItemStateChanged(item);
        }

        /**
         * Called when the system needs to temporarily prevent the application
         * from painting the screen.  The primary use of this method is to
         * allow
         * a system service to temporarily utilize the screen, e.g. to provide
         * input method or  command processing.
         *
         * This method should prevent application-based paints (i.e. those
         * generated by Canvas.repaint(), Canvas.serviceRepaints() or some
         * internal paint method on another kind of Displayable) from changing
         * the contents of the screen in any way.
         */
        public void suspendPainting() {
            foreground.suspendPainting();
        }

        /**
         * Called when the system is ready to give up its control over the
         * screen.  The application should receive a request for a full
         * repaint when this is called, and is subsequently free to process
         * paint events from Canvas.repaint(), Canvas.serviceRepaints() or
         * internal paint methods on Displayable.
         */
        public void resumePainting() {
            foreground.resumePainting();
        }

        /**
         * Called by event delivery when an input method event is processed.
         * @param str The string from the input method.
         */
        public void inputMethodEvent(String str) {
            foreground.inputMethodEvent(str);
        }

        /**
         * Called to notify the display manager when a display
         * wants its current displayable displayed or not.
         * <p>
         * If it is the current foreground display and wantsForeground is false
         * then another MIDlet may be made foreground display.
         * If it is not the foreground display by wantsForeground is true,
         * it will get the display if there are no other displays that
         * that want the display (wantsForeground is true).
         *
         * @param display the Display being  changed
         * @param wantsForeground is true if the Displable is being set to
         *        non-null, <code>false</code> otherwise.
         */
        private synchronized void notifyWantsForeground(DisplayAccess display,
                boolean wantsForeground) {
            DisplayAccess newForeground;

            synchronized (displays) {
                if (allSuspended ||
                       (display == foreground && wantsForeground) ||
                       (display != foreground && (!wantsForeground ||
                           foreground.wantsForeground()))) {
                    // no need to change the foreground
                    return;
                }

                /*
                 * Start with no foreground and then find a display
                 * this is done because timimg issues, the display may have
                 * been removed from the list by the scheduler, while
                 * the MIDlet called setCurrent.
                 */
                newForeground = noForeground;

                for (int i = 0; i < displays.size(); i++) {
                    DisplayAccess temp = (DisplayAccess)displays.elementAt(i);

                    if (temp.getDisplay().getCurrent() != null) {
                        // this display is a candidate for the foreground
                        newForeground = temp;
                    }

                    if (temp.wantsForeground()) {
                        // this display wants the foreground
                        newForeground = temp;
                        break;
                    }
                }

                foreground.foregroundNotify(false);
                foreground = newForeground;
                foreground.foregroundNotify(true);
                return;
            }
        }
    }
}

/*
 * ************* Class, DeviceCaps
 */

/**
 * Current device capabilities.
 */
class DeviceCaps {
    /** horizontal width of the current device */
    int width;
    /** vertical height of the current device */
    int height;
    /** 
     * vertical height available to draw on when in normal 
     * i.e. non-fullscreen mode 
     */
    int adornedHeight;
    /** color to use when erasing pixels in the device. */
    int eraseColor;
    /** depth of the display, e.g. 8 is full color */
    int displayDepth;
    /** true, if the device supports color */
    boolean displayIsColor;
    /** true, if the device supports a pointing device. */
    boolean pointerSupported;
    /** true, if the device supports motion events. */
    boolean motionSupported;
    /** true, if the device supported repeated events. */
    boolean repeatSupported;
    /** true, if the device supports double buffering. */
    boolean isDoubleBuffered;
    /** number of alpha levels supported by this device */
    int numAlphaLevels;
    /** keyCode for up arrow */
    int keyCodeUp;
    /** keyCode for down arrow */
    int keyCodeDown;
    /** keyCode for left arrow */
    int keyCodeLeft;
    /** keyCode for right arrow */
    int keyCodeRight;
    /** keyCode for select */
    int keyCodeSelect;

    /** initialize the device capabilities. */
    DeviceCaps() {
        init();
    }
    /** 
     * native method to retreive initial settings for 
     * display capabilities
     */
    private native void init();
}