Path: blob/master/src/java.desktop/share/classes/javax/swing/BufferStrategyPaintManager.java
41153 views
/*1* Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package javax.swing;2526import java.awt.*;27import java.awt.event.*;28import java.awt.image.*;29import java.lang.ref.WeakReference;30import java.util.*;3132import com.sun.java.swing.SwingUtilities3;33import sun.awt.AWTAccessor;3435import sun.awt.SubRegionShowable;36import sun.java2d.SunGraphics2D;37import sun.java2d.pipe.hw.ExtendedBufferCapabilities;38import sun.awt.SunToolkit;39import sun.util.logging.PlatformLogger;4041/**42* A PaintManager implementation that uses a BufferStrategy for43* rendering.44*45* @author Scott Violet46*/47class BufferStrategyPaintManager extends RepaintManager.PaintManager {48//49// All drawing is done to a BufferStrategy. At the end of painting50// (endPaint) the region that was painted is flushed to the screen51// (using BufferStrategy.show).52//53// PaintManager.show is overriden to show directly from the54// BufferStrategy (when using blit), if successful true is55// returned and a paint event will not be generated. To avoid56// showing from the buffer while painting a locking scheme is57// implemented. When beginPaint is invoked the field painting is58// set to true. If painting is true and show is invoked we59// immediately return false. This is done to avoid blocking the60// toolkit thread while painting happens. In a similar way when61// show is invoked the field showing is set to true, beginPaint62// will then block until showing is true. This scheme ensures we63// only ever have one thread using the BufferStrategy and it also64// ensures the toolkit thread remains as responsive as possible.65//66// If we're using a flip strategy the contents of the backbuffer may67// have changed and so show only attempts to show from the backbuffer68// if we get a blit strategy.69//7071private static final PlatformLogger LOGGER = PlatformLogger.getLogger(72"javax.swing.BufferStrategyPaintManager");7374/**75* List of BufferInfos. We don't use a Map primarily because76* there are typically only a handful of top level components making77* a Map overkill.78*/79private ArrayList<BufferInfo> bufferInfos;8081/**82* Indicates <code>beginPaint</code> has been invoked. This is83* set to true for the life of beginPaint/endPaint pair.84*/85private boolean painting;86/**87* Indicates we're in the process of showing. All painting, on the EDT,88* is blocked while this is true.89*/90private boolean showing;9192//93// Region that we need to flush. When beginPaint is called these are94// reset and any subsequent calls to paint/copyArea then update these95// fields accordingly. When endPaint is called we then try and show96// the accumulated region.97// These fields are in the coordinate system of the root.98//99private int accumulatedX;100private int accumulatedY;101private int accumulatedMaxX;102private int accumulatedMaxY;103104//105// The following fields are set by prepare106//107108/**109* Farthest JComponent ancestor for the current paint/copyArea.110*/111private JComponent rootJ;112/**113* Location of component being painted relative to root.114*/115private int xOffset;116/**117* Location of component being painted relative to root.118*/119private int yOffset;120/**121* Graphics from the BufferStrategy.122*/123private Graphics bsg;124/**125* BufferStrategy currently being used.126*/127private BufferStrategy bufferStrategy;128/**129* BufferInfo corresponding to root.130*/131private BufferInfo bufferInfo;132133/**134* Set to true if the bufferInfo needs to be disposed when current135* paint loop is done.136*/137private boolean disposeBufferOnEnd;138139BufferStrategyPaintManager() {140bufferInfos = new ArrayList<BufferInfo>(1);141}142143//144// PaintManager methods145//146147/**148* Cleans up any created BufferStrategies.149*/150protected void dispose() {151// dipose can be invoked at any random time. To avoid152// threading dependancies we do the actual diposing via an153// invokeLater.154SwingUtilities.invokeLater(new Runnable() {155public void run() {156java.util.List<BufferInfo> bufferInfos;157synchronized(BufferStrategyPaintManager.this) {158while (showing) {159try {160BufferStrategyPaintManager.this.wait();161} catch (InterruptedException ie) {162}163}164bufferInfos = BufferStrategyPaintManager.this.bufferInfos;165BufferStrategyPaintManager.this.bufferInfos = null;166}167dispose(bufferInfos);168}169});170}171172private void dispose(java.util.List<BufferInfo> bufferInfos) {173if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {174LOGGER.finer("BufferStrategyPaintManager disposed",175new RuntimeException());176}177if (bufferInfos != null) {178for (BufferInfo bufferInfo : bufferInfos) {179bufferInfo.dispose();180}181}182}183184/**185* Shows the specified region of the back buffer. This will return186* true if successful, false otherwise. This is invoked on the187* toolkit thread in response to an expose event.188*/189public boolean show(Container c, int x, int y, int w, int h) {190synchronized(this) {191if (painting) {192// Don't show from backbuffer while in the process of193// painting.194return false;195}196showing = true;197}198try {199BufferInfo info = getBufferInfo(c);200BufferStrategy bufferStrategy;201if (info != null && info.isInSync() &&202(bufferStrategy = info.getBufferStrategy(false)) != null) {203SubRegionShowable bsSubRegion =204(SubRegionShowable)bufferStrategy;205boolean paintAllOnExpose = info.getPaintAllOnExpose();206info.setPaintAllOnExpose(false);207if (bsSubRegion.showIfNotLost(x, y, (x + w), (y + h))) {208return !paintAllOnExpose;209}210// Mark the buffer as needing to be repainted. We don't211// immediately do a repaint as this method will return false212// indicating a PaintEvent should be generated which will213// trigger a complete repaint.214bufferInfo.setContentsLostDuringExpose(true);215}216}217finally {218synchronized(this) {219showing = false;220notifyAll();221}222}223return false;224}225226public boolean paint(JComponent paintingComponent,227JComponent bufferComponent, Graphics g,228int x, int y, int w, int h) {229Container root = fetchRoot(paintingComponent);230231if (prepare(paintingComponent, root, true, x, y, w, h)) {232if ((g instanceof SunGraphics2D) &&233((SunGraphics2D)g).getDestination() == root) {234// BufferStrategy may have already constrained the Graphics. To235// account for that we revert the constrain, then apply a236// constrain for Swing on top of that.237int cx = ((SunGraphics2D)bsg).constrainX;238int cy = ((SunGraphics2D)bsg).constrainY;239if (cx != 0 || cy != 0) {240bsg.translate(-cx, -cy);241}242((SunGraphics2D)bsg).constrain(xOffset + cx, yOffset + cy,243x + w, y + h);244bsg.setClip(x, y, w, h);245paintingComponent.paintToOffscreen(bsg, x, y, w, h,246x + w, y + h);247accumulate(xOffset + x, yOffset + y, w, h);248return true;249} else {250// Assume they are going to eventually render to the screen.251// This disables showing from backbuffer until a complete252// repaint occurs.253bufferInfo.setInSync(false);254// Fall through to old rendering.255}256}257// Invalid root, do what Swing has always done.258if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {259LOGGER.finer("prepare failed");260}261return super.paint(paintingComponent, bufferComponent, g, x, y, w, h);262}263264public void copyArea(JComponent c, Graphics g, int x, int y, int w, int h,265int deltaX, int deltaY, boolean clip) {266// Note: this method is only called internally and we know that267// g is from a heavyweight Component, so no check is necessary as268// it is in paint() above.269//270// If the buffer isn't in sync there is no point in doing a copyArea,271// it has garbage.272Container root = fetchRoot(c);273274if (prepare(c, root, false, 0, 0, 0, 0) && bufferInfo.isInSync()) {275if (clip) {276Rectangle cBounds = c.getVisibleRect();277int relX = xOffset + x;278int relY = yOffset + y;279bsg.clipRect(xOffset + cBounds.x,280yOffset + cBounds.y,281cBounds.width, cBounds.height);282bsg.copyArea(relX, relY, w, h, deltaX, deltaY);283}284else {285bsg.copyArea(xOffset + x, yOffset + y, w, h, deltaX,286deltaY);287}288accumulate(x + xOffset + deltaX, y + yOffset + deltaY, w, h);289} else {290if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {291LOGGER.finer("copyArea: prepare failed or not in sync");292}293// Prepare failed, or not in sync. By calling super.copyArea294// we'll copy on screen. We need to flush any pending paint to295// the screen otherwise we'll do a copyArea on the wrong thing.296if (!flushAccumulatedRegion()) {297// Flush failed, copyArea will be copying garbage,298// force repaint of all.299rootJ.repaint();300} else {301super.copyArea(c, g, x, y, w, h, deltaX, deltaY, clip);302}303}304}305306public void beginPaint() {307synchronized(this) {308painting = true;309// Make sure another thread isn't attempting to show from310// the back buffer.311while(showing) {312try {313wait();314} catch (InterruptedException ie) {315}316}317}318if (LOGGER.isLoggable(PlatformLogger.Level.FINEST)) {319LOGGER.finest("beginPaint");320}321// Reset the area that needs to be painted.322resetAccumulated();323}324325public void endPaint() {326if (LOGGER.isLoggable(PlatformLogger.Level.FINEST)) {327LOGGER.finest("endPaint: region " + accumulatedX + " " +328accumulatedY + " " + accumulatedMaxX + " " +329accumulatedMaxY);330}331if (painting) {332if (!flushAccumulatedRegion()) {333if (!isRepaintingRoot()) {334repaintRoot(rootJ);335}336else {337// Contents lost twice in a row, punt.338resetDoubleBufferPerWindow();339// In case we've left junk on the screen, force a repaint.340rootJ.repaint();341}342}343}344345BufferInfo toDispose = null;346synchronized(this) {347painting = false;348if (disposeBufferOnEnd) {349disposeBufferOnEnd = false;350toDispose = bufferInfo;351bufferInfos.remove(toDispose);352}353}354if (toDispose != null) {355toDispose.dispose();356}357}358359/**360* Renders the BufferStrategy to the screen.361*362* @return true if successful, false otherwise.363*/364private boolean flushAccumulatedRegion() {365boolean success = true;366if (accumulatedX != Integer.MAX_VALUE) {367SubRegionShowable bsSubRegion = (SubRegionShowable)bufferStrategy;368boolean contentsLost = bufferStrategy.contentsLost();369if (!contentsLost) {370bsSubRegion.show(accumulatedX, accumulatedY,371accumulatedMaxX, accumulatedMaxY);372contentsLost = bufferStrategy.contentsLost();373}374if (contentsLost) {375if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {376LOGGER.finer("endPaint: contents lost");377}378// Shown region was bogus, mark buffer as out of sync.379bufferInfo.setInSync(false);380success = false;381}382}383resetAccumulated();384return success;385}386387private void resetAccumulated() {388accumulatedX = Integer.MAX_VALUE;389accumulatedY = Integer.MAX_VALUE;390accumulatedMaxX = 0;391accumulatedMaxY = 0;392}393394/**395* Invoked when the double buffering or useTrueDoubleBuffering396* changes for a JRootPane. If the rootpane is not double397* buffered, or true double buffering changes we throw out any398* cache we may have.399*/400public void doubleBufferingChanged(final JRootPane rootPane) {401if ((!rootPane.isDoubleBuffered() ||402!rootPane.getUseTrueDoubleBuffering()) &&403rootPane.getParent() != null) {404if (!SwingUtilities.isEventDispatchThread()) {405Runnable updater = new Runnable() {406public void run() {407doubleBufferingChanged0(rootPane);408}409};410SwingUtilities.invokeLater(updater);411}412else {413doubleBufferingChanged0(rootPane);414}415}416}417418/**419* Does the work for doubleBufferingChanged.420*/421private void doubleBufferingChanged0(JRootPane rootPane) {422// This will only happen on the EDT.423BufferInfo info;424synchronized(this) {425// Make sure another thread isn't attempting to show from426// the back buffer.427while(showing) {428try {429wait();430} catch (InterruptedException ie) {431}432}433info = getBufferInfo(rootPane.getParent());434if (painting && bufferInfo == info) {435// We're in the process of painting and the user grabbed436// the Graphics. If we dispose now, endPaint will attempt437// to show a bogus BufferStrategy. Set a flag so that438// endPaint knows it needs to dispose this buffer.439disposeBufferOnEnd = true;440info = null;441} else if (info != null) {442bufferInfos.remove(info);443}444}445if (info != null) {446info.dispose();447}448}449450/**451* Calculates information common to paint/copyArea.452*453* @return true if should use buffering per window in painting.454*/455private boolean prepare(JComponent c, Container root, boolean isPaint, int x, int y,456int w, int h) {457if (bsg != null) {458bsg.dispose();459bsg = null;460}461bufferStrategy = null;462if (root != null) {463boolean contentsLost = false;464BufferInfo bufferInfo = getBufferInfo(root);465if (bufferInfo == null) {466contentsLost = true;467bufferInfo = new BufferInfo(root);468bufferInfos.add(bufferInfo);469if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {470LOGGER.finer("prepare: new BufferInfo: " + root);471}472}473this.bufferInfo = bufferInfo;474if (!bufferInfo.hasBufferStrategyChanged()) {475bufferStrategy = bufferInfo.getBufferStrategy(true);476if (bufferStrategy != null) {477bsg = bufferStrategy.getDrawGraphics();478if (bufferStrategy.contentsRestored()) {479contentsLost = true;480if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {481LOGGER.finer("prepare: contents restored in prepare");482}483}484}485else {486// Couldn't create BufferStrategy, fallback to normal487// painting.488return false;489}490if (bufferInfo.getContentsLostDuringExpose()) {491contentsLost = true;492bufferInfo.setContentsLostDuringExpose(false);493if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {494LOGGER.finer("prepare: contents lost on expose");495}496}497if (isPaint && c == rootJ && x == 0 && y == 0 &&498c.getWidth() == w && c.getHeight() == h) {499bufferInfo.setInSync(true);500}501else if (contentsLost) {502// We either recreated the BufferStrategy, or the contents503// of the buffer strategy were restored. We need to504// repaint the root pane so that the back buffer is in sync505// again.506bufferInfo.setInSync(false);507if (!isRepaintingRoot()) {508repaintRoot(rootJ);509}510else {511// Contents lost twice in a row, punt512resetDoubleBufferPerWindow();513}514}515return (bufferInfos != null);516}517}518return false;519}520521private Container fetchRoot(JComponent c) {522boolean encounteredHW = false;523rootJ = c;524Container root = c;525xOffset = yOffset = 0;526while (root != null &&527(!(root instanceof Window) &&528!SunToolkit.isInstanceOf(root, "java.applet.Applet"))) {529xOffset += root.getX();530yOffset += root.getY();531root = root.getParent();532if (root != null) {533if (root instanceof JComponent) {534rootJ = (JComponent)root;535}536else if (!root.isLightweight()) {537if (!encounteredHW) {538encounteredHW = true;539}540else {541// We've encountered two hws now and may have542// a containment hierarchy with lightweights containing543// heavyweights containing other lightweights.544// Heavyweights poke holes in lightweight545// rendering so that if we call show on the BS546// (which is associated with the Window) you will547// not see the contents over any child548// heavyweights. If we didn't do this when we549// went to show the descendants of the nested hw550// you would see nothing, so, we bail out here.551return null;552}553}554}555}556if ((root instanceof RootPaneContainer) &&557(rootJ instanceof JRootPane)) {558// We're in a Swing heavyeight (JFrame/JWindow...), use double559// buffering if double buffering enabled on the JRootPane and560// the JRootPane wants true double buffering.561if (rootJ.isDoubleBuffered() &&562((JRootPane)rootJ).getUseTrueDoubleBuffering()) {563// Whether or not a component is double buffered is a564// bit tricky with Swing. This gives a good approximation565// of the various ways to turn on double buffering for566// components.567return root;568}569}570// Don't do true double buffering.571return null;572}573574/**575* Turns off double buffering per window.576*/577private void resetDoubleBufferPerWindow() {578if (bufferInfos != null) {579dispose(bufferInfos);580bufferInfos = null;581repaintManager.setPaintManager(null);582}583}584585/**586* Returns the BufferInfo for the specified root or null if one587* hasn't been created yet.588*/589private BufferInfo getBufferInfo(Container root) {590for (int counter = bufferInfos.size() - 1; counter >= 0; counter--) {591BufferInfo bufferInfo = bufferInfos.get(counter);592Container biRoot = bufferInfo.getRoot();593if (biRoot == null) {594// Window gc'ed595bufferInfos.remove(counter);596if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {597LOGGER.finer("BufferInfo pruned, root null");598}599}600else if (biRoot == root) {601return bufferInfo;602}603}604return null;605}606607private void accumulate(int x, int y, int w, int h) {608accumulatedX = Math.min(x, accumulatedX);609accumulatedY = Math.min(y, accumulatedY);610accumulatedMaxX = Math.max(accumulatedMaxX, x + w);611accumulatedMaxY = Math.max(accumulatedMaxY, y + h);612}613614615616/**617* BufferInfo is used to track the BufferStrategy being used for618* a particular Component. In addition to tracking the BufferStrategy619* it will install a WindowListener and ComponentListener. When the620* component is hidden/iconified the buffer is marked as needing to be621* completely repainted.622*/623private class BufferInfo extends ComponentAdapter implements624WindowListener {625// NOTE: This class does NOT hold a direct reference to the root, if it626// did there would be a cycle between the BufferPerWindowPaintManager627// and the Window so that it could never be GC'ed628//629// Reference to BufferStrategy is referenced via WeakReference for630// same reason.631private WeakReference<BufferStrategy> weakBS;632private WeakReference<Container> root;633// Indicates whether or not the backbuffer and display are in sync.634// This is set to true when a full repaint on the rootpane is done.635private boolean inSync;636// Indicates the contents were lost during and expose event.637private boolean contentsLostDuringExpose;638// Indicates we need to generate a paint event on expose.639private boolean paintAllOnExpose;640641642public BufferInfo(Container root) {643this.root = new WeakReference<Container>(root);644root.addComponentListener(this);645if (root instanceof Window) {646((Window)root).addWindowListener(this);647}648}649650public void setPaintAllOnExpose(boolean paintAllOnExpose) {651this.paintAllOnExpose = paintAllOnExpose;652}653654public boolean getPaintAllOnExpose() {655return paintAllOnExpose;656}657658public void setContentsLostDuringExpose(boolean value) {659contentsLostDuringExpose = value;660}661662public boolean getContentsLostDuringExpose() {663return contentsLostDuringExpose;664}665666public void setInSync(boolean inSync) {667this.inSync = inSync;668}669670/**671* Whether or not the contents of the buffer strategy672* is in sync with the window. This is set to true when the root673* pane paints all, and false when contents are lost/restored.674*/675public boolean isInSync() {676return inSync;677}678679/**680* Returns the Root (Window or Applet) that this BufferInfo references.681*/682public Container getRoot() {683return (root == null) ? null : root.get();684}685686/**687* Returns the BufferStartegy. This will return null if688* the BufferStartegy hasn't been created and <code>create</code> is689* false, or if there is a problem in creating the690* <code>BufferStartegy</code>.691*692* @param create If true, and the BufferStartegy is currently null,693* one will be created.694*/695public BufferStrategy getBufferStrategy(boolean create) {696BufferStrategy bs = (weakBS == null) ? null : weakBS.get();697if (bs == null && create) {698bs = createBufferStrategy();699if (bs != null) {700weakBS = new WeakReference<BufferStrategy>(bs);701}702if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {703LOGGER.finer("getBufferStrategy: created bs: " + bs);704}705}706return bs;707}708709/**710* Returns true if the buffer strategy of the component differs711* from current buffer strategy.712*/713public boolean hasBufferStrategyChanged() {714Container root = getRoot();715if (root != null) {716BufferStrategy ourBS = null;717BufferStrategy componentBS = null;718719ourBS = getBufferStrategy(false);720if (root instanceof Window) {721componentBS = ((Window)root).getBufferStrategy();722}723else {724componentBS = AWTAccessor.getComponentAccessor().getBufferStrategy(root);725}726if (componentBS != ourBS) {727// Component has a different BS, dispose ours.728if (ourBS != null) {729ourBS.dispose();730}731weakBS = null;732return true;733}734}735return false;736}737738/**739* Creates the BufferStrategy. If the appropriate system property740* has been set we'll try for flip first and then we'll try for741* blit.742*/743private BufferStrategy createBufferStrategy() {744Container root = getRoot();745if (root == null) {746return null;747}748BufferStrategy bs = null;749if (SwingUtilities3.isVsyncRequested(root)) {750bs = createBufferStrategy(root, true);751if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {752LOGGER.finer("createBufferStrategy: using vsynced strategy");753}754}755if (bs == null) {756bs = createBufferStrategy(root, false);757}758if (!(bs instanceof SubRegionShowable)) {759// We do this for two reasons:760// 1. So that we know we can cast to SubRegionShowable and761// invoke show with the minimal region to update762// 2. To avoid the possibility of invoking client code763// on the toolkit thread.764bs = null;765}766return bs;767}768769// Creates and returns a buffer strategy. If770// there is a problem creating the buffer strategy this will771// eat the exception and return null.772private BufferStrategy createBufferStrategy(Container root,773boolean isVsynced) {774BufferCapabilities caps;775if (isVsynced) {776caps = new ExtendedBufferCapabilities(777new ImageCapabilities(true), new ImageCapabilities(true),778BufferCapabilities.FlipContents.COPIED,779ExtendedBufferCapabilities.VSyncType.VSYNC_ON);780} else {781caps = new BufferCapabilities(782new ImageCapabilities(true), new ImageCapabilities(true),783null);784}785BufferStrategy bs = null;786if (SunToolkit.isInstanceOf(root, "java.applet.Applet")) {787try {788AWTAccessor.ComponentAccessor componentAccessor789= AWTAccessor.getComponentAccessor();790componentAccessor.createBufferStrategy(root, 2, caps);791bs = componentAccessor.getBufferStrategy(root);792} catch (AWTException e) {793// Type is not supported794if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {795LOGGER.finer("createBufferStratety failed",796e);797}798}799}800else {801try {802((Window)root).createBufferStrategy(2, caps);803bs = ((Window)root).getBufferStrategy();804} catch (AWTException e) {805// Type not supported806if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {807LOGGER.finer("createBufferStratety failed",808e);809}810}811}812return bs;813}814815/**816* Cleans up and removes any references.817*/818public void dispose() {819Container root = getRoot();820if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {821LOGGER.finer("disposed BufferInfo for: " + root);822}823if (root != null) {824root.removeComponentListener(this);825if (root instanceof Window) {826((Window)root).removeWindowListener(this);827}828BufferStrategy bs = getBufferStrategy(false);829if (bs != null) {830bs.dispose();831}832}833this.root = null;834weakBS = null;835}836837// We mark the buffer as needing to be painted on a hide/iconify838// because the developer may have conditionalized painting based on839// visibility.840// Ideally we would also move to having the BufferStrategy being841// a SoftReference in Component here, but that requires changes to842// Component and the like.843public void componentHidden(ComponentEvent e) {844Container root = getRoot();845if (root != null && root.isVisible()) {846// This case will only happen if a developer calls847// hide immediately followed by show. In this case848// the event is delivered after show and the window849// will still be visible. If a developer altered the850// contents of the window between the hide/show851// invocations we won't recognize we need to paint and852// the contents would be bogus. Calling repaint here853// fixs everything up.854root.repaint();855}856else {857setPaintAllOnExpose(true);858}859}860861public void windowIconified(WindowEvent e) {862setPaintAllOnExpose(true);863}864865// On a dispose we chuck everything.866public void windowClosed(WindowEvent e) {867// Make sure we're not showing.868synchronized(BufferStrategyPaintManager.this) {869while (showing) {870try {871BufferStrategyPaintManager.this.wait();872} catch (InterruptedException ie) {873}874}875bufferInfos.remove(this);876}877dispose();878}879880public void windowOpened(WindowEvent e) {881}882883public void windowClosing(WindowEvent e) {884}885886public void windowDeiconified(WindowEvent e) {887}888889public void windowActivated(WindowEvent e) {890}891892public void windowDeactivated(WindowEvent e) {893}894}895}896897898