1 /*
   2  * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing;
  26 
  27 import java.awt.*;
  28 import java.awt.event.*;
  29 import java.awt.image.*;
  30 import java.lang.ref.WeakReference;
  31 import java.util.*;
  32 
  33 import com.sun.java.swing.SwingUtilities3;
  34 import sun.awt.AWTAccessor;
  35 
  36 import sun.awt.SubRegionShowable;
  37 import sun.java2d.SunGraphics2D;
  38 import sun.java2d.pipe.hw.ExtendedBufferCapabilities;
  39 import sun.awt.SunToolkit;
  40 import sun.util.logging.PlatformLogger;
  41 
  42 /**
  43  * A PaintManager implementation that uses a BufferStrategy for
  44  * rendering.
  45  *
  46  * @author Scott Violet
  47  */
  48 class BufferStrategyPaintManager extends RepaintManager.PaintManager {
  49     //
  50     // All drawing is done to a BufferStrategy.  At the end of painting
  51     // (endPaint) the region that was painted is flushed to the screen
  52     // (using BufferStrategy.show).
  53     //
  54     // PaintManager.show is overriden to show directly from the
  55     // BufferStrategy (when using blit), if successful true is
  56     // returned and a paint event will not be generated.  To avoid
  57     // showing from the buffer while painting a locking scheme is
  58     // implemented.  When beginPaint is invoked the field painting is
  59     // set to true.  If painting is true and show is invoked we
  60     // immediately return false.  This is done to avoid blocking the
  61     // toolkit thread while painting happens.  In a similar way when
  62     // show is invoked the field showing is set to true, beginPaint
  63     // will then block until showing is true.  This scheme ensures we
  64     // only ever have one thread using the BufferStrategy and it also
  65     // ensures the toolkit thread remains as responsive as possible.
  66     //
  67     // If we're using a flip strategy the contents of the backbuffer may
  68     // have changed and so show only attempts to show from the backbuffer
  69     // if we get a blit strategy.
  70     //
  71 
  72     private static final PlatformLogger LOGGER = PlatformLogger.getLogger(
  73                            "javax.swing.BufferStrategyPaintManager");
  74 
  75     /**
  76      * List of BufferInfos.  We don't use a Map primarily because
  77      * there are typically only a handful of top level components making
  78      * a Map overkill.
  79      */
  80     private ArrayList<BufferInfo> bufferInfos;
  81 
  82     /**
  83      * Indicates <code>beginPaint</code> has been invoked.  This is
  84      * set to true for the life of beginPaint/endPaint pair.
  85      */
  86     private boolean painting;
  87     /**
  88      * Indicates we're in the process of showing.  All painting, on the EDT,
  89      * is blocked while this is true.
  90      */
  91     private boolean showing;
  92 
  93     //
  94     // Region that we need to flush.  When beginPaint is called these are
  95     // reset and any subsequent calls to paint/copyArea then update these
  96     // fields accordingly.  When endPaint is called we then try and show
  97     // the accumulated region.
  98     // These fields are in the coordinate system of the root.
  99     //
 100     private int accumulatedX;
 101     private int accumulatedY;
 102     private int accumulatedMaxX;
 103     private int accumulatedMaxY;
 104 
 105     //
 106     // The following fields are set by prepare
 107     //
 108 
 109     /**
 110      * Farthest JComponent ancestor for the current paint/copyArea.
 111      */
 112     private JComponent rootJ;
 113     /**
 114      * Location of component being painted relative to root.
 115      */
 116     private int xOffset;
 117     /**
 118      * Location of component being painted relative to root.
 119      */
 120     private int yOffset;
 121     /**
 122      * Graphics from the BufferStrategy.
 123      */
 124     private Graphics bsg;
 125     /**
 126      * BufferStrategy currently being used.
 127      */
 128     private BufferStrategy bufferStrategy;
 129     /**
 130      * BufferInfo corresponding to root.
 131      */
 132     private BufferInfo bufferInfo;
 133 
 134     /**
 135      * Set to true if the bufferInfo needs to be disposed when current
 136      * paint loop is done.
 137      */
 138     private boolean disposeBufferOnEnd;
 139 
 140     BufferStrategyPaintManager() {
 141         bufferInfos = new ArrayList<BufferInfo>(1);
 142     }
 143 
 144     //
 145     // PaintManager methods
 146     //
 147 
 148     /**
 149      * Cleans up any created BufferStrategies.
 150      */
 151     protected void dispose() {
 152         // dipose can be invoked at any random time. To avoid
 153         // threading dependancies we do the actual diposing via an
 154         // invokeLater.
 155         SwingUtilities.invokeLater(new Runnable() {
 156             public void run() {
 157                 java.util.List<BufferInfo> bufferInfos;
 158                 synchronized(BufferStrategyPaintManager.this) {
 159                     while (showing) {
 160                         try {
 161                             BufferStrategyPaintManager.this.wait();
 162                         } catch (InterruptedException ie) {
 163                         }
 164                     }
 165                     bufferInfos = BufferStrategyPaintManager.this.bufferInfos;
 166                     BufferStrategyPaintManager.this.bufferInfos = null;
 167                 }
 168                 dispose(bufferInfos);
 169             }
 170         });
 171     }
 172 
 173     private void dispose(java.util.List<BufferInfo> bufferInfos) {
 174         if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {
 175             LOGGER.finer("BufferStrategyPaintManager disposed",
 176                          new RuntimeException());
 177         }
 178         if (bufferInfos != null) {
 179             for (BufferInfo bufferInfo : bufferInfos) {
 180                 bufferInfo.dispose();
 181             }
 182         }
 183     }
 184 
 185     /**
 186      * Shows the specified region of the back buffer.  This will return
 187      * true if successful, false otherwise.  This is invoked on the
 188      * toolkit thread in response to an expose event.
 189      */
 190     public boolean show(Container c, int x, int y, int w, int h) {
 191         synchronized(this) {
 192             if (painting) {
 193                 // Don't show from backbuffer while in the process of
 194                 // painting.
 195                 return false;
 196             }
 197             showing = true;
 198         }
 199         try {
 200             BufferInfo info = getBufferInfo(c);
 201             BufferStrategy bufferStrategy;
 202             if (info != null && info.isInSync() &&
 203                 (bufferStrategy = info.getBufferStrategy(false)) != null) {
 204                 SubRegionShowable bsSubRegion =
 205                         (SubRegionShowable)bufferStrategy;
 206                 boolean paintAllOnExpose = info.getPaintAllOnExpose();
 207                 info.setPaintAllOnExpose(false);
 208                 if (bsSubRegion.showIfNotLost(x, y, (x + w), (y + h))) {
 209                     return !paintAllOnExpose;
 210                 }
 211                 // Mark the buffer as needing to be repainted.  We don't
 212                 // immediately do a repaint as this method will return false
 213                 // indicating a PaintEvent should be generated which will
 214                 // trigger a complete repaint.
 215                 bufferInfo.setContentsLostDuringExpose(true);
 216             }
 217         }
 218         finally {
 219             synchronized(this) {
 220                 showing = false;
 221                 notifyAll();
 222             }
 223         }
 224         return false;
 225     }
 226 
 227     public boolean paint(JComponent paintingComponent,
 228                          JComponent bufferComponent, Graphics g,
 229                          int x, int y, int w, int h) {
 230         Container root = fetchRoot(paintingComponent);
 231 
 232         if (prepare(paintingComponent, root, true, x, y, w, h)) {
 233             if ((g instanceof SunGraphics2D) &&
 234                     ((SunGraphics2D)g).getDestination() == root) {
 235                 // BufferStrategy may have already constrained the Graphics. To
 236                 // account for that we revert the constrain, then apply a
 237                 // constrain for Swing on top of that.
 238                 int cx = ((SunGraphics2D)bsg).constrainX;
 239                 int cy = ((SunGraphics2D)bsg).constrainY;
 240                 if (cx != 0 || cy != 0) {
 241                     bsg.translate(-cx, -cy);
 242                 }
 243                 ((SunGraphics2D)bsg).constrain(xOffset + cx, yOffset + cy,
 244                                                x + w, y + h);
 245                 bsg.setClip(x, y, w, h);
 246                 paintingComponent.paintToOffscreen(bsg, x, y, w, h,
 247                                                    x + w, y + h);
 248                 accumulate(xOffset + x, yOffset + y, w, h);
 249                 return true;
 250             } else {
 251                 // Assume they are going to eventually render to the screen.
 252                 // This disables showing from backbuffer until a complete
 253                 // repaint occurs.
 254                 bufferInfo.setInSync(false);
 255                 // Fall through to old rendering.
 256             }
 257         }
 258         // Invalid root, do what Swing has always done.
 259         if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {
 260             LOGGER.finer("prepare failed");
 261         }
 262         return super.paint(paintingComponent, bufferComponent, g, x, y, w, h);
 263     }
 264 
 265     public void copyArea(JComponent c, Graphics g, int x, int y, int w, int h,
 266                          int deltaX, int deltaY, boolean clip) {
 267         // Note: this method is only called internally and we know that
 268         // g is from a heavyweight Component, so no check is necessary as
 269         // it is in paint() above.
 270         //
 271         // If the buffer isn't in sync there is no point in doing a copyArea,
 272         // it has garbage.
 273         Container root = fetchRoot(c);
 274 
 275         if (prepare(c, root, false, 0, 0, 0, 0) && bufferInfo.isInSync()) {
 276             if (clip) {
 277                 Rectangle cBounds = c.getVisibleRect();
 278                 int relX = xOffset + x;
 279                 int relY = yOffset + y;
 280                 bsg.clipRect(xOffset + cBounds.x,
 281                              yOffset + cBounds.y,
 282                              cBounds.width, cBounds.height);
 283                 bsg.copyArea(relX, relY, w, h, deltaX, deltaY);
 284             }
 285             else {
 286                 bsg.copyArea(xOffset + x, yOffset + y, w, h, deltaX,
 287                              deltaY);
 288             }
 289             accumulate(x + xOffset + deltaX, y + yOffset + deltaY, w, h);
 290         } else {
 291             if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {
 292                 LOGGER.finer("copyArea: prepare failed or not in sync");
 293             }
 294             // Prepare failed, or not in sync. By calling super.copyArea
 295             // we'll copy on screen. We need to flush any pending paint to
 296             // the screen otherwise we'll do a copyArea on the wrong thing.
 297             if (!flushAccumulatedRegion()) {
 298                 // Flush failed, copyArea will be copying garbage,
 299                 // force repaint of all.
 300                 rootJ.repaint();
 301             } else {
 302                 super.copyArea(c, g, x, y, w, h, deltaX, deltaY, clip);
 303             }
 304         }
 305     }
 306 
 307     public void beginPaint() {
 308         synchronized(this) {
 309             painting = true;
 310             // Make sure another thread isn't attempting to show from
 311             // the back buffer.
 312             while(showing) {
 313                 try {
 314                     wait();
 315                 } catch (InterruptedException ie) {
 316                 }
 317             }
 318         }
 319         if (LOGGER.isLoggable(PlatformLogger.Level.FINEST)) {
 320             LOGGER.finest("beginPaint");
 321         }
 322         // Reset the area that needs to be painted.
 323         resetAccumulated();
 324     }
 325 
 326     public void endPaint() {
 327         if (LOGGER.isLoggable(PlatformLogger.Level.FINEST)) {
 328             LOGGER.finest("endPaint: region " + accumulatedX + " " +
 329                        accumulatedY + " " +  accumulatedMaxX + " " +
 330                        accumulatedMaxY);
 331         }
 332         if (painting) {
 333             if (!flushAccumulatedRegion()) {
 334                 if (!isRepaintingRoot()) {
 335                     repaintRoot(rootJ);
 336                 }
 337                 else {
 338                     // Contents lost twice in a row, punt.
 339                     resetDoubleBufferPerWindow();
 340                     // In case we've left junk on the screen, force a repaint.
 341                     rootJ.repaint();
 342                 }
 343             }
 344         }
 345 
 346         BufferInfo toDispose = null;
 347         synchronized(this) {
 348             painting = false;
 349             if (disposeBufferOnEnd) {
 350                 disposeBufferOnEnd = false;
 351                 toDispose = bufferInfo;
 352                 bufferInfos.remove(toDispose);
 353             }
 354         }
 355         if (toDispose != null) {
 356             toDispose.dispose();
 357         }
 358     }
 359 
 360     /**
 361      * Renders the BufferStrategy to the screen.
 362      *
 363      * @return true if successful, false otherwise.
 364      */
 365     private boolean flushAccumulatedRegion() {
 366         boolean success = true;
 367         if (accumulatedX != Integer.MAX_VALUE) {
 368             SubRegionShowable bsSubRegion = (SubRegionShowable)bufferStrategy;
 369             boolean contentsLost = bufferStrategy.contentsLost();
 370             if (!contentsLost) {
 371                 bsSubRegion.show(accumulatedX, accumulatedY,
 372                                  accumulatedMaxX, accumulatedMaxY);
 373                 contentsLost = bufferStrategy.contentsLost();
 374             }
 375             if (contentsLost) {
 376                 if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {
 377                     LOGGER.finer("endPaint: contents lost");
 378                 }
 379                 // Shown region was bogus, mark buffer as out of sync.
 380                 bufferInfo.setInSync(false);
 381                 success = false;
 382             }
 383         }
 384         resetAccumulated();
 385         return success;
 386     }
 387 
 388     private void resetAccumulated() {
 389         accumulatedX = Integer.MAX_VALUE;
 390         accumulatedY = Integer.MAX_VALUE;
 391         accumulatedMaxX = 0;
 392         accumulatedMaxY = 0;
 393     }
 394 
 395     /**
 396      * Invoked when the double buffering or useTrueDoubleBuffering
 397      * changes for a JRootPane.  If the rootpane is not double
 398      * buffered, or true double buffering changes we throw out any
 399      * cache we may have.
 400      */
 401     public void doubleBufferingChanged(final JRootPane rootPane) {
 402         if ((!rootPane.isDoubleBuffered() ||
 403                 !rootPane.getUseTrueDoubleBuffering()) &&
 404                 rootPane.getParent() != null) {
 405             if (!SwingUtilities.isEventDispatchThread()) {
 406                 Runnable updater = new Runnable() {
 407                     public void run() {
 408                         doubleBufferingChanged0(rootPane);
 409                     }
 410                 };
 411                 SwingUtilities.invokeLater(updater);
 412             }
 413             else {
 414                 doubleBufferingChanged0(rootPane);
 415             }
 416         }
 417     }
 418 
 419     /**
 420      * Does the work for doubleBufferingChanged.
 421      */
 422     private void doubleBufferingChanged0(JRootPane rootPane) {
 423         // This will only happen on the EDT.
 424         BufferInfo info;
 425         synchronized(this) {
 426             // Make sure another thread isn't attempting to show from
 427             // the back buffer.
 428             while(showing) {
 429                 try {
 430                     wait();
 431                 } catch (InterruptedException ie) {
 432                 }
 433             }
 434             info = getBufferInfo(rootPane.getParent());
 435             if (painting && bufferInfo == info) {
 436                 // We're in the process of painting and the user grabbed
 437                 // the Graphics. If we dispose now, endPaint will attempt
 438                 // to show a bogus BufferStrategy. Set a flag so that
 439                 // endPaint knows it needs to dispose this buffer.
 440                 disposeBufferOnEnd = true;
 441                 info = null;
 442             } else if (info != null) {
 443                 bufferInfos.remove(info);
 444             }
 445         }
 446         if (info != null) {
 447             info.dispose();
 448         }
 449     }
 450 
 451     /**
 452      * Calculates information common to paint/copyArea.
 453      *
 454      * @return true if should use buffering per window in painting.
 455      */
 456     private boolean prepare(JComponent c, Container root, boolean isPaint, int x, int y,
 457                             int w, int h) {
 458         if (bsg != null) {
 459             bsg.dispose();
 460             bsg = null;
 461         }
 462         bufferStrategy = null;
 463         if (root != null) {
 464             boolean contentsLost = false;
 465             BufferInfo bufferInfo = getBufferInfo(root);
 466             if (bufferInfo == null) {
 467                 contentsLost = true;
 468                 bufferInfo = new BufferInfo(root);
 469                 bufferInfos.add(bufferInfo);
 470                 if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {
 471                     LOGGER.finer("prepare: new BufferInfo: " + root);
 472                 }
 473             }
 474             this.bufferInfo = bufferInfo;
 475             if (!bufferInfo.hasBufferStrategyChanged()) {
 476                 bufferStrategy = bufferInfo.getBufferStrategy(true);
 477                 if (bufferStrategy != null) {
 478                     bsg = bufferStrategy.getDrawGraphics();
 479                     if (bufferStrategy.contentsRestored()) {
 480                         contentsLost = true;
 481                         if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {
 482                             LOGGER.finer("prepare: contents restored in prepare");
 483                         }
 484                     }
 485                 }
 486                 else {
 487                     // Couldn't create BufferStrategy, fallback to normal
 488                     // painting.
 489                     return false;
 490                 }
 491                 if (bufferInfo.getContentsLostDuringExpose()) {
 492                     contentsLost = true;
 493                     bufferInfo.setContentsLostDuringExpose(false);
 494                     if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {
 495                         LOGGER.finer("prepare: contents lost on expose");
 496                     }
 497                 }
 498                 if (isPaint && c == rootJ && x == 0 && y == 0 &&
 499                       c.getWidth() == w && c.getHeight() == h) {
 500                     bufferInfo.setInSync(true);
 501                 }
 502                 else if (contentsLost) {
 503                     // We either recreated the BufferStrategy, or the contents
 504                     // of the buffer strategy were restored.  We need to
 505                     // repaint the root pane so that the back buffer is in sync
 506                     // again.
 507                     bufferInfo.setInSync(false);
 508                     if (!isRepaintingRoot()) {
 509                         repaintRoot(rootJ);
 510                     }
 511                     else {
 512                         // Contents lost twice in a row, punt
 513                         resetDoubleBufferPerWindow();
 514                     }
 515                 }
 516                 return (bufferInfos != null);
 517             }
 518         }
 519         return false;
 520     }
 521 
 522     private Container fetchRoot(JComponent c) {
 523         boolean encounteredHW = false;
 524         rootJ = c;
 525         Container root = c;
 526         xOffset = yOffset = 0;
 527         while (root != null &&
 528                (!(root instanceof Window) &&
 529                 !SunToolkit.isInstanceOf(root, "java.applet.Applet"))) {
 530             xOffset += root.getX();
 531             yOffset += root.getY();
 532             root = root.getParent();
 533             if (root != null) {
 534                 if (root instanceof JComponent) {
 535                     rootJ = (JComponent)root;
 536                 }
 537                 else if (!root.isLightweight()) {
 538                     if (!encounteredHW) {
 539                         encounteredHW = true;
 540                     }
 541                     else {
 542                         // We've encountered two hws now and may have
 543                         // a containment hierarchy with lightweights containing
 544                         // heavyweights containing other lightweights.
 545                         // Heavyweights poke holes in lightweight
 546                         // rendering so that if we call show on the BS
 547                         // (which is associated with the Window) you will
 548                         // not see the contents over any child
 549                         // heavyweights.  If we didn't do this when we
 550                         // went to show the descendants of the nested hw
 551                         // you would see nothing, so, we bail out here.
 552                         return null;
 553                     }
 554                 }
 555             }
 556         }
 557         if ((root instanceof RootPaneContainer) &&
 558             (rootJ instanceof JRootPane)) {
 559             // We're in a Swing heavyeight (JFrame/JWindow...), use double
 560             // buffering if double buffering enabled on the JRootPane and
 561             // the JRootPane wants true double buffering.
 562             if (rootJ.isDoubleBuffered() &&
 563                     ((JRootPane)rootJ).getUseTrueDoubleBuffering()) {
 564                 // Whether or not a component is double buffered is a
 565                 // bit tricky with Swing. This gives a good approximation
 566                 // of the various ways to turn on double buffering for
 567                 // components.
 568                 return root;
 569             }
 570         }
 571         // Don't do true double buffering.
 572         return null;
 573     }
 574 
 575     /**
 576      * Turns off double buffering per window.
 577      */
 578     private void resetDoubleBufferPerWindow() {
 579         if (bufferInfos != null) {
 580             dispose(bufferInfos);
 581             bufferInfos = null;
 582             repaintManager.setPaintManager(null);
 583         }
 584     }
 585 
 586     /**
 587      * Returns the BufferInfo for the specified root or null if one
 588      * hasn't been created yet.
 589      */
 590     private BufferInfo getBufferInfo(Container root) {
 591         for (int counter = bufferInfos.size() - 1; counter >= 0; counter--) {
 592             BufferInfo bufferInfo = bufferInfos.get(counter);
 593             Container biRoot = bufferInfo.getRoot();
 594             if (biRoot == null) {
 595                 // Window gc'ed
 596                 bufferInfos.remove(counter);
 597                 if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {
 598                     LOGGER.finer("BufferInfo pruned, root null");
 599                 }
 600             }
 601             else if (biRoot == root) {
 602                 return bufferInfo;
 603             }
 604         }
 605         return null;
 606     }
 607 
 608     private void accumulate(int x, int y, int w, int h) {
 609         accumulatedX = Math.min(x, accumulatedX);
 610         accumulatedY = Math.min(y, accumulatedY);
 611         accumulatedMaxX = Math.max(accumulatedMaxX, x + w);
 612         accumulatedMaxY = Math.max(accumulatedMaxY, y + h);
 613     }
 614 
 615 
 616 
 617     /**
 618      * BufferInfo is used to track the BufferStrategy being used for
 619      * a particular Component.  In addition to tracking the BufferStrategy
 620      * it will install a WindowListener and ComponentListener.  When the
 621      * component is hidden/iconified the buffer is marked as needing to be
 622      * completely repainted.
 623      */
 624     private class BufferInfo extends ComponentAdapter implements
 625                                WindowListener {
 626         // NOTE: This class does NOT hold a direct reference to the root, if it
 627         // did there would be a cycle between the BufferPerWindowPaintManager
 628         // and the Window so that it could never be GC'ed
 629         //
 630         // Reference to BufferStrategy is referenced via WeakReference for
 631         // same reason.
 632         private WeakReference<BufferStrategy> weakBS;
 633         private WeakReference<Container> root;
 634         // Indicates whether or not the backbuffer and display are in sync.
 635         // This is set to true when a full repaint on the rootpane is done.
 636         private boolean inSync;
 637         // Indicates the contents were lost during and expose event.
 638         private boolean contentsLostDuringExpose;
 639         // Indicates we need to generate a paint event on expose.
 640         private boolean paintAllOnExpose;
 641 
 642 
 643         public BufferInfo(Container root) {
 644             this.root = new WeakReference<Container>(root);
 645             root.addComponentListener(this);
 646             if (root instanceof Window) {
 647                 ((Window)root).addWindowListener(this);
 648             }
 649         }
 650 
 651         public void setPaintAllOnExpose(boolean paintAllOnExpose) {
 652             this.paintAllOnExpose = paintAllOnExpose;
 653         }
 654 
 655         public boolean getPaintAllOnExpose() {
 656             return paintAllOnExpose;
 657         }
 658 
 659         public void setContentsLostDuringExpose(boolean value) {
 660             contentsLostDuringExpose = value;
 661         }
 662 
 663         public boolean getContentsLostDuringExpose() {
 664             return contentsLostDuringExpose;
 665         }
 666 
 667         public void setInSync(boolean inSync) {
 668             this.inSync = inSync;
 669         }
 670 
 671         /**
 672          * Whether or not the contents of the buffer strategy
 673          * is in sync with the window.  This is set to true when the root
 674          * pane paints all, and false when contents are lost/restored.
 675          */
 676         public boolean isInSync() {
 677             return inSync;
 678         }
 679 
 680         /**
 681          * Returns the Root (Window or Applet) that this BufferInfo references.
 682          */
 683         public Container getRoot() {
 684             return (root == null) ? null : root.get();
 685         }
 686 
 687         /**
 688          * Returns the BufferStartegy.  This will return null if
 689          * the BufferStartegy hasn't been created and <code>create</code> is
 690          * false, or if there is a problem in creating the
 691          * <code>BufferStartegy</code>.
 692          *
 693          * @param create If true, and the BufferStartegy is currently null,
 694          *               one will be created.
 695          */
 696         public BufferStrategy getBufferStrategy(boolean create) {
 697             BufferStrategy bs = (weakBS == null) ? null : weakBS.get();
 698             if (bs == null && create) {
 699                 bs = createBufferStrategy();
 700                 if (bs != null) {
 701                     weakBS = new WeakReference<BufferStrategy>(bs);
 702                 }
 703                 if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {
 704                     LOGGER.finer("getBufferStrategy: created bs: " + bs);
 705                 }
 706             }
 707             return bs;
 708         }
 709 
 710         /**
 711          * Returns true if the buffer strategy of the component differs
 712          * from current buffer strategy.
 713          */
 714         public boolean hasBufferStrategyChanged() {
 715             Container root = getRoot();
 716             if (root != null) {
 717                 BufferStrategy ourBS = null;
 718                 BufferStrategy componentBS = null;
 719 
 720                 ourBS = getBufferStrategy(false);
 721                 if (root instanceof Window) {
 722                     componentBS = ((Window)root).getBufferStrategy();
 723                 }
 724                 else {
 725                     componentBS = AWTAccessor.getComponentAccessor().getBufferStrategy(root);
 726                 }
 727                 if (componentBS != ourBS) {
 728                     // Component has a different BS, dispose ours.
 729                     if (ourBS != null) {
 730                         ourBS.dispose();
 731                     }
 732                     weakBS = null;
 733                     return true;
 734                 }
 735             }
 736             return false;
 737         }
 738 
 739         /**
 740          * Creates the BufferStrategy.  If the appropriate system property
 741          * has been set we'll try for flip first and then we'll try for
 742          * blit.
 743          */
 744         private BufferStrategy createBufferStrategy() {
 745             Container root = getRoot();
 746             if (root == null) {
 747                 return null;
 748             }
 749             BufferStrategy bs = null;
 750             if (SwingUtilities3.isVsyncRequested(root)) {
 751                 bs = createBufferStrategy(root, true);
 752                 if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {
 753                     LOGGER.finer("createBufferStrategy: using vsynced strategy");
 754                 }
 755             }
 756             if (bs == null) {
 757                 bs = createBufferStrategy(root, false);
 758             }
 759             if (!(bs instanceof SubRegionShowable)) {
 760                 // We do this for two reasons:
 761                 // 1. So that we know we can cast to SubRegionShowable and
 762                 //    invoke show with the minimal region to update
 763                 // 2. To avoid the possibility of invoking client code
 764                 //    on the toolkit thread.
 765                 bs = null;
 766             }
 767             return bs;
 768         }
 769 
 770         // Creates and returns a buffer strategy.  If
 771         // there is a problem creating the buffer strategy this will
 772         // eat the exception and return null.
 773         private BufferStrategy createBufferStrategy(Container root,
 774                 boolean isVsynced) {
 775             BufferCapabilities caps;
 776             if (isVsynced) {
 777                 caps = new ExtendedBufferCapabilities(
 778                     new ImageCapabilities(true), new ImageCapabilities(true),
 779                     BufferCapabilities.FlipContents.COPIED,
 780                     ExtendedBufferCapabilities.VSyncType.VSYNC_ON);
 781             } else {
 782                 caps = new BufferCapabilities(
 783                     new ImageCapabilities(true), new ImageCapabilities(true),
 784                     null);
 785             }
 786             BufferStrategy bs = null;
 787             if (SunToolkit.isInstanceOf(root, "java.applet.Applet")) {
 788                 try {
 789                     AWTAccessor.ComponentAccessor componentAccessor
 790                             = AWTAccessor.getComponentAccessor();
 791                     componentAccessor.createBufferStrategy(root, 2, caps);
 792                     bs = componentAccessor.getBufferStrategy(root);
 793                 } catch (AWTException e) {
 794                     // Type is not supported
 795                     if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {
 796                         LOGGER.finer("createBufferStratety failed",
 797                                      e);
 798                     }
 799                 }
 800             }
 801             else {
 802                 try {
 803                     ((Window)root).createBufferStrategy(2, caps);
 804                     bs = ((Window)root).getBufferStrategy();
 805                 } catch (AWTException e) {
 806                     // Type not supported
 807                     if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {
 808                         LOGGER.finer("createBufferStratety failed",
 809                                      e);
 810                     }
 811                 }
 812             }
 813             return bs;
 814         }
 815 
 816         /**
 817          * Cleans up and removes any references.
 818          */
 819         public void dispose() {
 820             Container root = getRoot();
 821             if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {
 822                 LOGGER.finer("disposed BufferInfo for: " + root);
 823             }
 824             if (root != null) {
 825                 root.removeComponentListener(this);
 826                 if (root instanceof Window) {
 827                     ((Window)root).removeWindowListener(this);
 828                 }
 829                 BufferStrategy bs = getBufferStrategy(false);
 830                 if (bs != null) {
 831                     bs.dispose();
 832                 }
 833             }
 834             this.root = null;
 835             weakBS = null;
 836         }
 837 
 838         // We mark the buffer as needing to be painted on a hide/iconify
 839         // because the developer may have conditionalized painting based on
 840         // visibility.
 841         // Ideally we would also move to having the BufferStrategy being
 842         // a SoftReference in Component here, but that requires changes to
 843         // Component and the like.
 844         public void componentHidden(ComponentEvent e) {
 845             Container root = getRoot();
 846             if (root != null && root.isVisible()) {
 847                 // This case will only happen if a developer calls
 848                 // hide immediately followed by show.  In this case
 849                 // the event is delivered after show and the window
 850                 // will still be visible.  If a developer altered the
 851                 // contents of the window between the hide/show
 852                 // invocations we won't recognize we need to paint and
 853                 // the contents would be bogus.  Calling repaint here
 854                 // fixs everything up.
 855                 root.repaint();
 856             }
 857             else {
 858                 setPaintAllOnExpose(true);
 859             }
 860         }
 861 
 862         public void windowIconified(WindowEvent e) {
 863             setPaintAllOnExpose(true);
 864         }
 865 
 866         // On a dispose we chuck everything.
 867         public void windowClosed(WindowEvent e) {
 868             // Make sure we're not showing.
 869             synchronized(BufferStrategyPaintManager.this) {
 870                 while (showing) {
 871                     try {
 872                         BufferStrategyPaintManager.this.wait();
 873                     } catch (InterruptedException ie) {
 874                     }
 875                 }
 876                 bufferInfos.remove(this);
 877             }
 878             dispose();
 879         }
 880 
 881         public void windowOpened(WindowEvent e) {
 882         }
 883 
 884         public void windowClosing(WindowEvent e) {
 885         }
 886 
 887         public void windowDeiconified(WindowEvent e) {
 888         }
 889 
 890         public void windowActivated(WindowEvent e) {
 891         }
 892 
 893         public void windowDeactivated(WindowEvent e) {
 894         }
 895     }
 896 }
--- EOF ---