From b2e29e38cde73a6a1800cd5a220c2c36fc968b37 Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 24 Aug 2017 22:00:32 +0200 Subject: [PATCH] Changed ActiveRenderer to only permit Canvas now (instead of JFrame) --- src/dorkbox/util/swing/ActiveRenderLoop.java | 41 ++++---- src/dorkbox/util/swing/SwingActiveRender.java | 97 ++++++++++--------- .../util/swing/SynchronizedEventQueue.java | 2 +- 3 files changed, 75 insertions(+), 65 deletions(-) diff --git a/src/dorkbox/util/swing/ActiveRenderLoop.java b/src/dorkbox/util/swing/ActiveRenderLoop.java index e0e3277..458fbc4 100644 --- a/src/dorkbox/util/swing/ActiveRenderLoop.java +++ b/src/dorkbox/util/swing/ActiveRenderLoop.java @@ -15,10 +15,11 @@ */ package dorkbox.util.swing; +import java.awt.Canvas; import java.awt.Graphics; import java.awt.Toolkit; -import java.awt.Window; import java.awt.image.BufferStrategy; +import java.util.List; import javax.swing.JComponent; @@ -65,28 +66,32 @@ class ActiveRenderLoop implements Runnable { actionHandlerLong.handle(updateDeltaNanos); } - // this needs to be synchronized because we don't want to our frame removed WHILE we are rendering it. + // this needs to be synchronized because we don't want to our canvas removed WHILE we are rendering it. synchronized (SwingActiveRender.activeRenders) { - for (int i = 0; i < SwingActiveRender.activeRenders.size(); i++) { - Window window = SwingActiveRender.activeRenders.get(i); + final List activeRenders = SwingActiveRender.activeRenders; - final BufferStrategy buffer = window.getBufferStrategy(); + for (Canvas canvas : activeRenders) { + if (!canvas.isDisplayable()) { + continue; + } + + BufferStrategy buffer = canvas.getBufferStrategy(); // maybe the frame was closed - if (buffer != null) { - try { - graphics = buffer.getDrawGraphics(); - window.paint(graphics); - } catch (Exception e) { - e.printStackTrace(); - } finally { - if (graphics != null) { - graphics.dispose(); + try { + graphics = buffer.getDrawGraphics(); + canvas.paint(graphics); + } catch (Exception e) { + // the frame can be close as well. can get a "java.lang.IllegalStateException: Component must have a valid + // peer" if it's already be closed during the getDrawGraphics call. + e.printStackTrace(); + } finally { + if (graphics != null) { + graphics.dispose(); - // blit the back buffer to the screen - if (!buffer.contentsLost()) { - buffer.show(); - } + // blit the back buffer to the screen + if (!buffer.contentsLost()) { + buffer.show(); } } } diff --git a/src/dorkbox/util/swing/SwingActiveRender.java b/src/dorkbox/util/swing/SwingActiveRender.java index f6e22f2..eb99118 100644 --- a/src/dorkbox/util/swing/SwingActiveRender.java +++ b/src/dorkbox/util/swing/SwingActiveRender.java @@ -15,17 +15,14 @@ */ package dorkbox.util.swing; -import java.awt.Component; +import java.awt.Canvas; import java.awt.EventQueue; -import java.awt.Window; -import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Collections; -import java.util.Deque; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import javax.swing.JComponent; +import javax.swing.SwingUtilities; import dorkbox.util.ActionHandlerLong; @@ -42,7 +39,7 @@ public final class SwingActiveRender { private static Thread activeRenderThread = null; - static final List activeRenders = new ArrayList(); + static final List activeRenders = new ArrayList(); static final List activeRenderEvents = new CopyOnWriteArrayList(); // volatile, so that access triggers thread synchrony, since 1.6. See the Java Language Spec, Chapter 17 @@ -56,56 +53,77 @@ class SwingActiveRender { /** - * Enables the window to to added to an "Active Render" thread, at a target "Frames-per-second". This is to support smooth, swing-based - * animations.
This works by removing this object from EDT updates, and instead manually calls paint(g) on the window, updating it - * on our own thread. + * Enables the canvas to to added to an "Active Render" thread, at a target "Frames-per-second". This is to support smooth, swing-based + * animations. + *

