1 /* 2 * Copyright (c) 1997, 2010, 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 /** 46 * This is an implementation of a GraphicsDevice object for a single 47 * X11 screen. 48 * 49 * @see GraphicsEnvironment 50 * @see GraphicsConfiguration 51 */ 52 public class X11GraphicsDevice 53 extends GraphicsDevice 54 implements DisplayChangedListener 55 { 56 int screen; 57 HashMap x11ProxyKeyMap = new HashMap(); 58 59 private static AWTPermission fullScreenExclusivePermission; 60 private static Boolean xrandrExtSupported; 61 private final Object configLock = new Object(); 62 private SunDisplayChanger topLevels = new SunDisplayChanger(); 63 private DisplayMode origDisplayMode; 64 private boolean shutdownHookRegistered; 65 66 public X11GraphicsDevice(int screennum) { 67 this.screen = screennum; 68 } 69 70 /* 71 * Initialize JNI field and method IDs for fields that may be 72 * accessed from C. 73 */ 74 private static native void initIDs(); 75 76 static { 77 if (!GraphicsEnvironment.isHeadless()) { 78 initIDs(); 79 } 80 } 81 82 /** 83 * Returns the X11 screen of the device. 84 */ 85 public int getScreen() { 86 return screen; 87 } 88 89 public Object getProxyKeyFor(SurfaceType st) { 90 synchronized (x11ProxyKeyMap) { 91 Object o = x11ProxyKeyMap.get(st); 92 if (o == null) { 93 o = new Object(); 94 x11ProxyKeyMap.put(st, o); 95 } 96 return o; 97 } 98 } 99 100 /** 101 * Returns the X11 Display of this device. 102 * This method is also in MDrawingSurfaceInfo but need it here 103 * to be able to allow a GraphicsConfigTemplate to get the Display. 104 */ 105 public native long getDisplay(); 106 107 /** 108 * Returns the type of the graphics device. 109 * @see #TYPE_RASTER_SCREEN 110 * @see #TYPE_PRINTER 111 * @see #TYPE_IMAGE_BUFFER 112 */ 113 public int getType() { 114 return TYPE_RASTER_SCREEN; 115 } 116 117 /** 118 * Returns the identification string associated with this graphics 119 * device. 120 */ 121 public String getIDstring() { 122 return ":0."+screen; 123 } 124 125 126 GraphicsConfiguration[] configs; 127 GraphicsConfiguration defaultConfig; 128 HashSet doubleBufferVisuals; 129 130 /** 131 * Returns all of the graphics 132 * configurations associated with this graphics device. 133 */ 134 public GraphicsConfiguration[] getConfigurations() { 135 if (configs == null) { 136 synchronized (configLock) { 137 makeConfigurations(); 138 } 139 } 140 return configs.clone(); 141 } 142 143 private void makeConfigurations() { 144 if (configs == null) { 145 int i = 1; // Index 0 is always the default config 146 int num = getNumConfigs(screen); 147 GraphicsConfiguration[] ret = new GraphicsConfiguration[num]; 148 if (defaultConfig == null) { 149 ret [0] = getDefaultConfiguration(); 150 } 151 else { 152 ret [0] = defaultConfig; 153 } 154 155 boolean glxSupported = X11GraphicsEnvironment.isGLXAvailable(); 156 boolean xrenderSupported = X11GraphicsEnvironment.isXRenderAvailable(); 157 158 boolean dbeSupported = isDBESupported(); 159 if (dbeSupported && doubleBufferVisuals == null) { 160 doubleBufferVisuals = new HashSet(); 161 getDoubleBufferVisuals(screen); 162 } 163 for ( ; i < num; i++) { 164 int visNum = getConfigVisualId(i, screen); 165 int depth = getConfigDepth (i, screen); 166 if (glxSupported) { 167 ret[i] = GLXGraphicsConfig.getConfig(this, visNum); 168 } 169 if (ret[i] == null) { 170 boolean doubleBuffer = 171 (dbeSupported && 172 doubleBufferVisuals.contains(Integer.valueOf(visNum))); 173 174 if (xrenderSupported) { 175 ret[i] = XRGraphicsConfig.getConfig(this, visNum, depth, getConfigColormap(i, screen), 176 doubleBuffer); 177 } else { 178 ret[i] = X11GraphicsConfig.getConfig(this, visNum, depth, 179 getConfigColormap(i, screen), 180 doubleBuffer); 181 } 182 } 183 } 184 configs = ret; 185 } 186 } 187 188 /* 189 * Returns the number of X11 visuals representable as an 190 * X11GraphicsConfig object. 191 */ 192 public native int getNumConfigs(int screen); 193 194 /* 195 * Returns the visualid for the given index of graphics configurations. 196 */ 197 public native int getConfigVisualId (int index, int screen); 198 /* 199 * Returns the depth for the given index of graphics configurations. 200 */ 201 public native int getConfigDepth (int index, int screen); 202 203 /* 204 * Returns the colormap for the given index of graphics configurations. 205 */ 206 public native int getConfigColormap (int index, int screen); 207 208 209 // Whether or not double-buffering extension is supported 210 public 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 * - XINERAMA is not enabled 288 */ 289 private static synchronized boolean isXrandrExtensionSupported() { 290 if (xrandrExtSupported == null) { 291 xrandrExtSupported = 292 Boolean.valueOf(initXrandrExtension()); 293 } 294 return xrandrExtSupported.booleanValue(); 295 } 296 297 @Override 298 public boolean isFullScreenSupported() { 299 // REMIND: for now we will only allow fullscreen exclusive mode 300 // on the primary screen; we could change this behavior slightly 301 // in the future by allowing only one screen to be in fullscreen 302 // exclusive mode at any given time... 303 boolean fsAvailable = (screen == 0) && isXrandrExtensionSupported(); 304 if (fsAvailable) { 305 SecurityManager security = System.getSecurityManager(); 306 if (security != null) { 307 if (fullScreenExclusivePermission == null) { 308 fullScreenExclusivePermission = 309 new AWTPermission("fullScreenExclusive"); 310 } 311 try { 312 security.checkPermission(fullScreenExclusivePermission); 313 } catch (SecurityException e) { 314 return false; 315 } 316 } 317 } 318 return fsAvailable; 319 } 320 321 @Override 322 public boolean isDisplayChangeSupported() { 323 return (isFullScreenSupported() && (getFullScreenWindow() != null)); 324 } 325 326 private static void enterFullScreenExclusive(Window w) { 327 X11ComponentPeer peer = (X11ComponentPeer)w.getPeer(); 328 if (peer != null) { 329 enterFullScreenExclusive(peer.getContentWindow()); 330 peer.setFullScreenExclusiveModeState(true); 331 } 332 } 333 334 private static void exitFullScreenExclusive(Window w) { 335 X11ComponentPeer peer = (X11ComponentPeer)w.getPeer(); 336 if (peer != null) { 337 peer.setFullScreenExclusiveModeState(false); 338 exitFullScreenExclusive(peer.getContentWindow()); 339 } 340 } 341 342 @Override 343 public synchronized void setFullScreenWindow(Window w) { 344 Window old = getFullScreenWindow(); 345 if (w == old) { 346 return; 347 } 348 349 boolean fsSupported = isFullScreenSupported(); 350 if (fsSupported && old != null) { 351 // enter windowed mode (and restore original display mode) 352 exitFullScreenExclusive(old); 353 setDisplayMode(origDisplayMode); 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 return getCurrentDisplayMode(screen); 381 } else { 382 if (origDisplayMode == null) { 383 origDisplayMode = getDefaultDisplayMode(); 384 } 385 return origDisplayMode; 386 } 387 } 388 389 @Override 390 public synchronized DisplayMode[] getDisplayModes() { 391 if (!isFullScreenSupported()) { 392 return super.getDisplayModes(); 393 } 394 ArrayList<DisplayMode> modes = new ArrayList<DisplayMode>(); 395 enumDisplayModes(screen, modes); 396 DisplayMode[] retArray = new DisplayMode[modes.size()]; 397 return modes.toArray(retArray); 398 } 399 400 @Override 401 public synchronized void setDisplayMode(DisplayMode dm) { 402 if (!isDisplayChangeSupported()) { 403 super.setDisplayMode(dm); 404 return; 405 } 406 Window w = getFullScreenWindow(); 407 if (w == null) { 408 throw new IllegalStateException("Must be in fullscreen mode " + 409 "in order to set display mode"); 410 } 411 if (getDisplayMode().equals(dm)) { 412 return; 413 } 414 if (dm == null || 415 (dm = getMatchingDisplayMode(dm)) == null) 416 { 417 throw new IllegalArgumentException("Invalid display mode"); 418 } 419 420 if (!shutdownHookRegistered) { 421 // register a shutdown hook so that we return to the 422 // original DisplayMode when the VM exits (if the application 423 // is already in the original DisplayMode at that time, this 424 // hook will have no effect) 425 shutdownHookRegistered = true; 426 PrivilegedAction<Void> a = new PrivilegedAction<Void>() { 427 public Void run() { 428 ThreadGroup mainTG = Thread.currentThread().getThreadGroup(); 429 ThreadGroup parentTG = mainTG.getParent(); 430 while (parentTG != null) { 431 mainTG = parentTG; 432 parentTG = mainTG.getParent(); 433 } 434 Runnable r = new Runnable() { 435 public void run() { 436 Window old = getFullScreenWindow(); 437 if (old != null) { 438 exitFullScreenExclusive(old); 439 setDisplayMode(origDisplayMode); 440 } 441 } 442 }; 443 Thread t = new Thread(mainTG, r,"Display-Change-Shutdown-Thread-"+screen); 444 t.setContextClassLoader(null); 445 Runtime.getRuntime().addShutdownHook(t); 446 return null; 447 } 448 }; 449 AccessController.doPrivileged(a); 450 } 451 452 // switch to the new DisplayMode 453 configDisplayMode(screen, 454 dm.getWidth(), dm.getHeight(), 455 dm.getRefreshRate()); 456 457 // update bounds of the fullscreen window 458 w.setBounds(0, 0, dm.getWidth(), dm.getHeight()); 459 460 // configDisplayMode() is synchronous, so the display change will be 461 // complete by the time we get here (and it is therefore safe to call 462 // displayChanged() now) 463 ((X11GraphicsEnvironment) 464 GraphicsEnvironment.getLocalGraphicsEnvironment()).displayChanged(); 465 } 466 467 private synchronized DisplayMode getMatchingDisplayMode(DisplayMode dm) { 468 if (!isDisplayChangeSupported()) { 469 return null; 470 } 471 DisplayMode[] modes = getDisplayModes(); 472 for (DisplayMode mode : modes) { 473 if (dm.equals(mode) || 474 (dm.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN && 475 dm.getWidth() == mode.getWidth() && 476 dm.getHeight() == mode.getHeight() && 477 dm.getBitDepth() == mode.getBitDepth())) 478 { 479 return mode; 480 } 481 } 482 return null; 483 } 484 485 /** 486 * From the DisplayChangedListener interface; called from 487 * X11GraphicsEnvironment when the display mode has been changed. 488 */ 489 public synchronized void displayChanged() { 490 // reset the list of configs (and default config) 491 defaultConfig = null; 492 configs = null; 493 doubleBufferVisuals = null; 494 495 // reset the native data structures associated with this device (they 496 // will be reinitialized when the GraphicsConfigs are configured) 497 resetNativeData(screen); 498 499 // pass on to all top-level windows on this screen 500 topLevels.notifyListeners(); 501 } 502 503 /** 504 * From the DisplayChangedListener interface; devices do not need 505 * to react to this event. 506 */ 507 public void paletteChanged() { 508 } 509 510 /** 511 * Add a DisplayChangeListener to be notified when the display settings 512 * are changed. Typically, only top-level containers need to be added 513 * to X11GraphicsDevice. 514 */ 515 public void addDisplayChangedListener(DisplayChangedListener client) { 516 topLevels.add(client); 517 } 518 519 /** 520 * Remove a DisplayChangeListener from this X11GraphicsDevice. 521 */ 522 public void removeDisplayChangedListener(DisplayChangedListener client) { 523 topLevels.remove(client); 524 } 525 526 public String toString() { 527 return ("X11GraphicsDevice[screen="+screen+"]"); 528 } 529 }