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