Package org.jnode.awt

Source Code of org.jnode.awt.JNodeToolkit

/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.awt;

import gnu.java.awt.ClasspathToolkit;
import gnu.java.awt.EmbeddedWindow;
import gnu.java.awt.peer.ClasspathFontPeer;
import gnu.java.awt.peer.EmbeddedWindowPeer;
import gnu.java.security.action.GetPropertyAction;
import java.awt.AWTError;
import java.awt.AWTEvent;
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.PrintJob;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Insets;
import java.awt.Point;
import java.awt.datatransfer.Clipboard;
import java.awt.event.ComponentEvent;
import java.awt.geom.AffineTransform;
import java.awt.im.InputMethodHighlight;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ImageConsumer;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import java.awt.image.VolatileImage;
import java.awt.peer.DesktopPeer;
import java.awt.peer.FontPeer;
import java.awt.peer.RobotPeer;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.imageio.ImageIO;
import javax.naming.NamingException;
import javax.swing.JDesktopPane;
import org.apache.log4j.Logger;
import org.jnode.awt.font.FontManager;
import org.jnode.awt.image.BufferedImageSurface;
import org.jnode.awt.image.JNodeImage;
import org.jnode.driver.DeviceException;
import org.jnode.driver.sound.speaker.SpeakerUtils;
import org.jnode.driver.video.AlreadyOpenException;
import org.jnode.driver.video.FrameBufferAPI;
import org.jnode.driver.video.FrameBufferAPIOwner;
import org.jnode.driver.video.Surface;
import org.jnode.driver.video.UnknownConfigurationException;
import org.jnode.naming.InitialNaming;
import org.jnode.annotation.SharedStatics;
import sun.awt.AppContext;
import sun.awt.SunToolkit;
import sun.awt.image.ToolkitImage;

/**
* @author epr
* @author Levente S\u00e1ntha
*/
@SharedStatics
public abstract class JNodeToolkit extends ClasspathToolkit implements FrameBufferAPIOwner {
    protected static final Logger log = Logger.getLogger(JNodeToolkit.class);

    private final Object initCloseLock = new Object();
    private EventQueue waitingNativeQueue;
    private Clipboard systemClipboard;
    private FrameBufferAPI api;
    private JNodeFrameBufferDevice fbDevice;
    private JNodeGraphicsConfiguration config;
    private JNodeEventQueue _eventQueue;
    private LRUCache<Map<?, ?>, ClasspathFontPeer> fontCache = new LRUCache<Map<?, ?>, ClasspathFontPeer>(50);
    private Surface graphics;
    private boolean graphicsMode;
    private KeyboardHandler keyboardHandler;
    private MouseHandler mouseHandler;
    private int refCount = 0;
    private final Dimension screenSize = new Dimension(640, 480);
    private Frame top;
    private Runnable exitAction;
    @SuppressWarnings("unused")
    private Insets screenInsets;

    public JNodeToolkit() {
        refCount = 0;
        systemClipboard = new Clipboard("JNodeSystemClipboard");

        //initialize the main AppContext
        AppContext appContext = AppContext.getAppContext();

        synchronized (this) {
            if (appContext.get(AppContext.EVENT_QUEUE_KEY) == null || _eventQueue == null) {
                String eqName = System.getProperty("AWT.EventQueueClass", "org.jnode.awt.JNodeEventQueue");
                try {
                    _eventQueue = (JNodeEventQueue) Class.forName(eqName).newInstance();
                } catch (Exception e) {
                    e.printStackTrace();
                    System.err.println("Failed loading " + eqName + ": " + e);
                    _eventQueue = new JNodeEventQueue();
                }
                appContext.put(AppContext.EVENT_QUEUE_KEY, _eventQueue);
            }
        }
    }

