As I’m quite sure that this is a case of “stupid me” I’m posting the example code here. For some reasons, this does work even with the original Animator only sometimes (but it worked all the time in the original application). This seems to be some kind of timing issue, because sometimes it works, sometimes I get the second canvas running, sometimes I get the first frame of the second canvas. Thanks in advance for your help…
package com.dreivier.test;
import java.awt.EventQueue;
import java.awt.Frame;
import net.java.games.jogl.Animator;
import net.java.games.jogl.GL;
import net.java.games.jogl.GLCanvas;
import net.java.games.jogl.GLCapabilities;
import net.java.games.jogl.GLDrawable;
import net.java.games.jogl.GLDrawableFactory;
import net.java.games.jogl.GLEventListener;
import net.java.games.jogl.GLException;
import net.java.games.jogl.GLU;
import net.java.games.jogl.impl.SingleThreadedWorkaround;
public class AnimatorTest {
public AnimatorTest() {
}
public static void main(String[] args) {
GLDrawableFactory factory = GLDrawableFactory.getFactory();
Frame frame1 = new Frame("screen 1");
GLCanvas canvas1 = factory.createGLCanvas(new GLCapabilities());
canvas1.addGLEventListener(new MyRenderer());
frame1.add(canvas1);
frame1.setSize(640, 480);
frame1.show();
Frame frame2 = new Frame("screen 2");
GLCanvas canvas2 = factory.createGLCanvas(new GLCapabilities());
canvas2.addGLEventListener(new MyRenderer());
frame2.add(canvas2);
frame2.setSize(640, 480);
frame2.show();
MyAnimator ani1 = new MyAnimator(canvas1);
ani1.start();
MyAnimator ani2 = new MyAnimator(canvas2);
ani2.start();
}
}
class MyAnimator {
private GLDrawable drawable;
private Runnable runnable;
private Thread thread;
private boolean shouldStop;
/** Creates a new Animator for a particular drawable. */
public MyAnimator(GLDrawable drawable) {
this.drawable = drawable;
// Workaround for NVidia driver bug 80174
if (drawable instanceof GLCanvas) {
//((GLCanvas) drawable).willSetRenderingThread();
}
}
/** Starts this animator. */
public synchronized void start() {
if (thread != null) {
throw new GLException("Already started");
}
if (runnable == null) {
runnable = new Runnable() {
public void run() {
boolean noException = false;
try {
// Try to get OpenGL context optimization since we know
// we
// will be rendering this one drawable continually from
// this thread; make the context current once instead of
// making it current and freeing it each frame.
drawable.setRenderingThread(Thread.currentThread());
// Since setRenderingThread is currently advisory
// (because
// of the poor JAWT implementation in the Motif AWT,
// which
// performs excessive locking) we also prevent
// repaint(),
// which is called from the AWT thread, from having an
// effect for better multithreading behavior. This call
// is
// not strictly necessary, but if end users write their
// own animation loops which update multiple drawables
// per
// tick then it may be necessary to enforce the order of
// updates.
drawable.setNoAutoRedrawMode(true);
while (!shouldStop) {
noException = false;
drawable.display();
noException = true;
}
} finally {
shouldStop = false;
drawable.setNoAutoRedrawMode(false);
try {
// The surface is already unlocked and rendering
// thread is already null if an exception occurred
// during display(), so don't disable the rendering
// thread again.
if (noException) {
// Destruction of the underlying GLContext may
// have
// disabled the setRenderingThread optimization
// out
// from under us
if (drawable.getRenderingThread() != null) {
drawable.setRenderingThread(null);
}
}
} finally {
synchronized (MyAnimator.this) {
thread = null;
MyAnimator.this.notify();
}
}
}
}
};
}
thread = new Thread(runnable);
thread.start();
}
/**
* Stops this animator, blocking until the animation thread has finished.
*/
public synchronized void stop() {
shouldStop = true;
// 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 ((Thread.currentThread() == thread)
|| (SingleThreadedWorkaround.doWorkaround() && EventQueue.isDispatchThread())) {
return;
}
while (shouldStop && thread != null) {
try {
wait();
} catch (InterruptedException ie) {
}
}
}
}
class MyRenderer implements GLEventListener {
private int width;
private int height;
private boolean b;
public MyRenderer() {
}
public void init(GLDrawable drawable) {
}
public void reshape(GLDrawable drawable, int x, int y, int width, int height) {
this.width = width;
this.height = height;
}
public void display(GLDrawable drawable) {
final GL gl = drawable.getGL();
final GLU glu = drawable.getGLU();
gl.glClear(GL.GL_COLOR_BUFFER_BIT);
gl.glLoadIdentity();
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluOrtho2D(0.0, width, 0.0, height);
gl.glPushMatrix();
if (b)
gl.glColor4f(1.0f, 0.0f, 0.0f, 1f);
else
gl.glColor4f(0.0f, 1.0f, 0.0f, 1f);
b = !b;
gl.glBegin(GL.GL_QUADS);
gl.glVertex2i(0, 0);
gl.glVertex2i(0, 12);
gl.glVertex2i(12, 12);
gl.glVertex2i(12, 0);
gl.glEnd();
gl.glPopMatrix();
gl.glFlush();
gl.glFinish();
}
public void displayChanged(GLDrawable drawable, boolean modeChanged, boolean deviceChanged) {
}
}