aboutsummaryrefslogtreecommitdiffstats
path: root/ardor3d-awt/src/main
diff options
context:
space:
mode:
authorneothemachine <[email protected]>2012-12-05 17:03:16 +0100
committerneothemachine <[email protected]>2012-12-05 17:03:16 +0100
commit9dd02f103042cb8a196f8a3ed2278da443e345bf (patch)
tree422449f0c62ff9518316ce5d4219bb2b12f0ed15 /ardor3d-awt/src/main
parent2b26b12fd794de0f03a064a10024a3d9f5583756 (diff)
move all files from trunk to root folder
Diffstat (limited to 'ardor3d-awt/src/main')
-rw-r--r--ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageLoader.java267
-rw-r--r--ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageUtil.java132
-rw-r--r--ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTTextureUtil.java43
-rw-r--r--ardor3d-awt/src/main/java/com/ardor3d/image/util/AwtColorUtil.java38
-rw-r--r--ardor3d-awt/src/main/java/com/ardor3d/image/util/ScreenShotImageExporter.java156
-rw-r--r--ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtFocusWrapper.java50
-rw-r--r--ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKey.java154
-rw-r--r--ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKeyboardWrapper.java126
-rw-r--r--ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseManager.java167
-rw-r--r--ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseWrapper.java326
10 files changed, 1459 insertions, 0 deletions
diff --git a/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageLoader.java b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageLoader.java
new file mode 100644
index 0000000..86a93d3
--- /dev/null
+++ b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageLoader.java
@@ -0,0 +1,267 @@
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.image.util;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.awt.image.renderable.RenderableImage;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.logging.Logger;
+
+import javax.imageio.ImageIO;
+
+import com.ardor3d.image.Image;
+import com.ardor3d.image.ImageDataFormat;
+import com.ardor3d.image.PixelDataType;
+import com.ardor3d.renderer.state.TextureState;
+import com.ardor3d.util.geom.BufferUtils;
+import com.google.common.collect.Lists;
+
+/**
+ * Image loader that makes use of AWT's ImageIO to load image file data.
+ */
+public class AWTImageLoader implements ImageLoader {
+ private static final Logger logger = Logger.getLogger(AWTImageLoader.class.getName());
+
+ private static boolean createOnHeap = false;
+
+ private static String[] supportedFormats;
+
+ public static String[] getSupportedFormats() {
+ return supportedFormats;
+ }
+
+ public static void registerLoader() {
+ if (supportedFormats == null) {
+ final List<String> formats = Lists.newArrayList();
+ for (String format : ImageIO.getReaderFormatNames()) {
+ format = "." + format.toUpperCase();
+ if (!formats.contains(format)) {
+ formats.add(format);
+ }
+ }
+ supportedFormats = formats.toArray(new String[formats.size()]);
+ }
+ ImageLoaderUtil.registerHandler(new AWTImageLoader(), supportedFormats);
+ }
+
+ public Image load(final InputStream is, final boolean flipImage) throws IOException {
+ final BufferedImage image = ImageIO.read(is);
+ if (image == null) {
+ return null;
+ }
+
+ return makeArdor3dImage(image, flipImage);
+ }
+
+ public static Image makeArdor3dImage(final BufferedImage image, final boolean flipImage) {
+ if (image == null) {
+ return null;
+ }
+
+ final boolean hasAlpha = image.getColorModel().hasAlpha();
+ final boolean grayscale = image.getColorModel().getNumComponents() == 1;
+ BufferedImage tex;
+
+ if (flipImage
+ || ((image).getType() != BufferedImage.TYPE_BYTE_GRAY && (hasAlpha ? (image).getType() != BufferedImage.TYPE_4BYTE_ABGR
+ : (image).getType() != BufferedImage.TYPE_3BYTE_BGR))) {
+ // Obtain the image data.
+ try {
+ tex = new BufferedImage(image.getWidth(null), image.getHeight(null),
+ grayscale ? BufferedImage.TYPE_BYTE_GRAY : hasAlpha ? BufferedImage.TYPE_4BYTE_ABGR
+ : BufferedImage.TYPE_3BYTE_BGR);
+ } catch (final IllegalArgumentException e) {
+ logger.warning("Problem creating buffered Image: " + e.getMessage());
+ return TextureState.getDefaultTextureImage();
+ }
+
+ final int imageWidth = image.getWidth(null);
+ final int imageHeight = image.getHeight(null);
+ final int[] tmpData = new int[imageWidth];
+ int row = 0;
+ for (int y = imageHeight - 1; y >= 0; y--) {
+ image.getRGB(0, (flipImage ? row++ : y), imageWidth, 1, tmpData, 0, imageWidth);
+ tex.setRGB(0, y, imageWidth, 1, tmpData, 0, imageWidth);
+ }
+
+ } else {
+ tex = image;
+ }
+
+ // Get a pointer to the image memory
+ final byte data[] = asByteArray(tex);
+ final ByteBuffer scratch = createOnHeap ? BufferUtils.createByteBufferOnHeap(data.length) : BufferUtils
+ .createByteBuffer(data.length);
+ scratch.clear();
+ scratch.put(data);
+ scratch.flip();
+ final Image ardorImage = new Image();
+ ardorImage.setDataFormat(grayscale ? ImageDataFormat.Luminance : hasAlpha ? ImageDataFormat.RGBA
+ : ImageDataFormat.RGB);
+ ardorImage.setDataType(PixelDataType.UnsignedByte);
+ ardorImage.setWidth(tex.getWidth());
+ ardorImage.setHeight(tex.getHeight());
+ ardorImage.setData(scratch);
+ return ardorImage;
+ }
+
+ public static Image makeArdor3dImage(final RenderableImage image, final boolean flipImage) {
+ return makeArdor3dImage(image.createDefaultRendering(), flipImage);
+ }
+
+ public static Image makeArdor3dImage(final RenderedImage image, final boolean flipImage) {
+ if (image == null) {
+ return null;
+ }
+
+ final ColorModel colorModel = image.getColorModel();
+ final boolean hasAlpha = colorModel.hasAlpha();
+ final boolean grayscale = colorModel.getNumComponents() == 1;
+
+ // Get a pointer to the image memory
+ final byte data[] = asByteArray(image, grayscale, hasAlpha);
+ final ByteBuffer scratch = createOnHeap ? BufferUtils.createByteBufferOnHeap(data.length) : BufferUtils
+ .createByteBuffer(data.length);
+ scratch.clear();
+ scratch.put(data);
+ scratch.flip();
+ final Image ardorImage = new Image();
+ ardorImage.setDataFormat(grayscale ? ImageDataFormat.Luminance : hasAlpha ? ImageDataFormat.RGBA
+ : ImageDataFormat.RGB);
+ ardorImage.setDataType(PixelDataType.UnsignedByte);
+ ardorImage.setWidth(image.getWidth());
+ ardorImage.setHeight(image.getHeight());
+ ardorImage.setData(scratch);
+ return ardorImage;
+ }
+
+ public static byte[] asByteArray(final BufferedImage image) {
+ final int imageWidth = image.getWidth(null);
+ final int imageHeight = image.getHeight(null);
+ final boolean hasAlpha = image.getColorModel().hasAlpha();
+ final boolean grayscale = image.getColorModel().getNumComponents() == 1;
+
+ if (image.getRaster().getTransferType() == DataBuffer.TYPE_BYTE) {
+ return (byte[]) image.getRaster().getDataElements(0, 0, imageWidth, imageHeight, null);
+ }
+
+ final byte[] rVal = new byte[imageWidth * imageHeight * (grayscale ? 1 : (hasAlpha ? 4 : 3))];
+ final int[] tmpData = new int[imageWidth];
+ int index = 0;
+ for (int y = 0; y < imageHeight; y++) {
+ image.getRGB(0, y, imageWidth, 1, tmpData, 0, imageWidth);
+ for (int i = 0; i < imageWidth; i++) {
+ final int argb = tmpData[i];
+ if (grayscale) {
+ rVal[index++] = (byte) (argb & 0xFF);
+ } else {
+ rVal[index++] = (byte) ((argb >> 16) & 0xFF);
+ rVal[index++] = (byte) ((argb >> 8) & 0xFF);
+ rVal[index++] = (byte) (argb & 0xFF);
+ if (hasAlpha) {
+ rVal[index++] = (byte) ((argb >> 24) & 0xFF);
+ }
+ }
+ }
+ }
+ return rVal;
+ }
+
+ public static byte[] asByteArray(final RenderedImage image, final boolean isGreyscale, final boolean hasAlpha) {
+ final int imageWidth = image.getWidth();
+ final int imageHeight = image.getHeight();
+ final Raster raster = image.getData();
+
+ if (raster.getTransferType() == DataBuffer.TYPE_BYTE) {
+ return (byte[]) image.getData().getDataElements(0, 0, imageWidth, imageHeight, null);
+ }
+
+ final byte[] rVal = new byte[imageWidth * imageHeight * (isGreyscale ? 1 : (hasAlpha ? 4 : 3))];
+ final int[] tmpData = new int[imageWidth];
+ int index = 0;
+ for (int y = 0; y < imageHeight; y++) {
+ getRGB(raster, image.getColorModel(), 0, y, imageWidth, 1, tmpData, 0, imageWidth);
+ for (int i = 0; i < imageWidth; i++) {
+ final int argb = tmpData[i];
+ if (isGreyscale) {
+ rVal[index++] = (byte) (argb & 0xFF);
+ } else {
+ rVal[index++] = (byte) ((argb >> 16) & 0xFF);
+ rVal[index++] = (byte) ((argb >> 8) & 0xFF);
+ rVal[index++] = (byte) (argb & 0xFF);
+ if (hasAlpha) {
+ rVal[index++] = (byte) ((argb >> 24) & 0xFF);
+ }
+ }
+ }
+ }
+ return rVal;
+ }
+
+ /**
+ * Extract rgb values from raster using the colormodel.
+ */
+ private static int[] getRGB(final Raster raster, final ColorModel colorModel, final int startX, final int startY,
+ final int w, final int h, int[] rgbArray, final int offset, final int scansize) {
+ Object data;
+ final int nbands = raster.getNumBands();
+ final int dataType = raster.getDataBuffer().getDataType();
+ switch (dataType) {
+ case DataBuffer.TYPE_BYTE:
+ data = new byte[nbands];
+ break;
+ case DataBuffer.TYPE_USHORT:
+ data = new short[nbands];
+ break;
+ case DataBuffer.TYPE_INT:
+ data = new int[nbands];
+ break;
+ case DataBuffer.TYPE_FLOAT:
+ data = new float[nbands];
+ break;
+ case DataBuffer.TYPE_DOUBLE:
+ data = new double[nbands];
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown data buffer type: " + dataType);
+ }
+
+ if (rgbArray == null) {
+ rgbArray = new int[offset + h * scansize];
+ }
+
+ int yoff = offset;
+ int off;
+ for (int y = startY; y < startY + h; y++, yoff += scansize) {
+ off = yoff;
+ for (int x = startX; x < startX + w; x++) {
+ rgbArray[off++] = colorModel.getRGB(raster.getDataElements(x, y, data));
+ }
+ }
+
+ return rgbArray;
+ }
+
+ public static void setCreateOnHeap(final boolean createOnHeap) {
+ AWTImageLoader.createOnHeap = createOnHeap;
+ }
+
+ public static boolean isCreateOnHeap() {
+ return createOnHeap;
+ }
+}
diff --git a/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageUtil.java b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageUtil.java
new file mode 100644
index 0000000..3a8fa19
--- /dev/null
+++ b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTImageUtil.java
@@ -0,0 +1,132 @@
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.image.util;
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import com.ardor3d.image.Image;
+import com.ardor3d.image.PixelDataType;
+import com.google.common.collect.Lists;
+
+/**
+ * Utility methods for converting Ardor3D Images to AWT BufferedImages.
+ */
+public abstract class AWTImageUtil {
+
+ /**
+ * Convert the given Ardor3D Image to a List of BufferedImages. It is a List because Ardor3D Images may contain
+ * multiple layers (for example, in the case of cube maps or 3D textures).
+ *
+ * @param input
+ * the Ardor3D Image to convert
+ * @return the BufferedImage(s) created in the conversion
+ */
+ public static List<BufferedImage> convertToAWT(final Image input) {
+ // convert, using a full white tint (i.e. no applied color change from original data.)
+ return convertToAWT(input, Color.WHITE);
+ }
+
+ /**
+ * Convert the given Ardor3D Image to a List of BufferedImages. It is a List because Ardor3D Images may contain
+ * multiple layers (for example, in the case of cube maps or 3D textures). The given AWT Color is used to modulate
+ * or "tint" the returned image.
+ *
+ * TODO: Add support for more formats.<br/>
+ * XXX: Note that only images of data type ImageDataType.UnsignedByte and ImageDataFormat of RGB or RGBA are
+ * currently supported.
+ *
+ * @param input
+ * the Ardor3D Image to convert
+ * @param tint
+ * the Color to apply to the generated image
+ * @return the BufferedImage(s) created in the conversion
+ */
+ public static List<BufferedImage> convertToAWT(final Image input, final Color tint) {
+ if (input.getDataType() != PixelDataType.UnsignedByte) {
+ throw new Error("Unhandled Ardor3D image data type: " + input.getDataType());
+ }
+ // count the number of layers we will be converting.
+ final int size = input.getData().size();
+
+ // grab our image width and height
+ final int width = input.getWidth(), height = input.getHeight();
+
+ // create our return list
+ final List<BufferedImage> rVal = Lists.newArrayList();
+
+ // Calculate our modulation or "tint" values per channel
+ final double tRed = tint != null ? tint.getRed() / 255. : 1.0;
+ final double tGreen = tint != null ? tint.getGreen() / 255. : 1.0;
+ final double tBlue = tint != null ? tint.getBlue() / 255. : 1.0;
+ final double tAlpha = tint != null ? tint.getAlpha() / 255. : 1.0;
+
+ // go through each layer
+ for (int i = 0; i < size; i++) {
+ BufferedImage image;
+ final ByteBuffer data = input.getData(i);
+ data.rewind();
+ boolean alpha = false;
+ switch (input.getDataFormat()) {
+ case RGBA:
+ alpha = true;
+ // Falls through on purpose.
+ case RGB:
+ if (alpha) {
+ image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ } else {
+ image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+ }
+
+ int index,
+ r,
+ g,
+ b,
+ a,
+ argb;
+
+ // Go through each pixel
+ for (int x = 0; x < width; x++) {
+ for (int y = 0; y < height; y++) {
+ index = (alpha ? 4 : 3) * (y * width + x);
+ r = (int) Math.round(((data.get(index + 0)) & 0xFF) * tRed);
+ g = (int) Math.round(((data.get(index + 1)) & 0xFF) * tGreen);
+ b = (int) Math.round(((data.get(index + 2)) & 0xFF) * tBlue);
+
+ // convert to integer expression
+ argb = (r << 16) | (g << 8) | (b);
+
+ // add alpha, if applicable
+ if (alpha) {
+ a = (int) Math.round(((data.get(index + 3)) & 0xFF) * tAlpha);
+ argb |= (a & 0xFF) << 24;
+ }
+
+ // apply to image
+ image.setRGB(x, y, argb);
+ }
+ }
+ break;
+ default:
+ throw new Error("Unhandled image data format: " + input.getDataFormat());
+ }
+
+ // add to our list
+ rVal.add(image);
+ }
+
+ // return list
+ return rVal;
+ }
+
+}
diff --git a/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTTextureUtil.java b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTTextureUtil.java
new file mode 100644
index 0000000..827b915
--- /dev/null
+++ b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AWTTextureUtil.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.image.util;
+
+import java.awt.image.BufferedImage;
+
+import com.ardor3d.image.Image;
+import com.ardor3d.image.Texture;
+import com.ardor3d.image.TextureStoreFormat;
+import com.ardor3d.util.TextureKey;
+import com.ardor3d.util.TextureManager;
+
+public abstract class AWTTextureUtil {
+
+ /**
+ * Convenience Utility for loading a BufferedImage as an Ardor3D Texture.
+ *
+ * @param image
+ * our buffered image
+ * @param minFilter
+ * the filter for the near values. Used to determine if we should generate mipmaps.
+ * @param flipVertically
+ * If true, the image is flipped vertically during image conversion.
+ * @param storeFormat
+ * the specific format to use when storing this texture on the card.
+ * @return our new Texture.
+ */
+ public static final Texture loadTexture(final BufferedImage image, final Texture.MinificationFilter minFilter,
+ final TextureStoreFormat storeFormat, final boolean flipVertically) {
+ final Image imageData = AWTImageLoader.makeArdor3dImage(image, flipVertically);
+ final String fileType = (image != null) ? "" + image.hashCode() : null;
+ final TextureKey tkey = TextureKey.getKey(null, flipVertically, storeFormat, fileType, minFilter);
+ return TextureManager.loadFromKey(tkey, imageData, null);
+ }
+}
diff --git a/ardor3d-awt/src/main/java/com/ardor3d/image/util/AwtColorUtil.java b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AwtColorUtil.java
new file mode 100644
index 0000000..be24c26
--- /dev/null
+++ b/ardor3d-awt/src/main/java/com/ardor3d/image/util/AwtColorUtil.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.image.util;
+
+import java.awt.Color;
+
+import com.ardor3d.math.ColorRGBA;
+import com.ardor3d.math.type.ReadOnlyColorRGBA;
+
+public class AwtColorUtil {
+
+ public static ColorRGBA makeColorRGBA(final Color color) {
+ if (color == null) {
+ return new ColorRGBA(0, 0, 0, 1);
+ }
+ return new ColorRGBA(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f,
+ color.getAlpha() / 255f);
+ }
+
+ public static Color makeColor(final ReadOnlyColorRGBA color) {
+ return makeColor(color, true);
+ }
+
+ public static Color makeColor(final ReadOnlyColorRGBA color, final boolean useAlpha) {
+ if (color == null) {
+ return new Color(0, 0, 0, 1);
+ }
+ return new Color(color.getRed(), color.getGreen(), color.getBlue(), useAlpha ? color.getAlpha() : 1.0f);
+ }
+}
diff --git a/ardor3d-awt/src/main/java/com/ardor3d/image/util/ScreenShotImageExporter.java b/ardor3d-awt/src/main/java/com/ardor3d/image/util/ScreenShotImageExporter.java
new file mode 100644
index 0000000..3247d5e
--- /dev/null
+++ b/ardor3d-awt/src/main/java/com/ardor3d/image/util/ScreenShotImageExporter.java
@@ -0,0 +1,156 @@
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.image.util;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.imageio.ImageIO;
+
+import com.ardor3d.image.ImageDataFormat;
+import com.ardor3d.util.screen.ScreenExportable;
+
+public class ScreenShotImageExporter implements ScreenExportable {
+ private static final Logger logger = Logger.getLogger(ScreenShotImageExporter.class.getName());
+
+ protected File _directory;
+ protected String _prepend;
+ protected String _fileFormat;
+ protected boolean _useAlpha;
+
+ protected File _lastFile;
+
+ /**
+ * Make a new exporter with the default settings:
+ *
+ * <pre>
+ * directory: local working directory
+ * prepend: &quot;capture_&quot;
+ * format: &quot;png&quot;
+ * useAlpha: false
+ * </pre>
+ */
+ public ScreenShotImageExporter() {
+ this(new File(System.getProperty("user.dir")), "capture_", "png", false);
+ }
+
+ /**
+ * Construct a new exporter.
+ *
+ * @param directory
+ * the directory to save the screen shots in.
+ * @param prepend
+ * a value to prepend onto the generated file name. This must be at least 3 characters long.
+ * @param format
+ * the format to use for saving the image. ImageIO is used for this, so safe values are likely: "png",
+ * "jpg", "gif" and "bmp"
+ * @param useAlpha
+ * true for alpha values to be stored in image (as applicable, depending on the given format)
+ */
+ public ScreenShotImageExporter(final File directory, final String prepend, final String format,
+ final boolean useAlpha) {
+ _directory = directory;
+ _prepend = prepend;
+ _fileFormat = format;
+ _useAlpha = useAlpha;
+ }
+
+ public void export(final ByteBuffer data, final int width, final int height) {
+ final BufferedImage img = new BufferedImage(width, height, _useAlpha ? BufferedImage.TYPE_INT_ARGB
+ : BufferedImage.TYPE_INT_RGB);
+
+ int index, r, g, b, a;
+ int argb;
+ for (int x = 0; x < width; x++) {
+ for (int y = 0; y < height; y++) {
+ index = (_useAlpha ? 4 : 3) * ((height - y - 1) * width + x);
+ r = ((data.get(index + 0)));
+ g = ((data.get(index + 1)));
+ b = ((data.get(index + 2)));
+
+ argb = ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF);
+
+ if (_useAlpha) {
+ a = ((data.get(index + 3)));
+ argb |= (a & 0xFF) << 24;
+ }
+
+ img.setRGB(x, y, argb);
+ }
+ }
+
+ try {
+ final File out = new File(_directory, _prepend + System.currentTimeMillis() + "." + _fileFormat);
+ logger.fine("Taking screenshot: " + out.getAbsolutePath());
+
+ // write out the screen shot image to a file.
+ ImageIO.write(img, _fileFormat, out);
+
+ // save our successful file to be accessed as desired.
+ _lastFile = out;
+ } catch (final IOException e) {
+ logger
+ .logp(Level.WARNING, getClass().getName(), "export(ByteBuffer, int, int)", e.getLocalizedMessage(),
+ e);
+ }
+ }
+
+ public ImageDataFormat getFormat() {
+ if (_useAlpha) {
+ return ImageDataFormat.RGBA;
+ } else {
+ return ImageDataFormat.RGB;
+ }
+ }
+
+ /**
+ * @return the last File written by this exporter, or null if none were written.
+ */
+ public File getLastFile() {
+ return _lastFile;
+ }
+
+ public File getDirectory() {
+ return _directory;
+ }
+
+ public void setDirectory(final File directory) {
+ _directory = directory;
+ }
+
+ public String getPrepend() {
+ return _prepend;
+ }
+
+ public void setPrepend(final String prepend) {
+ _prepend = prepend;
+ }
+
+ public boolean isUseAlpha() {
+ return _useAlpha;
+ }
+
+ public void setUseAlpha(final boolean useAlpha) {
+ _useAlpha = useAlpha;
+ }
+
+ public String getFileFormat() {
+ return _fileFormat;
+ }
+
+ public void setFileFormat(final String format) {
+ _fileFormat = format;
+ }
+}
diff --git a/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtFocusWrapper.java b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtFocusWrapper.java
new file mode 100644
index 0000000..5514856
--- /dev/null
+++ b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtFocusWrapper.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.input.awt;
+
+import java.awt.Component;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+
+import com.ardor3d.input.FocusWrapper;
+
+/**
+ * Focus listener class for use with AWT.
+ */
+public class AwtFocusWrapper implements FocusWrapper, FocusListener {
+ protected volatile boolean _focusLost = false;
+
+ protected final Component _component;
+
+ public AwtFocusWrapper(final Component component) {
+ _component = component;
+ }
+
+ public void focusGained(final FocusEvent e) {
+ // do nothing
+ }
+
+ public void focusLost(final FocusEvent e) {
+ _focusLost = true;
+ }
+
+ public boolean getAndClearFocusLost() {
+ final boolean result = _focusLost;
+
+ _focusLost = false;
+
+ return result;
+ }
+
+ public void init() {
+ _component.addFocusListener(this);
+ }
+}
diff --git a/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKey.java b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKey.java
new file mode 100644
index 0000000..f464154
--- /dev/null
+++ b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKey.java
@@ -0,0 +1,154 @@
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.input.awt;
+
+import java.awt.event.KeyEvent;
+
+import com.ardor3d.input.Key;
+
+/**
+ * Enum used for translations between AWT key codes and Ardor3D {@link Key} instances.
+ */
+public enum AwtKey {
+
+ ZERO(KeyEvent.VK_0, Key.ZERO), //
+ ONE(KeyEvent.VK_1, Key.ONE), //
+ TWO(KeyEvent.VK_2, Key.TWO), //
+ THREE(KeyEvent.VK_3, Key.THREE), //
+ FOUR(KeyEvent.VK_4, Key.FOUR), //
+ FIVE(KeyEvent.VK_5, Key.FIVE), //
+ SIX(KeyEvent.VK_6, Key.SIX), //
+ SEVEN(KeyEvent.VK_7, Key.SEVEN), //
+ EIGHT(KeyEvent.VK_8, Key.EIGHT), //
+ NINE(KeyEvent.VK_9, Key.NINE), //
+ A(KeyEvent.VK_A, Key.A), //
+ ADD(KeyEvent.VK_ADD, Key.NUMPADADD), //
+ AT(KeyEvent.VK_AT, Key.AT), //
+ B(KeyEvent.VK_B, Key.B), //
+ BACK_QUOTE(KeyEvent.VK_BACK_QUOTE, Key.GRAVE), //
+ BACK_SPACE(KeyEvent.VK_BACK_SPACE, Key.BACK), //
+ BACK_SLASH(KeyEvent.VK_BACK_SLASH, Key.BACKSLASH), //
+ C(KeyEvent.VK_C, Key.C), //
+ CAPS_LOCK(KeyEvent.VK_CAPS_LOCK, Key.CAPITAL), //
+ CIRCUMFLEX(KeyEvent.VK_CIRCUMFLEX, Key.CIRCUMFLEX), //
+ COLON(KeyEvent.VK_COLON, Key.COLON), //
+ COMMA(KeyEvent.VK_COMMA, Key.COMMA), //
+ CONVERT(KeyEvent.VK_CONVERT, Key.CONVERT), //
+ D(KeyEvent.VK_D, Key.D), //
+ DECIMAL(KeyEvent.VK_DECIMAL, Key.DECIMAL), //
+ DELETE(KeyEvent.VK_DELETE, Key.DELETE), //
+ DIVIDE(KeyEvent.VK_DIVIDE, Key.DIVIDE), //
+ DOWN(KeyEvent.VK_DOWN, Key.DOWN), //
+ E(KeyEvent.VK_E, Key.E), //
+ END(KeyEvent.VK_END, Key.END), //
+ EQUALS(KeyEvent.VK_EQUALS, Key.EQUALS), //
+ ESCAPE(KeyEvent.VK_ESCAPE, Key.ESCAPE), //
+ F(KeyEvent.VK_F, Key.F), //
+ F1(KeyEvent.VK_F1, Key.F1), //
+ F2(KeyEvent.VK_F2, Key.F2), //
+ F3(KeyEvent.VK_F3, Key.F3), //
+ F4(KeyEvent.VK_F4, Key.F4), //
+ F5(KeyEvent.VK_F5, Key.F5), //
+ F6(KeyEvent.VK_F6, Key.F6), //
+ F7(KeyEvent.VK_F7, Key.F7), //
+ F8(KeyEvent.VK_F8, Key.F8), //
+ F9(KeyEvent.VK_F9, Key.F9), //
+ F10(KeyEvent.VK_F10, Key.F10), //
+ F11(KeyEvent.VK_F11, Key.F11), //
+ F12(KeyEvent.VK_F12, Key.F12), //
+ F13(KeyEvent.VK_F13, Key.F13), //
+ F14(KeyEvent.VK_F14, Key.F14), //
+ F15(KeyEvent.VK_F15, Key.F15), //
+ G(KeyEvent.VK_G, Key.G), //
+ H(KeyEvent.VK_H, Key.H), //
+ HOME(KeyEvent.VK_HOME, Key.HOME), //
+ I(KeyEvent.VK_I, Key.I), //
+ INSERT(KeyEvent.VK_INSERT, Key.INSERT), //
+ J(KeyEvent.VK_J, Key.J), //
+ K(KeyEvent.VK_K, Key.K), //
+ KANA(KeyEvent.VK_KANA, Key.KANA), //
+ KANJI(KeyEvent.VK_KANJI, Key.KANJI), //
+ L(KeyEvent.VK_L, Key.L), //
+ OPEN_BRACKET(KeyEvent.VK_OPEN_BRACKET, Key.LBRACKET), //
+ CONTROL(KeyEvent.VK_CONTROL, Key.LCONTROL), //
+ LEFT(KeyEvent.VK_LEFT, Key.LEFT), //
+ ALT(KeyEvent.VK_ALT, Key.LMENU), //
+ META(KeyEvent.VK_META, Key.LMETA), //
+ SHIFT(KeyEvent.VK_SHIFT, Key.LSHIFT), //
+ M(KeyEvent.VK_M, Key.M), //
+ MINUS(KeyEvent.VK_MINUS, Key.MINUS), //
+ MULTIPLY(KeyEvent.VK_MULTIPLY, Key.MULTIPLY), //
+ N(KeyEvent.VK_N, Key.N), //
+ PAGE_DOWN(KeyEvent.VK_PAGE_DOWN, Key.PAGEDOWN_NEXT), //
+ NONCONVERT(KeyEvent.VK_NONCONVERT, Key.NOCONVERT), //
+ NUM_LOCK(KeyEvent.VK_NUM_LOCK, Key.NUMLOCK), //
+ NUMPAD0(KeyEvent.VK_NUMPAD0, Key.NUMPAD0), //
+ NUMPAD1(KeyEvent.VK_NUMPAD1, Key.NUMPAD1), //
+ NUMPAD2(KeyEvent.VK_NUMPAD2, Key.NUMPAD2), //
+ NUMPAD3(KeyEvent.VK_NUMPAD3, Key.NUMPAD3), //
+ NUMPAD4(KeyEvent.VK_NUMPAD4, Key.NUMPAD4), //
+ NUMPAD5(KeyEvent.VK_NUMPAD5, Key.NUMPAD5), //
+ NUMPAD6(KeyEvent.VK_NUMPAD6, Key.NUMPAD6), //
+ NUMPAD7(KeyEvent.VK_NUMPAD7, Key.NUMPAD7), //
+ NUMPAD8(KeyEvent.VK_NUMPAD8, Key.NUMPAD8), //
+ NUMPAD9(KeyEvent.VK_NUMPAD9, Key.NUMPAD9), //
+ O(KeyEvent.VK_O, Key.O), //
+ P(KeyEvent.VK_P, Key.P), //
+ PAUSE(KeyEvent.VK_PAUSE, Key.PAUSE), //
+ PERIOD(KeyEvent.VK_PERIOD, Key.PERIOD), //
+ PAGE_UP(KeyEvent.VK_PAGE_UP, Key.PAGEUP_PRIOR), //
+ Q(KeyEvent.VK_Q, Key.Q), //
+ QUOTE(KeyEvent.VK_QUOTE, Key.APOSTROPHE), //
+ R(KeyEvent.VK_R, Key.R), //
+ CLOSE_BRACKET(KeyEvent.VK_CLOSE_BRACKET, Key.RBRACKET), //
+ ENTER(KeyEvent.VK_ENTER, Key.RETURN), //
+ RIGHT(KeyEvent.VK_RIGHT, Key.RIGHT), //
+ S(KeyEvent.VK_S, Key.S), //
+ SCROLL_LOCK(KeyEvent.VK_SCROLL_LOCK, Key.SCROLL), //
+ SEMICOLON(KeyEvent.VK_SEMICOLON, Key.SEMICOLON), //
+ SLASH(KeyEvent.VK_SLASH, Key.SLASH), //
+ SPACE(KeyEvent.VK_SPACE, Key.SPACE), //
+ STOP(KeyEvent.VK_STOP, Key.STOP), //
+ PRINTSCREEN(KeyEvent.VK_PRINTSCREEN, Key.SYSRQ), //
+ T(KeyEvent.VK_T, Key.T), //
+ TAB(KeyEvent.VK_TAB, Key.TAB), //
+ U(KeyEvent.VK_U, Key.U), //
+ UNDERSCORE(KeyEvent.VK_UNDERSCORE, Key.UNDERLINE), //
+ UP(KeyEvent.VK_UP, Key.UP), //
+ V(KeyEvent.VK_V, Key.V), //
+ W(KeyEvent.VK_W, Key.W), //
+ X(KeyEvent.VK_X, Key.X), //
+ Y(KeyEvent.VK_Y, Key.Y), //
+ Z(KeyEvent.VK_Z, Key.Z), //
+ UNDEFINED(KeyEvent.VK_UNDEFINED, Key.UNKNOWN);
+
+ private final int _awtCode;
+ private final Key _key;
+
+ private AwtKey(final int awtCode, final Key key) {
+ _awtCode = awtCode;
+ _key = key;
+ }
+
+ public static Key findByCode(final int awtCode) {
+ for (final AwtKey ak : values()) {
+ if (ak._awtCode == awtCode) {
+ return ak._key;
+ }
+ }
+
+ return Key.UNKNOWN;
+ }
+
+ public int getAwtCode() {
+ return _awtCode;
+ }
+}
diff --git a/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKeyboardWrapper.java b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKeyboardWrapper.java
new file mode 100644
index 0000000..5aa9a4d
--- /dev/null
+++ b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtKeyboardWrapper.java
@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.input.awt;
+
+import java.awt.Component;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.KeyListener;
+import java.util.EnumSet;
+import java.util.LinkedList;
+
+import com.ardor3d.annotation.GuardedBy;
+import com.ardor3d.input.Key;
+import com.ardor3d.input.KeyEvent;
+import com.ardor3d.input.KeyState;
+import com.ardor3d.input.KeyboardWrapper;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.AbstractIterator;
+import com.google.common.collect.PeekingIterator;
+
+/**
+ * Keyboard wrapper class for use with AWT.
+ */
+public class AwtKeyboardWrapper implements KeyboardWrapper, KeyListener {
+ @GuardedBy("this")
+ protected final LinkedList<KeyEvent> _upcomingEvents = new LinkedList<KeyEvent>();
+
+ @GuardedBy("this")
+ protected AwtKeyboardIterator _currentIterator = null;
+
+ protected final Component _component;
+
+ protected boolean _consumeEvents = false;
+
+ protected final EnumSet<Key> _pressedList = EnumSet.noneOf(Key.class);
+
+ public AwtKeyboardWrapper(final Component component) {
+ _component = Preconditions.checkNotNull(component, "component");
+ }
+
+ public void init() {
+ _component.addKeyListener(this);
+ _component.addFocusListener(new FocusListener() {
+ public void focusLost(final FocusEvent e) {}
+
+ public void focusGained(final FocusEvent e) {
+ _pressedList.clear();
+ }
+ });
+ }
+
+ public synchronized PeekingIterator<KeyEvent> getEvents() {
+ if (_currentIterator == null || !_currentIterator.hasNext()) {
+ _currentIterator = new AwtKeyboardIterator();
+ }
+
+ return _currentIterator;
+ }
+
+ public synchronized void keyTyped(final java.awt.event.KeyEvent e) {
+ if (_consumeEvents) {
+ e.consume();
+ // ignore this event
+ }
+ }
+
+ public synchronized void keyPressed(final java.awt.event.KeyEvent e) {
+ final Key pressed = fromKeyEventToKey(e);
+ if (!_pressedList.contains(pressed)) {
+ _upcomingEvents.add(new KeyEvent(pressed, KeyState.DOWN, e.getKeyChar()));
+ _pressedList.add(pressed);
+ }
+ if (_consumeEvents) {
+ e.consume();
+ }
+ }
+
+ public synchronized void keyReleased(final java.awt.event.KeyEvent e) {
+ final Key released = fromKeyEventToKey(e);
+ _upcomingEvents.add(new KeyEvent(released, KeyState.UP, e.getKeyChar()));
+ _pressedList.remove(released);
+ if (_consumeEvents) {
+ e.consume();
+ }
+ }
+
+ /**
+ * Convert from AWT key event to Ardor3D Key. Override to provide additional or custom behavior.
+ *
+ * @param e
+ * the AWT KeyEvent received by the input system.
+ * @return an Ardor3D Key, to be forwarded to the Predicate/Trigger system.
+ */
+ public synchronized Key fromKeyEventToKey(final java.awt.event.KeyEvent e) {
+ return AwtKey.findByCode(e.getKeyCode());
+ }
+
+ private class AwtKeyboardIterator extends AbstractIterator<KeyEvent> implements PeekingIterator<KeyEvent> {
+ @Override
+ protected KeyEvent computeNext() {
+ synchronized (AwtKeyboardWrapper.this) {
+ if (_upcomingEvents.isEmpty()) {
+ return endOfData();
+ }
+
+ return _upcomingEvents.poll();
+ }
+ }
+ }
+
+ public boolean isConsumeEvents() {
+ return _consumeEvents;
+ }
+
+ public void setConsumeEvents(final boolean consumeEvents) {
+ _consumeEvents = consumeEvents;
+ }
+}
diff --git a/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseManager.java b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseManager.java
new file mode 100644
index 0000000..8841f3a
--- /dev/null
+++ b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseManager.java
@@ -0,0 +1,167 @@
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.input.awt;
+
+import java.awt.AWTException;
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Point;
+import java.awt.Robot;
+import java.awt.Toolkit;
+import java.awt.image.BufferedImage;
+import java.lang.reflect.InvocationTargetException;
+import java.util.logging.Logger;
+
+import javax.swing.SwingUtilities;
+
+import com.ardor3d.image.util.AWTImageUtil;
+import com.ardor3d.input.GrabbedState;
+import com.ardor3d.input.MouseCursor;
+import com.ardor3d.input.MouseManager;
+
+/**
+ * Implementation of the {@link com.ardor3d.input.MouseManager} interface for use with AWT windows. This implementation
+ * supports the optional {@link #setGrabbed(com.ardor3d.input.GrabbedState)} and {@link #setPosition(int, int)} methods
+ * if an AWT robot can be created on the current system. The constructor takes an AWT {@link java.awt.Component}
+ * instance, for which the cursor is set. In a multi-canvas application, each canvas can have its own AwtMouseManager
+ * instance, or it is possible to use a single one for the AWT container that includes the canvases.
+ */
+public class AwtMouseManager implements MouseManager {
+ private static final Logger logger = Logger.getLogger(AwtMouseManager.class.getName());
+
+ private static Cursor _transparentCursor;
+
+ private final Component _component;
+ private Robot _robot;
+
+ /** our current grabbed state */
+ private GrabbedState _grabbedState;
+
+ /** Our cursor prior to a setGrabbed(GRABBED) operation. Stored to be used when cursor is "ungrabbed" */
+ private Cursor _pregrabCursor;
+
+ public AwtMouseManager(final Component component) {
+ _component = component;
+
+ // Attempt to make
+ try {
+ _robot = new Robot();
+ } catch (final AWTException ex) {
+ logger.warning("Unable to create java.awt.Robot. setPosition and setGrabbed will not be supported.");
+ }
+ }
+
+ public void setCursor(final MouseCursor cursor) {
+ if (cursor == MouseCursor.SYSTEM_DEFAULT) {
+ if (_grabbedState == GrabbedState.GRABBED) {
+ _pregrabCursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
+ } else {
+ _component.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ }
+ return;
+ }
+
+ final BufferedImage image = AWTImageUtil.convertToAWT(cursor.getImage()).get(0);
+
+ // the hotSpot values must be less than the Dimension returned by getBestCursorSize
+ final Dimension bestCursorSize = Toolkit.getDefaultToolkit().getBestCursorSize(cursor.getHotspotX(),
+ cursor.getHotspotY());
+ final Point hotSpot = new Point(Math.min(cursor.getHotspotX(), (int) bestCursorSize.getWidth() - 1), Math.min(
+ cursor.getHotspotY(), (int) bestCursorSize.getHeight() - 1));
+
+ final Cursor awtCursor = Toolkit.getDefaultToolkit().createCustomCursor(image, hotSpot, cursor.getName());
+ if (_grabbedState == GrabbedState.GRABBED) {
+ _pregrabCursor = awtCursor;
+ } else {
+ _component.setCursor(awtCursor);
+ }
+ }
+
+ public void setPosition(final int x, final int y) {
+ if (!isSetPositionSupported()) {
+ throw new UnsupportedOperationException();
+ }
+
+ try {
+ // Only queue up if we are not already in the event thread.
+ if (java.awt.EventQueue.isDispatchThread()) {
+ setMousePosition(x, y);
+ } else {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ setMousePosition(x, y);
+ }
+ });
+ }
+ } catch (final InterruptedException ex) {
+ } catch (final InvocationTargetException ex) {
+ }
+ }
+
+ private void setMousePosition(final int ardorX, final int ardorY) {
+ Component c = _component;
+ if (c instanceof Frame && ((Frame) c).getComponentCount() > 0) {
+ c = ((Frame) c).getComponent(0);
+ }
+ final Point p = new Point(ardorX, c.getHeight() - ardorY);
+ SwingUtilities.convertPointToScreen(p, c);
+ _robot.mouseMove(p.x, p.y);
+ }
+
+ public void setGrabbed(final GrabbedState grabbedState) {
+ if (!isSetGrabbedSupported()) {
+ throw new UnsupportedOperationException();
+ }
+
+ // check if we should be here.
+ if (_grabbedState == grabbedState) {
+ return;
+ }
+
+ // remember our grabbed state mode.
+ _grabbedState = grabbedState;
+
+ if (grabbedState == GrabbedState.GRABBED) {
+ // remember our old cursor
+ _pregrabCursor = _component.getCursor();
+
+ // set our cursor to be invisible
+ _component.setCursor(getTransparentCursor());
+ } else {
+ // restore our old cursor
+ _component.setCursor(_pregrabCursor);
+ }
+ }
+
+ private static final Cursor getTransparentCursor() {
+ if (_transparentCursor == null) {
+ final BufferedImage cursorImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
+ cursorImage.setRGB(0, 0, 0);
+ _transparentCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(0, 0),
+ "empty cursor");
+ }
+ return _transparentCursor;
+ }
+
+ public boolean isSetPositionSupported() {
+ return _robot != null;
+ }
+
+ public boolean isSetGrabbedSupported() {
+ return _robot != null;
+ }
+
+ public GrabbedState getGrabbed() {
+ return _grabbedState;
+ }
+} \ No newline at end of file
diff --git a/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseWrapper.java b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseWrapper.java
new file mode 100644
index 0000000..c64b70b
--- /dev/null
+++ b/ardor3d-awt/src/main/java/com/ardor3d/input/awt/AwtMouseWrapper.java
@@ -0,0 +1,326 @@
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.input.awt;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.awt.Component;
+import java.awt.Frame;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.LinkedList;
+
+import com.ardor3d.annotation.GuardedBy;
+import com.ardor3d.input.ButtonState;
+import com.ardor3d.input.GrabbedState;
+import com.ardor3d.input.MouseButton;
+import com.ardor3d.input.MouseManager;
+import com.ardor3d.input.MouseState;
+import com.ardor3d.input.MouseWrapper;
+import com.google.common.collect.AbstractIterator;
+import com.google.common.collect.EnumMultiset;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multiset;
+import com.google.common.collect.PeekingIterator;
+
+/**
+ * Mouse wrapper class for use with AWT.
+ */
+public class AwtMouseWrapper implements MouseWrapper, MouseListener, MouseWheelListener, MouseMotionListener {
+ @GuardedBy("this")
+ protected final LinkedList<MouseState> _upcomingEvents = Lists.newLinkedList();
+
+ @GuardedBy("this")
+ protected AwtMouseIterator _currentIterator = null;
+
+ @GuardedBy("this")
+ protected MouseState _lastState = null;
+
+ protected boolean _consumeEvents = false;
+
+ protected final Component _component;
+ protected final Frame _frame;
+ protected final MouseManager _manager;
+
+ protected final Multiset<MouseButton> _clicks = EnumMultiset.create(MouseButton.class);
+ protected final EnumMap<MouseButton, Long> _lastClickTime = Maps.newEnumMap(MouseButton.class);
+ protected final EnumSet<MouseButton> _clickArmed = EnumSet.noneOf(MouseButton.class);
+
+ protected int _ignoreX = Integer.MAX_VALUE;
+ protected int _ignoreY = Integer.MAX_VALUE;
+
+ public AwtMouseWrapper(final Component component, final MouseManager manager) {
+ _manager = manager;
+ if (component instanceof Frame) {
+ _frame = (Frame) (_component = component);
+ } else {
+ _component = checkNotNull(component, "component");
+ _frame = null;
+ }
+ for (final MouseButton mb : MouseButton.values()) {
+ _lastClickTime.put(mb, 0L);
+ }
+ }
+
+ public void init() {
+ _component.addMouseListener(this);
+ _component.addMouseMotionListener(this);
+ _component.addMouseWheelListener(this);
+ }
+
+ public synchronized PeekingIterator<MouseState> getEvents() {
+ expireClickEvents();
+
+ if (_currentIterator == null || !_currentIterator.hasNext()) {
+ _currentIterator = new AwtMouseIterator();
+ }
+
+ return _currentIterator;
+ }
+
+ private void expireClickEvents() {
+ if (!_clicks.isEmpty()) {
+ for (final MouseButton mb : MouseButton.values()) {
+ if (System.currentTimeMillis() - _lastClickTime.get(mb) > MouseState.CLICK_TIME_MS) {
+ _clicks.setCount(mb, 0);
+ }
+ }
+ }
+ }
+
+ public synchronized void mousePressed(final MouseEvent e) {
+ final MouseButton b = getButtonForEvent(e);
+ if (_clickArmed.contains(b)) {
+ _clicks.setCount(b, 0);
+ }
+ _clickArmed.add(b);
+ _lastClickTime.put(b, System.currentTimeMillis());
+
+ initState(e);
+ if (_consumeEvents) {
+ e.consume();
+ }
+
+ final EnumMap<MouseButton, ButtonState> buttons = _lastState.getButtonStates();
+
+ setStateForButton(e, buttons, ButtonState.DOWN);
+
+ addNewState(e, buttons, null);
+ }
+
+ public synchronized void mouseReleased(final MouseEvent e) {
+ initState(e);
+ if (_consumeEvents) {
+ e.consume();
+ }
+
+ final EnumMap<MouseButton, ButtonState> buttons = _lastState.getButtonStates();
+
+ setStateForButton(e, buttons, ButtonState.UP);
+
+ final MouseButton b = getButtonForEvent(e);
+ if (_clickArmed.contains(b) && (System.currentTimeMillis() - _lastClickTime.get(b) <= MouseState.CLICK_TIME_MS)) {
+ _clicks.add(b); // increment count of clicks for button b.
+ // XXX: Note the double event add... this prevents sticky click counts, but is it the best way?
+ addNewState(e, buttons, EnumMultiset.create(_clicks));
+ } else {
+ _clicks.setCount(b, 0); // clear click count for button b.
+ }
+ _clickArmed.remove(b);
+
+ addNewState(e, buttons, null);
+ }
+
+ public synchronized void mouseDragged(final MouseEvent e) {
+ // forward to mouseMoved.
+ mouseMoved(e);
+ }
+
+ public synchronized void mouseMoved(final MouseEvent e) {
+ _clickArmed.clear();
+ _clicks.clear();
+
+ // check that we have a valid _lastState
+ initState(e);
+ if (_consumeEvents) {
+ e.consume();
+ }
+
+ // remember our current ardor3d position
+ final int oldX = _lastState.getX(), oldY = _lastState.getY();
+
+ // check the state against the "ignore next" values
+ if (_ignoreX != Integer.MAX_VALUE // shortcut to prevent dx/dy calculations
+ && (_ignoreX == getDX(e) && _ignoreY == getDY(e))) {
+
+ // we matched, so we'll consider this a "mouse pointer reset move"
+ // so reset ignore to let the next move event through.
+ _ignoreX = Integer.MAX_VALUE;
+ _ignoreY = Integer.MAX_VALUE;
+
+ // exit without adding an event to our queue
+ return;
+ }
+
+ // save our old "last state."
+ final MouseState _savedState = _lastState;
+
+ // Add our latest state info to the queue
+ addNewState(e, _lastState.getButtonStates(), null);
+
+ // If we have a valid move... should always be the case, but occasionally something slips through.
+ if (_lastState.getDx() != 0 || _lastState.getDy() != 0) {
+
+ // Ask our manager if we're currently "captured"
+ if (_manager.getGrabbed() == GrabbedState.GRABBED) {
+
+ // if so, set "ignore next" to the inverse of this move
+ _ignoreX = -_lastState.getDx();
+ _ignoreY = -_lastState.getDy();
+
+ // Move us back to our last position.
+ _manager.setPosition(oldX, oldY);
+
+ // And finally, revert our _lastState.
+ _lastState = _savedState;
+ } else {
+ // otherwise, set us to not ignore anything. This may be unnecessary, but prevents any possible
+ // "ignore" bleeding.
+ _ignoreX = Integer.MAX_VALUE;
+ _ignoreY = Integer.MAX_VALUE;
+ }
+ }
+ }
+
+ public void mouseWheelMoved(final MouseWheelEvent e) {
+ initState(e);
+
+ addNewState(e, _lastState.getButtonStates(), null);
+ if (_consumeEvents) {
+ e.consume();
+ }
+ }
+
+ private void initState(final MouseEvent mouseEvent) {
+ if (_lastState == null) {
+ _lastState = new MouseState(mouseEvent.getX(), getArdor3DY(mouseEvent), 0, 0, 0, null, null);
+ }
+ }
+
+ private void addNewState(final MouseEvent mouseEvent, final EnumMap<MouseButton, ButtonState> enumMap,
+ final Multiset<MouseButton> clicks) {
+ final MouseState newState = new MouseState(mouseEvent.getX(), getArdor3DY(mouseEvent), getDX(mouseEvent),
+ getDY(mouseEvent), (mouseEvent instanceof MouseWheelEvent ? ((MouseWheelEvent) mouseEvent)
+ .getWheelRotation() : 0), enumMap, clicks);
+
+ synchronized (AwtMouseWrapper.this) {
+ _upcomingEvents.add(newState);
+ }
+ _lastState = newState;
+ }
+
+ private int getDX(final MouseEvent e) {
+ return e.getX() - _lastState.getX();
+ }
+
+ private int getDY(final MouseEvent e) {
+ return getArdor3DY(e) - _lastState.getY();
+ }
+
+ /**
+ * @param e
+ * our mouseEvent
+ * @return the Y coordinate of the event, flipped relative to the component since we expect an origin in the lower
+ * left corner.
+ */
+ private int getArdor3DY(final MouseEvent e) {
+ final int height = (_frame != null && _frame.getComponentCount() > 0) ? _frame.getComponent(0).getHeight()
+ : _component.getHeight();
+ return height - e.getY();
+ }
+
+ private void setStateForButton(final MouseEvent e, final EnumMap<MouseButton, ButtonState> buttons,
+ final ButtonState buttonState) {
+ final MouseButton button = getButtonForEvent(e);
+ buttons.put(button, buttonState);
+ }
+
+ private MouseButton getButtonForEvent(final MouseEvent e) {
+ MouseButton button;
+ switch (e.getButton()) {
+ case MouseEvent.BUTTON1:
+ button = MouseButton.LEFT;
+ break;
+ case MouseEvent.BUTTON2:
+ button = MouseButton.MIDDLE;
+ break;
+ case MouseEvent.BUTTON3:
+ button = MouseButton.RIGHT;
+ break;
+ default:
+ throw new RuntimeException("unknown button: " + e.getButton());
+ }
+ return button;
+ }
+
+ private class AwtMouseIterator extends AbstractIterator<MouseState> implements PeekingIterator<MouseState> {
+ @Override
+ protected MouseState computeNext() {
+ synchronized (AwtMouseWrapper.this) {
+ if (_upcomingEvents.isEmpty()) {
+ return endOfData();
+ }
+
+ return _upcomingEvents.poll();
+ }
+
+ }
+ }
+
+ // -- The following interface methods are not used. --
+
+ public synchronized void mouseClicked(final MouseEvent e) {
+ // Yes, we could use the click count here, but in the interests of this working the same way as SWT and Native,
+ // we
+ // will do it the same way they do it.
+ if (_consumeEvents) {
+ e.consume();
+ }
+ }
+
+ public synchronized void mouseEntered(final MouseEvent e) {
+ // ignore this
+ if (_consumeEvents) {
+ e.consume();
+ }
+ }
+
+ public synchronized void mouseExited(final MouseEvent e) {
+ // ignore this
+ if (_consumeEvents) {
+ e.consume();
+ }
+ }
+
+ public boolean isConsumeEvents() {
+ return _consumeEvents;
+ }
+
+ public void setConsumeEvents(final boolean consumeEvents) {
+ _consumeEvents = consumeEvents;
+ }
+}