    /**
     * @see gnu.java.awt.ClasspathToolkit#createEmbeddedWindow(gnu.java.awt.EmbeddedWindow)
     */
    @Override
    public EmbeddedWindowPeer createEmbeddedWindow(EmbeddedWindow w) {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * Gets the default toolkit casted to JNodeToolkit.
     *
     * @return The current toolkit casted to JNodeToolkit.
     * @throws AWTError If the default toolkit is not instanceof JNodeToolkit.
     */
    public static JNodeToolkit getJNodeToolkit() {
        final Toolkit tk = Toolkit.getDefaultToolkit();
        if (tk instanceof JNodeToolkit) {
            return (JNodeToolkit) tk;
        } else {
            throw new AWTError("Toolkit is not a JNodeToolkit");
        }
    }

    public static boolean isGuiActive() {
        final Toolkit tk = Toolkit.getDefaultToolkit();
        if (!(tk instanceof JNodeToolkit)) {
            throw new AWTError("Toolkit is not a JNodeToolkit");
        }
        return (((JNodeToolkit) tk).graphics != null);
    }

    public static void startGui() {
        final Toolkit tk = Toolkit.getDefaultToolkit();
        if (!(tk instanceof JNodeToolkit)) {
            throw new AWTError("Toolkit is not a JNodeToolkit");
        }
        ((JNodeToolkit) tk).incRefCount();
    }

    public static void initGui() {
        final Toolkit tk = Toolkit.getDefaultToolkit();
        if (!(tk instanceof JNodeToolkit)) {
            throw new AWTError("Toolkit is not a JNodeToolkit");
        }
        ((JNodeToolkit) tk).incRefCount();
    }

    public static void stopGui() {
        final Toolkit tk = Toolkit.getDefaultToolkit();
        if (!(tk instanceof JNodeToolkit)) {
            throw new AWTError("Toolkit is not a JNodeToolkit");
        }
        ((JNodeToolkit) tk).decRefCount(true);
        Toolkit.clearDefaultToolkit();
    }

    public static void refreshGui() {
        final Toolkit tk = Toolkit.getDefaultToolkit();
        if (!(tk instanceof JNodeToolkit)) {
            throw new AWTError("Toolkit is not a JNodeToolkit");
        }
        ((JNodeToolkit) tk).refresh();
    }

    protected void refresh() {
        // Override me
    }

    public static void waitUntilStopped() {
        final Toolkit tk = Toolkit.getDefaultToolkit();
        if (!(tk instanceof JNodeToolkit)) {
            throw new AWTError("Toolkit is not a JNodeToolkit");
        }
        ((JNodeToolkit) tk).doWaitUntilStopped();
    }

    /**
     * This method need only accessed from JNodeRobotPeer in the same package
     *
     * @return Returns the keyboardHandler.
     */
    final KeyboardHandler getKeyboardHandler() {
        return keyboardHandler;
    }

    /**
     * This method need only accessed from JNodeRobotPeer in the same package
     *
     * @return Returns the mouseHandler.
     */
    final MouseHandler getMouseHandler() {
        return mouseHandler;
    }

    /**
     * @see java.awt.Toolkit#beep()
     */
    public void beep() {
        SpeakerUtils.beep();
    }

    /**
     * @see java.awt.Toolkit#checkImage(java.awt.Image, int, int, java.awt.image.ImageObserver)
     */
    public int checkImage(Image image, int width, int height,
                          ImageObserver observer) {
        int status = ImageObserver.ALLBITS | ImageObserver.WIDTH | ImageObserver.HEIGHT;

        if (image instanceof JNodeImage) {
            status = ((JNodeImage) image).checkImage();
        }

        if (observer != null)
            observer.imageUpdate(image, status, -1, -1, image
                .getWidth(observer), image.getHeight(observer));

        return status;
    }

    /**
     * JNode specific method. Create a buffered image compatible with the
     * graphics configuration.
     *
     * @param width  image width
     * @param height image height
     * @return The compatible image
     */
    public BufferedImage createCompatibleImage(int width, int height) {
        return config.createCompatibleImage(width, height);
    }

    /**
     * @see gnu.java.awt.ClasspathToolkit#createRobot(java.awt.GraphicsDevice)
     */
    public RobotPeer createRobot(GraphicsDevice screen) throws AWTException {
        return new JNodeRobotPeer<JNodeToolkit>(this, screen);
    }

    /**
     * @see gnu.java.awt.ClasspathToolkit#createFont(int, java.io.InputStream)
     */
    public Font createFont(int format, InputStream stream) throws FontFormatException, IOException {
        return getFontManager().createFont(format, stream);
    }

    /**
     * @see java.awt.Toolkit#createImage(byte[], int, int)
     */
    public Image createImage(byte[] data, int offset, int len) {
        try {
            return ImageIO.read(new ByteArrayInputStream(data, offset, len));
        } catch (IOException e) {
            log.error("Image read error", e);
            // let it fall through to default code
        }
        return new ErrorImage();
    }

    /**
     * @see java.awt.Toolkit#createImage(java.awt.image.ImageProducer)
     */
    public Image createImage(ImageProducer producer) {
        ToolkitImage image = new ToolkitImage(producer);
        //force preloading
        image.getWidth();
        return image;
    }

    /**
     * @see java.awt.Toolkit#createImage(java.lang.String)
     */
    public Image createImage(String filename) {
        return getImage(filename);
    }

    /**
     * @see java.awt.Toolkit#createImage(java.net.URL)
     */
    public Image createImage(URL url) {
        return getImage(url);
    }

    public VolatileImage createVolatileImage(int width, int height) {
        //TODO implement volatile image support
        return null;
        //throw new RuntimeException("Not implemented");
    }

    /**
     * Decrement the peer reference count.
     *
     * @param forceClose if true the gui is always closed
     * @return the reference count
     */
    private int decRefCount(boolean forceClose) {
        final int rc;
        synchronized (initCloseLock) {
            refCount--;
            rc = refCount;
        }
        log.debug("refCount.dec=" + rc);
        if ((rc == 0) || forceClose) {
            onClose();
            final KeyboardHandler keyboardHandler = this.keyboardHandler;
            final MouseHandler mouseHandler = this.mouseHandler;
            final Surface graphics = this.graphics;

            if (keyboardHandler != null) {
                keyboardHandler.close();
            }
            if (mouseHandler != null) {
                mouseHandler.close();
            }
            if (graphics != null) {
                graphics.close();
            }

            final FrameBufferAPI savedApi = this.api;
            this.api = null;
            this.graphics = null;
            this.keyboardHandler = null;
            this.mouseHandler = null;

            // Shutdown the eventqueue as the last event
            final JNodeEventQueue eventQueue = this._eventQueue;
            if (eventQueue != null) {
                EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        eventQueue.shutdown();
                    }
                });
            }

