1 /*
   2  * Copyright (c) 2000, 2007, 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 
  26 package sun.awt;
  27 
  28 import java.awt.IllegalComponentStateException;
  29 import java.util.Collections;
  30 import java.util.Iterator;
  31 import java.util.HashMap;
  32 import java.util.HashSet;
  33 import java.util.Map;
  34 import java.util.Set;
  35 import java.util.WeakHashMap;
  36 
  37 import sun.util.logging.PlatformLogger;
  38 
  39 /**
  40  * This class is used to aid in keeping track of DisplayChangedListeners and
  41  * notifying them when a display change has taken place. DisplayChangedListeners
  42  * are notified when the display's bit depth is changed, or when a top-level
  43  * window has been dragged onto another screen.
  44  *
  45  * It is safe for a DisplayChangedListener to be added while the list is being
  46  * iterated.
  47  *
  48  * The displayChanged() call is propagated after some occurrence (either
  49  * due to user action or some other application) causes the display mode
  50  * (e.g., depth or resolution) to change.  All heavyweight components need
  51  * to know when this happens because they need to create new surfaceData
  52  * objects based on the new depth.
  53  *
  54  * displayChanged() is also called on Windows when they are moved from one
  55  * screen to another on a system equipped with multiple displays.
  56  */
  57 public class SunDisplayChanger {
  58 
  59     private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.multiscreen.SunDisplayChanger");
  60 
  61     // Create a new synchronized map with initial capacity of one listener.
  62     // It is asserted that the most common case is to have one GraphicsDevice
  63     // and one top-level Window.
  64     private Map<DisplayChangedListener, Void> listeners =
  65         Collections.synchronizedMap(new WeakHashMap<DisplayChangedListener, Void>(1));
  66 
  67     public SunDisplayChanger() {}
  68 
  69     /*
  70      * Add a DisplayChangeListener to this SunDisplayChanger so that it is
  71      * notified when the display is changed.
  72      */
  73     public void add(DisplayChangedListener theListener) {
  74         if (log.isLoggable(PlatformLogger.Level.FINE)) {
  75             if (theListener == null) {
  76                 log.fine("Assertion (theListener != null) failed");
  77             }
  78         }
  79         if (log.isLoggable(PlatformLogger.Level.FINER)) {
  80             log.finer("Adding listener: " + theListener);
  81         }
  82         listeners.put(theListener, null);
  83     }
  84 
  85     /*
  86      * Remove the given DisplayChangeListener from this SunDisplayChanger.
  87      */
  88     public void remove(DisplayChangedListener theListener) {
  89         if (log.isLoggable(PlatformLogger.Level.FINE)) {
  90             if (theListener == null) {
  91                 log.fine("Assertion (theListener != null) failed");
  92             }
  93         }
  94         if (log.isLoggable(PlatformLogger.Level.FINER)) {
  95             log.finer("Removing listener: " + theListener);
  96         }
  97         listeners.remove(theListener);
  98     }
  99 
 100     /*
 101      * Notify our list of DisplayChangedListeners that a display change has
 102      * taken place by calling their displayChanged() methods.
 103      */
 104     public void notifyListeners() {
 105         if (log.isLoggable(PlatformLogger.Level.FINEST)) {
 106             log.finest("notifyListeners");
 107         }
 108     // This method is implemented by making a clone of the set of listeners,
 109     // and then iterating over the clone.  This is because during the course
 110     // of responding to a display change, it may be appropriate for a
 111     // DisplayChangedListener to add or remove itself from a SunDisplayChanger.
 112     // If the set itself were iterated over, rather than a clone, it is
 113     // trivial to get a ConcurrentModificationException by having a
 114     // DisplayChangedListener remove itself from its list.
 115     // Because all display change handling is done on the event thread,
 116     // synchronization provides no protection against modifying the listener
 117     // list while in the middle of iterating over it.  -bchristi 7/10/2001
 118 
 119         Set<DisplayChangedListener> cloneSet;
 120 
 121         synchronized(listeners) {
 122             cloneSet = new HashSet<DisplayChangedListener>(listeners.keySet());
 123         }
 124 
 125         Iterator<DisplayChangedListener> itr = cloneSet.iterator();
 126         while (itr.hasNext()) {
 127             DisplayChangedListener current = itr.next();
 128             try {
 129                 if (log.isLoggable(PlatformLogger.Level.FINEST)) {
 130                     log.finest("displayChanged for listener: " + current);
 131                 }
 132                 current.displayChanged();
 133             } catch (IllegalComponentStateException e) {
 134                 // This DisplayChangeListener is no longer valid.  Most
 135                 // likely, a top-level window was dispose()d, but its
 136                 // Java objects have not yet been garbage collected.  In any
 137                 // case, we no longer need to track this listener, though we
 138                 // do need to remove it from the original list, not the clone.
 139                 listeners.remove(current);
 140             }
 141         }
 142     }
 143 
 144     /*
 145      * Notify our list of DisplayChangedListeners that a palette change has
 146      * taken place by calling their paletteChanged() methods.
 147      */
 148     public void notifyPaletteChanged() {
 149         if (log.isLoggable(PlatformLogger.Level.FINEST)) {
 150             log.finest("notifyPaletteChanged");
 151         }
 152     // This method is implemented by making a clone of the set of listeners,
 153     // and then iterating over the clone.  This is because during the course
 154     // of responding to a display change, it may be appropriate for a
 155     // DisplayChangedListener to add or remove itself from a SunDisplayChanger.
 156     // If the set itself were iterated over, rather than a clone, it is
 157     // trivial to get a ConcurrentModificationException by having a
 158     // DisplayChangedListener remove itself from its list.
 159     // Because all display change handling is done on the event thread,
 160     // synchronization provides no protection against modifying the listener
 161     // list while in the middle of iterating over it.  -bchristi 7/10/2001
 162 
 163         Set<DisplayChangedListener> cloneSet;
 164 
 165         synchronized (listeners) {
 166             cloneSet = new HashSet<DisplayChangedListener>(listeners.keySet());
 167         }
 168         Iterator<DisplayChangedListener> itr = cloneSet.iterator();
 169         while (itr.hasNext()) {
 170             DisplayChangedListener current = itr.next();
 171             try {
 172                 if (log.isLoggable(PlatformLogger.Level.FINEST)) {
 173                     log.finest("paletteChanged for listener: " + current);
 174                 }
 175                 current.paletteChanged();
 176             } catch (IllegalComponentStateException e) {
 177                 // This DisplayChangeListener is no longer valid.  Most
 178                 // likely, a top-level window was dispose()d, but its
 179                 // Java objects have not yet been garbage collected.  In any
 180                 // case, we no longer need to track this listener, though we
 181                 // do need to remove it from the original list, not the clone.
 182                 listeners.remove(current);
 183             }
 184         }
 185     }
 186 }