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 } |