            synchronized (initCloseLock) {
                this.refCount = 0;
                graphicsMode = false;
                initCloseLock.notifyAll();
            }
            savedApi.releaseOwnership(this);
            return 0;
        } else {
            return rc;
        }
    }

    private void doWaitUntilStopped() {
        synchronized (initCloseLock) {
            while (graphicsMode) {
                try {
                    initCloseLock.wait();
                } catch (InterruptedException ex) {
                    // Ignore
                }
            }
        }
    }

    /**
     * Gets the AWT context.
     *
     * @return the AWT context
     */
    public abstract JNodeAwtContext getAwtContext();

    /**
     * Newer method to produce a peer for a Font object, even though Sun's
     * design claims Font should now be peerless, we do not agree with this
     * model, hence "ClasspathFontPeer".
     */
    @SuppressWarnings("unchecked")
    public ClasspathFontPeer getClasspathFontPeer(String name, Map attrs) {
        final Map<String, String> keyMap = new HashMap<String, String>(attrs);
        // We don't know what kind of "name" the user requested (logical, face,
        // family), and we don't actually *need* to know here. The worst case
        // involves failure to consolidate fonts with the same backend in our
        // cache. This is harmless.
        keyMap.put("JNodeToolkit.RequestedFontName", name);
        if (fontCache.containsKey(keyMap))
            return fontCache.get(keyMap);
        else {
            ClasspathFontPeer newPeer = getFontManager().createFontPeer(name, attrs);
            fontCache.put(keyMap, newPeer);
            return newPeer;
        }
    }

    /**
     * @return The model
     * @see java.awt.Toolkit#getColorModel()
     */
    public ColorModel getColorModel() {
        return ColorModel.getRGBdefault();
    }

    /**
     * @return The fonts
     * @see java.awt.Toolkit#getFontList()
     */
    public String[] getFontList() {
        Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment()
            .getAllFonts();
        String[] names = new String[fonts.length];
        for (int i = 0; i < fonts.length; i++) {
            names[i] = fonts[i].getName();
        }
        return names;
    }

    /**
     * Gets the font manager, or null if not found.
     *
     * @return The font mananger
     */
    public FontManager getFontManager() {
        try {
            return InitialNaming.lookup(FontManager.NAME);
        } catch (NamingException ex) {
            return null;
        }
    }

    /**
     * @see java.awt.Toolkit#getFontMetrics(java.awt.Font)
     */
    public FontMetrics getFontMetrics(Font font) {
        final FontManager fm = getFontManager();
        if (fm != null) {
            return fm.getFontMetrics(font);
        } else {
            return null;
        }
    }

    /**
     * @see java.awt.Toolkit#getFontPeer(String, int)
     */
    protected final FontPeer getFontPeer(String name, int style) {
        // All fonts get a default size of 12 if size is not specified.
        return getFontPeer(name, style, 12);
    }

    /**
     * Private method that allows size to be set at initialization time.
     *
     * @param name  the font name
     * @param style the font style
     * @param size  the font size
     * @return the font peer
     */
    @SuppressWarnings("unchecked")
    private FontPeer getFontPeer(String name, int style, int size) {
        Map attrs = new HashMap();
        ClasspathFontPeer.copyStyleToAttrs(style, attrs);
        ClasspathFontPeer.copySizeToAttrs(size, attrs);
        return getClasspathFontPeer(name, attrs);
    }

    /**
     * @return The surface
     */
    public final Surface getGraphics() {
        return this.graphics;
    }

    /**
     * @return The configuration
     * @see java.awt.peer.ComponentPeer#getGraphicsConfiguration()
     */
    public final GraphicsConfiguration getGraphicsConfiguration() {
        return config;
    }

    /**
     * Test if the image is valid (!= null), otherwise return an error image.
     *
     * @param img the image to test
     * @return the image if img is not null, an error image otherwise
     */
    private Image testErrorImage(Image img) {
        if (img == null) {
            return new ErrorImage();
        } else {
            return img;
        }
    }

    /**
     * @see java.awt.Toolkit#getImage(java.lang.String)
     */
    public Image getImage(final String filename) {
        log.debug("getImage(" + filename + ")");
        return testErrorImage(AccessController.doPrivileged(new PrivilegedAction<Image>() {

            public Image run() {
                try {
                    final String userDir = AccessController.doPrivileged(new GetPropertyAction("user.dir"));
                    Image image = getImage(new URL("file:" + new File(userDir, filename)));
                    return image != null ? image : getImage(new URL("file:" + new File(filename).getAbsolutePath()));
                } catch (Exception ex) {
                    log.debug("Error loading image", ex);
                }
                return null;
            }
        }));
    }

    /**
     * @see java.awt.Toolkit#getImage(java.net.URL)
     */
    public Image getImage(final URL url) {
        return testErrorImage(AccessController.doPrivileged(new PrivilegedAction<Image>() {
            public Image run() {
                try {
                    return ImageIO.read(url);
                } catch (Exception ex) {
                    log.debug("Exception during getImage", ex);
                }
                return null;
            }
        }));
    }

    /**
     * @see gnu.java.awt.ClasspathToolkit#getLocalGraphicsEnvironment()
     */
    public GraphicsEnvironment getLocalGraphicsEnvironment() {
        return new JNodeGraphicsEnvironment();
    }

    /**
     * @see java.awt.Toolkit#getPrintJob(java.awt.Frame, java.lang.String,
     *      java.util.Properties)
     */
    public PrintJob getPrintJob(Frame frame, String title, Properties props) {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * @return int
     * @see java.awt.Toolkit#getScreenResolution()
     */
    public int getScreenResolution() {
        // TODO Auto-generated method stub
        return 0;
    }

    /**
     * @return The screen size
     * @see java.awt.Toolkit#getScreenSize()
     */
    public Dimension getScreenSize() {
        return new Dimension(screenSize);
    }

    /**
     * @return The clipboard
     * @see java.awt.Toolkit#getSystemClipboard()
     */
    public Clipboard getSystemClipboard() {
        return systemClipboard;
    }

    /**
     * @return The event queue
     */
    protected final EventQueue getSystemEventQueueImpl() {
        AppContext ac = AppContext.getAppContext();
        if (ac != null) {
            EventQueue eq = (EventQueue) ac.get(AppContext.EVENT_QUEUE_KEY);
            if (eq != null) {
                return eq;
            }
        }

        if ((_eventQueue == null) || (!_eventQueue.isLive() && isGuiActive())) {
            synchronized (this) {
                if ((_eventQueue == null) || (!_eventQueue.isLive() && isGuiActive())) {
                    _eventQueue = new JNodeEventQueue();
                }

                if (ac != null && ac.get(AppContext.EVENT_QUEUE_KEY) == null) {
                    ac.put(AppContext.EVENT_QUEUE_KEY, _eventQueue);
                }
            }
        }

        return _eventQueue;
    }

    public final synchronized EventQueue getMainEventQueue() {
        return _eventQueue;
    }

    public Frame getTop() {
        return top;
    }

    /**
     * Gets the top most visible component at a given location.
     *
     * @param x the x coordiante
     * @param y the y coordinate
     * @return the component
     */
    public Component getTopComponentAt(int x, int y) {
        final Frame f = getTop();
        if (f == null) {
            return null;
        }
        Component c = f.findComponentAt(x, y);
        if (c == null) {
            c = f;
        }
        return c;
    }

    /**
     * Increment the peer reference count
     *
     * @return the reference count
     */
    private int incRefCount() {
        final boolean initialize;
        final int rc;
        synchronized (initCloseLock) {
            refCount++;
            rc = refCount;
            initialize = (refCount == 1);
        }
        log.debug("refCount.inc=" + rc);
        if (initialize) {
            fbDevice = (JNodeFrameBufferDevice) GraphicsEnvironment
                .getLocalGraphicsEnvironment().getDefaultScreenDevice();
            if (fbDevice == null) {
                throw new AWTError("No framebuffer fbDevice found");
            }
            log.info("Supported graphics configurations: ");
            GraphicsConfiguration[] configurations = fbDevice.getConfigurations();
            for (GraphicsConfiguration g_conf : configurations) {
                log.info(g_conf);
            }
            String screen_size = AccessController.doPrivileged(new GetPropertyAction("jnode.awt.screensize", "none"));
            if ("none".equals(screen_size)) {
                config = (JNodeGraphicsConfiguration) fbDevice.getDefaultConfiguration();
            } else {
                boolean found = false;
                for (GraphicsConfiguration g_conf : configurations) {
                    if (screen_size.equals(g_conf.toString())) {
                        config = (JNodeGraphicsConfiguration) g_conf;
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    config = (JNodeGraphicsConfiguration) fbDevice.getDefaultConfiguration();
                }
            }
            log.info("Using: " + config);
            this.api = fbDevice.getAPI();
            try {
                log.debug("Opening AWT: Using fbDevice " + fbDevice.getIDstring());
                api.requestOwnership(this);
                this.graphics = api.open(config.getConfig());
                if (graphics == null) {
                    log.debug("No Graphics for fbDevice: " + fbDevice.getIDstring());
                    return rc;
                }
                graphicsMode = true;
                screenSize.width = config.getConfig().getScreenWidth();
                screenSize.height = config.getConfig().getScreenHeight();

                //drawStartupScreen();

                final EventQueue eventQueue = getSystemEventQueueImpl();
                this.keyboardHandler = new KeyboardHandler(eventQueue);
                this.mouseHandler = new MouseHandler(fbDevice.getDevice(),
                    screenSize, eventQueue, keyboardHandler);
                keyboardHandler.install();

                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        onInitialize();
                        return null;
                    }
                });
                this.refCount = rc;
            } catch (DeviceException ex) {
                decRefCount(true);
                throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
            } catch (UnknownConfigurationException ex) {
                decRefCount(true);
                throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
            } catch (AlreadyOpenException ex) {
                decRefCount(true);
                throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
            } catch (Throwable ex) {
                decRefCount(true);
                log.error("Unknown exception", ex);
                throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
            }
        }
        return rc;
    }

    public void updateCursor(Cursor cur) {
        if (mouseHandler != null) {
            int type = cur.getType();
            switch (type) {
                case Cursor.DEFAULT_CURSOR:
                    mouseHandler.setCursorImage(JNodeCursors.ARROW);
                    break;

                case Cursor.TEXT_CURSOR:
                    mouseHandler.setCursorImage(JNodeCursors.TEXT);
                    break;

                case Cursor.E_RESIZE_CURSOR:
                case Cursor.W_RESIZE_CURSOR:
                    mouseHandler.setCursorImage(JNodeCursors.RESIZE_HORIZONTAL);
                    break;

                case Cursor.N_RESIZE_CURSOR:
                case Cursor.S_RESIZE_CURSOR:
                    mouseHandler.setCursorImage(JNodeCursors.RESIZE_VERTICAL);
                    break;

                case Cursor.NE_RESIZE_CURSOR:
                case Cursor.SW_RESIZE_CURSOR:
                    mouseHandler.setCursorImage(JNodeCursors.RESIZE_NORTHEAST);
                    break;

                case Cursor.NW_RESIZE_CURSOR:
                case Cursor.SE_RESIZE_CURSOR:
                    mouseHandler.setCursorImage(JNodeCursors.RESIZE_NORTHWEST);
                    break;

                case Cursor.HAND_CURSOR:
                    mouseHandler.setCursorImage(JNodeCursors.HAND);
                    break;

                case Cursor.WAIT_CURSOR:
                    mouseHandler.setCursorImage(JNodeCursors.WAIT);
                    break;

                case Cursor.MOVE_CURSOR:
                    mouseHandler.setCursorImage(JNodeCursors.MOVE);
                    break;

                case Cursor.CROSSHAIR_CURSOR:
                    mouseHandler.setCursorImage(JNodeCursors.CROSSHAIR);
                    break;

                default:
                    mouseHandler.setCursorImage(JNodeCursors.ARROW);
            }
        }
    }

    @SuppressWarnings("unused")
    private void drawStartupScreen() {
        AffineTransform tx = new AffineTransform();
        graphics.fill(new Rectangle(0, 0, config.getBounds().width, config.getBounds().height), null, tx, Color.BLACK,
            Surface.PAINT_MODE);
        for (int i = 0; i < 100; i++)
            graphics.draw(new Rectangle(100 + i, 100 + i, config.getBounds().width - 2 * (100 + i),
                config.getBounds().height - 2 * (100 + i)), null, tx, (i % 2 == 0) ? Color.RED : Color.BLUE,
                Surface.PAINT_MODE);
    }

    private JNodeFrameBufferDevice getDevice() {
        final JNodeFrameBufferDevice device =
            (JNodeFrameBufferDevice) GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
        if (device == null) {
            throw new AWTError("No framebuffer fbDevice found");
        }

        return device;
    }

    private GraphicsConfiguration[] configs;

    public GraphicsConfiguration[] getConfigurations() {
        if (configs == null) {
            final GraphicsConfiguration[] configurations = getDevice().getConfigurations();

            configs = new GraphicsConfiguration[configurations.length];
            System.arraycopy(configurations, 0, configs, 0, configurations.length);
            Arrays.sort(configs, new Comparator<GraphicsConfiguration>() {
                @Override
                public int compare(GraphicsConfiguration o1, GraphicsConfiguration o2) {
                    final Rectangle b1 = o1.getBounds();
                    final Rectangle b2 = o2.getBounds();

                    int comp;
                    if (b1.getWidth() > b2.getWidth()) {
                        comp = +1;
                    } else if (b1.getWidth() < b2.getWidth()) {
                        comp = -1;
                    } else {
                        if (b1.getHeight() > b2.getHeight()) {
                            comp = +1;
                        } else if (b1.getHeight() < b2.getHeight()) {
                            comp = -1;
                        } else {
                            comp = 0;
                        }
                    }

                    return comp;
                }

            });
        }
        return configs;
    }

    public Dimension changeScreenSize(JNodeGraphicsConfiguration config) {
        final JNodeFrameBufferDevice device = getDevice();

        this.config = config;
        log.info("Using: " + config);
        this.api = device.getAPI();
        try {

            float xs = (float) mouseHandler.getX() / (float) screenSize.width;
            float ys = (float) mouseHandler.getY() / (float) screenSize.height;

            //close the old stuff
            this.graphics.close();
            this.mouseHandler.close();

            //open the new
            this.graphics = api.open(config.getConfig());
            if (graphics == null) {
                log.debug("No Graphics for fbDevice: " + device.getIDstring());
            }

            screenSize.width = config.getConfig().getScreenWidth();
            screenSize.height = config.getConfig().getScreenHeight();

            this.mouseHandler =
                new MouseHandler(device.getDevice(), screenSize, getSystemEventQueueImpl(), keyboardHandler);
            mouseHandler.setCursor((int) (xs * screenSize.width), (int) (ys * screenSize.height));
            getAwtContext().adjustDesktopSize(screenSize.width, screenSize.height);
            onResize();
            device.setDefaultConfiguration(this.config);
            return getScreenSize();
        } catch (Exception e) {
            throw (AWTError) new AWTError(e.getMessage()).initCause(e);
        }
    }

    BufferedImage backBuffer;

    /**
     * Leave the gui mode
     */
    public final void leaveGUI() {
        Dimension ss = getScreenSize();
        backBuffer = new BufferedImage((int) ss.getWidth(), (int) ss.getHeight(), BufferedImage.TYPE_INT_ARGB);

        final KeyboardHandler keyboardHandler = this.keyboardHandler;
        final MouseHandler mouseHandler = this.mouseHandler;
        final Surface graphics = this.graphics;
        this.graphics = new BufferedImageSurface(backBuffer);

        if (keyboardHandler != null) {
            keyboardHandler.close();
        }
        if (mouseHandler != null) {
            mouseHandler.close();
        }

        if (graphics != null) {
            graphics.close();
        }
        this.keyboardHandler = null;
        this.mouseHandler = null;

        synchronized (initCloseLock) {
            graphicsMode = false;
            initCloseLock.notifyAll();
        }

        api.releaseOwnership(this);
    }

    /**
     * Join the GUI mode
     */
    public final void joinGUI() {
        try {
            api.requestOwnership(this);

            this.graphics = api.open(config.getConfig());
            this.keyboardHandler = new KeyboardHandler(_eventQueue);
            this.mouseHandler = new MouseHandler(fbDevice.getDevice(),
                screenSize, _eventQueue, keyboardHandler);
            keyboardHandler.install();
            getAwtContext().getAwtRoot().repaint();
            synchronized (initCloseLock) {
                graphicsMode = true;
            }
        } catch (DeviceException ex) {
            decRefCount(true);
            throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
        } catch (UnknownConfigurationException ex) {
            decRefCount(true);
            throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
        } catch (AlreadyOpenException ex) {
            decRefCount(true);
            throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
        } catch (Throwable ex) {
            decRefCount(true);
            log.error("Unknown exception", ex);
            throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
        }
    }

    public void iterateNativeQueue(EventQueue locked, boolean block) {
        if (block) {
            this.waitingNativeQueue = locked;
            synchronized (locked) {
                try {
                    // Wait for as long as the human eye can tolerate it.
                    // We wait 100ms.
                    locked.wait(100);
                } catch (InterruptedException e) {
                    // Ignore
                }
                this.waitingNativeQueue = null;
            }
        }
    }

    /**
     * @see java.awt.Toolkit#mapInputMethodHighlight(java.awt.im.InputMethodHighlight)
     */
    @SuppressWarnings("unchecked")
    public Map mapInputMethodHighlight(InputMethodHighlight highlight) {
        // TODO Auto-generated method stub
        return null;
    }

    public boolean nativeQueueEmpty() {
        return true;
    }

    protected abstract void onClose();

    protected abstract void onInitialize();

    protected abstract void onResize();

    public abstract boolean isWindow(Component comp);

    /**
     * @see java.awt.Toolkit#prepareImage(java.awt.Image, int, int,
     *      java.awt.image.ImageObserver)
     */
    public boolean prepareImage(Image image, int width, int height,
                                ImageObserver observer) {
        if (image instanceof JNodeImage) {
            final JNodeImage i = (JNodeImage) image;
            return i.prepare(observer);
        } else {
            return true;
        }
    }

    protected void setTop(Frame frame) {
        this.top = frame;
    }

    /**
     * @see java.awt.Toolkit#sync()
     */
    public void sync() {
        // TODO Auto-generated method stub

    }

    public void wakeNativeQueue() {
        final EventQueue q = this.waitingNativeQueue;
        if (q != null) {
            synchronized (q) {
                q.notifyAll();
            }
        }
    }

    public int getMouseNumberOfButtons() {
        //todo implement it
        return super.getMouseNumberOfButtons();
    }

    public void activateWindow(Component source) {

    }

    /**
     * A helper class to return to clients in cases where a BufferedImage is
     * desired but its construction fails.
     */
    private class ErrorImage extends Image {
        public ErrorImage() {
        }

        public int getWidth(ImageObserver observer) {
            return 1;
        }

        public int getHeight(ImageObserver observer) {
            return 1;
        }

        public ImageProducer getSource() {

            return new ImageProducer() {
                Set<ImageConsumer> consumers = new HashSet<ImageConsumer>();

                public synchronized void addConsumer(ImageConsumer ic) {
                    consumers.add(ic);
                }

                public synchronized boolean isConsumer(ImageConsumer ic) {
                    return consumers.contains(ic);
                }

                public synchronized void removeConsumer(ImageConsumer ic) {
                    consumers.remove(ic);
                }

                public synchronized void startProduction(ImageConsumer ic) {
                    consumers.add(ic);
                    for (ImageConsumer c : consumers) {
                        c.imageComplete(ImageConsumer.IMAGEERROR);
                    }
                }

                public void requestTopDownLeftRightResend(ImageConsumer ic) {
                    startProduction(ic);
                }
            };
        }

        public Graphics getGraphics() {
            return null;
        }

        public Object getProperty(String name, ImageObserver observer) {
            return null;
        }

        public Image getScaledInstance(int width, int height, int flags) {
            return new ErrorImage();
        }

        public void flush() {
        }
    }

    @SuppressWarnings("serial")
    private class LRUCache<K, V> extends java.util.LinkedHashMap<K, V> {
        int max_entries;

        public LRUCache(int max) {
            super(max, 0.75f, true);
            max_entries = max;
        }

        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
            return size() > max_entries;
        }
    }


    public boolean isModalExclusionTypeSupported(Dialog.ModalExclusionType modalExclusionType) {
        //todo implementit
        return false;
    }


    protected DesktopPeer createDesktopPeer(Desktop target) throws HeadlessException {
        //todo implementit
        return null;
    }

    @Override
    public void ownershipLost() {
        if (isGuiActive()) {
            leaveGUI();
        }
    }

    @Override
    public void ownershipGained() {
        startAwt();
    }

    static void startAwt() {
        if (JNodeToolkit.isGuiActive()) {
            ((JNodeToolkit) Toolkit.getDefaultToolkit()).joinGUI();
            JNodeToolkit.waitUntilStopped();
        } else {
            JNodeToolkit.startGui();
            try {
                final String desktopClassName = System.getProperty("jnode.desktop");
                if (desktopClassName != null) {
                    final Class<?> desktopClass =
                        Thread.currentThread().getContextClassLoader().loadClass(desktopClassName);
                    final Object desktop = desktopClass.newInstance();
                    if (desktop instanceof Runnable) {
                        final Thread t = new Thread((Runnable) desktop);
                        t.start();
                    }
                }
            } catch (ClassNotFoundException ex) {
                log.error("Cannot find desktop class", ex);
            } catch (InstantiationException ex) {
                log.error("Cannot instantiate desktop class", ex);
            } catch (IllegalAccessException ex) {
                log.error("Cannot access desktop class", ex);
            } finally {
                JNodeToolkit.waitUntilStopped();
            }
        }
        ((JNodeToolkit) Toolkit.getDefaultToolkit()).runExitAction();
    }

    /**
     * Set the action to be performed after the GUI has been shutdown, and
     * before control is returned to (for instance) the CommandShell.
     *
     * @param exitAction an action, or <code>null</code>.
     */
    public static void setExitAction(Runnable exitAction) {
        // FIXME ... This method probably needs a security check.  (The way it
        // is currently used potentially offers a small window for some other
        // thread to insert an action that would then be executed in the security
        // context of the GUI's owner.)
        ((JNodeToolkit) Toolkit.getDefaultToolkit()).exitAction = exitAction;
    }

    private synchronized void runExitAction() {
        if (exitAction != null) {
            exitAction.run();
        }
    }

    /**
     * Post the given event on the system eventqueue.
     */
    public final void postEvent(AWTEvent event) {
        Object source = event.getSource();
        if (source instanceof Component) {
            AppContext ac = SunToolkit.targetToAppContext(source);
            if (ac != null) {
                java.awt.EventQueue eq = (java.awt.EventQueue) ac.get(sun.awt.AppContext.EVENT_QUEUE_KEY);
                if (eq != null) {
                    eq.postEvent(event);
                    return;
                }
            }
        }
        getSystemEventQueueImpl().postEvent(event);
    }

    public static void postToTarget(ComponentEvent event, Component target) {
        EventQueue queue;
        AppContext ac = SunToolkit.targetToAppContext(target);
        if (ac == null) {
            queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
        } else {
            queue = (EventQueue) ac.get(sun.awt.AppContext.EVENT_QUEUE_KEY);
            if (queue == null) {
                queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
            }
        }

        queue.postEvent(event);
    }

    @Override
    public Insets getScreenInsets(GraphicsConfiguration gc) throws HeadlessException {
        JNodeAwtContext awtc = getAwtContext();
        if (awtc == null)
            return super.getScreenInsets(gc);

        Component root = awtc.getTopLevelRootComponent();
        if (root == null)
            return super.getScreenInsets(gc);

        JDesktopPane jdp = awtc.getDesktop();
        if (jdp == null)
            return super.getScreenInsets(gc);

        Rectangle trc_bounds = root.getBounds();
        Point jdp_loc = jdp.getLocationOnScreen();
        Rectangle jdp_bounds = jdp.getBounds();

        return new Insets(jdp_loc.y, jdp_loc.x, trc_bounds.height - jdp_loc.y - jdp_bounds.height,
            trc_bounds.width - jdp_loc.x - jdp_bounds.width);
    }
}
TOP

Related Classes of org.jnode.awt.JNodeToolkit

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.