/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2014-15 The Processing Foundation
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, version 2.1.
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., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.awt;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Label;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.*;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PGraphics;
import processing.core.PImage;
import processing.core.PSurfaceNone;
import processing.event.KeyEvent;
import processing.event.MouseEvent;
public class PSurfaceAWT extends PSurfaceNone {
GraphicsDevice displayDevice;
// used for canvas to determine whether resizable or not
// boolean resizable; // default is false
// Internally, we know it's always a JFrame (not just a Frame)
// JFrame frame;
// Trying Frame again with a11 to see if this avoids some Swing nastiness.
// In the past, AWT Frames caused some problems on Windows and Linux,
// but those may not be a problem for our reworked PSurfaceAWT class.
Frame frame;
// Note that x and y may not be zero, depending on the display configuration
Rectangle screenRect;
// Used for resizing, at least on Windows insets size changes when
// frame.setResizable() is called, and in resize listener we need
// to know what size the window was before.
Insets currentInsets = new Insets(0, 0, 0, 0);
// 3.0a5 didn't use strategy, and active was shut off during init() w/ retina
// boolean useStrategy = true;
Canvas canvas;
// Component canvas;
// PGraphics graphics; // moved to PSurfaceNone
int sketchWidth;
int sketchHeight;
public PSurfaceAWT(PGraphics graphics) {
//this.graphics = graphics;
super(graphics);
/*
if (checkRetina()) {
// System.out.println("retina in use");
// The active-mode rendering seems to be 2x slower, so disable it
// with retina. On a non-retina machine, however, useActive seems
// the only (or best) way to handle the rendering.
// useActive = false;
// canvas = new JPanel(true) {
// @Override
// public void paint(Graphics screen) {
//// if (!sketch.insideDraw) {
// screen.drawImage(PSurfaceAWT.this.graphics.image, 0, 0, sketchWidth, sketchHeight, null);
//// }
// }
// };
// Under 1.8 and the current 3.0a6 threading regime, active mode w/o
// strategy is far faster, but perhaps only because it's blitting with
// flicker--pushing pixels out before the screen has finished rendering.
// useStrategy = false;
}
*/
canvas = new SmoothCanvas();
// if (useStrategy) {
//canvas.setIgnoreRepaint(true);
// }
// Pass tab key to the sketch, rather than moving between components
canvas.setFocusTraversalKeysEnabled(false);
canvas.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
if (!sketch.isLooping()) {
// make sure this is a real resize event, not just initial setup
// https://github.com/processing/processing/issues/3310
Dimension canvasSize = canvas.getSize();
if (canvasSize.width != sketch.sketchWidth() ||
canvasSize.height != sketch.sketchHeight()) {
sketch.redraw();
}
}
}
});
addListeners();
}
// /**
// * Handle grabbing the focus on startup. Other renderers can override this
// * if handling needs to be different. For the AWT, the request is invoked
// * later on the EDT. Other implementations may not require that, so the
// * invokeLater() happens in here rather than requiring the caller to wrap it.
// */
// @Override
// void requestFocus() {
//// System.out.println("requesFocus() outer " + EventQueue.isDispatchThread());
// // for 2.0a6, moving this request to the EDT
// EventQueue.invokeLater(new Runnable() {
// public void run() {
// // Call the request focus event once the image is sure to be on
// // screen and the component is valid. The OpenGL renderer will
// // request focus for its canvas inside beginDraw().
// // http://java.sun.com/j2se/1.4.2/docs/api/java/awt/doc-files/FocusSpec.html
// // Disabling for 0185, because it causes an assertion failure on OS X
// // http://code.google.com/p/processing/issues/detail?id=258
// // requestFocus();
//
// // Changing to this version for 0187
// // http://code.google.com/p/processing/issues/detail?id=279
// //requestFocusInWindow();
//
// // For 3.0, just call this directly on the Canvas object
// if (canvas != null) {
// //System.out.println("requesting focus " + EventQueue.isDispatchThread());
// //System.out.println("requesting focus " + frame.isVisible());
// //canvas.requestFocusInWindow();
// canvas.requestFocus();
// }
// }
// });
// }
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
public class SmoothCanvas extends Canvas {
private Dimension oldSize = new Dimension(0, 0);
private Dimension newSize = new Dimension(0, 0);
// Turns out getParent() returns a JPanel on a JFrame. Yech.
public Frame getFrame() {
return frame;
}
@Override
public Dimension getPreferredSize() {
return new Dimension(sketchWidth, sketchHeight);
}
@Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
@Override
public Dimension getMaximumSize() {
//return resizable ? super.getMaximumSize() : getPreferredSize();
return frame.isResizable() ? super.getMaximumSize() : getPreferredSize();
}
@Override
public void validate() {
super.validate();
newSize.width = getWidth();
newSize.height = getHeight();
// if (oldSize.equals(newSize)) {
//// System.out.println("validate() return " + oldSize);
// return;
// } else {
if (!oldSize.equals(newSize)) {
// System.out.println("validate() render old=" + oldSize + " -> new=" + newSize);
oldSize = newSize;
sketch.setSize(newSize.width, newSize.height);
// try {
render();
// } catch (IllegalStateException ise) {
// System.out.println(ise.getMessage());
// }
}
}
@Override
public void update(Graphics g) {
// System.out.println("updating");
paint(g);
}
@Override
public void paint(Graphics screen) {
// System.out.println("painting");
// if (useStrategy) {
render();
/*
if (graphics != null) {
System.out.println("drawing to screen " + canvas);
screen.drawImage(graphics.image, 0, 0, sketchWidth, sketchHeight, null);
}
*/
// } else {
//// new Exception("painting").printStackTrace(System.out);
//// if (graphics.image != null) { // && !sketch.insideDraw) {
// if (onscreen != null) {
//// synchronized (graphics.image) {
// // Needs the width/height to be set so that retina images are properly scaled down
//// screen.drawImage(graphics.image, 0, 0, sketchWidth, sketchHeight, null);
// synchronized (offscreenLock) {
// screen.drawImage(onscreen, 0, 0, sketchWidth, sketchHeight, null);
// }
// }
// }
}
}
/*
@Override
public void addNotify() {
// System.out.println("adding notify");
super.addNotify();
// prior to Java 7 on OS X, this no longer works [121222]
// createBufferStrategy(2);
}
*/
synchronized protected void render() {
if (canvas.isDisplayable() &&
graphics.image != null) {
if (canvas.getBufferStrategy() == null) {
canvas.createBufferStrategy(2);
}
BufferStrategy strategy = canvas.getBufferStrategy();
if (strategy != null) {
// Render single frame
// try {
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
Graphics2D draw = (Graphics2D) strategy.getDrawGraphics();
// draw to width/height, since this may be a 2x image
draw.drawImage(graphics.image, 0, 0, sketchWidth, sketchHeight, null);
draw.dispose();
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
}
}
/*
protected void blit() {
// Other folks that call render() (i.e. paint()) are already on the EDT.
// We need to be using the EDT since we're messing with the Canvas
// object and BufferStrategy and friends.
//EventQueue.invokeLater(new Runnable() {
//public void run() {
//((SmoothCanvas) canvas).render();
//}
//});
if (useStrategy) {
// Not necessary to be on the EDT to update BufferStrategy
//((SmoothCanvas) canvas).render();
render();
} else {
if (graphics.image != null) {
BufferedImage graphicsImage = (BufferedImage) graphics.image;
if (offscreen == null ||
offscreen.getWidth() != graphicsImage.getWidth() ||
offscreen.getHeight() != graphicsImage.getHeight()) {
System.out.println("creating new image");
offscreen = (BufferedImage)
canvas.createImage(graphicsImage.getWidth(),
graphicsImage.getHeight());
// off = offscreen.getGraphics();
}
// synchronized (offscreen) {
Graphics2D off = (Graphics2D) offscreen.getGraphics();
// off.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1));
off.drawImage(graphicsImage, 0, 0, null);
// }
off.dispose();
synchronized (offscreenLock) {
BufferedImage temp = onscreen;
onscreen = offscreen;
offscreen = temp;
}
canvas.repaint();
}
}
}
*/
// what needs to happen here?
@Override
public void initOffscreen(PApplet sketch) {
this.sketch = sketch;
}
/*
public Frame initOffscreen() {
Frame dummy = new Frame();
dummy.pack(); // get legit AWT graphics
// but don't show it
return dummy;
}
*/
/*
@Override
public Component initComponent(PApplet sketch) {
this.sketch = sketch;
// needed for getPreferredSize() et al
sketchWidth = sketch.sketchWidth();
sketchHeight = sketch.sketchHeight();
return canvas;
}
*/
@Override
public void initFrame(final PApplet sketch) {/*, int backgroundColor,
int deviceIndex, boolean fullScreen, boolean spanDisplays) {*/
this.sketch = sketch;
GraphicsEnvironment environment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
int displayNum = sketch.sketchDisplay();
// System.out.println("display from sketch is " + displayNum);
if (displayNum > 0) { // if -1, use the default device
GraphicsDevice[] devices = environment.getScreenDevices();
if (displayNum