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