modules/graphics/src/main/java/javafx/stage/Screen.java

Print this page




  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 javafx.stage;
  27 
  28 import java.util.List;
  29 import java.util.concurrent.atomic.AtomicBoolean;
  30 
  31 import javafx.collections.FXCollections;
  32 import javafx.collections.ObservableList;
  33 import javafx.geometry.Rectangle2D;
  34 
  35 import com.sun.javafx.stage.ScreenHelper;
  36 import com.sun.javafx.tk.ScreenConfigurationAccessor;
  37 import com.sun.javafx.tk.Toolkit;
  38 
  39 /**
  40  * Describes the characteristics of a graphics destination such as monitor.
  41  * In a virtual device multi-screen environment in which the desktop area
  42  * could span multiple physical screen devices, the bounds of the
  43  * {@code Screen} objects are relative to the {@code Screen.primary}.
  44  *
  45  * <p>
  46  * For example:
  47  * <pre><code>
  48  * Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();
  49  *
  50  * //set Stage boundaries to visible bounds of the main screen
  51  * stage.setX(primaryScreenBounds.getMinX());
  52  * stage.setY(primaryScreenBounds.getMinY());
  53  * stage.setWidth(primaryScreenBounds.getWidth());
  54  * stage.setHeight(primaryScreenBounds.getHeight());
  55  *
  56  * stage.show();
  57  * </code></pre>
  58  * </p>
  59  * @since JavaFX 2.0
  60  */
  61 public final class Screen {
  62 
  63     private static final AtomicBoolean configurationDirty =
  64             new AtomicBoolean(true);
  65 
  66     private static final ScreenConfigurationAccessor accessor;
  67 
  68     private static Screen primary;
  69     private static final ObservableList<Screen> screens =
  70             FXCollections.<Screen>observableArrayList();
  71     private static final ObservableList<Screen> unmodifiableScreens =
  72             FXCollections.unmodifiableObservableList(screens);
  73 
  74     static {
  75         ScreenHelper.setScreenAccessor(new ScreenHelper.ScreenAccessor() {
  76             @Override public float getRenderScale(Screen screen) { return screen.getRenderScale(); }
  77         });
  78 
  79         accessor = Toolkit.getToolkit().setScreenConfigurationListener(() -> updateConfiguration());
  80     }
  81 
  82     private Screen() {
  83     }
  84 
  85     private static void checkDirty() {
  86         if (configurationDirty.compareAndSet(true, false)) {
  87             updateConfiguration();
  88         }
  89     }
  90 
  91     private static void updateConfiguration() {
  92         Object primaryScreen = Toolkit.getToolkit().getPrimaryScreen();
  93         Screen screenTmp = nativeToScreen(primaryScreen, Screen.primary);
  94         if (screenTmp != null) {
  95             Screen.primary = screenTmp;
  96         }
  97 
  98         List<?> screens = Toolkit.getToolkit().getScreens();


 121         }
 122         if (!canKeepOld) {
 123             Screen.screens.clear();
 124             Screen.screens.addAll(newScreens);
 125         }
 126 
 127         configurationDirty.set(false);
 128     }
 129 
 130     // returns null if the new one is to be equal the old one
 131     private static Screen nativeToScreen(Object obj, Screen screen) {
 132         int minX = accessor.getMinX(obj);
 133         int minY = accessor.getMinY(obj);
 134         int width = accessor.getWidth(obj);
 135         int height = accessor.getHeight(obj);
 136         int visualMinX = accessor.getVisualMinX(obj);
 137         int visualMinY = accessor.getVisualMinY(obj);
 138         int visualWidth = accessor.getVisualWidth(obj);
 139         int visualHeight = accessor.getVisualHeight(obj);
 140         double dpi = accessor.getDPI(obj);
 141         float renderScale = accessor.getRenderScale(obj);

 142         if ((screen == null) ||
 143             (screen.bounds.getMinX() != minX) ||
 144             (screen.bounds.getMinY() != minY) ||
 145             (screen.bounds.getWidth() != width) ||
 146             (screen.bounds.getHeight() != height) ||
 147             (screen.visualBounds.getMinX() != visualMinX) ||
 148             (screen.visualBounds.getMinY() != visualMinY) ||
 149             (screen.visualBounds.getWidth() != visualWidth) ||
 150             (screen.visualBounds.getHeight() != visualHeight) ||
 151             (screen.dpi != dpi) ||
 152             (screen.renderScale != renderScale))

 153         {
 154             Screen s = new Screen();
 155             s.bounds = new Rectangle2D(minX, minY, width, height);
 156             s.visualBounds = new Rectangle2D(visualMinX, visualMinY, visualWidth, visualHeight);
 157             s.dpi = dpi;
 158             s.renderScale = renderScale;

 159             return s;
 160         } else {
 161             return null;
 162         }
 163     }
 164 
 165     static Screen getScreenForNative(Object obj) {
 166         double x = accessor.getMinX(obj);
 167         double y = accessor.getMinY(obj);
 168         double w = accessor.getWidth(obj);
 169         double h = accessor.getHeight(obj);
 170         Screen intScr = null;
 171         for (int i = 0; i < screens.size(); i++) {
 172             Screen scr = screens.get(i);
 173             if (scr.bounds.contains(x, y, w, h)) {
 174                 return scr;
 175             }
 176             if (intScr == null && scr.bounds.intersects(x, y, w, h)) {
 177                 intScr = scr;
 178             }


 222     }
 223 
 224     /**
 225       * Returns a ObservableList of {@code Screens} that intersects the provided rectangle.
 226       *
 227       * @param r The specified {@code Rectangle2D}
 228       * @return a ObservableList of {@code Screens} for which {@code Screen.bounds}
 229       *   intersects the provided rectangle
 230       */
 231     public static ObservableList<Screen> getScreensForRectangle(Rectangle2D r) {
 232         checkDirty();
 233         return getScreensForRectangle(r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight());
 234     }
 235 
 236     /**
 237      * The bounds of this {@code Screen}.
 238      */
 239     private Rectangle2D bounds = Rectangle2D.EMPTY;
 240     /**
 241      * Gets the bounds of this {@code Screen}.




 242      * @return The bounds of this {@code Screen}
 243      */
 244     public final Rectangle2D getBounds() {
 245         return bounds;
 246     }
 247 
 248     /**
 249      * The visual bounds of this {@code Screen}.
 250      *
 251      * These bounds account for objects in the native windowing system such as
 252      * task bars and menu bars. These bounds are contained by {@code Screen.bounds}.
 253      */
 254     private Rectangle2D visualBounds = Rectangle2D.EMPTY;
 255     /**
 256      * Gets the visual bounds of this {@code Screen}.
 257      *
 258      * These bounds account for objects in the native windowing system such as
 259      * task bars and menu bars. These bounds are contained by {@code Screen.bounds}.
 260      * @return The visual bounds of this {@code Screen}
 261      */
 262     public final Rectangle2D getVisualBounds() {
 263         return visualBounds;
 264     }
 265 
 266     /**
 267       * The resolution (dots per inch) of this {@code Screen}.
 268       */
 269     private double dpi;
 270     /**
 271      * Gets the resolution (dots per inch) of this {@code Screen}.
 272      * @return The resolution of this @{code Screen}
 273      */
 274     public final double getDpi() {
 275         return dpi;
 276     }
 277 
 278     /**
 279      * The scale factor of this {@code Screen}.



















 280      */
 281     private float renderScale;
 282 
 283     /**
 284      * Gets the scale factor of this {@code Screen}.
 285      * E.g. on Retina displays on Mac the scale factor may be equal to 2.0.
 286      * On regular displays this method returns 1.0.



 287      */
 288     private float getRenderScale() {
 289         return renderScale;
 290     }
 291 
 292     /**
 293      * Returns a hash code for this {@code Screen} object.
 294      * @return a hash code for this {@code Screen} object.
 295      */
 296     @Override public int hashCode() {
 297         long bits = 7L;
 298         bits = 37L * bits + bounds.hashCode();
 299         bits = 37L * bits + visualBounds.hashCode();
 300         bits = 37L * bits + Double.doubleToLongBits(dpi);
 301         bits = 37L * bits + Float.floatToIntBits(renderScale);

 302         return (int) (bits ^ (bits >> 32));
 303     }
 304 
 305     /**
 306      * Indicates whether some other object is "equal to" this one.
 307      * @param obj the reference object with which to compare.
 308      * @return {@code true} if this object is equal to the {@code obj} argument; {@code false} otherwise.
 309      */
 310     @Override public boolean equals(Object obj) {
 311         if (obj == this) return true;
 312         if (obj instanceof Screen) {
 313             Screen other = (Screen) obj;
 314             return (bounds == null ? other.bounds == null : bounds.equals(other.bounds))
 315               && (visualBounds == null ? other.visualBounds == null : visualBounds.equals(other.visualBounds))
 316               && other.dpi == dpi
 317               && other.renderScale == renderScale;
 318         } else return false;
 319     }
 320 
 321     /**
 322      * Returns a string representation of this {@code Screen} object.
 323      * @return a string representation of this {@code Screen} object.
 324      */
 325     @Override public String toString() {
 326         return super.toString() + " bounds:" + bounds + " visualBounds:" + visualBounds + " dpi:" + dpi + " renderScale:" + renderScale;

 327     }
 328 }


  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 javafx.stage;
  27 
  28 import java.util.List;
  29 import java.util.concurrent.atomic.AtomicBoolean;
  30 
  31 import javafx.collections.FXCollections;
  32 import javafx.collections.ObservableList;
  33 import javafx.geometry.Rectangle2D;
  34 

  35 import com.sun.javafx.tk.ScreenConfigurationAccessor;
  36 import com.sun.javafx.tk.Toolkit;
  37 
  38 /**
  39  * Describes the characteristics of a graphics destination such as monitor.
  40  * In a virtual device multi-screen environment in which the desktop area
  41  * could span multiple physical screen devices, the bounds of the
  42  * {@code Screen} objects are relative to the {@code Screen.primary}.
  43  *
  44  * <p>
  45  * For example:
  46  * <pre><code>
  47  * Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();
  48  *
  49  * //set Stage boundaries to visible bounds of the main screen
  50  * stage.setX(primaryScreenBounds.getMinX());
  51  * stage.setY(primaryScreenBounds.getMinY());
  52  * stage.setWidth(primaryScreenBounds.getWidth());
  53  * stage.setHeight(primaryScreenBounds.getHeight());
  54  *
  55  * stage.show();
  56  * </code></pre>
  57  * </p>
  58  * @since JavaFX 2.0
  59  */
  60 public final class Screen {
  61 
  62     private static final AtomicBoolean configurationDirty =
  63             new AtomicBoolean(true);
  64 
  65     private static final ScreenConfigurationAccessor accessor;
  66 
  67     private static Screen primary;
  68     private static final ObservableList<Screen> screens =
  69             FXCollections.<Screen>observableArrayList();
  70     private static final ObservableList<Screen> unmodifiableScreens =
  71             FXCollections.unmodifiableObservableList(screens);
  72 
  73     static {




  74         accessor = Toolkit.getToolkit().setScreenConfigurationListener(() -> updateConfiguration());
  75     }
  76 
  77     private Screen() {
  78     }
  79 
  80     private static void checkDirty() {
  81         if (configurationDirty.compareAndSet(true, false)) {
  82             updateConfiguration();
  83         }
  84     }
  85 
  86     private static void updateConfiguration() {
  87         Object primaryScreen = Toolkit.getToolkit().getPrimaryScreen();
  88         Screen screenTmp = nativeToScreen(primaryScreen, Screen.primary);
  89         if (screenTmp != null) {
  90             Screen.primary = screenTmp;
  91         }
  92 
  93         List<?> screens = Toolkit.getToolkit().getScreens();


 116         }
 117         if (!canKeepOld) {
 118             Screen.screens.clear();
 119             Screen.screens.addAll(newScreens);
 120         }
 121 
 122         configurationDirty.set(false);
 123     }
 124 
 125     // returns null if the new one is to be equal the old one
 126     private static Screen nativeToScreen(Object obj, Screen screen) {
 127         int minX = accessor.getMinX(obj);
 128         int minY = accessor.getMinY(obj);
 129         int width = accessor.getWidth(obj);
 130         int height = accessor.getHeight(obj);
 131         int visualMinX = accessor.getVisualMinX(obj);
 132         int visualMinY = accessor.getVisualMinY(obj);
 133         int visualWidth = accessor.getVisualWidth(obj);
 134         int visualHeight = accessor.getVisualHeight(obj);
 135         double dpi = accessor.getDPI(obj);
 136         float outScaleX = accessor.getRecommendedOutputScaleX(obj);
 137         float outScaleY = accessor.getRecommendedOutputScaleY(obj);
 138         if ((screen == null) ||
 139             (screen.bounds.getMinX() != minX) ||
 140             (screen.bounds.getMinY() != minY) ||
 141             (screen.bounds.getWidth() != width) ||
 142             (screen.bounds.getHeight() != height) ||
 143             (screen.visualBounds.getMinX() != visualMinX) ||
 144             (screen.visualBounds.getMinY() != visualMinY) ||
 145             (screen.visualBounds.getWidth() != visualWidth) ||
 146             (screen.visualBounds.getHeight() != visualHeight) ||
 147             (screen.dpi != dpi) ||
 148             (screen.outputScaleX != outScaleX) ||
 149             (screen.outputScaleY != outScaleY))
 150         {
 151             Screen s = new Screen();
 152             s.bounds = new Rectangle2D(minX, minY, width, height);
 153             s.visualBounds = new Rectangle2D(visualMinX, visualMinY, visualWidth, visualHeight);
 154             s.dpi = dpi;
 155             s.outputScaleX = outScaleX;
 156             s.outputScaleY = outScaleY;
 157             return s;
 158         } else {
 159             return null;
 160         }
 161     }
 162 
 163     static Screen getScreenForNative(Object obj) {
 164         double x = accessor.getMinX(obj);
 165         double y = accessor.getMinY(obj);
 166         double w = accessor.getWidth(obj);
 167         double h = accessor.getHeight(obj);
 168         Screen intScr = null;
 169         for (int i = 0; i < screens.size(); i++) {
 170             Screen scr = screens.get(i);
 171             if (scr.bounds.contains(x, y, w, h)) {
 172                 return scr;
 173             }
 174             if (intScr == null && scr.bounds.intersects(x, y, w, h)) {
 175                 intScr = scr;
 176             }


 220     }
 221 
 222     /**
 223       * Returns a ObservableList of {@code Screens} that intersects the provided rectangle.
 224       *
 225       * @param r The specified {@code Rectangle2D}
 226       * @return a ObservableList of {@code Screens} for which {@code Screen.bounds}
 227       *   intersects the provided rectangle
 228       */
 229     public static ObservableList<Screen> getScreensForRectangle(Rectangle2D r) {
 230         checkDirty();
 231         return getScreensForRectangle(r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight());
 232     }
 233 
 234     /**
 235      * The bounds of this {@code Screen}.
 236      */
 237     private Rectangle2D bounds = Rectangle2D.EMPTY;
 238     /**
 239      * Gets the bounds of this {@code Screen}.
 240      * The bounds will be reported adjusted for the {@code outputScale} so
 241      * that resizing a {@code Window} with these bounds and the same
 242      * {@code outputScale} as this {@code Screen} will cover the entire
 243      * screen.
 244      * @return The bounds of this {@code Screen}
 245      */
 246     public final Rectangle2D getBounds() {
 247         return bounds;
 248     }
 249 
 250     /**
 251      * The visual bounds of this {@code Screen}.
 252      *
 253      * These bounds account for objects in the native windowing system such as
 254      * task bars and menu bars. These bounds are contained by {@code Screen.bounds}.
 255      */
 256     private Rectangle2D visualBounds = Rectangle2D.EMPTY;
 257     /**
 258      * Gets the visual bounds of this {@code Screen}.
 259      *
 260      * These bounds account for objects in the native windowing system such as
 261      * task bars and menu bars. These bounds are contained by {@code Screen.bounds}.
 262      * @return The visual bounds of this {@code Screen}
 263      */
 264     public final Rectangle2D getVisualBounds() {
 265         return visualBounds;
 266     }
 267 
 268     /**
 269       * The resolution (dots per inch) of this {@code Screen}.
 270       */
 271     private double dpi;
 272     /**
 273      * Gets the resolution (dots per inch) of this {@code Screen}.
 274      * @return The resolution of this @{code Screen}
 275      */
 276     public final double getDpi() {
 277         return dpi;
 278     }
 279 
 280     /**
 281      * The recommended output scale factor of this {@code Screen} in the
 282      * X direction.
 283      */
 284     private float outputScaleX;
 285 
 286     /**
 287      * Gets the recommended output scale factor of this {@code Screen} in
 288      * the horizontal ({@code X}) direction.
 289      * This scale factor should be applied to a scene in order to compensate
 290      * for the resolution and viewing distance of the output device.
 291      * The visual bounds will be reported relative to this scale factor.
 292      * @return the recommended output scale factor for the screen.
 293      */
 294     public final double getOutputScaleX() {
 295         return outputScaleX;
 296     }
 297 
 298     /**
 299      * The recommended output scale factor of this {@code Screen} in the
 300      * Y direction.
 301      */
 302     private float outputScaleY;
 303 
 304     /**
 305      * Gets the recommended output scale factor of this {@code Screen} in
 306      * the vertical ({@code Y}) direction.
 307      * This scale factor will be applied to the scene in order to compensate
 308      * for the resolution and viewing distance of the output device.
 309      * The visual bounds will be reported relative to this scale factor.
 310      * @return the recommended output scale factor for the screen.
 311      */
 312     public final double getOutputScaleY() {
 313         return outputScaleY;
 314     }
 315 
 316     /**
 317      * Returns a hash code for this {@code Screen} object.
 318      * @return a hash code for this {@code Screen} object.
 319      */
 320     @Override public int hashCode() {
 321         long bits = 7L;
 322         bits = 37L * bits + bounds.hashCode();
 323         bits = 37L * bits + visualBounds.hashCode();
 324         bits = 37L * bits + Double.doubleToLongBits(dpi);
 325         bits = 37L * bits + Float.floatToIntBits(outputScaleX);
 326         bits = 37L * bits + Float.floatToIntBits(outputScaleY);
 327         return (int) (bits ^ (bits >> 32));
 328     }
 329 
 330     /**
 331      * Indicates whether some other object is "equal to" this one.
 332      * @param obj the reference object with which to compare.
 333      * @return {@code true} if this object is equal to the {@code obj} argument; {@code false} otherwise.
 334      */
 335     @Override public boolean equals(Object obj) {
 336         if (obj == this) return true;
 337         if (obj instanceof Screen) {
 338             Screen other = (Screen) obj;
 339             return (bounds == null ? other.bounds == null : bounds.equals(other.bounds))
 340               && (visualBounds == null ? other.visualBounds == null : visualBounds.equals(other.visualBounds))
 341               && other.dpi == dpi
 342               && other.outputScaleX == outputScaleX && other.outputScaleY == outputScaleY;
 343         } else return false;
 344     }
 345 
 346     /**
 347      * Returns a string representation of this {@code Screen} object.
 348      * @return a string representation of this {@code Screen} object.
 349      */
 350     @Override public String toString() {
 351         return super.toString() + " bounds:" + bounds + " visualBounds:" + visualBounds + " dpi:"
 352              + dpi + " outputScale:(" + outputScaleX + "," + outputScaleY + ")";
 353     }
 354 }