/*
* Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
* Copyright (c) 2010 JogAmp Community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution 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 Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
* MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
* ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
* DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
* SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed or intended for use
* in the design, construction, operation or maintenance of any nuclear
* facility.
*
* Sun gratefully acknowledges that this software was originally authored
* and developed by Kenneth Bradley Russell and Christopher John Kline.
*/
package com.jogamp.opengl.util;
import javax.media.opengl.GLAnimatorControl;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLException;
import javax.media.opengl.GLProfile;
import com.jogamp.opengl.impl.Debug;
import java.util.*;
/**
An Animator can be attached to one or more {@link
GLAutoDrawable}s to drive their display() methods in a loop.
The Animator class creates a background thread in which the
calls to display()
are performed. After each drawable
has been redrawn, a brief pause is performed to avoid swamping the
CPU, unless {@link #setRunAsFastAsPossible} has been called.
*/
public class Animator extends AnimatorBase {
protected ThreadGroup threadGroup;
private Runnable runnable;
private boolean runAsFastAsPossible;
protected volatile boolean isAnimating;
protected volatile boolean shouldPause;
protected volatile boolean shouldStop;
public Animator() {
super();
if(DEBUG) {
System.err.println("Animator created");
}
}
public Animator(ThreadGroup tg) {
super();
threadGroup = tg;
if(DEBUG) {
System.err.println("Animator created, ThreadGroup: "+threadGroup);
}
}
/** Creates a new Animator for a particular drawable. */
public Animator(GLAutoDrawable drawable) {
super();
add(drawable);
}
/** Creates a new Animator for a particular drawable. */
public Animator(ThreadGroup tg, GLAutoDrawable drawable) {
this(tg);
add(drawable);
}
protected String getBaseName(String prefix) {
return prefix + "Animator" ;
}
/**
* Sets a flag in this Animator indicating that it is to run as
* fast as possible. By default there is a brief pause in the
* animation loop which prevents the CPU from getting swamped.
* This method may not have an effect on subclasses.
*/
public final void setRunAsFastAsPossible(boolean runFast) {
runAsFastAsPossible = runFast;
}
class MainLoop implements Runnable {
public void run() {
try {
if(DEBUG) {
System.err.println("Animator started: "+Thread.currentThread());
}
startTime = System.currentTimeMillis();
curTime = startTime;
totalFrames = 0;
synchronized (Animator.this) {
isAnimating = true;
Animator.this.notifyAll();
}
while (!shouldStop) {
// Don't consume CPU unless there is work to be done and not paused
if ( !shouldStop && ( shouldPause || drawables.size() == 0 ) ) {
synchronized (Animator.this) {
boolean wasPaused = false;
while ( !shouldStop && ( shouldPause || drawables.size() == 0 ) ) {
if(DEBUG) {
System.err.println("Animator paused: "+Thread.currentThread());
}
isAnimating = false;
wasPaused = true;
Animator.this.notifyAll();
try {
Animator.this.wait();
} catch (InterruptedException e) {
}
}
if ( wasPaused ) {
startTime = System.currentTimeMillis();
curTime = startTime;
totalFrames = 0;
if(DEBUG) {
System.err.println("Animator resumed: "+Thread.currentThread());
}
isAnimating = true;
Animator.this.notifyAll();
}
}
}
if ( !shouldStop ) {
display();
if (!runAsFastAsPossible) {
// Avoid swamping the CPU
Thread.yield();
}
}
}
} finally {
synchronized (Animator.this) {
if(DEBUG) {
System.err.println("Animator stopped: "+Thread.currentThread());
}
shouldStop = false;
shouldPause = false;
thread = null;
isAnimating = false;
Animator.this.notifyAll();
}
}
}
}
public final synchronized boolean isStarted() {
return (thread != null);
}
public final synchronized boolean isAnimating() {
return (thread != null) && isAnimating;
}
public final synchronized boolean isPaused() {
return (thread != null) && shouldPause;
}
public synchronized void start() {
boolean started = null != thread;
if ( started || isAnimating ) {
throw new GLException("Already running (started "+started+" (false), animating "+isAnimating+" (false))");
}
if (runnable == null) {
runnable = new MainLoop();
}
resetCounter();
int id;
String threadName = Thread.currentThread().getName()+"-"+baseName;
if(null==threadGroup) {
thread = new Thread(runnable, threadName);
} else {
thread = new Thread(threadGroup, runnable, threadName);
}
thread.start();
if (!impl.skipWaitForCompletion(thread)) {
while (!isAnimating && thread != null) {
try {
wait();
} catch (InterruptedException ie) {
}
}
}
}
public synchronized void stop() {
boolean started = null != thread;
if ( !started || !isAnimating ) {
throw new GLException("Not running (started "+started+" (true), animating "+isAnimating+" (true) )");
}
shouldStop = true;
notifyAll();
// It's hard to tell whether the thread which calls stop() has
// dependencies on the Animator's internal thread. Currently we
// use a couple of heuristics to determine whether we should do
// the blocking wait().
if (!impl.skipWaitForCompletion(thread)) {
while (isAnimating && thread != null) {
try {
wait();
} catch (InterruptedException ie) {
}
}
}
}
public synchronized void pause() {
boolean started = null != thread;
if ( !started || !isAnimating || shouldPause ) {
throw new GLException("Invalid state (started "+started+" (true), animating "+isAnimating+" (true), paused "+shouldPause+" (false) )");
}
shouldPause = true;
notifyAll();
if (!impl.skipWaitForCompletion(thread)) {
while (isAnimating && thread != null) {
try {
wait();
} catch (InterruptedException ie) {
}
}
}
}
public synchronized void resume() {
boolean started = null != thread;
if ( !started || !shouldPause ) {
throw new GLException("Invalid state (started "+started+" (true), paused "+shouldPause+" (true) )");
}
shouldPause = false;
notifyAll();
if (!impl.skipWaitForCompletion(thread)) {
while (!isAnimating && thread != null) {
try {
wait();
} catch (InterruptedException ie) {
}
}
}
resetCounter();
}
protected final boolean getShouldPause() {
return shouldPause;
}
protected final boolean getShouldStop() {
return shouldStop;
}
}