javauis/lcdui_akn/javalcdui/javasrc/javax/microedition/lcdui/Toolkit.java
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 15 Jul 2010 18:31:06 +0300
branchRCL_3
changeset 23 e5618cc85d74
parent 19 71c436fe3ce0
child 24 6c158198356e
permissions -rw-r--r--
Revision: v2.1.32 Kit: 2010127

/*
* Copyright (c) 1999 - 2004 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/


package javax.microedition.lcdui;

import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import java.io.IOException;
import java.io.InputStream;

import com.nokia.mj.impl.rt.legacy.LegacyRtPort;
import com.nokia.mj.impl.rt.legacy.LegacySupport;
import com.nokia.mj.impl.rt.legacy.MIDEventServer;
import com.nokia.mj.impl.rt.legacy.NativeError;
import com.nokia.mj.impl.rt.legacy.ToolkitObserver;
import com.nokia.mj.impl.rt.legacy.ToolkitObserverNGAExtension;

import com.nokia.mj.impl.rt.support.ApplicationUtils;
import com.nokia.mj.impl.rt.support.ApplicationInfo;
import com.nokia.mj.impl.rt.support.Finalizer;
import com.nokia.mj.impl.rt.support.ShutdownListener;
import com.nokia.mj.impl.utils.Logger;

final class Toolkit
{
    static
    {
        com.nokia.mj.impl.rt.support.Jvm.loadSystemLibrary("javalcdui");
        DirectGraphicsInvoker.createInvoker();
        FreeSizeFontInvoker.createInvoker();
        LcduiPropertyInvoker.createInvoker();
        CanvasGraphicsItemPainterInvoker.createInvoker();
    }

    private Finalizer mFinalizer = new Finalizer()
    {
        public void finalizeImpl()
        {
            doFinalize();
        }
    };
    //
    private static final String SERVER_NAME = "javax.microedition.lcdui";
    private static final String DEFAULT_MIDLET_NAME = "MIDlet ";
    private static final int    DEFAULT_MIDLET_UID  = 0x101F9515;

    // NGA specific change.
    // Java side doesn't support native (c++) style macro flags and
    // that is why Toolkit & ToolkitInvoker have been harnessed to substitutes
    // NGA flag & its index
    static final int FLAG_NONE = 0;
    static final int FLAG_NGA_INDEX = 0;
    static final int FLAG_NGA = 1 << FLAG_NGA_INDEX;
    // Defines total flag count. Must be keep up to date.
    private static final int FLAG_COUNT = 1;

    private static final String RESOURCE_OR_PLUGIN_NOT_FOUND =
        "Cannot find lcdui dll, or lcdgr dll or lcdui resource file";

    //
    private static int iServerCount;

    private ToolkitInvoker  iInvoker;
    private Object iDestroySync = new Object();
    private static final int HANDLE_UNINITIALIZED = -0xfffffe;
    private int     iHandle = HANDLE_UNINITIALIZED;
    private Device  iDevice;
    private Display iDisplay;
    private Image   iIcon;
    private MIDEventServer iEventServer;
    private Hashtable iFontCache;
    Buffer  iBuffer;
    private Object iCallbackLock;

    private Vector iObservers;
    private boolean iStartingInBackground = false;
    protected Vector tmpDisplayables;     //temporary Vector of created Displayables
    protected boolean activated = false;  //events are dispatched only if Toolkit is active, see Toolkit.start()

    // NGA specific change
    private int iFlags = FLAG_NONE;

    //
    // Event source : must keep in sync with TSourceType in lcdui.h
    //
    private static final int ITEM                           = 3;
    private static final int DISPLAYABLE                    = 4;
    private static final int MIDLET                         = 5;
    private static final int SERIAL                         = 6; // deprecated
    private static final int CANVAS_GRAPHICS_ITEM_PAINTER   = 7;

    //
    // Event type : must keep in sync with TEventType in lcdui.h
    //
    static final int EVENT_PAINT = 10;
    static final int EVENT_KEY_PRESSED      = 11;
    static final int EVENT_KEY_RELEASED     = 12;
    static final int EVENT_KEY_REPEATED     = 13;
    static final int EVENT_POINTER_PRESSED  = 14;
    static final int EVENT_POINTER_RELEASED = 15;
    static final int EVENT_POINTER_DRAGGED  = 16;
    static final int EVENT_SIZE_CHANGED     = 17;

    static final int EVENT_EXIT_REQUESTED = 18;
    static final int EVENT_FOREGROUND = 19;
    static final int EVENT_BACKGROUND = 20;
    static final int EVENT_PAUSE_REQUESTED   = 21;
    static final int EVENT_START_REQUESTED   = 22;
    static final int EVENT_DESTROY_REQUESTED = 23;
    static final int EVENT_COMMAND     = 24;
    static final int EVENT_VISIBLE     = 25;
    static final int EVENT_TRAVERSE    = 26;
    static final int EVENT_SET_CURRENT = 29;
    static final int EVENT_SERIAL      = 30;

    static final int EVET_CANVAS_GRAPHICS_ITEM_REPAINT = 31;
    static final int EVENT_M3G_DRAW = 32 ;
    // IMPLICIT EVENT TYPES
    static final int EVENT_DISMISS = 0;         // ALERT
    static final int EVENT_ITEM_CHANGED = 0;    // ITEM(S)
    static final int EVENT_SELECT  = 0;         // LIST


    //
    // Op codes for syncing to screen
    // These must be kept in sync with MMIDCanvas::TDrawOp
    //
    private static final int SYNC      = 0;
    private static final int SYNC_RECT = 1;

    // NGA specific change.
    // Op code indicating M3G content start
    private static final int M3G_CONTENT_START = 2;

    Toolkit(ToolkitInvoker aInvoker)
    {
        iInvoker     = aInvoker;
        tmpDisplayables = new Vector();

        iStartingInBackground = iInvoker.iStartInBackGround;

        ApplicationUtils.getInstance().addShutdownListener(new ShutdownListener()
        {
            public final void shuttingDown()
            {
                dispose();
            }
        });

    }

    /**
     * Initializes the Toolkit, if it is uninitialized.
     */
    private void ensureInitialized()
    {
        if (iEventServer == null)
        {
            initialize();
        }
    }

    /**
     * Initializes the Toolkit.
     */
    synchronized private void initialize()
    {
        if (iEventServer != null)
        {
            return;
        }
        /**
         * get UI server instance number
         */
        final String serverNumber = getServerNumber();

        /**
         * create mangled server name
         */
        final String serverName = SERVER_NAME.concat(serverNumber);

        ApplicationInfo appInfo = ApplicationInfo.getInstance();
        String appName = appInfo.getLocalizedName();
        if (null == appName)
        {
            appName = DEFAULT_MIDLET_NAME.concat(serverNumber);
        }

        int appUid = LegacyRtPort.getMidletUid();
        if (appUid == 0)
        {
            appUid = DEFAULT_MIDLET_UID;
        }

        String appHome = appInfo.getRootPath();
        if (appHome.endsWith("\\"))
        {
            appHome = appHome.substring(0, appHome.length() - 1);
        }

        /**
         * Create native event server + thread. This will run until shutdown
         * in Toolkit.dispose().
         */
        iEventServer  = new UiEventServer(serverName, appUid);
        iCallbackLock = new Object();

        final Hashtable properties = iInvoker.iMidletArgs;
        final String[]  attributes = new String [properties.size() * 2];
        int count = -1;
        for (final Enumeration keys = properties.keys(); keys.hasMoreElements();)
        {
            final String key = (String)keys.nextElement();
            attributes[++count] = key;
            attributes[++count] = (String)properties.get(key);
        }

        final int server = iEventServer.getHandle();

        // NGA specific change.
        // Array that will be filled with native flag macros
        int[] flags = new int[Toolkit.FLAG_COUNT];
        iHandle = _create(server, appName, appUid, appHome, attributes, flags);
        switch (iHandle)
        {
        case NativeError.KErrNotFound:
        case NativeError.KErrPathNotFound:
            throw new RuntimeException(RESOURCE_OR_PLUGIN_NOT_FOUND);
        default:
            checkHandle(iHandle); // leaves if error
            break;
        }
        // NGA specific change.
        // Update flags
        if (flags[Toolkit.FLAG_NGA_INDEX] > 0)
        {
            this.iFlags |= Toolkit.FLAG_NGA;
        }

        iDevice    = new Device(this);
        iBuffer    = new Buffer(this);
        iFontCache = new Hashtable();

    }

    InputStream getResourceAsStream(String aResource)
    throws
                IOException
    {
        return getClass().getResourceAsStream(aResource);
    }

    synchronized void start(Display aDisplay)
    {
        ensureInitialized();
        iDisplay = aDisplay;
        NativeError.check(_activate(iHandle));
        activated = true;
    }

    private static synchronized String getServerNumber()
    {
        return Integer.toString(++iServerCount);
    }

    /**
     * NGA specific change.
     * Check if flags are set.
     * @param flags Flags indexes
     * @return True if flags are on.
     * @since S60 9.2
     */
    boolean checkFlags(int flags)
    {
        return ((iFlags & flags) > 0);
    }

    public boolean isClosed()
    {
        return ((iHandle != HANDLE_UNINITIALIZED) &&
                (iHandle <= 0));
    }

    public int getHandle()
    {
        ensureInitialized();
        if (isClosed())
        {
            throw new RuntimeException("Toolkit closed");
        }
        return iHandle;
    }

    static Toolkit getToolkit()
    {
        ToolkitInvoker invoker = (ToolkitInvoker)ToolkitInvoker.getToolkitInvoker();
        return invoker.getCurrentToolkit();
    }

    final Device getDevice()
    {
        ensureInitialized();
        return iDevice;
    }

    final Display getDisplay()
    {
        ensureInitialized();
        return iDisplay;
    }

    final Object getCallbackLock()
    {
        ensureInitialized();
        return iCallbackLock;
    }

    final Hashtable getFontCache()
    {
        ensureInitialized();
        return iFontCache;
    }

    final synchronized void addObserver(ToolkitObserver aObserver)
    {
        if (aObserver == null)
        {
            return;
        }

        if (iObservers == null)
        {
            iObservers = new Vector(2);
        }

        if (!iObservers.contains(aObserver))
        {
            iObservers.addElement(aObserver);
        }
    }

    final synchronized void removeObserver(ToolkitObserver aObserver)
    {
        if ((iObservers != null) && (aObserver != null))
        {
            iObservers.removeElement(aObserver);
        }
    }

    /**
     * If toolkit is already disposed, then native object has already been deleted
     * aHandle can be null if exception thrown in the object's constructor (where the handle
     * is typically assigned
     */
    final synchronized void disposeObject(int aHandle)
    {
        ensureInitialized();

        if (aHandle == 0)
        {
            Logger.LOG(Logger.EJavaUI, Logger.EInfo,
                       "disposeObject(" + aHandle + ") < ignoring NULL handle");
            return;
        }

        if (iHandle <= 0)
        {
            Logger.LOG(Logger.EJavaUI, Logger.EInfo,
                       "disposeObject(" + aHandle + ") < : Toolkit already closed!");
            return;
        }

        //
        // Lock buffer for duration of dispose.
        //
        synchronized (iBuffer)
        {
            iBuffer.sync();
            _disposeObject(iHandle, aHandle);
        }

    }

    private void doFinalize()
    {
        if (mFinalizer != null)
        {
            registeredFinalize();
            mFinalizer = null;
        }
    }

    void registeredFinalize()
    {
        dispose();
    }

    final synchronized void dispose()
    {
        if ((iHandle == HANDLE_UNINITIALIZED) || (iHandle > 0))
        {
            //
            // Notify observers that toolkit is being destroyed.
            //
            if (null != iObservers)
            {
                final int lastIndex = iObservers.size() -1;
                for (int ii=lastIndex; ii>=0; ii--)
                {
                    final ToolkitObserver observer = (ToolkitObserver)iObservers.elementAt(ii);
                    try
                    {
                        observer.destroyNotify();
                    }
                    catch (Exception ex)
                    {
                        Logger.ELOG(Logger.EJavaUI,
                                    "exception thrown in destroyNotify: ", ex);
                        // close must complete otherwise could get panic, so ignore
                        // any exception thrown by observers.
                    }
                }
            }

            iInvoker.removeToolkit();

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

            //
            // Close the graphics buffer if present.
            //
            if (null != iBuffer)
            {
                synchronized (iBuffer)
                {
                    iBuffer.close();
                }
                iBuffer = null;
            }

            if (iHandle > 0)
            {
                _dispose(iHandle);
                iHandle = 0;
            }

            //
            // Shutdown the ui event thread.
            //
            if (iEventServer != null)
            {
                iEventServer.shutdown();
            }
        }
    }

    /* deprecated */
    private void handleAsyncEvent(Object aSource, int aResult)
    {
        ((AsyncCall)aSource).complete(aResult);
    }

    private boolean resetThread()
    {
        if (!isClosed())
        {
            return true;
        }
        return false;
    }

    private void handleItemEvent(Item aSource, int aEvent, int aParam0, int aParam1, int aParam2)
    {
        if (resetThread())
        {
            aSource.handleEvent(aEvent, aParam0, aParam1, aParam2);
        }
    }

    private void handleDisplayableEvent(Displayable aSource, int aEvent, int aParam0, int aParam1, int aParam2)
    {
        synchronized (iDestroySync)
        {
            //S60 Modification ==>
            try
            {
                if (resetThread())
                {
                    aSource.handleEvent(aEvent, aParam0, aParam1);
                }
            }
            catch (Throwable t)
            {
                String className = t.getClass().getName();
                int index = className.lastIndexOf('.');
                if (index != -1)
                {
                    className = className.substring(index + 1, className.length());
                }
                String errStr= "Exception in handleDisplayableEvent. Reason: "+className+
                               "\n  event is : "+aEvent+
                               "\n  source is : "+aSource+"\n  p0 is     : "+aParam0+
                               "\n  p1 is     : "+aParam1+"\n  p2 is     : "+aParam2;
                Logger.ELOG(Logger.EJavaUI,errStr, t);

                //Removing Displayable if is exception thrown
                if (tmpDisplayables.contains(this))
                {
                    tmpDisplayables.removeElement(this);
                }
            }
        }
    }

    private void handleDisplayEvent(Toolkit aToolkit, int aEvent, int aParam0, int aParam1, int aParam2)
    {
        synchronized (iDestroySync)
        {
            if (resetThread())
            {
                switch (aEvent)
                {
                case EVENT_EXIT_REQUESTED:
                    ApplicationUtils.getInstance().notifyExitCmd();
                    break;
                case EVENT_DESTROY_REQUESTED:
                    ApplicationUtils.getInstance().notifyExitCmd();
                    break;
                case EVENT_PAUSE_REQUESTED:
                    ApplicationUtils.getInstance().pauseApplication();
                    break;
                case EVENT_START_REQUESTED:
                    ApplicationUtils.getInstance().resumeApplication();
                    break;
                case EVENT_FOREGROUND:
                    iDisplay.handleForeground(true);
                    notifyForeground(true);
                    break;
                case EVENT_BACKGROUND:
                    iDisplay.handleForeground(false);
                    notifyForeground(false);
                    break;
                case EVENT_SET_CURRENT:
                    iDisplay.switchCurrent();
                    break;
                case EVENT_SERIAL:
                    iDisplay.processSerialEvents();
                    break;
                default:
                    throw new IllegalArgumentException();
                }
            }
        }
    }

    private synchronized void notifyForeground(boolean foreground)
    {
        if (null != iObservers)
        {
            final int lastIndex = iObservers.size() - 1;
            for (int ii=lastIndex; ii>=0; ii--)
            {
                if (iObservers.elementAt(ii) instanceof ToolkitObserverNGAExtension)
                {
                    final ToolkitObserverNGAExtension observer =
                        (ToolkitObserverNGAExtension)iObservers.elementAt(ii);
                    observer.foregroundEvent(foreground);
                }
            }
        }
    }

    private void handleCanvasGraphicsItemPainterEvent(CanvasGraphicsItemPainter aSource, int aEvent, int aParam0, int aParam1, int aParam2)
    {
        if (resetThread())
        {
            aSource.handleEvent(aEvent, aParam0, aParam1);
        }
    }

    synchronized void setForeground(boolean aForeground)
    {
        if (iStartingInBackground)
        {
            NativeError.check(_setForeground(getHandle(), false));
            iStartingInBackground = false;
        }
        else
        {
            NativeError.check(_setForeground(getHandle(), aForeground));
        }
    }

    /**
     * blocks until all drawing complete
     */
    void sync()
    {
        ensureInitialized();
        synchronized (iBuffer)
        {
            iBuffer.sync();
        }
    }

    /*
     * NGA specific change.
     * Adds m3g content start op code to buffer,
     * if Graphics's target is Canvas.
     * The buffer is synchronized in any case.
     * @param aGraphics Graphics instance
     * @see ToolkitInvoker#toolkitSync(Object, Object)
     * @since S60 9.2
     */
    void sync(Object aObject)
    {
        ensureInitialized();
        Graphics graphics = (Graphics)aObject;
        if (checkFlags(FLAG_NGA) &&
                graphics != null &&
                graphics.iTarget instanceof Canvas)
        {
            Canvas canvas = (Canvas)graphics.iTarget;
            canvas.setM3GContent(true);
            synchronized (iBuffer)
            {
                iBuffer.write(canvas.getContentHandle(), M3G_CONTENT_START);
                iBuffer.sync();
            }
        }
        else
        {
            sync();
        }
    }

    /**
     * Write buffer command which when processed will draw to screen.
     * Used by Canvas and CustomItem.
     */
    void sync(int aDrawable)
    {
        ensureInitialized();
        synchronized (iBuffer)
        {
            iBuffer.write(aDrawable, SYNC);
            iBuffer.sync();
        }
    }

    /**
     * Write buffer command which when processed will draw to screen.
     * Used by Canvas and CustomItem.
     */
    void sync(int aDrawable, int aX, int aY, int aW, int aH)
    {
        ensureInitialized();
        synchronized (iBuffer)
        {
            final int x2=aX+aW;
            final int y2=aY+aH;
            iBuffer.write(aDrawable, SYNC_RECT, aX, aY, x2, y2);
            iBuffer.sync();
        }
    }

    /**
     JSR 135 Support
     */
    final int getEventServerHandle()
    {
        ensureInitialized();
        return iEventServer.getHandle();
    }

    void postEvent(int aEvent)
    {
        NativeError.check(_postEvent(getHandle(), 0, MIDLET, aEvent));
    }

    void postEvent(Item aItem, int aEvent)
    {
        NativeError.check(_postEvent(getHandle(), aItem.getHandle(), ITEM, aEvent));
    }

    void postEvent(Displayable aDisplayable, int aEvent)
    {
        NativeError.check(_postEvent(getHandle(), aDisplayable.getContainerHandle(), DISPLAYABLE, aEvent));
    }

    void postEvent(CanvasGraphicsItemPainter aCanvasGraphicsItem, int aEvent)
    {
        NativeError.check(_postEvent(getHandle(), aCanvasGraphicsItem.getHandle(), CANVAS_GRAPHICS_ITEM_PAINTER, aEvent));
    }

    static int checkHandle(int aHandle)
    {
        if (NativeError.check(aHandle) == 0)
        {
            throw new OutOfMemoryError();
        }
        return aHandle;
    }

    /**
     * NGA specific change.
     * Create native peer on server thread reference by aContext.
     * @param aFlags The array that is filled according to native
     *        side's macro definitions
     */
    native int _create(
        int aContext,String aName, int aUid, String aHomeDir, String[] aAttributes, int[] aFlags);

    /**
     * Open toolkit - enable events.
     */
    native int  _activate(int aToolkit);

    /**
     * Delete toolkit - invalidates handle so no remaining threads must be
     * holding the toolkit handle.
     */
    native void _dispose(int aToolkit);

    /**
     * Deletion native UI object with handle aObject
     */
    native void _disposeObject(int aToolkit,int aObject);

    native int  _postEvent(int aToolkit, int aSource, int aType, int aEvent);
    native int  _setCurrent(int aToolkit,int aDisplayable);
    native int  _setForeground(int aToolkit, boolean aForeground);
}