1 /* 2 * Copyright (c) 1997, 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 26 package sun.awt; 27 28 import java.awt.AWTPermission; 29 import java.awt.DisplayMode; 30 import java.awt.GraphicsEnvironment; 31 import java.awt.GraphicsDevice; 32 import java.awt.GraphicsConfiguration; 33 import java.awt.Rectangle; 34 import java.awt.Window; 35 import java.security.AccessController; 36 import java.security.PrivilegedAction; 37 import java.util.ArrayList; 38 import java.util.HashSet; 39 import java.util.HashMap; 40 41 import sun.java2d.opengl.GLXGraphicsConfig; 42 import sun.java2d.xr.XRGraphicsConfig; 43 import sun.java2d.loops.SurfaceType; 44 45 import sun.awt.util.ThreadGroupUtils; 46 import sun.misc.ManagedLocalsThread; 47 48 /** 49 * This is an implementation of a GraphicsDevice object for a single 50 * X11 screen. 51 * 52 * @see GraphicsEnvironment 53 * @see GraphicsConfiguration 54 */ 55 public final class X11GraphicsDevice extends GraphicsDevice 56 implements DisplayChangedListener { 57 int screen; 58 HashMap<SurfaceType, Object> x11ProxyKeyMap = new HashMap<>(); 59 60 private static AWTPermission fullScreenExclusivePermission; 61 private static Boolean xrandrExtSupported; 62 private final Object configLock = new Object(); 63 private SunDisplayChanger topLevels = new SunDisplayChanger(); 64 private DisplayMode origDisplayMode; 65 private boolean shutdownHookRegistered; 66 67 public X11GraphicsDevice(int screennum) { 68 this.screen = screennum; 69 } 70 71 /* 72 * Initialize JNI field and method IDs for fields that may be 73 * accessed from C. 74 */ 75 private static native void initIDs(); 76 77 static { 78 if (!GraphicsEnvironment.isHeadless()) { 79 initIDs(); 80 } 81 } 82 83 /** 84 * Returns the X11 screen of the device. 85 */ 86 public int getScreen() { 87 return screen; 88 } 89 90 public Object getProxyKeyFor(SurfaceType st) { 91 synchronized (x11ProxyKeyMap) { 92 Object o = x11ProxyKeyMap.get(st); 93 if (o == null) { 94 o = new Object(); 95 x11ProxyKeyMap.put(st, o); 96 } 97 return o; 98 } 99 } 100 101 /** 102 * Returns the X11 Display of this device. 103 * This method is also in MDrawingSurfaceInfo but need it here 104 * to be able to allow a GraphicsConfigTemplate to get the Display. 105 */ 106 public native long getDisplay(); 107 108 /** 109 * Returns the type of the graphics device. 110 * @see #TYPE_RASTER_SCREEN 111 * @see #TYPE_PRINTER 112 * @see #TYPE_IMAGE_BUFFER 113 */ 114 public int getType() { 115 return TYPE_RASTER_SCREEN; 116 } 117 118 /** 119 * Returns the identification string associated with this graphics 120 * device. 121 */ 122 public String getIDstring() { 123 return ":0."+screen; 124 } 125 126 127 GraphicsConfiguration[] configs; 128 GraphicsConfiguration defaultConfig; 129 HashSet<Integer> doubleBufferVisuals; 130 131 /** 132 * Returns all of the graphics 133 * configurations associated with this graphics device. 134 */ 135 public GraphicsConfiguration[] getConfigurations() { 136 if (configs == null) { 137 synchronized (configLock) { 138 makeConfigurations(); 139 } 140 } 141 return configs.clone(); 142 } 143 144 private void makeConfigurations() { 145 if (configs == null) { 146 int i = 1; // Index 0 is always the default config 147 int num = getNumConfigs(screen); 148 GraphicsConfiguration[] ret = new GraphicsConfiguration[num]; 149 if (defaultConfig == null) { 150 ret [0] = getDefaultConfiguration(); 151 } 152 else { 153 ret [0] = defaultConfig; 154 } 155 156 boolean glxSupported = X11GraphicsEnvironment.isGLXAvailable(); 157 boolean xrenderSupported = X11GraphicsEnvironment.isXRenderAvailable(); 158 159 boolean dbeSupported = isDBESupported(); 160 if (dbeSupported && doubleBufferVisuals == null) { 161 doubleBufferVisuals = new HashSet<>(); 162 getDoubleBufferVisuals(screen); 163 } 164 for ( ; i < num; i++) { 165 int visNum = getConfigVisualId(i, screen); 166 int depth = getConfigDepth (i, screen); 167 if (glxSupported) { 168 ret[i] = GLXGraphicsConfig.getConfig(this, visNum); 169 } 170 if (ret[i] == null) { 171 boolean doubleBuffer = 172 (dbeSupported && 173 doubleBufferVisuals.contains(Integer.valueOf(visNum))); 174 175 if (xrenderSupported) { 176 ret[i] = XRGraphicsConfig.getConfig(this, visNum, depth, getConfigColormap(i, screen), 177 doubleBuffer); 178 } else { 179 ret[i] = X11GraphicsConfig.getConfig(this, visNum, depth, 180 getConfigColormap(i, screen), 181 doubleBuffer); 182 } 183 } 184 } 185 configs = ret; 186 } 187 } 188 189 /* 190 * Returns the number of X11 visuals representable as an 191 * X11GraphicsConfig object. 192 */ 193 public native int getNumConfigs(int screen); 194 195 /* 196 * Returns the visualid for the given index of graphics configurations. 197 */ 198 public native int getConfigVisualId (int index, int screen); 199 /* 200 * Returns the depth for the given index of graphics configurations. 201 */ 202 private native int getConfigDepth(int index, int screen); 203 204 /* 205 * Returns the colormap for the given index of graphics configurations. 206 */ 207 private native int getConfigColormap(int index, int screen); 208 209 // Whether or not double-buffering extension is supported 210 static native boolean isDBESupported(); 211 // Callback for adding a new double buffer visual into our set 212 private void addDoubleBufferVisual(int visNum) { 213 doubleBufferVisuals.add(Integer.valueOf(visNum)); 214 } 215 // Enumerates all visuals that support double buffering 216 private native void getDoubleBufferVisuals(int screen); 217 218 /** 219 * Returns the default graphics configuration 220 * associated with this graphics device. 221 */ 222 public GraphicsConfiguration getDefaultConfiguration() { 223 if (defaultConfig == null) { 224 synchronized (configLock) { 225 makeDefaultConfiguration(); 226 } 227 } 228 return defaultConfig; 229 } 230 231 private void makeDefaultConfiguration() { 232 if (defaultConfig == null) { 233 int visNum = getConfigVisualId(0, screen); 234 if (X11GraphicsEnvironment.isGLXAvailable()) { 235 defaultConfig = GLXGraphicsConfig.getConfig(this, visNum); 236 if (X11GraphicsEnvironment.isGLXVerbose()) { 237 if (defaultConfig != null) { 238 System.out.print("OpenGL pipeline enabled"); 239 } else { 240 System.out.print("Could not enable OpenGL pipeline"); 241 } 242 System.out.println(" for default config on screen " + 243 screen); 244 } 245 } 246 if (defaultConfig == null) { 247 int depth = getConfigDepth(0, screen); 248 boolean doubleBuffer = false; 249 if (isDBESupported() && doubleBufferVisuals == null) { 250 doubleBufferVisuals = new HashSet<>(); 251 getDoubleBufferVisuals(screen); 252 doubleBuffer = 253 doubleBufferVisuals.contains(Integer.valueOf(visNum)); 254 } 255 256 if (X11GraphicsEnvironment.isXRenderAvailable()) { 257 if (X11GraphicsEnvironment.isXRenderVerbose()) { 258 System.out.println("XRender pipeline enabled"); 259 } 260 defaultConfig = XRGraphicsConfig.getConfig(this, visNum, 261 depth, getConfigColormap(0, screen), 262 doubleBuffer); 263 } else { 264 defaultConfig = X11GraphicsConfig.getConfig(this, visNum, 265 depth, getConfigColormap(0, screen), 266 doubleBuffer); 267 } 268 } 269 } 270 } 271 272 private static native void enterFullScreenExclusive(long window); 273 private static native void exitFullScreenExclusive(long window); 274 private static native boolean initXrandrExtension(); 275 private static native DisplayMode getCurrentDisplayMode(int screen); 276 private static native void enumDisplayModes(int screen, 277 ArrayList<DisplayMode> modes); 278 private static native void configDisplayMode(int screen, 279 int width, int height, 280 int displayMode); 281 private static native void resetNativeData(int screen); 282 283 /** 284 * Returns true only if: 285 * - the Xrandr extension is present 286 * - the necessary Xrandr functions were loaded successfully 287 */ 288 private static synchronized boolean isXrandrExtensionSupported() { 289 if (xrandrExtSupported == null) { 290 xrandrExtSupported = 291 Boolean.valueOf(initXrandrExtension()); 292 } 293 return xrandrExtSupported.booleanValue(); 294 } 295 296 @Override 297 public boolean isFullScreenSupported() { 298 boolean fsAvailable = isXrandrExtensionSupported(); 299 if (fsAvailable) { 300 SecurityManager security = System.getSecurityManager(); 301 if (security != null) { 302 if (fullScreenExclusivePermission == null) { 303 fullScreenExclusivePermission = 304 new AWTPermission("fullScreenExclusive"); 305 } 306 try { 307 security.checkPermission(fullScreenExclusivePermission); 308 } catch (SecurityException e) { 309 return false; 310 } 311 } 312 } 313 return fsAvailable; 314 } 315 316 @Override 317 public boolean isDisplayChangeSupported() { 318 return (isFullScreenSupported() 319 && (getFullScreenWindow() != null) 320 && !((X11GraphicsEnvironment) GraphicsEnvironment 321 .getLocalGraphicsEnvironment()).runningXinerama()); 322 } 323 324 private static void enterFullScreenExclusive(Window w) { 325 X11ComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(w); 326 if (peer != null) { 327 enterFullScreenExclusive(peer.getWindow()); 328 peer.setFullScreenExclusiveModeState(true); 329 } 330 } 331 332 private static void exitFullScreenExclusive(Window w) { 333 X11ComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(w); 334 if (peer != null) { 335 peer.setFullScreenExclusiveModeState(false); 336 exitFullScreenExclusive(peer.getWindow()); 337 } 338 } 339 340 @Override 341 public synchronized void setFullScreenWindow(Window w) { 342 Window old = getFullScreenWindow(); 343 if (w == old) { 344 return; 345 } 346 347 boolean fsSupported = isFullScreenSupported(); 348 if (fsSupported && old != null) { 349 // enter windowed mode (and restore original display mode) 350 exitFullScreenExclusive(old); 351 if (isDisplayChangeSupported()) { 352 setDisplayMode(origDisplayMode); 353 } 354 } 355 356 super.setFullScreenWindow(w); 357 358 if (fsSupported && w != null) { 359 // save original display mode 360 if (origDisplayMode == null) { 361 origDisplayMode = getDisplayMode(); 362 } 363 364 // enter fullscreen mode 365 enterFullScreenExclusive(w); 366 } 367 } 368 369 private DisplayMode getDefaultDisplayMode() { 370 GraphicsConfiguration gc = getDefaultConfiguration(); 371 Rectangle r = gc.getBounds(); 372 return new DisplayMode(r.width, r.height, 373 DisplayMode.BIT_DEPTH_MULTI, 374 DisplayMode.REFRESH_RATE_UNKNOWN); 375 } 376 377 @Override 378 public synchronized DisplayMode getDisplayMode() { 379 if (isFullScreenSupported()) { 380 DisplayMode mode = getCurrentDisplayMode(screen); 381 if (mode == null) { 382 mode = getDefaultDisplayMode(); 383 } 384 return mode; 385 } else { 386 if (origDisplayMode == null) { 387 origDisplayMode = getDefaultDisplayMode(); 388 } 389 return origDisplayMode; 390 } 391 } 392 393 @Override 394 public synchronized DisplayMode[] getDisplayModes() { 395 if (!isFullScreenSupported()) { 396 return super.getDisplayModes(); 397 } 398 ArrayList<DisplayMode> modes = new ArrayList<DisplayMode>(); 399 enumDisplayModes(screen, modes); 400 DisplayMode[] retArray = new DisplayMode[modes.size()]; 401 return modes.toArray(retArray); 402 } 403 404 @Override 405 public synchronized void setDisplayMode(DisplayMode dm) { 406 if (!isDisplayChangeSupported()) { 407 super.setDisplayMode(dm); 408 return; 409 } 410 Window w = getFullScreenWindow(); 411 if (w == null) { 412 throw new IllegalStateException("Must be in fullscreen mode " + 413 "in order to set display mode"); 414 } 415 if (getDisplayMode().equals(dm)) { 416 return; 417 } 418 if (dm == null || 419 (dm = getMatchingDisplayMode(dm)) == null) 420 { 421 throw new IllegalArgumentException("Invalid display mode"); 422 } 423 424 if (!shutdownHookRegistered) { 425 // register a shutdown hook so that we return to the 426 // original DisplayMode when the VM exits (if the application 427 // is already in the original DisplayMode at that time, this 428 // hook will have no effect) 429 shutdownHookRegistered = true; 430 PrivilegedAction<Void> a = () -> { 431 Runnable r = () -> { 432 Window old = getFullScreenWindow(); 433 if (old != null) { 434 exitFullScreenExclusive(old); 435 if (isDisplayChangeSupported()) { 436 setDisplayMode(origDisplayMode); 437 } 438 } 439 }; 440 String name = "Display-Change-Shutdown-Thread-" + screen; 441 Thread t = new ManagedLocalsThread( 442 ThreadGroupUtils.getRootThreadGroup(), r, name); 443 t.setContextClassLoader(null); 444 Runtime.getRuntime().addShutdownHook(t); 445 return null; 446 }; 447 AccessController.doPrivileged(a); 448 } 449 450 // switch to the new DisplayMode 451 configDisplayMode(screen, 452 dm.getWidth(), dm.getHeight(), 453 dm.getRefreshRate()); 454 455 // update bounds of the fullscreen window 456 w.setBounds(0, 0, dm.getWidth(), dm.getHeight()); 457 458 // configDisplayMode() is synchronous, so the display change will be 459 // complete by the time we get here (and it is therefore safe to call 460 // displayChanged() now) 461 ((X11GraphicsEnvironment) 462 GraphicsEnvironment.getLocalGraphicsEnvironment()).displayChanged(); 463 } 464 465 private synchronized DisplayMode getMatchingDisplayMode(DisplayMode dm) { 466 if (!isDisplayChangeSupported()) { 467 return null; 468 } 469 DisplayMode[] modes = getDisplayModes(); 470 for (DisplayMode mode : modes) { 471 if (dm.equals(mode) || 472 (dm.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN && 473 dm.getWidth() == mode.getWidth() && 474 dm.getHeight() == mode.getHeight() && 475 dm.getBitDepth() == mode.getBitDepth())) 476 { 477 return mode; 478 } 479 } 480 return null; 481 } 482 483 /** 484 * From the DisplayChangedListener interface; called from 485 * X11GraphicsEnvironment when the display mode has been changed. 486 */ 487 public synchronized void displayChanged() { 488 // On X11 the visuals do not change, and therefore we don't need 489 // to reset the defaultConfig, config, doubleBufferVisuals, 490 // neither do we need to reset the native data. 491 492 // pass on to all top-level windows on this screen 493 topLevels.notifyListeners(); 494 } 495 496 /** 497 * From the DisplayChangedListener interface; devices do not need 498 * to react to this event. 499 */ 500 public void paletteChanged() { 501 } 502 503 /** 504 * Add a DisplayChangeListener to be notified when the display settings 505 * are changed. Typically, only top-level containers need to be added 506 * to X11GraphicsDevice. 507 */ 508 public void addDisplayChangedListener(DisplayChangedListener client) { 509 topLevels.add(client); 510 } 511 512 /** 513 * Remove a DisplayChangeListener from this X11GraphicsDevice. 514 */ 515 public void removeDisplayChangedListener(DisplayChangedListener client) { 516 topLevels.remove(client); 517 } 518 519 public String toString() { 520 return ("X11GraphicsDevice[screen="+screen+"]"); 521 } 522 }