SquirrelJME/SquirrelJME

View on GitHub
modules/midp-lcdui/src/main/java/javax/microedition/lcdui/Display.java

Summary

Maintainability
A
0 mins
Test Coverage
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// SquirrelJME
//     Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the Mozilla Public License Version 2.0.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------

package javax.microedition.lcdui;

import cc.squirreljme.jvm.mle.ThreadShelf;
import cc.squirreljme.jvm.mle.brackets.UIDisplayBracket;
import cc.squirreljme.jvm.mle.brackets.UIFormBracket;
import cc.squirreljme.jvm.mle.callbacks.UIDisplayCallback;
import cc.squirreljme.jvm.mle.constants.UIInputFlag;
import cc.squirreljme.jvm.mle.constants.UIItemPosition;
import cc.squirreljme.jvm.mle.constants.UIMetricType;
import cc.squirreljme.jvm.mle.constants.UIPixelFormat;
import cc.squirreljme.runtime.cldc.annotation.Api;
import cc.squirreljme.runtime.cldc.debug.Debugging;
import cc.squirreljme.runtime.lcdui.SerializedEvent;
import cc.squirreljme.runtime.lcdui.common.CommonColors;
import cc.squirreljme.runtime.lcdui.mle.DisplayWidget;
import cc.squirreljme.runtime.lcdui.mle.StaticDisplayState;
import cc.squirreljme.runtime.lcdui.mle.UIBackend;
import cc.squirreljme.runtime.lcdui.mle.UIBackendFactory;
import cc.squirreljme.runtime.lcdui.mle.Vibration;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.microedition.midlet.MIDlet;
import org.jetbrains.annotations.Async;
import org.jetbrains.annotations.NonBlocking;

