1/*
2 * Copyright (c) 2011, 2017, 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.*;
29import java.util.*;
30
31import sun.java2d.*;
32
33/**
34 * This is an implementation of a GraphicsEnvironment object for the default
35 * local GraphicsEnvironment used by the Java Runtime Environment for Mac OS X
36 * GUI environments.
37 *
38 * @see GraphicsDevice
39 * @see GraphicsConfiguration
40 */
41public final class CGraphicsEnvironment extends SunGraphicsEnvironment {
42
43    /**
44     * Fetch an array of all valid CoreGraphics display identifiers.
45     */
46    private static native int[] getDisplayIDs();
47
48    /**
49     * Fetch the CoreGraphics display ID for the 'main' display.
50     */
51    private static native int getMainDisplayID();
52
53    /**
54     * Noop function that just acts as an entry point for someone to force a
55     * static initialization of this class.
56     */
57    public static void init() { }
58
59    static {
60        // Load libraries and initialize the Toolkit.
61        Toolkit.getDefaultToolkit();
62        // Install the correct surface manager factory.
63        SurfaceManagerFactory.setInstance(new MacosxSurfaceManagerFactory());
64    }
65
66    /**
67     * Register the instance with CGDisplayRegisterReconfigurationCallback().
68     * The registration uses a weak global reference -- if our instance is
69     * garbage collected, the reference will be dropped.
70     *
71     * @return Return the registration context (a pointer).
72     */
73    private native long registerDisplayReconfiguration();
74
75    /**
76     * Remove the instance's registration with CGDisplayRemoveReconfigurationCallback()
77     */
78    private native void deregisterDisplayReconfiguration(long context);
79
80    /** Available CoreGraphics displays. */
81    private final Map<Integer, CGraphicsDevice> devices = new HashMap<>(5);
82    /**
83     * The key in the {@link #devices} for the main display.
84     */
85    private int mainDisplayID;
86
87    /** Reference to the display reconfiguration callback context. */
88    private final long displayReconfigContext;
89
90    /**
91     * Construct a new instance.
92     */
93    public CGraphicsEnvironment() {
94        if (isHeadless()) {
95            displayReconfigContext = 0L;
96            return;
97        }
98
99        /* Populate the device table */
100        initDevices();
101
102        /* Register our display reconfiguration listener */
103        displayReconfigContext = registerDisplayReconfiguration();
104        if (displayReconfigContext == 0L) {
105            throw new RuntimeException("Could not register CoreGraphics display reconfiguration callback");
106        }
107    }
108
109    /**
110     * Called by the CoreGraphics Display Reconfiguration Callback.
111     *
112     * @param displayId CoreGraphics displayId
113     * @param removed   true if displayId was removed, false otherwise.
114     */
115    void _displayReconfiguration(final int displayId, final boolean removed) {
116        synchronized (this) {
117            if (removed && devices.containsKey(displayId)) {
118                final CGraphicsDevice gd = devices.remove(displayId);
119                gd.invalidate(getMainDisplayID());
120                gd.displayChanged();
121            }
122        }
123        initDevices();
124    }
125
126    @Override
127    @SuppressWarnings("deprecation")
128    protected void finalize() throws Throwable {
129        try {
130            super.finalize();
131        } finally {
132            deregisterDisplayReconfiguration(displayReconfigContext);
133        }
134    }
135
136    /**
137     * (Re)create all CGraphicsDevices, reuses a devices if it is possible.
138     */
139    private void initDevices() {
140        synchronized (this) {
141            final Map<Integer, CGraphicsDevice> old = new HashMap<>(devices);
142            devices.clear();
143
144            mainDisplayID = getMainDisplayID();
145
146            // initialization of the graphics device may change
147            // list of displays on hybrid systems via an activation
148            // of discrete video.
149            // So, we initialize the main display first, and then
150            // retrieve actual list of displays.
151            if (!old.containsKey(mainDisplayID)) {
152                old.put(mainDisplayID, new CGraphicsDevice(mainDisplayID));
153            }
154
155            for (final int id : getDisplayIDs()) {
156                devices.put(id, old.containsKey(id) ? old.get(id)
157                                                    : new CGraphicsDevice(id));
158            }
159        }
160        displayChanged();
161    }
162
163    @Override
164    public synchronized GraphicsDevice getDefaultScreenDevice() throws HeadlessException {
165        CGraphicsDevice d = devices.get(mainDisplayID);
166        if (d == null) {
167            // we do not expect that this may happen, the only response
168            // is to re-initialize the list of devices
169            initDevices();
170
171            d = devices.get(mainDisplayID);
172            if (d == null) {
173                throw new AWTError("no screen devices");
174            }
175        }
176        return d;
177    }
178
179    @Override
180    public synchronized GraphicsDevice[] getScreenDevices() throws HeadlessException {
181        return devices.values().toArray(new CGraphicsDevice[devices.values().size()]);
182    }
183
184    public synchronized GraphicsDevice getScreenDevice(int displayID) {
185        return devices.get(displayID);
186    }
187
188    @Override
189    protected synchronized int getNumScreens() {
190        return devices.size();
191    }
192
193    @Override
194    protected GraphicsDevice makeScreenDevice(int screennum) {
195        throw new UnsupportedOperationException("This method is unused and should not be called in this implementation");
196    }
197
198    @Override
199    public boolean isDisplayLocal() {
200       return true;
201    }
202
203    static String[] sLogicalFonts = { "Serif", "SansSerif", "Monospaced", "Dialog", "DialogInput" };
204
205    @Override
206    public Font[] getAllFonts() {
207
208        Font[] newFonts;
209        Font[] superFonts = super.getAllFonts();
210
211        int numLogical = sLogicalFonts.length;
212        int numOtherFonts = superFonts.length;
213
214        newFonts = new Font[numOtherFonts + numLogical];
215        System.arraycopy(superFonts,0,newFonts,numLogical,numOtherFonts);
216
217        for (int i = 0; i < numLogical; i++)
218        {
219            newFonts[i] = new Font(sLogicalFonts[i], Font.PLAIN, 1);
220        }
221        return newFonts;
222    }
223
224}
225