lwjgl/src/java/org/lwjgl/opengl/AWTGLCanvas.java

415 lines
12 KiB
Java

/*
* Copyright (c) 2002-2008 LWJGL Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'LWJGL' nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.lwjgl.opengl;
import org.lwjgl.LWJGLException;
import org.lwjgl.LWJGLUtil;
import org.lwjgl.PointerBuffer;
import org.lwjgl.Sys;
import java.awt.*;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import static org.lwjgl.opengl.GL11.*;
/**
* <p/>
* An AWT rendering context.
* <p/>
*
* @author $Author$
* $Id$
* @version $Revision$
*/
public class AWTGLCanvas extends Canvas implements DrawableLWJGL, ComponentListener, HierarchyListener {
private static final long serialVersionUID = 1L;
private static final AWTCanvasImplementation implementation;
private boolean update_context;
private Object SYNC_LOCK = new Object();
/** The requested pixel format */
private final PixelFormat pixel_format;
/** The drawable to share context with */
private final Drawable drawable;
/** The ContextAttribs to use when creating the context */
private final ContextAttribs attribs;
/** Context handle */
private PeerInfo peer_info;
private ContextGL context;
/**
* re-entry counter for support for re-entrant
* redrawing in paint(). It happens when using dialog boxes.
*/
private int reentry_count;
/** Tracks whether initGL() needs to be called */
private boolean first_run;
static {
Sys.initialize();
implementation = createImplementation();
}
static AWTCanvasImplementation createImplementation() {
switch ( LWJGLUtil.getPlatform() ) {
case LWJGLUtil.PLATFORM_LINUX:
return new LinuxCanvasImplementation();
case LWJGLUtil.PLATFORM_WINDOWS:
return new WindowsCanvasImplementation();
case LWJGLUtil.PLATFORM_MACOSX:
return new MacOSXCanvasImplementation();
default:
throw new IllegalStateException("Unsupported platform");
}
}
private void setUpdate() {
synchronized ( SYNC_LOCK ) {
update_context = true;
}
}
public void setPixelFormat(final PixelFormatLWJGL pf) throws LWJGLException {
throw new UnsupportedOperationException();
}
public void setPixelFormat(final PixelFormatLWJGL pf, final ContextAttribs attribs) throws LWJGLException {
throw new UnsupportedOperationException();
}
public PixelFormatLWJGL getPixelFormat() {
return pixel_format;
}
/** This method should only be called internally. */
public ContextGL getContext() {
return context;
}
/** This method should only be called internally. */
public ContextGL createSharedContext() throws LWJGLException {
synchronized ( SYNC_LOCK ) {
if ( context == null ) throw new IllegalStateException("Canvas not yet displayable");
return new ContextGL(peer_info, context.getContextAttribs(), context);
}
}
public void checkGLError() {
Util.checkGLError();
}
public void initContext(final float r, final float g, final float b) {
// set background clear color
glClearColor(r, g, b, 0.0f);
// Clear window to avoid the desktop "showing through"
glClear(GL_COLOR_BUFFER_BIT);
}
/** Constructor using the default PixelFormat. */
public AWTGLCanvas() throws LWJGLException {
this(new PixelFormat());
}
/**
* Create an AWTGLCanvas with the requested PixelFormat on the default GraphicsDevice.
*
* @param pixel_format The desired pixel format. May not be null
*/
public AWTGLCanvas(PixelFormat pixel_format) throws LWJGLException {
this(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(), pixel_format);
}
/**
* Create an AWTGLCanvas with the requested PixelFormat on the default GraphicsDevice.
*
* @param device the device to create the canvas on.
* @param pixel_format The desired pixel format. May not be null
*/
public AWTGLCanvas(GraphicsDevice device, PixelFormat pixel_format) throws LWJGLException {
this(device, pixel_format, null);
}
/**
* Create an AWTGLCanvas with the requested PixelFormat on the specified GraphicsDevice.
*
* @param device the device to create the canvas on.
* @param pixel_format The desired pixel format. May not be null
* @param drawable The Drawable to share context with
*/
public AWTGLCanvas(GraphicsDevice device, PixelFormat pixel_format, Drawable drawable) throws LWJGLException {
this(device, pixel_format, drawable, null);
}
/**
* Create an AWTGLCanvas with the requested PixelFormat on the specified GraphicsDevice.
*
* @param device the device to create the canvas on.
* @param pixel_format The desired pixel format. May not be null
* @param drawable The Drawable to share context with
* @param attribs The ContextAttribs to use when creating the context. (optional, may be null)
*/
public AWTGLCanvas(GraphicsDevice device, PixelFormat pixel_format, Drawable drawable, ContextAttribs attribs) throws LWJGLException {
super(implementation.findConfiguration(device, pixel_format));
if ( pixel_format == null )
throw new NullPointerException("Pixel format must be non-null");
addHierarchyListener(this);
addComponentListener(this);
this.drawable = drawable;
this.pixel_format = pixel_format;
this.attribs = attribs;
}
/* (non-Javadoc)
* @see java.awt.Canvas#addNotify()
*/
public void addNotify() {
super.addNotify();
}
/* (non-Javadoc)
* @see java.awt.Component#removeNotify()
*/
public void removeNotify() {
synchronized ( SYNC_LOCK ) {
destroy();
super.removeNotify();
}
}
/** Set swap interval. */
public void setSwapInterval(int swap_interval) throws LWJGLException {
synchronized ( SYNC_LOCK ) {
if ( context == null )
throw new IllegalStateException("Canvas not yet displayable");
ContextGL.setSwapInterval(swap_interval);
}
}
/** Enable vsync */
public void setVSyncEnabled(boolean enabled) throws LWJGLException {
setSwapInterval(enabled ? 1 : 0);
}
/** Swap the canvas' buffer */
public void swapBuffers() throws LWJGLException {
synchronized ( SYNC_LOCK ) {
if ( context == null )
throw new IllegalStateException("Canvas not yet displayable");
ContextGL.swapBuffers();
}
}
public boolean isCurrent() throws LWJGLException {
synchronized ( SYNC_LOCK ) {
if ( context == null ) throw new IllegalStateException("Canvas not yet displayable");
return context.isCurrent();
}
}
/**
* Make the canvas' context current. It is highly recommended that the context
* is only made current inside the AWT thread (for example in an overridden paintGL()).
*/
public void makeCurrent() throws LWJGLException {
synchronized ( SYNC_LOCK ) {
if ( context == null )
throw new IllegalStateException("Canvas not yet displayable");
context.makeCurrent();
}
}
public void releaseContext() throws LWJGLException {
synchronized ( SYNC_LOCK ) {
if ( context == null )
throw new IllegalStateException("Canvas not yet displayable");
if ( context.isCurrent() )
context.releaseCurrent();
}
}
/** Destroy the OpenGL context. This happens when the component becomes undisplayable */
public final void destroy() {
synchronized ( SYNC_LOCK ) {
try {
if ( context != null ) {
context.forceDestroy();
context = null;
reentry_count = 0;
peer_info.destroy();
peer_info = null;
}
} catch (LWJGLException e) {
throw new RuntimeException(e);
}
}
}
public final void setCLSharingProperties(final PointerBuffer properties) throws LWJGLException {
synchronized ( SYNC_LOCK ) {
if ( context == null )
throw new IllegalStateException("Canvas not yet displayable");
context.setCLSharingProperties(properties);
}
}
/**
* Override this to do initialising of the context.
* It will be called once from paint(), immediately after
* the context is created and made current.
*/
protected void initGL() {
}
/** Override this to do painting */
protected void paintGL() {
}
/**
* The default paint() operation makes the context current and calls paintGL() which should
* be overridden to do GL operations.
*/
public final void paint(Graphics g) {
LWJGLException exception = null;
synchronized ( SYNC_LOCK ) {
if ( !isDisplayable() )
return;
try {
if ( peer_info == null ) {
this.peer_info = implementation.createPeerInfo(this, pixel_format, attribs);
}
peer_info.lockAndGetHandle();
try {
if ( context == null ) {
this.context = new ContextGL(peer_info, attribs, drawable != null ? (ContextGL)((DrawableLWJGL)drawable).getContext() : null);
first_run = true;
}
if ( reentry_count == 0 )
context.makeCurrent();
reentry_count++;
try {
if ( update_context ) {
context.update();
update_context = false;
}
if ( first_run ) {
first_run = false;
initGL();
}
paintGL();
} finally {
reentry_count--;
if ( reentry_count == 0 )
context.releaseCurrent();
}
} finally {
peer_info.unlock();
}
} catch (LWJGLException e) {
exception = e;
}
}
if ( exception != null )
exceptionOccurred(exception);
}
/**
* This method will be called if an unhandled LWJGLException occurs in paint().
* Override this method to be notified of this.
*
* @param exception The exception that occurred.
*/
protected void exceptionOccurred(LWJGLException exception) {
LWJGLUtil.log("Unhandled exception occurred, skipping paint(): " + exception);
}
/** override update to avoid clearing */
public void update(Graphics g) {
paint(g);
}
public void componentShown(ComponentEvent e) {
}
public void componentHidden(ComponentEvent e) {
}
public void componentResized(ComponentEvent e) {
setUpdate();
}
public void componentMoved(ComponentEvent e) {
setUpdate();
}
public void setLocation(int x, int y) {
super.setLocation(x, y);
setUpdate();
}
public void setLocation(Point p) {
super.setLocation(p);
setUpdate();
}
public void setSize(Dimension d) {
super.setSize(d);
setUpdate();
}
public void setSize(int width, int height) {
super.setSize(width, height);
setUpdate();
}
public void setBounds(int x, int y, int width, int height) {
super.setBounds(x, y, width, height);
setUpdate();
}
public void hierarchyChanged(HierarchyEvent e) {
setUpdate();
}
}