1 /*
2  * Copyright (c) 2012, 2020, 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.AWTPermission;
29 import java.awt.DisplayMode;
30 import java.awt.GraphicsConfiguration;
31 import java.awt.GraphicsDevice;
32 import java.awt.Insets;
33 import java.awt.Rectangle;
34 import java.awt.Window;
35 import java.awt.geom.Rectangle2D;
36 import java.awt.peer.WindowPeer;
37 import java.util.Objects;
38 
39 import sun.java2d.SunGraphicsEnvironment;
40 import sun.java2d.opengl.CGLGraphicsConfig;
41 
42 import static java.awt.peer.ComponentPeer.SET_BOUNDS;
43 
44 public final class CGraphicsDevice extends GraphicsDevice
45         implements DisplayChangedListener {
46 
47     /**
48      * CoreGraphics display ID. This identifier can become non-valid at any time
49      * therefore methods, which is using this id should be ready to it.
50      */
51     private volatile int displayID;
52     private volatile double xResolution;
53     private volatile double yResolution;
54     private volatile Rectangle bounds;
55     private volatile int scale;
56 
57     private final GraphicsConfiguration config;
58 
59     private static AWTPermission fullScreenExclusivePermission;
60 
61     // Save/restore DisplayMode for the Full Screen mode
62     private DisplayMode originalMode;
63 
CGraphicsDevice(final int displayID)64     public CGraphicsDevice(final int displayID) {
65         this.displayID = displayID;
66         config = CGLGraphicsConfig.getConfig(this);
67         // initializes default device state, might be redundant step since we
68         // call "displayChanged()" later anyway, but we do not want to leave the
69         // device in an inconsistent state after construction
70         displayChanged();
71     }
72 
73     /**
74      * Return a list of all configurations.
75      */
76     @Override
getConfigurations()77     public GraphicsConfiguration[] getConfigurations() {
78         return new GraphicsConfiguration[]{config};
79     }
80 
81     /**
82      * Return the default configuration.
83      */
84     @Override
getDefaultConfiguration()85     public GraphicsConfiguration getDefaultConfiguration() {
86         return config;
87     }
88 
89     /**
90      * Return a human-readable screen description.
91      */
92     @Override
getIDstring()93     public String getIDstring() {
94         return "Display " + displayID;
95     }
96 
97     /**
98      * Returns the type of the graphics device.
99      * @see #TYPE_RASTER_SCREEN
100      * @see #TYPE_PRINTER
101      * @see #TYPE_IMAGE_BUFFER
102      */
103     @Override
getType()104     public int getType() {
105         return TYPE_RASTER_SCREEN;
106     }
107 
getXResolution()108     public double getXResolution() {
109         return xResolution;
110     }
111 
getYResolution()112     public double getYResolution() {
113         return yResolution;
114     }
115 
getBounds()116     Rectangle getBounds() {
117         return bounds.getBounds();
118     }
119 
getScreenInsets()120     public Insets getScreenInsets() {
121         // the insets are queried synchronously and are not cached
122         // since there are no Quartz or Cocoa means to receive notifications
123         // on insets changes (e.g. when the Dock is resized):
124         // the existing CGDisplayReconfigurationCallBack is not notified
125         // as well as the NSApplicationDidChangeScreenParametersNotification
126         // is fired on the Dock location changes only
127         return nativeGetScreenInsets(displayID);
128     }
129 
getScaleFactor()130     public int getScaleFactor() {
131         return scale;
132     }
133 
134     /**
135      * Invalidates this device so it will point to some other "new" device.
136      *
137      * @param  device the new device, usually the main screen
138      */
invalidate(CGraphicsDevice device)139     public void invalidate(CGraphicsDevice device) {
140         //TODO do we need to restore the full-screen window/modes on old device?
141         displayID = device.displayID;
142     }
143 
144     @Override
displayChanged()145     public void displayChanged() {
146         xResolution = nativeGetXResolution(displayID);
147         yResolution = nativeGetYResolution(displayID);
148         bounds = nativeGetBounds(displayID).getBounds(); //does integer rounding
149         initScaleFactor();
150         resizeFSWindow(getFullScreenWindow(), bounds);
151         //TODO configs?
152     }
153 
154     @Override
paletteChanged()155     public void paletteChanged() {
156         // devices do not need to react to this event.
157     }
158 
159     /**
160      * Enters full-screen mode, or returns to windowed mode.
161      */
162     @Override
setFullScreenWindow(Window w)163     public synchronized void setFullScreenWindow(Window w) {
164         Window old = getFullScreenWindow();
165         if (w == old) {
166             return;
167         }
168 
169         boolean fsSupported = isFullScreenSupported();
170 
171         if (fsSupported && old != null) {
172             // enter windowed mode and restore original display mode
173             exitFullScreenExclusive(old);
174             if (originalMode != null) {
175                 setDisplayMode(originalMode);
176                 originalMode = null;
177             }
178         }
179 
180         super.setFullScreenWindow(w);
181 
182         if (fsSupported && w != null) {
183             if (isDisplayChangeSupported()) {
184                 originalMode = getDisplayMode();
185             }
186             // enter fullscreen mode
187             enterFullScreenExclusive(w);
188         }
189     }
190 
191     /**
192      * Returns true if this GraphicsDevice supports
193      * full-screen exclusive mode and false otherwise.
194      */
195     @Override
isFullScreenSupported()196     public boolean isFullScreenSupported() {
197         return isFSExclusiveModeAllowed();
198     }
199 
isFSExclusiveModeAllowed()200     private static boolean isFSExclusiveModeAllowed() {
201         SecurityManager security = System.getSecurityManager();
202         if (security != null) {
203             if (fullScreenExclusivePermission == null) {
204                 fullScreenExclusivePermission =
205                     new AWTPermission("fullScreenExclusive");
206             }
207             try {
208                 security.checkPermission(fullScreenExclusivePermission);
209             } catch (SecurityException e) {
210                 return false;
211             }
212         }
213         return true;
214     }
215 
enterFullScreenExclusive(Window w)216     private static void enterFullScreenExclusive(Window w) {
217         FullScreenCapable peer = AWTAccessor.getComponentAccessor().getPeer(w);
218         if (peer != null) {
219             peer.enterFullScreenMode();
220         }
221     }
222 
exitFullScreenExclusive(Window w)223     private static void exitFullScreenExclusive(Window w) {
224         FullScreenCapable peer = AWTAccessor.getComponentAccessor().getPeer(w);
225         if (peer != null) {
226             peer.exitFullScreenMode();
227         }
228     }
229 
230     /**
231      * Reapplies the size of this device to the full-screen window.
232      */
resizeFSWindow(final Window w, final Rectangle b)233     private static void resizeFSWindow(final Window w, final Rectangle b) {
234         if (w != null) {
235             WindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(w);
236             if (peer != null) {
237                 peer.setBounds(b.x, b.y, b.width, b.height, SET_BOUNDS);
238             }
239         }
240     }
241 
242     @Override
isDisplayChangeSupported()243     public boolean isDisplayChangeSupported() {
244         return true;
245     }
246 
247     @Override
setDisplayMode(final DisplayMode dm)248     public void setDisplayMode(final DisplayMode dm) {
249         if (dm == null) {
250             throw new IllegalArgumentException("Invalid display mode");
251         }
252         if (!Objects.equals(dm, getDisplayMode())) {
253             nativeSetDisplayMode(displayID, dm.getWidth(), dm.getHeight(),
254                                  dm.getBitDepth(), dm.getRefreshRate());
255         }
256     }
257 
258     @Override
getDisplayMode()259     public DisplayMode getDisplayMode() {
260         return nativeGetDisplayMode(displayID);
261     }
262 
263     @Override
getDisplayModes()264     public DisplayMode[] getDisplayModes() {
265         return nativeGetDisplayModes(displayID);
266     }
267 
initScaleFactor()268     private void initScaleFactor() {
269         if (SunGraphicsEnvironment.isUIScaleEnabled()) {
270             double debugScale = SunGraphicsEnvironment.getDebugScale();
271             scale = (int) (debugScale >= 1
272                     ? Math.round(debugScale)
273                     : nativeGetScaleFactor(displayID));
274         } else {
275             scale = 1;
276         }
277     }
278 
nativeGetScaleFactor(int displayID)279     private static native double nativeGetScaleFactor(int displayID);
280 
nativeSetDisplayMode(int displayID, int w, int h, int bpp, int refrate)281     private static native void nativeSetDisplayMode(int displayID, int w, int h, int bpp, int refrate);
282 
nativeGetDisplayMode(int displayID)283     private static native DisplayMode nativeGetDisplayMode(int displayID);
284 
nativeGetDisplayModes(int displayID)285     private static native DisplayMode[] nativeGetDisplayModes(int displayID);
286 
nativeGetXResolution(int displayID)287     private static native double nativeGetXResolution(int displayID);
288 
nativeGetYResolution(int displayID)289     private static native double nativeGetYResolution(int displayID);
290 
nativeGetScreenInsets(int displayID)291     private static native Insets nativeGetScreenInsets(int displayID);
292 
nativeGetBounds(int displayID)293     private static native Rectangle2D nativeGetBounds(int displayID);
294 }
295