| /* | |
| * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package sun.awt; | |
| import java.awt.Font; | |
| import java.awt.GraphicsConfiguration; | |
| import java.awt.GraphicsDevice; | |
| import java.awt.HeadlessException; | |
| import java.awt.Toolkit; | |
| import java.lang.ref.WeakReference; | |
| import java.util.ArrayList; | |
| import java.util.HashMap; | |
| import java.util.List; | |
| import java.util.ListIterator; | |
| import java.util.Map; | |
| import sun.java2d.SunGraphicsEnvironment; | |
| /** | |
| * This is an implementation of a GraphicsEnvironment object for the default | |
| * local GraphicsEnvironment used by the Java Runtime Environment for Mac OS X | |
| * GUI environments. | |
| * | |
| * @see GraphicsDevice | |
| * @see GraphicsConfiguration | |
| */ | |
| public final class CGraphicsEnvironment extends SunGraphicsEnvironment { | |
| /** | |
| * Fetch an array of all valid CoreGraphics display identifiers. | |
| */ | |
| private static native int[] getDisplayIDs(); | |
| /** | |
| * Fetch the CoreGraphics display ID for the 'main' display. | |
| */ | |
| private static native int getMainDisplayID(); | |
| /** | |
| * Noop function that just acts as an entry point for someone to force a | |
| * static initialization of this class. | |
| */ | |
| public static void init() { } | |
| static { | |
| // Load libraries and initialize the Toolkit. | |
| Toolkit.getDefaultToolkit(); | |
| } | |
| /** | |
| * Register the instance with CGDisplayRegisterReconfigurationCallback(). | |
| * The registration uses a weak global reference -- if our instance is | |
| * garbage collected, the reference will be dropped. | |
| * | |
| * @return Return the registration context (a pointer). | |
| */ | |
| private native long registerDisplayReconfiguration(); | |
| /** | |
| * Remove the instance's registration with CGDisplayRemoveReconfigurationCallback() | |
| */ | |
| private native void deregisterDisplayReconfiguration(long context); | |
| /** Available CoreGraphics displays. */ | |
| private final Map<Integer, CGraphicsDevice> devices = new HashMap<>(5); | |
| /** | |
| * The key in the {@link #devices} for the main display. | |
| */ | |
| private int mainDisplayID; | |
| /** Reference to the display reconfiguration callback context. */ | |
| private final long displayReconfigContext; | |
| // list of invalidated graphics devices (those which were removed) | |
| private List<WeakReference<CGraphicsDevice>> oldDevices = new ArrayList<>(); | |
| /** | |
| * Construct a new instance. | |
| */ | |
| public CGraphicsEnvironment() { | |
| if (isHeadless()) { | |
| displayReconfigContext = 0L; | |
| return; | |
| } | |
| /* Populate the device table */ | |
| rebuildDevices(); | |
| /* Register our display reconfiguration listener */ | |
| displayReconfigContext = registerDisplayReconfiguration(); | |
| if (displayReconfigContext == 0L) { | |
| throw new RuntimeException("Could not register CoreGraphics display reconfiguration callback"); | |
| } | |
| } | |
| /** | |
| * Updates the list of devices and notify listeners. | |
| */ | |
| private void rebuildDevices() { | |
| initDevices(); | |
| displayChanged(); | |
| } | |
| /** | |
| * Called by the CoreGraphics Display Reconfiguration Callback. | |
| * | |
| * @param displayId CoreGraphics displayId | |
| * @param removed true if displayId was removed, false otherwise. | |
| */ | |
| void _displayReconfiguration(int displayId, boolean removed) { | |
| // we ignore the passed parameters and check removed devices ourself | |
| // Note that it is possible that this callback is called when the | |
| // monitors are not added nor removed, but when the video card is | |
| // switched to/from the discrete video card, so we should try to map the | |
| // old to the new devices. | |
| rebuildDevices(); | |
| } | |
| @Override | |
| @SuppressWarnings("removal") | |
| protected void finalize() throws Throwable { | |
| try { | |
| super.finalize(); | |
| } finally { | |
| deregisterDisplayReconfiguration(displayReconfigContext); | |
| } | |
| } | |
| /** | |
| * (Re)create all CGraphicsDevices, reuses a devices if it is possible. | |
| */ | |
| private synchronized void initDevices() { | |
| Map<Integer, CGraphicsDevice> old = new HashMap<>(devices); | |
| devices.clear(); | |
| mainDisplayID = getMainDisplayID(); | |
| // initialization of the graphics device may change list of displays on | |
| // hybrid systems via an activation of discrete video. | |
| // So, we initialize the main display first, then retrieve actual list | |
| // of displays, and then recheck the main display again. | |
| if (!old.containsKey(mainDisplayID)) { | |
| old.put(mainDisplayID, new CGraphicsDevice(mainDisplayID)); | |
| } | |
| int[] displayIDs = getDisplayIDs(); | |
| if (displayIDs.length == 0) { | |
| // we could throw AWTError in this case. | |
| displayIDs = new int[]{mainDisplayID}; | |
| } | |
| for (int id : displayIDs) { | |
| devices.put(id, old.containsKey(id) ? old.remove(id) | |
| : new CGraphicsDevice(id)); | |
| } | |
| // fetch the main display again, the old value might be outdated | |
| mainDisplayID = getMainDisplayID(); | |
| // unlikely but make sure the main screen is in the list of screens, | |
| // most probably one more "displayReconfiguration" is on the road if not | |
| if (!devices.containsKey(mainDisplayID)) { | |
| mainDisplayID = displayIDs[0]; // best we can do | |
| } | |
| // if a device was not reused it should be invalidated | |
| for (CGraphicsDevice gd : old.values()) { | |
| oldDevices.add(new WeakReference<>(gd)); | |
| } | |
| // Need to notify old devices, in case the user hold the reference to it | |
| for (ListIterator<WeakReference<CGraphicsDevice>> it = | |
| oldDevices.listIterator(); it.hasNext(); ) { | |
| CGraphicsDevice gd = it.next().get(); | |
| if (gd != null) { | |
| // If the old device has the same bounds as some new device | |
| // then map that old device to the new, or to the main screen. | |
| CGraphicsDevice similarDevice = getSimilarDevice(gd); | |
| if (similarDevice == null) { | |
| gd.invalidate(devices.get(mainDisplayID)); | |
| } else { | |
| gd.invalidate(similarDevice); | |
| } | |
| gd.displayChanged(); | |
| } else { | |
| // no more references to this device, remove it | |
| it.remove(); | |
| } | |
| } | |
| } | |
| private CGraphicsDevice getSimilarDevice(CGraphicsDevice old) { | |
| for (CGraphicsDevice device : devices.values()) { | |
| if (device.getBounds().equals(old.getBounds())) { | |
| // for now we will use the bounds only | |
| return device; | |
| } | |
| } | |
| return null; | |
| } | |
| @Override | |
| public synchronized GraphicsDevice getDefaultScreenDevice() throws HeadlessException { | |
| return devices.get(mainDisplayID); | |
| } | |
| @Override | |
| public synchronized GraphicsDevice[] getScreenDevices() throws HeadlessException { | |
| return devices.values().toArray(new CGraphicsDevice[devices.values().size()]); | |
| } | |
| public synchronized GraphicsDevice getScreenDevice(int displayID) { | |
| return devices.get(displayID); | |
| } | |
| @Override | |
| protected synchronized int getNumScreens() { | |
| return devices.size(); | |
| } | |
| @Override | |
| protected GraphicsDevice makeScreenDevice(int screennum) { | |
| throw new UnsupportedOperationException("This method is unused and should not be called in this implementation"); | |
| } | |
| @Override | |
| public boolean isDisplayLocal() { | |
| return true; | |
| } | |
| static String[] sLogicalFonts = { "Serif", "SansSerif", "Monospaced", "Dialog", "DialogInput" }; | |
| @Override | |
| public Font[] getAllFonts() { | |
| Font[] newFonts; | |
| Font[] superFonts = super.getAllFonts(); | |
| int numLogical = sLogicalFonts.length; | |
| int numOtherFonts = superFonts.length; | |
| newFonts = new Font[numOtherFonts + numLogical]; | |
| System.arraycopy(superFonts,0,newFonts,numLogical,numOtherFonts); | |
| for (int i = 0; i < numLogical; i++) | |
| { | |
| newFonts[i] = new Font(sLogicalFonts[i], Font.PLAIN, 1); | |
| } | |
| return newFonts; | |
| } | |
| } |