+ * This works by removing this object from EDT updates, and instead manually calls paint(g) on the canvas, updating it on our own thread. * - * @param window the window to add to the ActiveRender thread. + * @param canvas the canvas to add to the ActiveRender thread. */ + @SuppressWarnings("Duplicates") public static - void addActiveRender(final Window window) { + void addActiveRender(final Canvas canvas) { // this should be on the EDT if (!EventQueue.isDispatchThread()) { - throw new RuntimeException("adding a swing Window to be actively rendered must be done on the EDT."); + SwingUtilities.invokeLater(new Runnable() { + @Override + public + void run() { + addActiveRender(canvas); + } + }); + return; } // setup double-buffering, so we can properly use Active-Rendering, so the animations will be smooth - window.createBufferStrategy(2); - - // have to specify ALL children in Window to ignore EDT paint requests - Deque components = new ArrayDeque(8); - components.add(window); - - Component[] c; - Component pop; - while ((pop = components.poll()) != null) { - pop.setIgnoreRepaint(true); - - if (pop instanceof JComponent) { - c = ((JComponent) pop).getComponents(); - Collections.addAll(components, c); - } + try { + canvas.createBufferStrategy(2); + } catch (Exception e) { + // sometimes it's added too early. Postpone the event until later + // note: this is different than SwingUtil, because we MUST invoke it later (and not in the current thread) + SwingUtilities.invokeLater(new Runnable() { + @Override + public + void run() { + addActiveRender(canvas); + } + }); + return; } + canvas.setIgnoreRepaint(true); + synchronized (activeRenders) { if (!hasActiveRenders) { setupActiveRenderThread(); } hasActiveRenders = true; - activeRenders.add(window); + activeRenders.add(canvas); } } /** - * Removes a window from the ActiveRender queue. This should happen when the window is closed. + * Removes a canvas from the ActiveRender queue. This should happen when the canvas is closed. * - * @param window the window to remove + * @param canvas the canvas to remove */ public static - void removeActiveRender(final Window window) { + void removeActiveRender(final Canvas canvas) { + // this should be on the EDT + if (!EventQueue.isDispatchThread()) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public + void run() { + removeActiveRender(canvas); + } + }); + return; + } + synchronized (activeRenders) { - activeRenders.remove(window); + activeRenders.remove(canvas); final boolean hadActiveRenders = !activeRenders.isEmpty(); hasActiveRenders = hadActiveRenders; @@ -115,20 +133,7 @@ class SwingActiveRender { } } - // have to specify ALL children in window to obey EDT paint requests - Deque components = new ArrayDeque(8); - components.add(window); - - Component[] c; - Component pop; - while ((pop = components.poll()) != null) { - pop.setIgnoreRepaint(false); - - if (pop instanceof JComponent) { - c = ((JComponent) pop).getComponents(); - Collections.addAll(components, c); - } - } + canvas.setIgnoreRepaint(false); } /** diff --git a/src/dorkbox/util/swing/SynchronizedEventQueue.java b/src/dorkbox/util/swing/SynchronizedEventQueue.java index 703f510..2467260 100644 --- a/src/dorkbox/util/swing/SynchronizedEventQueue.java +++ b/src/dorkbox/util/swing/SynchronizedEventQueue.java @@ -24,7 +24,7 @@ class SynchronizedEventQueue extends EventQueue { public static final Object MUTEX = new Object(); private static final SynchronizedEventQueue instance = new SynchronizedEventQueue(); - private static boolean alreadyInUse = false; + private static volatile boolean alreadyInUse = false; public static synchronized void install() {