1 /* 2 * Copyright (c) 2011, 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.Font; 29 import java.awt.GraphicsConfiguration; 30 import java.awt.GraphicsDevice; 31 import java.awt.HeadlessException; 32 import java.awt.Toolkit; 33 import java.lang.ref.WeakReference; 34 import java.util.ArrayList; 35 import java.util.HashMap; 36 import java.util.List; 37 import java.util.ListIterator; 38 import java.util.Map; 39 40 import sun.java2d.MacosxSurfaceManagerFactory; 41 import sun.java2d.SunGraphicsEnvironment; 42 import sun.java2d.SurfaceManagerFactory; 43 44 /** 45 * This is an implementation of a GraphicsEnvironment object for the default 46 * local GraphicsEnvironment used by the Java Runtime Environment for Mac OS X 47 * GUI environments. 48 * 49 * @see GraphicsDevice 50 * @see GraphicsConfiguration 51 */ 52 public final class CGraphicsEnvironment extends SunGraphicsEnvironment { 53 54 /** 55 * Fetch an array of all valid CoreGraphics display identifiers. 56 */ 57 private static native int[] getDisplayIDs(); 58 59 /** 60 * Fetch the CoreGraphics display ID for the 'main' display. 61 */ 62 private static native int getMainDisplayID(); 63 64 /** 65 * Noop function that just acts as an entry point for someone to force a 66 * static initialization of this class. 67 */ 68 public static void init() { } 69 70 static { 71 // Load libraries and initialize the Toolkit. 72 Toolkit.getDefaultToolkit(); 73 // Install the correct surface manager factory. 74 SurfaceManagerFactory.setInstance(new MacosxSurfaceManagerFactory()); 75 } 76 77 /** 78 * Register the instance with CGDisplayRegisterReconfigurationCallback(). 79 * The registration uses a weak global reference -- if our instance is 80 * garbage collected, the reference will be dropped. 81 * 82 * @return Return the registration context (a pointer). 83 */ 84 private native long registerDisplayReconfiguration(); 85 86 /** 87 * Remove the instance's registration with CGDisplayRemoveReconfigurationCallback() 88 */ 89 private native void deregisterDisplayReconfiguration(long context); 90 91 /** Available CoreGraphics displays. */ 92 private final Map<Integer, CGraphicsDevice> devices = new HashMap<>(5); 93 /** 94 * The key in the {@link #devices} for the main display. 95 */ 96 private int mainDisplayID; 97 98 /** Reference to the display reconfiguration callback context. */ 99 private final long displayReconfigContext; 100 101 // list of invalidated graphics devices (those which were removed) 102 private List<WeakReference<CGraphicsDevice>> oldDevices = new ArrayList<>(); 103 104 /** 105 * Construct a new instance. 106 */ 107 public CGraphicsEnvironment() { 108 if (isHeadless()) { 109 displayReconfigContext = 0L; 110 return; 111 } 112 113 /* Populate the device table */ 114 rebuildDevices(); 115 116 /* Register our display reconfiguration listener */ 117 displayReconfigContext = registerDisplayReconfiguration(); 118 if (displayReconfigContext == 0L) { 119 throw new RuntimeException("Could not register CoreGraphics display reconfiguration callback"); 120 } 121 } 122 123 /** 124 * Updates the list of devices and notify listeners. 125 */ 126 private void rebuildDevices() { 127 initDevices(); 128 displayChanged(); 129 } 130 131 /** 132 * Called by the CoreGraphics Display Reconfiguration Callback. 133 * 134 * @param displayId CoreGraphics displayId 135 * @param removed true if displayId was removed, false otherwise. 136 */ 137 void _displayReconfiguration(int displayId, boolean removed) { 138 // we ignore the passed parameters and check removed devices ourself 139 // Note that it is possible that this callback is called when the 140 // monitors are not added nor removed, but when the video card is 141 // switched to/from the discrete video card, so we should try to map the 142 // old to the new devices. 143 rebuildDevices(); 144 } 145 146 @Override 147 @SuppressWarnings("deprecation") 148 protected void finalize() throws Throwable { 149 try { 150 super.finalize(); 151 } finally { 152 deregisterDisplayReconfiguration(displayReconfigContext); 153 } 154 } 155 156 /** 157 * (Re)create all CGraphicsDevices, reuses a devices if it is possible. 158 */ 159 private synchronized void initDevices() { 160 Map<Integer, CGraphicsDevice> old = new HashMap<>(devices); 161 devices.clear(); 162 mainDisplayID = getMainDisplayID(); 163 164 // initialization of the graphics device may change 165 // list of displays on hybrid systems via an activation 166 // of discrete video. 167 // So, we initialize the main display first, and then 168 // retrieve actual list of displays. 169 if (!old.containsKey(mainDisplayID)) { 170 old.put(mainDisplayID, new CGraphicsDevice(mainDisplayID)); 171 } 172 for (int id : getDisplayIDs()) { 173 devices.put(id, old.containsKey(id) ? old.remove(id) 174 : new CGraphicsDevice(id)); 175 } 176 177 // unlikely but make sure the main screen is in the list of screens, 178 // most probably one more "displayReconfiguration" is on the road 179 if (!devices.containsKey(mainDisplayID)) { 180 devices.put(mainDisplayID, new CGraphicsDevice(mainDisplayID)); 181 } 182 // if a device was not reused it should be invalidated 183 for (CGraphicsDevice gd : old.values()) { 184 oldDevices.add(new WeakReference<>(gd)); 185 } 186 // Need to notify old devices, in case the user hold the reference to it 187 for (ListIterator<WeakReference<CGraphicsDevice>> it = 188 oldDevices.listIterator(); it.hasNext(); ) { 189 CGraphicsDevice gd = it.next().get(); 190 if (gd != null) { 191 // If the old device has the same bounds as some new device 192 // then map that old device to the new, or to the main screen. 193 CGraphicsDevice similarDevice = getSimilarDevice(gd); 194 if (similarDevice == null) { 195 gd.invalidate(devices.get(mainDisplayID)); 196 } else { 197 gd.invalidate(similarDevice); 198 } 199 gd.displayChanged(); 200 } else { 201 // no more references to this device, remove it 202 it.remove(); 203 } 204 } 205 } 206 207 private CGraphicsDevice getSimilarDevice(CGraphicsDevice old) { 208 for (CGraphicsDevice device : devices.values()) { 209 if (device.getBounds().equals(old.getBounds())) { 210 // for now we will use the bounds only 211 return device; 212 } 213 } 214 return null; 215 } 216 217 @Override 218 public synchronized GraphicsDevice getDefaultScreenDevice() throws HeadlessException { 219 return devices.get(mainDisplayID); 220 } 221 222 @Override 223 public synchronized GraphicsDevice[] getScreenDevices() throws HeadlessException { 224 return devices.values().toArray(new CGraphicsDevice[devices.values().size()]); 225 } 226 227 public synchronized GraphicsDevice getScreenDevice(int displayID) { 228 return devices.get(displayID); 229 } 230 231 @Override 232 protected synchronized int getNumScreens() { 233 return devices.size(); 234 } 235 236 @Override 237 protected GraphicsDevice makeScreenDevice(int screennum) { 238 throw new UnsupportedOperationException("This method is unused and should not be called in this implementation"); 239 } 240 241 @Override 242 public boolean isDisplayLocal() { 243 return true; 244 } 245 246 static String[] sLogicalFonts = { "Serif", "SansSerif", "Monospaced", "Dialog", "DialogInput" }; 247 248 @Override 249 public Font[] getAllFonts() { 250 251 Font[] newFonts; 252 Font[] superFonts = super.getAllFonts(); 253 254 int numLogical = sLogicalFonts.length; 255 int numOtherFonts = superFonts.length; 256 257 newFonts = new Font[numOtherFonts + numLogical]; 258 System.arraycopy(superFonts,0,newFonts,numLogical,numOtherFonts); 259 260 for (int i = 0; i < numLogical; i++) 261 { 262 newFonts[i] = new Font(sLogicalFonts[i], Font.PLAIN, 1); 263 } 264 return newFonts; 265 } 266 267 }