@BeforeClass
  public static void beforeClass() throws Exception {

    final GLCanvas canvas = new GLCanvas();
    canvas.addGLEventListener(new RedSquareES2());
    new AWTMouseAdapter(_testMouseListener, canvas).addTo(canvas);

    _testFrame = new JFrame("Event Modifier Test AWTCanvas");
    _testFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

    SwingUtilities.invokeAndWait(
        new Runnable() {
          public void run() {
            _testFrame.getContentPane().add(canvas);
            _testFrame.setBounds(TEST_FRAME_X, TEST_FRAME_Y, TEST_FRAME_WIDTH, TEST_FRAME_HEIGHT);
            _testFrame.setVisible(true);
          }
        });
    Assert.assertEquals(true, AWTRobotUtil.waitForVisible(_testFrame, true));
    Assert.assertTrue(AWTRobotUtil.waitForVisible(canvas, true));
    Assert.assertTrue(AWTRobotUtil.waitForRealized(canvas, true));

    AWTRobotUtil.assertRequestFocusAndWait(null, canvas, canvas, null, null); // programmatic
    Assert.assertNotNull(_robot);
    AWTRobotUtil.requestFocus(
        _robot, canvas,
        false); // within unit framework, prev. tests (TestFocus02SwingAWTRobot) 'confuses' Windows
                // keyboard input
  }
  protected GLJPanel newGLJPanel(
      final JFrame frame,
      final GLCapabilities caps,
      final FPSAnimator animator,
      final SnapshotGLEventListener snap)
      throws AWTException, InterruptedException, InvocationTargetException {
    final GLJPanel glJPanel = new GLJPanel(caps);
    Assert.assertNotNull(glJPanel);
    glJPanel.setSkipGLOrientationVerticalFlip(skipGLOrientationVerticalFlip);
    glJPanel.setMinimumSize(wsize);
    glJPanel.setPreferredSize(wsize);
    glJPanel.setSize(wsize);
    glJPanel.setSurfaceScale(reqSurfacePixelScale);
    {
      final GLEventListener demo = createDemo(caps);
      if (null != demo) {
        glJPanel.addGLEventListener(demo);
      }
    }
    if (null != snap) {
      glJPanel.addGLEventListener(snap);
    }
    glJPanel.addGLEventListener(
        new GLEventListener() {
          @Override
          public void init(final GLAutoDrawable drawable) {}

          @Override
          public void dispose(final GLAutoDrawable drawable) {}

          @Override
          public void display(final GLAutoDrawable drawable) {}

          @Override
          public void reshape(
              final GLAutoDrawable drawable,
              final int x,
              final int y,
              final int width,
              final int height) {
            setTitle(frame, glJPanel, caps);
          }
        });
    setTitle(frame, glJPanel, caps);

    frame.addComponentListener(
        new ComponentListener() {
          @Override
          public void componentResized(final ComponentEvent e) {
            setTitle(frame, glJPanel, caps);
          }

          @Override
          public void componentMoved(final ComponentEvent e) {
            setTitle(frame, glJPanel, caps);
          }

          @Override
          public void componentShown(final ComponentEvent e) {}

          @Override
          public void componentHidden(final ComponentEvent e) {}
        });

    if (SwingUtilities.isEventDispatchThread()) {
      frame.getContentPane().add(glJPanel, BorderLayout.CENTER);
      frame.getContentPane().validate();
      frame.pack();
      frame.setVisible(true);
    } else {
      SwingUtilities.invokeAndWait(
          new Runnable() {
            public void run() {
              frame.getContentPane().add(glJPanel, BorderLayout.CENTER);
              frame.getContentPane().validate();
              frame.pack();
              frame.setVisible(true);
            }
          });
      Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame, true));
      Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glJPanel, true));

      final float[] minSurfacePixelScale = glJPanel.getMinimumSurfaceScale(new float[2]);
      final float[] maxSurfacePixelScale = glJPanel.getMaximumSurfaceScale(new float[2]);
      final float[] valReqSurfacePixelScale = glJPanel.getRequestedSurfaceScale(new float[2]);
      final float[] hasSurfacePixelScale = glJPanel.getCurrentSurfaceScale(new float[2]);
      System.err.println(
          "HiDPI PixelScale: min "
              + minSurfacePixelScale[0]
              + "x"
              + minSurfacePixelScale[1]
              + ", max "
              + maxSurfacePixelScale[0]
              + "x"
              + maxSurfacePixelScale[1]
              + ", req "
              + reqSurfacePixelScale[0]
              + "x"
              + reqSurfacePixelScale[1]
              + " -> val "
              + valReqSurfacePixelScale[0]
              + "x"
              + valReqSurfacePixelScale[1]
              + " -> has "
              + hasSurfacePixelScale[0]
              + "x"
              + hasSurfacePixelScale[1]);
      setTitle(frame, glJPanel, caps);
    }

    if (null != animator) {
      animator.add(glJPanel);
      animator.setUpdateFPSFrames(60, System.err);
    }
    return glJPanel;
  }
  void doTest(final GLCapabilitiesImmutable reqGLCaps, final GLEventListener demo)
      throws InterruptedException {
    System.out.println("Requested  GL Caps: " + reqGLCaps);
    final GLDrawableFactory factory = GLDrawableFactory.getFactory(reqGLCaps.getGLProfile());
    final GLCapabilitiesImmutable expGLCaps =
        GLGraphicsConfigurationUtil.fixGLCapabilities(reqGLCaps, factory, null);
    System.out.println("Expected   GL Caps: " + expGLCaps);

    //
    // Create native OpenGL resources .. XGL/WGL/CGL ..
    // equivalent to GLAutoDrawable methods: setVisible(true)
    //
    final GLOffscreenAutoDrawable glad =
        factory.createOffscreenAutoDrawable(
            null, reqGLCaps, null, widthStep * szStep, heightStep * szStep);

    Assert.assertNotNull(glad);
    System.out.println(
        "Drawable    Pre-GL(0): "
            + glad.getClass().getName()
            + ", "
            + glad.getNativeSurface().getClass().getName());
    Assert.assertTrue(glad.isRealized());

    // Check caps of NativeWindow config w/o GL
    final CapabilitiesImmutable chosenCaps = glad.getChosenGLCapabilities();
    System.out.println("Drawable Caps Pre_GL : " + chosenCaps);
    Assert.assertNotNull(chosenCaps);
    Assert.assertTrue(chosenCaps.getGreenBits() > 4);
    Assert.assertTrue(chosenCaps.getBlueBits() > 4);
    Assert.assertTrue(chosenCaps.getRedBits() > 4);

    glad.display(); // force native context creation

    // Check caps of GLDrawable after realization
    final GLCapabilitiesImmutable chosenGLCaps = glad.getChosenGLCapabilities();
    System.out.println("Chosen     GL CTX (1): " + glad.getContext().getGLVersion());
    System.out.println("Chosen     GL Caps(1): " + chosenGLCaps);
    System.out.println(
        "Chosen     GL Caps(2): "
            + glad.getNativeSurface().getGraphicsConfiguration().getChosenCapabilities());

    Assert.assertNotNull(chosenGLCaps);
    Assert.assertTrue(chosenGLCaps.getGreenBits() > 4);
    Assert.assertTrue(chosenGLCaps.getBlueBits() > 4);
    Assert.assertTrue(chosenGLCaps.getRedBits() > 4);
    Assert.assertTrue(chosenGLCaps.getDepthBits() > 4);
    Assert.assertEquals(expGLCaps.isOnscreen(), chosenGLCaps.isOnscreen());
    Assert.assertEquals(expGLCaps.isFBO(), chosenGLCaps.isFBO());
    Assert.assertEquals(expGLCaps.isPBuffer(), chosenGLCaps.isPBuffer());
    Assert.assertEquals(expGLCaps.isBitmap(), chosenGLCaps.isBitmap());
    /**
     * Single/Double buffer cannot be checked since result may vary .. if(chosenGLCaps.isOnscreen()
     * || chosenGLCaps.isFBO()) { // dbl buffer may be disabled w/ offscreen pbuffer and bitmap
     * Assert.assertEquals(expGLCaps.getDoubleBuffered(), chosenGLCaps.getDoubleBuffered()); }
     */
    glad.addGLEventListener(demo);

    final SnapshotGLEventListener snapshotGLEventListener = new SnapshotGLEventListener();
    glad.addGLEventListener(snapshotGLEventListener);

    glad.display(); // initial resize/display

    // 1 - szStep = 2
    Assert.assertTrue(
        "Size not reached: Expected "
            + (widthStep * szStep)
            + "x"
            + (heightStep * szStep)
            + ", Is "
            + glad.getSurfaceWidth()
            + "x"
            + glad.getSurfaceHeight(),
        AWTRobotUtil.waitForSize(glad, widthStep * szStep, heightStep * szStep));
    snapshotGLEventListener.setMakeSnapshot();
    glad.display();

    // 2, 3 (resize + display)
    szStep = 1;
    glad.setSurfaceSize(widthStep * szStep, heightStep * szStep);
    Assert.assertTrue(
        "Size not reached: Expected "
            + (widthStep * szStep)
            + "x"
            + (heightStep * szStep)
            + ", Is "
            + glad.getSurfaceWidth()
            + "x"
            + glad.getSurfaceHeight(),
        AWTRobotUtil.waitForSize(glad, widthStep * szStep, heightStep * szStep));
    snapshotGLEventListener.setMakeSnapshot();
    glad.display();

    // 4, 5 (resize + display)
    szStep = 4;
    glad.setSurfaceSize(widthStep * szStep, heightStep * szStep);
    Assert.assertTrue(
        "Size not reached: Expected "
            + (widthStep * szStep)
            + "x"
            + (heightStep * szStep)
            + ", Is "
            + glad.getSurfaceWidth()
            + "x"
            + glad.getSurfaceHeight(),
        AWTRobotUtil.waitForSize(glad, widthStep * szStep, heightStep * szStep));
    snapshotGLEventListener.setMakeSnapshot();
    glad.display();

    Thread.sleep(50);

    glad.destroy();
    System.out.println("Fin Drawable: " + glad);
  }
  public void testImpl(final boolean keepTextureBound, final int texUnit)
      throws InterruptedException, IOException {
    final GLReadBufferUtil screenshot = new GLReadBufferUtil(true, false);
    GLProfile glp;
    if (GLProfile.isAvailable(GLProfile.GL2ES2)) {
      glp = GLProfile.getGL2ES2();
    } else {
      System.err.println(getSimpleTestName(".") + ": GLProfile n/a");
      return;
    }
    final GLCapabilities caps = new GLCapabilities(glp);

    final GLJPanel glc = new GLJPanel(caps);
    // final GLCanvas glc = new GLCanvas(caps);
    final Dimension glc_sz = new Dimension(640, 480);
    final Dimension glc_sz2 = new Dimension(800, 400);
    glc.setMinimumSize(glc_sz);
    glc.setPreferredSize(glc_sz);
    final JFrame frame = new JFrame("TestGLJPanelTextureStateAWT");
    Assert.assertNotNull(frame);
    frame.getContentPane().add(glc);

    final GLEventListener gle0;
    {
      final GearsES2 gle0sub = new GearsES2(0);
      // gle1sub.setClearBuffers(false);
      final TextureDraw02ES2ListenerFBO demo = new TextureDraw02ES2ListenerFBO(gle0sub, 1, texUnit);
      demo.setKeepTextureBound(keepTextureBound);
      demo.setClearBuffers(false);
      gle0 = demo;
    }

    final GLEventListener gle1;
    {
      final RedSquareES2 demo = new RedSquareES2(1);
      demo.setClearBuffers(false);
      gle1 = demo;
    }

    final boolean[] glelError = {false};

    glc.addGLEventListener(
        new GLEventListener() {
          int gle0X, gle0Y, gle0W, gle0H;
          int gle1X, gle1Y, gle1W, gle1H;
          int tX, tY, tW, tH;
          int shot = 0;

          void setupTex(GL gl) {
            // Note: FBObject uses diff defaults, i.e.: GL_NEAREST and GL_CLAMP_TO_EDGE
            if (keepTextureBound) {
              gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
              gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
              gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
              gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
            }
          }

          @Override
          public void init(GLAutoDrawable drawable) {
            // Initialize w/ arbitrary values !
            GL2ES2 gl = drawable.getGL().getGL2ES2();
            gl.glActiveTexture(GL.GL_TEXTURE0 + 1);
            gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
            gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
            gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
            gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
            gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
            gl.glActiveTexture(GL.GL_TEXTURE0 + 0);
            gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
            gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
            gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
            gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
            gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);

            gle0.init(drawable);
            gle1.init(drawable);
            setupTex(gl);
          }

          @Override
          public void dispose(GLAutoDrawable drawable) {
            gle0.dispose(drawable);
            gle1.dispose(drawable);
          }

          @Override
          public void display(GLAutoDrawable drawable) {
            GL2ES2 gl = drawable.getGL().getGL2ES2();

            gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

            // test viewport
            {
              final int[] viewport = new int[] {0, 0, 0, 0};
              gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);
              if (gle1X != viewport[0]
                  || gle1Y != viewport[1]
                  || gle1W != viewport[2]
                  || gle1H != viewport[3]) {
                final String msg =
                    "Expected "
                        + gle1X
                        + "/"
                        + gle1Y
                        + " "
                        + gle1W
                        + "x"
                        + gle1H
                        + ", actual "
                        + viewport[0]
                        + "/"
                        + viewport[1]
                        + " "
                        + viewport[2]
                        + "x"
                        + viewport[3];
                Assert.assertTrue("Viewport not restored: " + msg, false);
                glelError[0] = true;
              }
            }

            gl.glViewport(gle0X, gle0Y, gle0W, gle0H);
            gle0.display(drawable);

            gl.glViewport(gle1X, gle1Y, gle1W, gle1H);
            gle1.display(drawable);

            shot++;
            if (4 == shot) {
              gl.glViewport(tX, tY, tW, tH);
              snapshot(0, null, drawable.getGL(), screenshot, TextureIO.PNG, null);
              gl.glViewport(gle1X, gle1Y, gle1W, gle1H); // restore viewport test
            }

            final TextureState ts =
                new TextureState(drawable.getGL(), GL.GL_TEXTURE_2D); // as set via gle0!
            // System.err.println("XXX: "+ts);
            Assert.assertEquals("Texture unit changed", GL.GL_TEXTURE0 + texUnit, ts.getUnit());
            if (keepTextureBound) {
              Assert.assertEquals(
                  "Texture mag-filter changed: " + ts, GL.GL_LINEAR, ts.getMagFilter());
              Assert.assertEquals(
                  "Texture mag-filter changed: " + ts, GL.GL_LINEAR, ts.getMinFilter());
              Assert.assertEquals("Texture wrap-s changed: " + ts, GL.GL_REPEAT, ts.getWrapS());
              Assert.assertEquals("Texture wrap-t changed: " + ts, GL.GL_REPEAT, ts.getWrapT());
              glelError[0] =
                  GL.GL_LINEAR != ts.getMagFilter()
                      || GL.GL_LINEAR != ts.getMinFilter()
                      || GL.GL_REPEAT != ts.getWrapS()
                      || GL.GL_REPEAT != ts.getWrapT();
            }
          }

          final int border = 5;

          @Override
          public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
            gle0X = x + border;
            gle0Y = y;
            gle0W = width / 2 - 2 * border;
            gle0H = height;
            // System.err.println("GLEL0 "+gle0X+"/"+gle0Y+" "+gle0W+"x"+gle0H);

            gle1X = gle0X + gle0W + 2 * border;
            gle1Y = y;
            gle1W = width / 2 - 2 * border;
            gle1H = height;
            // System.err.println("GLEL1 "+gle1X+"/"+gle1Y+" "+gle1W+"x"+gle1H);

            tX = x;
            tY = y;
            tW = width;
            tH = height;
            // System.err.println("Total "+tX+"/"+tY+" "+tW+"x"+tH);

            GL2ES2 gl = drawable.getGL().getGL2ES2();
            gl.glViewport(gle0X, gle0Y, gle0W, gle0H);
            gle0.reshape(drawable, 0, 0, gle0W, gle0H); // don't 'skip' about gle0X/gle0Y

            gl.glViewport(gle1X, gle1Y, gle1W, gle1H);
            gle1.reshape(drawable, 0, 0, gle1W, gle1H); // don't 'skip' about gle0X/gle0Y

            if (keepTextureBound) {
              setupTex(gl);
            }
          }
        });

    final QuitAdapter quitAdapter = new QuitAdapter();
    new com.jogamp.newt.event.awt.AWTKeyAdapter(quitAdapter, glc).addTo(glc);
    new com.jogamp.newt.event.awt.AWTWindowAdapter(quitAdapter, glc).addTo(glc);
    try {
      javax.swing.SwingUtilities.invokeAndWait(
          new Runnable() {
            public void run() {
              frame.pack();
              frame.setVisible(true);
            }
          });
    } catch (Throwable throwable) {
      throwable.printStackTrace();
      Assume.assumeNoException(throwable);
    }
    Assert.assertTrue("Component didn't become visible", AWTRobotUtil.waitForVisible(glc, true));
    Assert.assertTrue("Component didn't become realized", AWTRobotUtil.waitForRealized(glc, true));
    Thread.sleep(100);
    setFrameSize(frame, true, glc_sz2);
    System.err.println(
        "window resize pos/siz: "
            + glc.getX()
            + "/"
            + glc.getY()
            + " "
            + glc.getSurfaceWidth()
            + "x"
            + glc.getSurfaceHeight());
    Thread.sleep(100);

    final long t0 = System.currentTimeMillis();
    while (!quitAdapter.shouldQuit() && System.currentTimeMillis() - t0 < duration) {
      glc.display();
      Thread.sleep(100);
    }

    try {
      javax.swing.SwingUtilities.invokeAndWait(
          new Runnable() {
            public void run() {
              frame.setVisible(false);
              frame.remove(glc);
              frame.dispose();
            }
          });
    } catch (Throwable throwable) {
      throwable.printStackTrace();
      Assume.assumeNoException(throwable);
    }
    Assume.assumeFalse("Error occured in GLEL .. see log file above", glelError[0]);
  }
  public void asyncEachOneAnimator(final boolean destroyCleanOrder)
      throws InterruptedException, InvocationTargetException {
    final Frame f1 = new Frame();
    final Animator a1 = new Animator();
    final GearsES2 g1 = new GearsES2(0);
    final GLCanvas c1 = createGLCanvas(f1, 0, 0, g1);
    a1.add(c1);
    a1.start();
    // f1.setVisible(true); // we do this post f2 .. to test pending creation!

    final Frame f2 = new Frame();
    final Animator a2 = new Animator();
    final GearsES2 g2 = new GearsES2(0);
    g2.setSharedGears(g1);
    final GLCanvas c2 = createGLCanvas(f2, f1.getX() + width, f1.getY() + 0, g2);
    c2.setSharedAutoDrawable(c1);
    a2.add(c2);
    a2.start();
    javax.swing.SwingUtilities.invokeAndWait(
        new Runnable() {
          public void run() {
            f2.setVisible(true);
          }
        });

    Thread.sleep(200); // wait a while ..

    javax.swing.SwingUtilities.invokeAndWait(
        new Runnable() {
          public void run() {
            f1.setVisible(true); // test pending creation of f2
          }
        });

    final Frame f3 = new Frame();
    final Animator a3 = new Animator();
    final GearsES2 g3 = new GearsES2(0);
    g3.setSharedGears(g1);
    final GLCanvas c3 = createGLCanvas(f3, f1.getX() + 0, f1.getY() + height, g3);
    c3.setSharedAutoDrawable(c1);
    a3.add(c3);
    a3.start();
    javax.swing.SwingUtilities.invokeAndWait(
        new Runnable() {
          public void run() {
            f3.setVisible(true);
          }
        });

    Thread.sleep(
        1000 / 60
            * 10); // wait ~10 frames giving a chance to create (blocking until master share is
    // valid)

    Assert.assertTrue(AWTRobotUtil.waitForRealized(c1, true));
    Assert.assertTrue(AWTRobotUtil.waitForVisible(c1, true));
    Assert.assertTrue(AWTRobotUtil.waitForContextCreated(c1, true));
    Assert.assertTrue("Gears1 not initialized", g1.waitForInit(true));

    Assert.assertTrue(AWTRobotUtil.waitForRealized(c2, true));
    Assert.assertTrue(AWTRobotUtil.waitForVisible(c2, true));
    Assert.assertTrue(AWTRobotUtil.waitForContextCreated(c2, true));
    Assert.assertTrue("Gears2 not initialized", g2.waitForInit(true));

    Assert.assertTrue(AWTRobotUtil.waitForRealized(c3, true));
    Assert.assertTrue(AWTRobotUtil.waitForVisible(c3, true));
    Assert.assertTrue(AWTRobotUtil.waitForContextCreated(c3, true));
    Assert.assertTrue("Gears3 not initialized", g3.waitForInit(true));

    final GLContext ctx1 = c1.getContext();
    final GLContext ctx2 = c2.getContext();
    final GLContext ctx3 = c3.getContext();
    {
      final List<GLContext> ctx1Shares = ctx1.getCreatedShares();
      final List<GLContext> ctx2Shares = ctx2.getCreatedShares();
      final List<GLContext> ctx3Shares = ctx3.getCreatedShares();
      MiscUtils.dumpSharedGLContext("XXX-C-3.1", ctx1);
      MiscUtils.dumpSharedGLContext("XXX-C-3.2", ctx2);
      MiscUtils.dumpSharedGLContext("XXX-C-3.3", ctx3);

      Assert.assertTrue("Ctx1 is not shared", ctx1.isShared());
      Assert.assertTrue("Ctx2 is not shared", ctx2.isShared());
      Assert.assertTrue("Ctx3 is not shared", ctx3.isShared());
      Assert.assertEquals("Ctx1 has unexpected number of created shares", 2, ctx1Shares.size());
      Assert.assertEquals("Ctx2 has unexpected number of created shares", 2, ctx2Shares.size());
      Assert.assertEquals("Ctx3 has unexpected number of created shares", 2, ctx3Shares.size());
    }

    Assert.assertTrue("Gears1 is shared", !g1.usesSharedGears());
    Assert.assertTrue("Gears2 is not shared", g2.usesSharedGears());
    Assert.assertTrue("Gears3 is not shared", g3.usesSharedGears());

    try {
      Thread.sleep(duration);
    } catch (final Exception e) {
      e.printStackTrace();
    }
    // Stopped animator allows native windowing system 'repaint' event
    // to trigger GLAD 'display'
    a1.stop();
    Assert.assertEquals(false, a1.isAnimating());
    a2.stop();
    Assert.assertEquals(false, a2.isAnimating());
    a3.stop();
    Assert.assertEquals(false, a3.isAnimating());

    if (destroyCleanOrder) {
      System.err.println("XXX Destroy in clean order NOW");
    } else {
      System.err.println(
          "XXX Destroy in creation order NOW - Driver Impl. Ma trigger driver Bug i.e. not postponing GL ctx destruction after releasing all refs.");
    }
    javax.swing.SwingUtilities.invokeAndWait(
        new Runnable() {
          public void run() {
            try {
              if (destroyCleanOrder) {
                f3.dispose();
              } else {
                f1.dispose();
              }
            } catch (final Throwable t) {
              throw new RuntimeException(t);
            }
          }
        });

    javax.swing.SwingUtilities.invokeAndWait(
        new Runnable() {
          public void run() {
            try {
              f2.dispose();
            } catch (final Throwable t) {
              throw new RuntimeException(t);
            }
          }
        });

    javax.swing.SwingUtilities.invokeAndWait(
        new Runnable() {
          public void run() {
            try {
              if (destroyCleanOrder) {
                f1.dispose();
              } else {
                f3.dispose();
              }
            } catch (final Throwable t) {
              throw new RuntimeException(t);
            }
          }
        });

    Assert.assertTrue(AWTRobotUtil.waitForRealized(c1, false));
    Assert.assertTrue(AWTRobotUtil.waitForRealized(c2, false));
    Assert.assertTrue(AWTRobotUtil.waitForRealized(c3, false));
  }
  public void testImpl() throws InterruptedException {
    final JFrame frame = new JFrame(this.getSimpleTestName("."));

    //
    // GLDrawableFactory factory = GLDrawableFactory.getFactory(GLProfile.get(GLProfile.GL2));
    // GLContext sharedContext = factory.getOrCreateSharedContext(factory.getDefaultDevice());
    //
    final GLCapabilities glCapabilities = new GLCapabilities(GLProfile.get(GLProfile.GL2));
    glCapabilities.setSampleBuffers(true);
    glCapabilities.setNumSamples(4);

    final GearsES2 eventListener1 = new GearsES2(0);
    final GearsES2 eventListener2 = new GearsES2(1);

    final Component openGLComponent1;
    final Component openGLComponent2;
    final GLAutoDrawable openGLAutoDrawable1;
    final GLAutoDrawable openGLAutoDrawable2;

    final GLWindow glWindow1 = GLWindow.create(glCapabilities);
    final NewtCanvasAWT newtCanvasAWT1 = new NewtCanvasAWT(glWindow1);
    newtCanvasAWT1.setPreferredSize(new Dimension(640, 480));
    glWindow1.addGLEventListener(eventListener1);
    //
    final GLWindow glWindow2 = GLWindow.create(glCapabilities);
    final NewtCanvasAWT newtCanvasAWT2 = new NewtCanvasAWT(glWindow2);
    newtCanvasAWT2.setPreferredSize(new Dimension(640, 480));
    glWindow2.addGLEventListener(eventListener2);

    openGLComponent1 = newtCanvasAWT1;
    openGLComponent2 = newtCanvasAWT2;
    openGLAutoDrawable1 = glWindow1;
    openGLAutoDrawable2 = glWindow2;

    // group both OpenGL canvases / windows into a horizontal panel
    final JPanel openGLPanel = new JPanel();
    openGLPanel.setLayout(new BoxLayout(openGLPanel, BoxLayout.LINE_AXIS));
    openGLPanel.add(openGLComponent1);
    openGLPanel.add(Box.createHorizontalStrut(5));
    openGLPanel.add(openGLComponent2);

    final JPanel mainPanel = (JPanel) frame.getContentPane();
    mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.LINE_AXIS));
    mainPanel.add(Box.createHorizontalGlue());
    mainPanel.add(openGLPanel);
    mainPanel.add(Box.createHorizontalGlue());

    final Animator animator = new Animator(Thread.currentThread().getThreadGroup());
    animator.setUpdateFPSFrames(1, null);
    animator.add(openGLAutoDrawable1);
    animator.add(openGLAutoDrawable2);

    // make the window visible using the EDT
    SwingUtilities.invokeLater(
        new Runnable() {
          public void run() {
            frame.pack();
            frame.setVisible(true);
          }
        });

    Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame, true));
    Assert.assertEquals(true, AWTRobotUtil.waitForRealized(openGLComponent1, true));
    Assert.assertEquals(true, AWTRobotUtil.waitForRealized(openGLComponent2, true));

    animator.start();

    // sleep for test duration, then request the window to close, wait for the window to close,s and
    // stop the animation
    while (animator.isAnimating() && animator.getTotalFPSDuration() < durationPerTest) {
      Thread.sleep(100);
    }

    animator.stop();

    // ask the EDT to dispose of the frame;
    // if using newt, explicitly dispose of the canvases because otherwise it seems our destroy
    // methods are not called
    SwingUtilities.invokeLater(
        new Runnable() {
          public void run() {
            newtCanvasAWT1.destroy(); // removeNotify does not destroy GLWindow
            newtCanvasAWT2.destroy(); // removeNotify does not destroy GLWindow
            frame.dispose();
          }
        });
    Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame, false));
    Assert.assertEquals(true, AWTRobotUtil.waitForRealized(openGLComponent1, false));
    Assert.assertEquals(true, AWTRobotUtil.waitForRealized(openGLComponent2, false));
  }
  protected void runTestGL(final GLCapabilitiesImmutable caps) throws InterruptedException {
    final Display nDisplay =
        NewtFactory.createDisplay(NativeWindowFactory.TYPE_AWT, null, false); // local display
    final Screen nScreen = NewtFactory.createScreen(nDisplay, 0); // screen 0
    final Window nWindow = NewtFactory.createWindow(nScreen, caps);

    final GLWindow glWindow = GLWindow.create(nWindow);
    Assert.assertNotNull(glWindow);
    glWindow.setTitle("Gears NewtAWTWrapper Test");

    glWindow.addGLEventListener(new GearsES2(1));

    final Animator animator = useAnimator ? new Animator(glWindow) : null;
    final QuitAdapter quitAdapter = new QuitAdapter();

    glWindow.addKeyListener(new TraceKeyAdapter(quitAdapter));
    glWindow.addWindowListener(new TraceWindowAdapter(quitAdapter));

    if (useAnimator) {
      animator.start();
    }

    int div = 3;
    glWindow.setSize(width / div, height / div);
    glWindow.setVisible(true);
    if (doResizeTest) {
      glWindow.display();
      final int[] expSurfaceSize =
          glWindow.getNativeSurface().convertToPixelUnits(new int[] {width / div, height / div});
      Assert.assertTrue(
          "Surface Size not reached: Expected "
              + expSurfaceSize[0]
              + "x"
              + expSurfaceSize[1]
              + ", Is "
              + glWindow.getSurfaceWidth()
              + "x"
              + glWindow.getSurfaceHeight(),
          AWTRobotUtil.waitForSize(glWindow, expSurfaceSize[0], expSurfaceSize[1]));
      Thread.sleep(600);

      div = 2;
      glWindow.setSize(width / div, height / div);
      glWindow.display();
      expSurfaceSize[0] = width / div;
      expSurfaceSize[1] = height / div;
      glWindow.getNativeSurface().convertToPixelUnits(expSurfaceSize);
      Assert.assertTrue(
          "Surface Size not reached: Expected "
              + expSurfaceSize[0]
              + "x"
              + expSurfaceSize[1]
              + ", Is "
              + glWindow.getSurfaceWidth()
              + "x"
              + glWindow.getSurfaceHeight(),
          AWTRobotUtil.waitForSize(glWindow, expSurfaceSize[0], expSurfaceSize[1]));
      Thread.sleep(600);

      div = 1;
      glWindow.setSize(width / div, height / div);
      glWindow.display();
      expSurfaceSize[0] = width / div;
      expSurfaceSize[1] = height / div;
      glWindow.getNativeSurface().convertToPixelUnits(expSurfaceSize);
      Assert.assertTrue(
          "Surface Size not reached: Expected "
              + expSurfaceSize[0]
              + "x"
              + expSurfaceSize[1]
              + ", Is "
              + glWindow.getSurfaceWidth()
              + "x"
              + glWindow.getSurfaceHeight(),
          AWTRobotUtil.waitForSize(glWindow, expSurfaceSize[0], expSurfaceSize[1]));
      Thread.sleep(600);
    }

    final long t0 = System.currentTimeMillis();
    long t1 = t0;
    while (!quitAdapter.shouldQuit() && t1 - t0 < duration) {
      Thread.sleep(100);
      t1 = System.currentTimeMillis();
    }

    if (useAnimator) {
      animator.stop();
    }
    glWindow.destroy();
  }
  protected void runTestGL(
      final GLCapabilities caps,
      final FrameLayout frameLayout,
      final boolean twoCanvas,
      final boolean resizeByComp)
      throws InterruptedException, InvocationTargetException {
    final JFrame frame = new JFrame("Bug816: " + this.getTestMethodName());
    Assert.assertNotNull(frame);
    final Container framePane = frame.getContentPane();

    final GLCanvas glCanvas1 = new GLCanvas(caps);
    Assert.assertNotNull(glCanvas1);
    final GLCanvas glCanvas2;
    if (twoCanvas) {
      glCanvas2 = new GLCanvas(caps);
      Assert.assertNotNull(glCanvas2);
    } else {
      glCanvas2 = null;
    }

    final Dimension glcDim = new Dimension(width / 2, height);
    final Dimension frameDim = new Dimension(twoCanvas ? width + 64 : width / 2 + 64, height + 64);

    setComponentSize(null, glCanvas1, glcDim, glCanvas2, glcDim);

    switch (frameLayout) {
      case None:
        {
          framePane.add(glCanvas1);
        }
        break;
      case Flow:
        {
          final Container c = new Container();
          c.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
          c.add(glCanvas1);
          if (twoCanvas) {
            c.add(glCanvas2);
          }
          framePane.add(c);
        }
        break;
      case DoubleBorderCenterSurrounded:
        {
          final Container c = new Container();
          c.setLayout(new BorderLayout());
          c.add(new Button("north"), BorderLayout.NORTH);
          c.add(new Button("south"), BorderLayout.SOUTH);
          c.add(new Button("east"), BorderLayout.EAST);
          c.add(new Button("west"), BorderLayout.WEST);
          if (twoCanvas) {
            final Container c2 = new Container();
            c2.setLayout(new GridLayout(1, 2));
            c2.add(glCanvas1);
            c2.add(glCanvas2);
            c.add(c2, BorderLayout.CENTER);
          } else {
            c.add(glCanvas1, BorderLayout.CENTER);
          }
          framePane.setLayout(new BorderLayout());
          framePane.add(new Button("NORTH"), BorderLayout.NORTH);
          framePane.add(new Button("SOUTH"), BorderLayout.SOUTH);
          framePane.add(new Button("EAST"), BorderLayout.EAST);
          framePane.add(new Button("WEST"), BorderLayout.WEST);
          framePane.add(c, BorderLayout.CENTER);
        }
        break;
      case Box:
        {
          final Container c = new Container();
          c.setLayout(new BoxLayout(c, BoxLayout.X_AXIS));
          c.add(glCanvas1);
          if (twoCanvas) {
            c.add(glCanvas2);
          }
          framePane.add(c);
        }
        break;
      case Split:
        {
          final Dimension sbDim = new Dimension(16, 16);
          final JScrollPane vsp =
              new JScrollPane(
                  ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
                  ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
          {
            final JScrollBar vsb = vsp.getVerticalScrollBar();
            vsb.setPreferredSize(sbDim);
            final BoundedRangeModel model = vsb.getModel();
            model.setMinimum(0);
            model.setMaximum(100);
            model.setValue(50);
            model.setExtent(1);
            vsb.setEnabled(true);
          }
          final JScrollPane hsp =
              new JScrollPane(
                  ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
                  ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
          {
            final JScrollBar hsb = hsp.getHorizontalScrollBar();
            hsb.setPreferredSize(sbDim);
            final BoundedRangeModel model = hsb.getModel();
            model.setMinimum(0);
            model.setMaximum(100);
            model.setValue(50);
            model.setExtent(1);
            hsb.setEnabled(true);
          }
          final JSplitPane horizontalSplitPane =
              new JSplitPane(
                  JSplitPane.HORIZONTAL_SPLIT, true, twoCanvas ? glCanvas2 : vsp, glCanvas1);
          horizontalSplitPane.setResizeWeight(0.5);
          final JSplitPane verticalSplitPane =
              new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, horizontalSplitPane, hsp);
          verticalSplitPane.setResizeWeight(0.5);
          framePane.add(verticalSplitPane);
        }
        break;
    }
    final GearsES2 demo1 = new GearsES2(swapInterval);
    glCanvas1.addGLEventListener(demo1);
    if (twoCanvas) {
      final RedSquareES2 demo2 = new RedSquareES2(swapInterval);
      glCanvas2.addGLEventListener(demo2);
    }

    final Animator animator = new Animator();
    animator.add(glCanvas1);
    if (twoCanvas) {
      animator.add(glCanvas2);
    }
    final QuitAdapter quitAdapter = new QuitAdapter();
    new AWTWindowAdapter(new TraceWindowAdapter(quitAdapter), glCanvas1).addTo(frame);

    javax.swing.SwingUtilities.invokeAndWait(
        new Runnable() {
          public void run() {
            if (resizeByComp) {
              frame.pack();
            } else {
              setFrameSize(frame, true, frameDim);
            }
            frame.setVisible(true);
          }
        });
    Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame, true));
    Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glCanvas1, true));
    if (twoCanvas) {
      Assert.assertEquals(true, AWTRobotUtil.waitForRealized(glCanvas2, true));
    }

    animator.start();
    Assert.assertTrue(animator.isStarted());
    Assert.assertTrue(animator.isAnimating());

    System.err.println(
        "canvas1 pos/siz: "
            + glCanvas1.getX()
            + "/"
            + glCanvas1.getY()
            + " "
            + glCanvas1.getSurfaceWidth()
            + "x"
            + glCanvas1.getSurfaceHeight());
    if (twoCanvas) {
      System.err.println(
          "canvas2 pos/siz: "
              + glCanvas2.getX()
              + "/"
              + glCanvas2.getY()
              + " "
              + glCanvas2.getSurfaceWidth()
              + "x"
              + glCanvas2.getSurfaceHeight());
    }

    Thread.sleep(Math.max(1000, duration / 2));
    if (null != rwsize) {
      final Dimension compRSizeHalf = new Dimension(rwsize.width / 2, rwsize.height);
      final Dimension frameRSizeHalf =
          new Dimension(twoCanvas ? rwsize.width + 64 : rwsize.width / 2 + 64, rwsize.height + 64);
      if (resizeByComp) {
        setComponentSize(frame, glCanvas1, compRSizeHalf, glCanvas2, compRSizeHalf);
      } else {
        setFrameSize(frame, true, frameRSizeHalf);
      }
      System.err.println(
          "resize canvas1 pos/siz: "
              + glCanvas1.getX()
              + "/"
              + glCanvas1.getY()
              + " "
              + glCanvas1.getSurfaceWidth()
              + "x"
              + glCanvas1.getSurfaceHeight());
      if (twoCanvas) {
        System.err.println(
            "resize canvas2 pos/siz: "
                + glCanvas2.getX()
                + "/"
                + glCanvas2.getY()
                + " "
                + glCanvas2.getSurfaceWidth()
                + "x"
                + glCanvas2.getSurfaceHeight());
      }
    }

    final long t0 = System.currentTimeMillis();
    long t1 = t0;
    while (!quitAdapter.shouldQuit() && t1 - t0 < duration) {
      Thread.sleep(100);
      t1 = System.currentTimeMillis();
    }

    Assert.assertNotNull(frame);
    Assert.assertNotNull(glCanvas1);
    if (twoCanvas) {
      Assert.assertNotNull(glCanvas2);
    } else {
      Assert.assertNull(glCanvas2);
    }

    Assert.assertNotNull(animator);
    animator.stop();
    Assert.assertFalse(animator.isAnimating());
    Assert.assertFalse(animator.isStarted());

    javax.swing.SwingUtilities.invokeAndWait(
        new Runnable() {
          public void run() {
            frame.setVisible(false);
          }
        });
    Assert.assertEquals(false, frame.isVisible());
    javax.swing.SwingUtilities.invokeAndWait(
        new Runnable() {
          public void run() {
            frame.remove(glCanvas1);
            if (twoCanvas) {
              frame.remove(glCanvas2);
            }
            frame.dispose();
          }
        });
  }