@SuppressWarnings("OverlyComplexClass")
@Api
public class Display
    implements DisplayWidget
{
    /** The soft-key for the left command. */
    static final int _SOFTKEY_LEFT_COMMAND =
        Display.SOFTKEY_BOTTOM + 1;
    
    /** The soft-key for the right command. */
    static final int _SOFTKEY_RIGHT_COMMAND =
        Display.SOFTKEY_BOTTOM + 2;
    
    @Api
    public static final int ALERT =
        3;

    @Api
    public static final int CHOICE_GROUP_ELEMENT =
        2;

    @Api
    public static final int COLOR_BACKGROUND =
        0;

    @Api
    public static final int COLOR_BORDER =
        4;

    @Api
    public static final int COLOR_FOREGROUND =
        1;

    @Api
    public static final int COLOR_HIGHLIGHTED_BACKGROUND =
        2;

    @Api
    public static final int COLOR_HIGHLIGHTED_BORDER =
        5;

    @Api
    public static final int COLOR_HIGHLIGHTED_FOREGROUND =
        3;

    @Api
    public static final int COLOR_IDLE_BACKGROUND =
        6;

    @Api
    public static final int COLOR_IDLE_FOREGROUND =
        7;

    @Api
    @SuppressWarnings("FieldNamingConvention")
    public static final int COLOR_IDLE_HIGHLIGHTED_BACKGROUND =
        8;

    @Api
    @SuppressWarnings("FieldNamingConvention")
    public static final int COLOR_IDLE_HIGHLIGHTED_FOREGROUND =
        9;

    @Api
    public static final int COMMAND =
        5;

    @Api
    public static final int DISPLAY_HARDWARE_ABSENT =
        2;

    @Api
    public static final int DISPLAY_HARDWARE_DISABLED =
        1;
    
    @Api
    public static final int DISPLAY_HARDWARE_ENABLED =
        0;

    @Api
    public static final int LIST_ELEMENT =
        1;

    @Api
    public static final int MENU =
        7;

    /** This is the activity mode that enables power saving inhibition. */
    @Api
    public static final int MODE_ACTIVE =
        1;
    
    /** This is the activity mode that is the default behavior. */
    @Api
    public static final int MODE_NORMAL =
        0;

    @Api
    public static final int NOTIFICATION =
        6;

    @Api
    public static final int ORIENTATION_LANDSCAPE =
        2;

    @Api
    public static final int ORIENTATION_LANDSCAPE_180 =
        8;

    @Api
    public static final int ORIENTATION_PORTRAIT =
        1;

    @Api
    public static final int ORIENTATION_PORTRAIT_180 =
        4;

    /** The mask and number of items that are permitted for soft-key items. */
    @Api
    public static final int SOFTKEY_INDEX_MASK =
        15;

    /** Displayed at the bottom of the screen. */
    @Api
    public static final int SOFTKEY_BOTTOM =
        800;

    /** Displayed on the left side of the screen. */
    @Api
    public static final int SOFTKEY_LEFT =
        820;

    /** Displayed at the top of the screen. */
    @Api
    public static final int SOFTKEY_TOP =
        840;

    /** Displayed on the right side of the screen. */
    @Api
    public static final int SOFTKEY_RIGHT =
        860;

    /** Displayed off-screen, using physical hardware buttons. */
    @Api
    public static final int SOFTKEY_OFFSCREEN =
        880;

    @Api
    public static final int STATE_BACKGROUND =
        0;

    @Api
    public static final int STATE_FOREGROUND =
        2;

    @Api
    public static final int STATE_VISIBLE =
        1;

    @Api
    public static final int SUPPORTS_ALERTS =
        32;

    @Api
    public static final int SUPPORTS_COMMANDS =
        2;

    @Api
    public static final int SUPPORTS_FILESELECTORS =
        512;

    @Api
    public static final int SUPPORTS_FORMS =
        4;

    @Api
    public static final int SUPPORTS_IDLEITEM =
        2048;

    /** This specifies that the display supports user input. */
    @Api
    public static final int SUPPORTS_INPUT_EVENTS =
        1;

    @Api
    public static final int SUPPORTS_LISTS =
        64;

    @Api
    public static final int SUPPORTS_MENUS =
        1024;

    @Api
    public static final int SUPPORTS_ORIENTATION_LANDSCAPE =
        8192;

    @Api
    @SuppressWarnings("FieldNamingConvention")
    public static final int SUPPORTS_ORIENTATION_LANDSCAPE180 =
        32768;

    @Api
    public static final int SUPPORTS_ORIENTATION_PORTRAIT =
        4096;

    @Api
    public static final int SUPPORTS_ORIENTATION_PORTRAIT180 =
        16384;

    @Api
    public static final int SUPPORTS_TABBEDPANES =
        256;

    @Api
    public static final int SUPPORTS_TEXTBOXES =
        128;

    @Api
    public static final int SUPPORTS_TICKER =
        8;

    @Api
    public static final int SUPPORTS_TITLE =
        16;

    @Api
    public static final int TAB =
        4;
    
    /** Serial runs of a given method for this display. */
    final Map<Integer, Runnable> _serialRuns =
        new LinkedHashMap<>();
    
    /** The number of times there has been a non-unique serial run. */
    private static volatile int _NON_UNIQUE_SERIAL_RUNS;
    
    /** The native display instance. */ 
    final UIDisplayBracket _uiDisplay;
    
    /** The displayable to show. */
    private volatile Displayable _current;
    
    /** The displayable to show on exit. */
    private volatile Displayable _exit;
    
    /** The layout policy of this display. */
    private CommandLayoutPolicy _layoutPolicy;
    
    /**
     * Initializes the display instance.
     *
     * @param __uiDisplay The native display.
     * @throws NullPointerException On null arguments.
     * @since 2018/03/16
     */
    Display(UIDisplayBracket __uiDisplay)
        throws NullPointerException
    {
        if (__uiDisplay == null)
            throw new NullPointerException("NARG");
        
        this._uiDisplay = __uiDisplay;
        
        // Check and ensure that the background thread exists
        synchronized (StaticDisplayState.class)
        {
            // If there is no background thread yet, initialize it
            Thread bgThread = StaticDisplayState.backgroundThread();
            if (bgThread == null)
            {
                // The user interface thread to use
                __MLEUIThread__ uiRunner = new __MLEUIThread__();
                
                // Initialize thread and make it a background worker
                bgThread = new Thread(uiRunner, "SquirrelJME-LCDUI");
                ThreadShelf.javaThreadSetDaemon(bgThread);
                
                // Set background thread state and start it
                StaticDisplayState.setBackgroundThread(bgThread, uiRunner);
                bgThread.start();
            }
            
            // Register this object for the native display
            UIDisplayBracket uiDisplay = this._uiDisplay;
            StaticDisplayState.register(this, uiDisplay);
            
            // Register the display for callbacks
            UIBackendFactory.getInstance(true).callback(uiDisplay,
                (UIDisplayCallback)StaticDisplayState.callback());
        }
    }
    
    /**
     * Calls the given runner within the event handler serially.
     * 
     * Note that the {@link Runnable#run()} will be called as if it were
     * serialized like everything else with {@link SerializedEvent}.
     * 
     * Calls to this method will never block and wait for the {@link Runnable}
     * to complete.
     * 
     * @param __run The method to run.
     * @throws NullPointerException On null arguments.
     * @since 2020/10/03
     */
    @Api
    @NonBlocking
    @Async.Schedule
    public void callSerially(Runnable __run)
        throws NullPointerException
    {
        // Enqueue serialized call
        this.__queueSerialRunner(__run);
    }
    
    /**
     * Flashes the display LED for the given number of milliseconds.
     *
     * In SquirrelJME this flashes an LED and not the back light, since it is
     * not a popular means to notify the user and additionally due to
     * medical concerns such as epilepsy.
     *
     * @param __ms The number of milliseconds to flash for.
     * @return {@code true} if the backlight is controlled by the application
     * and the display is in the foreground, otherwise {@code false}.
     * @since 2019/10/05
     */
    @Api
    public boolean flashBacklight(int __ms)
        throws IllegalArgumentException
    {
        /* {@squirreljme.error EB30 Cannot blink for a negative duration.} */
        if (__ms < 0)
            throw new IllegalArgumentException("EB30");
        
        throw Debugging.todo();
        /*
        // Blink!
        throw Debugging.todo();
        /*
        Assembly.sysCall(SystemCallIndex.DEVICE_FEEDBACK,
            DeviceFeedbackType.BLINK_LED, __ms);
        
        // Only return true if no error was generated
        return (SystemCallError.NO_ERROR ==
            Assembly.sysCallV(SystemCallIndex.ERROR_GET,
                SystemCallIndex.DEVICE_FEEDBACK));*/
                
    }
    
    /**
     * Returns the current activity mode that the display is within, if
     * active mode is set then the display will inhibit power saving features.
     *
     * @return Either {@link #MODE_ACTIVE} or {@link #MODE_NORMAL}.
     * @since 2016/10/08
     */
    @Api
    public int getActivityMode()
    {
        throw Debugging.todo();
    }
    
    /**
     * Returns the height of the image that should be used for the given
     * display element.
     *
     * Valid elements are:
     * {@link #LIST_ELEMENT},
     * {@link #CHOICE_GROUP_ELEMENT},
     * {@link #ALERT},
     * {@link #TAB},
     * {@link #COMMAND},
     * {@link #NOTIFICATION}, and
     * {@link #MENU}.
     *
     * @param __a If display element.
     * @return The height of the image for that element.
     * @throws IllegalArgumentException On null arguments.
     * @since 2016/10/14
     */
    @Api
    public int getBestImageHeight(int __a)
        throws IllegalArgumentException
    {
        return this.__bestImageSize(__a, true);
    }
    
    /**
     * Returns the width of the image that should be used for the given
     * display element.
     *
     * Valid elements are:
     * {@link #LIST_ELEMENT},
     * {@link #CHOICE_GROUP_ELEMENT},
     * {@link #ALERT},
     * {@link #TAB},
     * {@link #COMMAND},
     * {@link #NOTIFICATION}, and
     * {@link #MENU}.
     *
     * @param __a If display element.
     * @return The width of the image for that element.
     * @throws IllegalArgumentException On null arguments.
     * @since 2016/10/14
     */
    @Api
    public int getBestImageWidth(int __a)
        throws IllegalArgumentException
    {
        return this.__bestImageSize(__a, false);
    }
    
    @Api
    public int getBorderStyle(boolean __a)
    {
        throw Debugging.todo();
    }
    
    /**
     * This returns the capabilities that the display supports. This means that
     * displays which do not support specific widget types can be known so that
     * potential alternative handling may be performed.
     *
     * The capabilities are the constants starting with {@code SUPPORTS_}
     *
     * @return A bit field where set bits indicate supported capabilities, if
     * {@code 0} is returned then only a {@link Canvas} is supported.
     * @since 2016/10/08
     */
    @Api
    public int getCapabilities()
    {
        // These are all standard and expected to always be supported
        int rv = Display.__defaultCapabilities();
            
        UIBackend backend = UIBackendFactory.getInstance(true);
        
        // Supports any kind of input?
        if (0 != backend.metric(_uiDisplay, UIMetricType.INPUT_FLAGS))
            rv |= Display.SUPPORTS_INPUT_EVENTS;
        
        return rv;
    }
    
    /**
     * Returns the color used for the specified interface item.
     *
     * The value values are:
     * {@link #COLOR_BACKGROUND},
     * {@link #COLOR_BORDER},
     * {@link #COLOR_FOREGROUND},
     * {@link #COLOR_HIGHLIGHTED_BACKGROUND},
     * {@link #COLOR_HIGHLIGHTED_BORDER},
     * {@link #COLOR_HIGHLIGHTED_FOREGROUND},
     * {@link #COLOR_IDLE_BACKGROUND},
     * {@link #COLOR_IDLE_FOREGROUND},
     * {@link #COLOR_IDLE_HIGHLIGHTED_BACKGROUND}, and
     * {@link #COLOR_IDLE_HIGHLIGHTED_FOREGROUND}
     *
     * @param __c The color to get.
     * @return The ARGB color for the specified user interface item, it will
     * be in the form of {@code 0x00RRGGBB}.
     * @throws IllegalArgumentException If the specified color is not valid.
     * @since 2016/10/14
     */
    @Api
    public int getColor(int __c)
        throws IllegalArgumentException
    {
        int rv;
        switch (__c)
        {
            case Display.COLOR_BORDER:
                rv = CommonColors.BORDER;
                break;
            
            case Display.COLOR_BACKGROUND:
            case Display.COLOR_IDLE_BACKGROUND:
                rv = CommonColors.BACKGROUND;
                break;
            
            case Display.COLOR_FOREGROUND:
            case Display.COLOR_IDLE_FOREGROUND:
                rv = CommonColors.FOREGROUND;
                break;
            
            case Display.COLOR_HIGHLIGHTED_BORDER:
                rv = CommonColors.HIGHLIGHTED_BORDER;
                break;
                
            case Display.COLOR_HIGHLIGHTED_BACKGROUND:
            case Display.COLOR_IDLE_HIGHLIGHTED_BACKGROUND:
                rv = CommonColors.HIGHLIGHTED_BACKGROUND;
                break;
            
            case Display.COLOR_HIGHLIGHTED_FOREGROUND:
            case Display.COLOR_IDLE_HIGHLIGHTED_FOREGROUND:
                rv = CommonColors.HIGHLIGHTED_FOREGROUND;
                break;
        
                /* {@squirreljme.error EB1h Unknown color specifier. (The
                color specifier)} */
            default:
                throw new IllegalArgumentException("EB1h " + __c);
        }
        
        // Clip the alpha away
        return (rv & 0xFFFFFF);
    }
    
    /**
     * Returns the current command layout policy. The policy of the
     * {@link Displayable} takes precedence.
     * 
     * @return The current command layout policy, may be {@code null}.
     * @since 2020/09/27
     */
    @Api
    public CommandLayoutPolicy getCommandLayoutPolicy()
    {
        return this._layoutPolicy;
    }
    
    /**
     * Returns the preferred placement for commands.
     * 
     * @param __ct The command type, see {@link Command}.
     * @return The preferred placements or {@code null} if there are none.
     * @throws IllegalArgumentException If the command type is not valid.
     * @since 2020/09/27
     */
    @Api
    public int[] getCommandPreferredPlacements(int __ct)
        throws IllegalArgumentException
    {
        /* {@squirreljme.error EB3l Invalid command type. (The type)} */
        if (__ct < Command.SCREEN || __ct > Command.ITEM)
            throw new IllegalArgumentException("EB3l " + __ct);
        
        // In SquirrelJME, commands are in the same places as menu items
        return this.getMenuPreferredPlacements();
    }
    
    /**
     * Returns the current displayable.
     *
     * @return The current displayable or {@code null} if it is not set.
     * @since 2016/10/08
     */
    @Api
    public Displayable getCurrent()
    {
        return this._current;
    }
    
    @Api
    public int getDisplayState()
    {
        throw Debugging.todo();
    }
    
    /**
     * Returns the dot pitch of the display in microns (also known as
     * micrometers or um).
     *
     * If pixels are not square then the pitch should be the average of the
     * two.
     *
     * @return The dot pitch in microns.
     * @since 2016/10/14
     */
    @Api
    public int getDotPitch()
    {
        throw Debugging.todo();
    }
    
    /**
     * Returns all of the possible exact placements where items may go on
     * a given border.
     * 
     * The orientation of the display does affect the border positions, if
     * the orientation has changed then this must be called again.
     * 
     * For top/bottom borders, the order is from left to right.
     * 
     * For left/right borders, the order is top to bottom.
     * 
     * The first possible placement on a border is always {@code BORDER + 1}.
     * 
     * @param __b The border to get, must be one of {@link #SOFTKEY_TOP},
     * {@link #SOFTKEY_BOTTOM}, {@link #SOFTKEY_LEFT}, {@link #SOFTKEY_RIGHT},
     * or {@link #SOFTKEY_OFFSCREEN}.
     * @return The valid placements for the given border, or {@code null}
     * if the border is not supported.
     * @throws IllegalArgumentException If the border is not valid.
     * @since 2020/09/27
     */
    @Api
    public int[] getExactPlacementPositions(int __b)
        throws IllegalArgumentException
    {
        // Un-project the layout to get the correct order
        __b = this.__layoutProject(__b);
        
        // Depends on the border that was requested
        switch (__b)
        {
                // None of these positions are valid in SquirrelJME
            case Display.SOFTKEY_OFFSCREEN:
            case Display.SOFTKEY_TOP:
            case Display.SOFTKEY_LEFT:
            case Display.SOFTKEY_RIGHT:
                return null;
                
                // There are only two slots along the bottom of the screen
            case Display.SOFTKEY_BOTTOM:
                return new int[]{
                    this.__layoutProject(Display.SOFTKEY_BOTTOM + 1),
                    this.__layoutProject(Display.SOFTKEY_BOTTOM + 2)};
            
                /* {@squirreljme.error EB1p Invalid border. (The border)} */
            default:
                throw new IllegalArgumentException("EB1p " + __b);
        }
    }
    
    /**
     * Returns the current hardware state.
     *
     * @return The hardware state.
     * @since 2018/12/10
     */
    @Api
    public int getHardwareState()
    {
        throw Debugging.todo();
        /*
        if (__EventCallback__._CALLBACK._registered)
            return DISPLAY_HARDWARE_ENABLED;
        return DISPLAY_HARDWARE_DISABLED;
        */
    }
    
    /**
     * Returns the maximum height of the display.
     *
     * @return The maximum display height.
     * @since 2016/10/14
     */
    @Api
    public int getHeight()
    {
        return UIBackendFactory.getInstance(true)
            .metric(_uiDisplay, UIMetricType.DISPLAY_MAX_HEIGHT);
    }
    
    @Api
    public IdleItem getIdleItem()
    {
        throw Debugging.todo();
    }
    
    /**
     * Returns the preferred placement for menus.
     * 
     * @return The preferred placements or {@code null} if there are none.
     * @since 2020/09/27
     */
    @Api
    public int[] getMenuPreferredPlacements()
    {
        // The preferred placements for menus are the same as the supported
        // ones
        return this.getMenuSupportedPlacements();
    }
    
    /**
     * Returns all of the placements which support menu items.
     *  
     * @return The list of supported menu item placements.
     * @since 2020/09/27
     */
    @Api
    public int[] getMenuSupportedPlacements()
    {
        // In SquirrelJME, commands and menus can only be placed along the
        // bottom of the screen
        return this.getExactPlacementPositions(Display.SOFTKEY_BOTTOM);
    }
    
    /**
     * Returns the current orientation of the display.
     *
     * @return The display orientation.
     * @since 2017/10/27
     */
    @Api
    public int getOrientation()
    {
        int width, height;
        
        // If a form is being shown, use those dimensions
        Displayable form = this._current;
        if (form != null)
        {
            width = Displayable.__getWidth(form, null);
            height = Displayable.__getHeight(form, null);
        }
        
        // Otherwise use the display dimensions
        else
        {
            width = this.getWidth();
            height = this.getHeight();
        }
        
        // Landscape just means a longer width
        if (width > height)
            return Display.ORIENTATION_LANDSCAPE;
        else
            return Display.ORIENTATION_PORTRAIT;
    }
    
    /**
     * Returns the maximum width of the display.
     *
     * @return The maximum display width.
     * @since 2016/10/14
     */
    @Api
    public int getWidth()
    {
        return UIBackendFactory.getInstance(true)
            .metric(_uiDisplay, UIMetricType.DISPLAY_MAX_WIDTH);
    }
    
    /**
     * Are mouse/stylus press and release events supported?
     *
     * @return {@code true} if they are supported.
     * @since 2016/10/14
     */
    @Api
    public boolean hasPointerEvents()
    {
        return (UIBackendFactory.getInstance(true).metric(_uiDisplay,
            UIMetricType.INPUT_FLAGS) & UIInputFlag.POINTER) ==
            (UIInputFlag.POINTER);
    }
    
    /**
     * Are mouse/stylus move/drag events supported?
     *
     * @return {@code true} if they are supported.
     * @since 2016/10/14
     */
    @Api
    public boolean hasPointerMotionEvents()
    {
        return (UIBackendFactory.getInstance(true).metric(_uiDisplay,
            UIMetricType.INPUT_FLAGS) &
            (UIInputFlag.POINTER | UIInputFlag.POINTER_MOTION)) ==
            (UIInputFlag.POINTER | UIInputFlag.POINTER_MOTION);
    }
    
    /**
     * Is this display built into the device or is it an auxiliary display?
     *
     * @return {@code true} if it is built-in.
     * @since 2016/10/14
     */
    @Api
    public boolean isBuiltIn()
    {
        throw Debugging.todo();
    }
    
    /**
     * Is color supported by this display?
     *
     * @return {@code true} if color is supported.
     * @since 2016/10/14
     */
    @Api
    public boolean isColor()
    {
        return UIBackendFactory.getInstance(true).metric(_uiDisplay,
            UIMetricType.DISPLAY_MONOCHROMATIC) == 0;
    }
    
    /**
     * Returns the number of alpha-transparency levels. Alpha levels range
     * from fully transparent to fully opaque.
     * 
     * It is required by implementations to support at least 16 levels of
     * alpha transparency.
     *
     * @return The alpha transparency levels.
     * @since 2016/10/14
     */
    @Api
    @SuppressWarnings({"MagicNumber", "SwitchStatementWithTooFewBranches"})
    public int numAlphaLevels()
    {
        switch (UIBackendFactory.getInstance(true).metric(_uiDisplay,
            UIMetricType.DISPLAY_PIXEL_FORMAT))
        {
                // If the display format is 16-bit, just use this here
            case UIPixelFormat.SHORT_RGBA4444:
                return 16;
            
                // Use 256 since all other image formats would get their
                // alpha colors calculated.
            default:
                return 256;
        }
    }
    
    /**
     * Returns the number of colors available to the display.
     *
     * Monochrome (black and white) displays only have two colors.
     *
     * There will always be at least two colors.
     *
     * @return The number of available colors.
     * @since 2016/10/14
     */
    @Api
    @SuppressWarnings("MagicNumber")
    public int numColors()
    {
        int pf;
        switch ((pf = UIBackendFactory.getInstance(true).metric(_uiDisplay,
            UIMetricType.DISPLAY_PIXEL_FORMAT)))
        {
            case UIPixelFormat.INT_RGB888:
            case UIPixelFormat.INT_RGBA8888:
                return 16_777_216;
            
            case UIPixelFormat.SHORT_INDEXED65536:
                return 65536;
            
            case UIPixelFormat.SHORT_RGB565:
                return 8192;
            
            case UIPixelFormat.SHORT_RGBA4444:
                return 4096;
            
            case UIPixelFormat.BYTE_INDEXED256:
                return 256;
            
            case UIPixelFormat.PACKED_INDEXED4:
                return 16;
            
            case UIPixelFormat.PACKED_INDEXED2:
                return 4;
            
            case UIPixelFormat.PACKED_INDEXED1:
                return 2;
            
                /* {@squirreljme.error EB3j Unhandled pixel format. (Format)} */
            default:
                throw Debugging.oops("EB3j", pf);
        }
    }
    
    /**
     * Removes the currently displayed displayable.
     * 
     * @since 2020/10/04
     */
    @Api
    public void removeCurrent()
    {
        // Just performs the internal hiding logic
        this.__doHideCurrent();
    }
    
    /**
     * Sets the activity mode of the display. If active mode is set then
     * power saving features are inhibited.
     *
     * @param __m The activity mode, either {@link #MODE_ACTIVE} or
     * {@link #MODE_NORMAL}.
     * @throws IllegalArgumentException If the specified mode is not valid.
     * @since 2016/10/08
     */
    @Api
    public void setActivityMode(int __m)
        throws IllegalArgumentException
    {
        // Active?
        if (__m == Display.MODE_ACTIVE)
            throw Debugging.todo();
    
        // Normal
        else if (__m == Display.MODE_NORMAL)
            throw Debugging.todo();
    
        /* {@squirreljme.error EB1i Unknown activity mode specified.} */
        else
            throw new IllegalArgumentException("EB1i");
    }
    
    @Api
    public void setCommandLayoutPolicy(CommandLayoutPolicy __clp)
    {
        throw Debugging.todo();
    }
    
    /**
     * Shows the given alert on this display, when the alert is finished the
     * specified displayable is shown when it exits.
     *
     * This follows the same semantics as {@link #setCurrent(Displayable)}.
     *
     * @param __show The alert to show.
     * @param __exit The displayable to show when the alert that is
     * set is dismissed. This cannot be an {@link Alert}.
     * @throws DisplayCapabilityException If the display cannot show the given
     * displayable.
     * @throws IllegalStateException If the display hardware is missing; If
     * the displayables are associated with another display or tab pane; or
     * the next displayable item is an alter.
     * @throws NullPointerException On null arguments.
     * @since 2016/10/08
     */
    @Api
    public void setCurrent(Alert __show, Displayable __exit)
        throws DisplayCapabilityException, IllegalStateException,
            NullPointerException
    {
        /* {@squirreljme.error EB1j Cannot show another alert when the alert
        to show is cleared.} */
        if (__exit instanceof Alert)
            throw new IllegalStateException("EB1j");
        
        // Check
        if (__show == null || __exit == null)
            throw new NullPointerException("NARG");
        
        /* {@squirreljme.error EB1k The displayable to show on exit after
        showing an alert cannot be an alert.} */
        if (__exit instanceof Alert)
            throw new IllegalStateException("EB1k");
        
        // Debug
        Debugging.debugNote("Showing alert \"%s\"", __show._message);
        
        // Perform call on this display
        throw Debugging.todo();
        /*
        try
        {
            // Set widgets
            if (true)
                throw new todo.TODO();
            /*
            LcdServiceCall.<VoidType>call(VoidType.class,
                LcdFunction.WIDGET_ALERT_SHOW, this._handle,
                __show._handle, __exit._handle);
            * /
            
            // Hold onto these so they do not get GCed
            this._heldcurrent = __show;
            this._heldexit = __exit;
        }
        
        /* {@squirreljme.error EB1l Could not set the alert and its exit
        displayable because it is already set on a display.} * /
        catch (LcdWidgetOwnedException e)
        {
            throw new IllegalStateException("EB1l", e);
        }*/
    }
    
    /**
     * Sets the current displayable to be displayed.
     *
     * If the value to be passed is an {@link Alert} then this acts as if
     * {@code setCurrent(__show, getCurrent())} was called.
     *
     * The displayable if specified will be put into the foreground state.
     *
     * Note that it is unspecified when the displayable is made current, it may
     * be done when this is called or it may be queued for later.
     *
     * @param __show The displayable to show, if {@code null} this tells the
     * {@link Display} to enter the background state.
     * @throws DisplayCapabilityException If the display cannot show the given
     * displayable.
     * @throws IllegalStateException If the display hardware is missing; If
     * the displayable is associated with another display or tab pane.
     * @since 2016/10/08
     */
    @Api
    public void setCurrent(Displayable __show)
        throws DisplayCapabilityException, IllegalStateException
    {
        // There technically is no background state in SquirrelJME, a
        // background state being one that turns off the display or
        // otherwise
        if (__show == null)
            return;
        
        // If we are trying to show the same display, force foreground
        Displayable current = this._current;
        if (current == __show)
        {
            // This will force the form to be currently shown on the screen
            // even if another task has set the form. Since displays may only
            // have a single form associated with them, this effectively
            // retakes control accordingly
            this.__doShowCurrent(__show);
            
            return;
        }
        
        // If showing an alert, it gets displayed instead
        if (__show instanceof Alert)
        {
            this.setCurrent((Alert)__show, this.getCurrent());
            return;
        }
        
        /* {@squirreljme.error EB1m The displayable to be displayed is already
        being displayed.} */
        if (__show._display != null)
            throw new IllegalStateException("EB1m");
        
        // Hide the current display then show the new one
        this.__doHideCurrent();
        this.__doShowCurrent(__show);
    }
    
    @Api
    public void setCurrentItem(Item __a)
    {
        throw Debugging.todo();
    }
    
    @Api
    public void setIdleItem(IdleItem __i)
    {
        throw Debugging.todo();
    }
    
    @Api
    public void setPreferredOrientation(int __o)
    {
        throw Debugging.todo();
    }
    
    /**
     * Attempts to vibrate the device for the given number of milliseconds.
     *
     * The values here only set the duration to vibrate for from the current
     * point in time and will not increase the length of vibration.
     *
     * The return value will be {@code false} if the display is in the
     * background, the device cannot vibrate, or the vibrator cannot be
     * controlled.
     *
     * Note that excessive vibration may cause the battery life for a device to
     * be lowered, thus it should be used sparingly.
     *
     * @param __d The number of milliseconds to vibrate for, if zero the
     * vibrator will be switched off.
     * @return {@code true} if the vibrator is controllable by this application
     * and the display is active.
     * @throws IllegalArgumentException If the duration is negative.
     * @since 2017/02/26
     */
    @Api
    public boolean vibrate(int __d)
        throws IllegalArgumentException
    {
        // Forward
        return Vibration.vibrate(__d);
    }
    
    /**
     * This wraps getting the best image size.
     *
     * @param __e The element to get it for.
     * @param __h Return the height?
     * @return The best image size.
     * @throws IllegalArgumentException If the element type is not valid.
     * @since 2016/10/14
     */
    private int __bestImageSize(int __e, boolean __h)
        throws IllegalArgumentException
    {
        // Depends
        UIBackend backend = UIBackendFactory.getInstance(true);
        switch (__e)
        {
            case Display.CHOICE_GROUP_ELEMENT:
                throw Debugging.todo();
                
            case Display.ALERT:
                throw Debugging.todo();
                
            case Display.TAB:
                throw Debugging.todo();
                
            case Display.NOTIFICATION:
                throw Debugging.todo();
                
            case Display.LIST_ELEMENT:
                return backend.metric(_uiDisplay, UIMetricType.LIST_ITEM_HEIGHT);
                
            case Display.MENU:
            case Display.COMMAND:
                return backend.metric(_uiDisplay, UIMetricType.COMMAND_BAR_HEIGHT);
                
                /* {@squirreljme.error EB1o Cannot get the best image size of
                the specified element. (The element specifier)} */
            default:
                throw new IllegalArgumentException(String.format("EB1o %d",
                    __e));
        }
    }
    
    /**
     * Hides the current displayable.
     * 
     * @return The currently displayed displayable unless not set.
     * @since 2020/09/27
     */
    private Displayable __doHideCurrent()
    {
        // Nothing was ever visible on this display?
        Displayable current = this._current;
        if (current == null)
            return null;
        
        // Hide the form on the display, but only if it is currently shown
        // as we do not want to un-hide another form being displayed if it
        // is from another process
        if (current.__isShown())
            UIBackendFactory.getInstance(true)
                .displayShow(this._uiDisplay, null);
        
        // Unlink display
        current._display = null;
        this._current = null;
        
        // Inform canvases that they are now hidden
        if (current instanceof Canvas)
            ((Canvas)current).hideNotify();
        
        return current;
    }
    
    /**
     * Do current show logic.
     *
     * @param __show The displayable to show.
     * @throws NullPointerException On null arguments.
     * @since 2019/05/16
     */
    final void __doShowCurrent(Displayable __show)
        throws NullPointerException
    {
        if (__show == null)
            throw new NullPointerException("NARG");
        
        // Debug
        Debugging.debugNote("Showing %s on display.", __show.getClass());
        
        // Get the backend to call on
        UIBackend backend = UIBackendFactory.getInstance(true);
        
        // Use the global callback thread
        synchronized (StaticDisplayState.class)
        {
            // Set callback for the displayed form so it can receive events
            backend.callback(
                __show.__state(Displayable.__DisplayableState__.class)._uiForm,
                StaticDisplayState.callback());
        }
        
        // Show the form on the display, as long as it is not already on there
        UIDisplayBracket uiDisplay = this._uiDisplay;
        UIFormBracket wasForm = backend.displayCurrent(uiDisplay);
        if (wasForm == null || !backend.equals(
            __show.__state(Displayable.__DisplayableState__.class)._uiForm, wasForm))
            backend.displayShow(uiDisplay,
                __show.__state(Displayable.__DisplayableState__.class)._uiForm);
        
        // Set new parent
        __show._display = this;
        this._current = __show;
        
        // Notify that it was shown
        this.__queueSerialRunner(new __NotifyShow__(__show));
    }
    
    /**
     * Projects the border for the given layout according to the screen
     * orientation. Calling this method twice with a previously projected item
     * will result in the projection being reversed.
     * 
     * @param __b The border or item to project.
     * @return The projection of the border.
     * @since 2020/09/27
     */
    final int __layoutProject(int __b)
    {
        // Get the true border and the item position
        int border = __b & (~Display.SOFTKEY_INDEX_MASK);
        int position = __b & Display.SOFTKEY_INDEX_MASK;
        
        // Depends if the display is flipped or not
        switch (this.getOrientation())
        {
                // Normal layout, does not get modified
            case Display.ORIENTATION_PORTRAIT:
            case Display.ORIENTATION_LANDSCAPE:
                return __b;
            
                // Rotation will adjust which border items appear on
            case Display.ORIENTATION_PORTRAIT_180:
            case Display.ORIENTATION_LANDSCAPE_180:
                switch (border)
                {
                        // If the item is offscreen, it never gets adjusted
                    case Display.SOFTKEY_OFFSCREEN:
                        return __b;
                    
                    case Display.SOFTKEY_TOP:
                        return Display.SOFTKEY_BOTTOM + position;
                        
                    case Display.SOFTKEY_BOTTOM:
                        return Display.SOFTKEY_TOP + position;
                        
                    case Display.SOFTKEY_LEFT:
                        return Display.SOFTKEY_RIGHT + position;
                        
                    case Display.SOFTKEY_RIGHT:
                        return Display.SOFTKEY_LEFT + position;
                    
                        /* {@squirreljme.error EB3k Invalid border position.
                        (The border position).} */
                    default:
                        throw new IllegalArgumentException("EB3k " + border);
                }
            
                // This should not occur
            default:
                throw Debugging.oops("Invalid orientation.");
        }
    }
    
    /**
     * Queues the serial runner.
     *
     * @param __run The method to run.
     * @return The identifier for the runner item.
     * @throws NullPointerException On null arguments.
     * @since 2020/11/27
     */
    @SuppressWarnings({"WrapperTypeMayBePrimitive"})
    final int __queueSerialRunner(Runnable __run)
        throws NullPointerException
    {
        if (__run == null)
            throw new NullPointerException("NARG");
        
        // Get next ID to use.
        Integer idRunner;
        synchronized (Display.class)
        {
            idRunner = (++Display._NON_UNIQUE_SERIAL_RUNS);
        }
        
        // Perform the serialization call
        synchronized (this)
        {
            // Store into the serial runner
            Map<Integer, Runnable> serialRuns = this._serialRuns;
            serialRuns.put(idRunner, __run);
        }
        
        // Perform the call so it is done later
        UIBackendFactory.getInstance(true)
            .later(this._uiDisplay, idRunner);
        
        // This is the ID used to refer to this runner
        return idRunner;
    }
    
    /**
     * Performs a serial run.
     * 
     * @param __serialId The serial run ID.
     * @since 2023/01/14
     */
    @SerializedEvent
    @Async.Execute
    protected void __serialRun(int __serialId)
    {
        // Look to see if it is a valid call
        Integer key = __serialId;
        Runnable runner;
        synchronized (this)
        {
            // Locate the runner
            runner = this._serialRuns.get(key);
        }
        
        // Run it
        try
        {
            if (runner != null)
                runner.run();
        }
        finally
        {
            synchronized (this)
            {
                // Always clear it, even with failures
                this._serialRuns.remove(key);
                
                // Notify all the display threads that something happened
                this.notifyAll();
            }
        }
    }
    
    /**
     * Adds the specified listener for changes to displays.
     *
     * The order in which listeners are executed in is
     * implementation specified.
     *
     * @param __dl The listener to add.
     * @throws NullPointerException On null arguments.
     * @since 2018/03/24
     */
    @Api
    public static void addDisplayListener(DisplayListener __dl)
        throws NullPointerException
    {
        StaticDisplayState.addListener(__dl);
    }
    
    /**
     * Obtains the display that is associated with the given MIDlet.
     *
     * @param __m The display to get the midlet for.
     * @return The display for the given midlet.
     * @throws NullPointerException On null arguments.
     * @since 2016/10/08
     */
    @Api
    public static Display getDisplay(MIDlet __m)
        throws NullPointerException
    {
        // Check
        if (__m == null)
            throw new NullPointerException("NARG");
        
        // Use the first display that is available.
        // In the runtime, each program only ever gets a single MIDlet and
        // creating new MIDlets is illegal. Thus since getDisplays() has zero
        // be the return value for this method, that is used here.
        Display[] all = Display.getDisplays(0);
        if (all.length > 0)
            return all[0];
        
        /* {@squirreljme.error EB1p Could not get the display for the specified
        MIDlet because no displays are available.} */
        throw new IllegalStateException("EB1p");
    }
    
    /**
     * Obtains the displays which have the given capability from all internal
     * display providers.
     *
     * @param __caps The capabilities to use, this is a bitfield and the values
     * include all of the {@code SUPPORT_} prefixed constants. If {@code 0} is
     * specified then capabilities are not checked.
     * @return An array containing the displays with these capabilities.
     * @throws IllegalStateException If there are no compatible displays.
     * @since 2016/10/08
     */
    @Api
    public static Display[] getDisplays(int __caps)
        throws IllegalStateException
    {
        // Use cached displays, but otherwise load them
        Display[] all = StaticDisplayState.DISPLAYS;
        if (all == null)
        {
            // Get the displays that are attached to the system
            UIDisplayBracket[] uiDisplays =
                UIBackendFactory.getInstance(true).displays();
            int n = uiDisplays.length;
            
            // Initialize display instances
            all = new Display[n];
            for (int i = 0; i < n; i++)
                all[i] = new Display(uiDisplays[i]);
            
            // Use these for future calls
            StaticDisplayState.DISPLAYS = all;
            
            // Inform any listeners that the displays exist now
            for (DisplayListener listener : StaticDisplayState.listeners())
                for (Display display : all) 
                    listener.displayAdded(display);
        }
        
        // If we do not care for the capabilities of the displays then just
        // return all of them
        if (__caps == 0)
            return all.clone();
        
        // Find possible displays
        List<Display> possible = new ArrayList<>(all.length);
        for (Display potential : all)
            if ((potential.getCapabilities() & __caps) == __caps)
                possible.add(potential);
        
        /* {@squirreljme.error EB1q No displays are available.} */
        if (possible.isEmpty())
            throw new IllegalStateException("EB1q");
        
        return possible.<Display>toArray(new Display[possible.size()]);
    }
    
    /**
     * Removes the specified display listener so that it is no longer called
     * when events occur.
     *
     * @param __dl The listener to remove.
     * @throws IllegalStateException If the listener is not in the display.
     * @throws NullPointerException On null arguments.
     * @since 2018/03/24
     */
    @Api
    public static void removeDisplayListener(DisplayListener __dl)
        throws IllegalStateException, NullPointerException
    {
        StaticDisplayState.removeListener(__dl);
    }
    
    /**
     * The default display capabilities.
     * 
     * @return The default display capabilities.
     * @since 2021/11/30
     */
    static int __defaultCapabilities()
    {
        return Display.SUPPORTS_COMMANDS | Display.SUPPORTS_FORMS |
            Display.SUPPORTS_TICKER | Display.SUPPORTS_ALERTS |
            Display.SUPPORTS_LISTS | Display.SUPPORTS_TEXTBOXES |
            Display.SUPPORTS_FILESELECTORS | Display.SUPPORTS_TABBEDPANES |
            Display.SUPPORTS_MENUS;
    }
    
    /**
     * Converts a {@link UIItemPosition} to a softkey.
     * 
     * @param __pos The {@link UIItemPosition} to get the soft key of.
     * @return The softkey position or a negative value if not valid.
     * @since 2020/10/03
     */
    static int __layoutPosToSoftKey(int __pos)
    {
        switch (__pos)
        {
            case UIItemPosition.LEFT_COMMAND:
                return Display._SOFTKEY_LEFT_COMMAND;
                
            case UIItemPosition.RIGHT_COMMAND:
                return Display._SOFTKEY_RIGHT_COMMAND;
            
            default:
                return -1;
        }
    }
    
    /**
     * Returns the position where the soft key belongs.
     * 
     * @param __softKey The soft key to get the UI position from.
     * @return The {@link UIItemPosition} of the item, or
     * {@link UIItemPosition#NOT_ON_FORM} if not valid.
     * @since 2020/10/03
     */
    static int __layoutSoftKeyToPos(int __softKey)
    {
        switch (__softKey)
        {
            case Display._SOFTKEY_LEFT_COMMAND:
                return UIItemPosition.LEFT_COMMAND;
                
            case Display._SOFTKEY_RIGHT_COMMAND:
                return UIItemPosition.RIGHT_COMMAND;
            
            default:
                return UIItemPosition.NOT_ON_FORM;
        }
    }
}