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.InnocuousThread; 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 && !((X11GraphicsEnvironment) GraphicsEnvironment 320 .getLocalGraphicsEnvironment()).runningXinerama()); 321 } 322 323 private static void enterFullScreenExclusive(Window w) { 324 X11ComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(w); 325 if (peer != null) { 326 enterFullScreenExclusive(peer.getWindow()); 327 peer.setFullScreenExclusiveModeState(true); 328 } 329 } 330 331 private static void exitFullScreenExclusive(Window w) { 332 X11ComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(w); 333 if (peer != null) { 334 peer.setFullScreenExclusiveModeState(false); 335 exitFullScreenExclusive(peer.getWindow()); 336 } 337 } 338 339 @Override 340 public synchronized void setFullScreenWindow(Window w) { 341 Window old = getFullScreenWindow(); 342 if (w == old) { 343 return; 344 } 345 346 boolean fsSupported = isFullScreenSupported(); 347 if (fsSupported && old != null) { 348 // enter windowed mode (and restore original display mode) 349 exitFullScreenExclusive(old); 350 if (isDisplayChangeSupported()) { 351 setDisplayMode(origDisplayMode); 352 } 353 } 354 355 super.setFullScreenWindow(w); 356 357 if (fsSupported && w != null) { 358 // save original display mode 359 if (origDisplayMode == null) { 360 origDisplayMode = getDisplayMode(); 361 } 362 363 // enter fullscreen mode 364 enterFullScreenExclusive(w); 365 } 366 } 367 368 private DisplayMode getDefaultDisplayMode() { 369 GraphicsConfiguration gc = getDefaultConfiguration(); 370 Rectangle r = gc.getBounds(); 371 return new DisplayMode(r.width, r.height, 372 DisplayMode.BIT_DEPTH_MULTI, 373 DisplayMode.REFRESH_RATE_UNKNOWN); 374 } 375 376 @Override 377 public synchronized DisplayMode getDisplayMode() { 378 if (isFullScreenSupported()) { 379 DisplayMode mode = getCurrentDisplayMode(screen); 380 if (mode == null) { 381 mode = getDefaultDisplayMode(); 382 } 383 return mode; 384 } else { 385 if (origDisplayMode == null) { 386 origDisplayMode = getDefaultDisplayMode(); 387 } 388 return origDisplayMode; 389 } 390 } 391 392 @Override 393 public synchronized DisplayMode[] getDisplayModes() { 394 if (!isFullScreenSupported()) { 395 return super.getDisplayModes(); 396 } 397 ArrayList<DisplayMode> modes = new ArrayList<DisplayMode>(); 398 enumDisplayModes(screen, modes); 399 DisplayMode[] retArray = new DisplayMode[modes.size()]; 400 return modes.toArray(retArray); 401 } 402 403 @Override 404 public synchronized void setDisplayMode(DisplayMode dm) { 405 if (!isDisplayChangeSupported()) { 406 super.setDisplayMode(dm); 407 return; 408 } 409 Window w = getFullScreenWindow(); 410 if (w == null) { 411 throw new IllegalStateException("Must be in fullscreen mode " + 412 "in order to set display mode"); 413 } 414 if (getDisplayMode().equals(dm)) { 415 return; 416 } 417 if (dm == null || 418 (dm = getMatchingDisplayMode(dm)) == null) 419 { 420 throw new IllegalArgumentException("Invalid display mode"); 421 } 422 423 if (!shutdownHookRegistered) { 424 // register a shutdown hook so that we return to the 425 // original DisplayMode when the VM exits (if the application 426 // is already in the original DisplayMode at that time, this 427 // hook will have no effect) 428 shutdownHookRegistered = true; 429 PrivilegedAction<Void> a = () -> { 430 Runnable r = () -> { 431 Window old = getFullScreenWindow(); 432 if (old != null) { 433 exitFullScreenExclusive(old); 434 if (isDisplayChangeSupported()) { 435 setDisplayMode(origDisplayMode); 436 } 437 } 438 }; 439 String name = "Display-Change-Shutdown-Thread-" + screen; 440 Thread t; 441 if (System.getSecurityManager() == null) { 442 t = new Thread(ThreadGroupUtils.getRootThreadGroup(), r, name); 443 } else { 444 t = new InnocuousThread(r, name); 445 } 446 t.setContextClassLoader(null); 447 Runtime.getRuntime().addShutdownHook(t); 448 return null; 449 }; 450 AccessController.doPrivileged(a); 451 } 452 453 // switch to the new DisplayMode 454 configDisplayMode(screen, 455 dm.getWidth(), dm.getHeight(), 456 dm.getRefreshRate()); 457 458 // update bounds of the fullscreen window 459 w.setBounds(0, 0, dm.getWidth(), dm.getHeight()); 460 461 // configDisplayMode() is synchronous, so the display change will be 462 // complete by the time we get here (and it is therefore safe to call 463 // displayChanged() now) 464 ((X11GraphicsEnvironment) 465 GraphicsEnvironment.getLocalGraphicsEnvironment()).displayChanged(); 466 } 467 468 private synchronized DisplayMode getMatchingDisplayMode(DisplayMode dm) { 469 if (!isDisplayChangeSupported()) { 470 return null; 471 } 472 DisplayMode[] modes = getDisplayModes(); 473 for (DisplayMode mode : modes) { 474 if (dm.equals(mode) || 475 (dm.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN && 476 dm.getWidth() == mode.getWidth() && 477 dm.getHeight() == mode.getHeight() && 478 dm.getBitDepth() == mode.getBitDepth())) 479 { 480 return mode; 481 } 482 } 483 return null; 484 } 485 486 /** 487 * From the DisplayChangedListener interface; called from 488 * X11GraphicsEnvironment when the display mode has been changed. 489 */ 490 public synchronized void displayChanged() { 491 // On X11 the visuals do not change, and therefore we don't need 492 // to reset the defaultConfig, config, doubleBufferVisuals, 493 // neither do we need to reset the native data. 494 495 // pass on to all top-level windows on this screen 496 topLevels.notifyListeners(); 497 } 498 499 /** 500 * From the DisplayChangedListener interface; devices do not need 501 * to react to this event. 502 */ 503 public void paletteChanged() { 504 } 505 506 /** 507 * Add a DisplayChangeListener to be notified when the display settings 508 * are changed. Typically, only top-level containers need to be added 509 * to X11GraphicsDevice. 510 */ 511 public void addDisplayChangedListener(DisplayChangedListener client) { 512 topLevels.add(client); 513 } 514 515 /** 516 * Remove a DisplayChangeListener from this X11GraphicsDevice. 517 */ 518 public void removeDisplayChangedListener(DisplayChangedListener client) { 519 topLevels.remove(client); 520 } 521 522 public String toString() { 523 return ("X11GraphicsDevice[screen="+screen+"]"); 524 } 525 }