1 /* 2 * Copyright (c) 1997, 2019, 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.GraphicsConfiguration; 31 import java.awt.GraphicsDevice; 32 import java.awt.GraphicsEnvironment; 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.HashMap; 39 import java.util.HashSet; 40 41 import sun.awt.util.ThreadGroupUtils; 42 import sun.java2d.SunGraphicsEnvironment; 43 import sun.java2d.loops.SurfaceType; 44 import sun.java2d.opengl.GLXGraphicsConfig; 45 import sun.java2d.xr.XRGraphicsConfig; 46 47 /** 48 * This is an implementation of a GraphicsDevice object for a single 49 * X11 screen. 50 * 51 * @see GraphicsEnvironment 52 * @see GraphicsConfiguration 53 */ 54 public final class X11GraphicsDevice extends GraphicsDevice 55 implements DisplayChangedListener { 56 int screen; 57 HashMap<SurfaceType, Object> 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 private int scale; 66 67 public X11GraphicsDevice(int screennum) { 68 this.screen = screennum; 69 this.scale = initScaleFactor(); 70 } 71 72 /* 73 * Initialize JNI field and method IDs for fields that may be 74 * accessed from C. 75 */ 76 private static native void initIDs(); 77 78 /** 79 * Returns the X11 screen of the device. 80 */ 81 public int getScreen() { 82 return screen; 83 } 84 85 public Object getProxyKeyFor(SurfaceType st) { 86 synchronized (x11ProxyKeyMap) { 87 Object o = x11ProxyKeyMap.get(st); 88 if (o == null) { 89 o = new Object(); 90 x11ProxyKeyMap.put(st, o); 91 } 92 return o; 93 } 94 } 95 96 /** 97 * Returns the X11 Display of this device. 98 * This method is also in MDrawingSurfaceInfo but need it here 99 * to be able to allow a GraphicsConfigTemplate to get the Display. 100 */ 101 public native long getDisplay(); 102 103 /** 104 * Returns the type of the graphics device. 105 * @see #TYPE_RASTER_SCREEN 106 * @see #TYPE_PRINTER 107 * @see #TYPE_IMAGE_BUFFER 108 */ 109 @Override 110 public int getType() { 111 return TYPE_RASTER_SCREEN; 112 } 113 114 /** 115 * Returns the identification string associated with this graphics 116 * device. 117 */ 118 @Override 119 public String getIDstring() { 120 return ":0."+screen; 121 } 122 123 124 GraphicsConfiguration[] configs; 125 GraphicsConfiguration defaultConfig; 126 HashSet<Integer> doubleBufferVisuals; 127 128 /** 129 * Returns all of the graphics 130 * configurations associated with this graphics device. 131 */ 132 @Override 133 public GraphicsConfiguration[] getConfigurations() { 134 if (configs == null) { 135 synchronized (configLock) { 136 makeConfigurations(); 137 } 138 } 139 return configs.clone(); 140 } 141 142 private void makeConfigurations() { 143 if (configs == null) { 144 int i = 1; // Index 0 is always the default config 145 int num = getNumConfigs(screen); 146 GraphicsConfiguration[] ret = new GraphicsConfiguration[num]; 147 if (defaultConfig == null) { 148 ret [0] = getDefaultConfiguration(); 149 } 150 else { 151 ret [0] = defaultConfig; 152 } 153 154 boolean glxSupported = X11GraphicsEnvironment.isGLXAvailable(); 155 boolean xrenderSupported = X11GraphicsEnvironment.isXRenderAvailable(); 156 157 boolean dbeSupported = isDBESupported(); 158 if (dbeSupported && doubleBufferVisuals == null) { 159 doubleBufferVisuals = new HashSet<>(); 160 getDoubleBufferVisuals(screen); 161 } 162 for ( ; i < num; i++) { 163 int visNum = getConfigVisualId(i, screen); 164 int depth = getConfigDepth (i, screen); 165 if (glxSupported) { 166 ret[i] = GLXGraphicsConfig.getConfig(this, visNum); 167 } 168 if (ret[i] == null) { 169 boolean doubleBuffer = 170 (dbeSupported && 171 doubleBufferVisuals.contains(Integer.valueOf(visNum))); 172 173 if (xrenderSupported) { 174 ret[i] = XRGraphicsConfig.getConfig(this, visNum, depth, getConfigColormap(i, screen), 175 doubleBuffer); 176 } else { 177 ret[i] = X11GraphicsConfig.getConfig(this, visNum, depth, 178 getConfigColormap(i, screen), 179 doubleBuffer); 180 } 181 } 182 } 183 configs = ret; 184 } 185 } 186 187 /* 188 * Returns the number of X11 visuals representable as an 189 * X11GraphicsConfig object. 190 */ 191 public native int getNumConfigs(int screen); 192 193 /* 194 * Returns the visualid for the given index of graphics configurations. 195 */ 196 public native int getConfigVisualId (int index, int screen); 197 /* 198 * Returns the depth for the given index of graphics configurations. 199 */ 200 private native int getConfigDepth(int index, int screen); 201 202 /* 203 * Returns the colormap for the given index of graphics configurations. 204 */ 205 private native int getConfigColormap(int index, int screen); 206 207 // Whether or not double-buffering extension is supported 208 static native boolean isDBESupported(); 209 // Callback for adding a new double buffer visual into our set 210 private void addDoubleBufferVisual(int visNum) { 211 doubleBufferVisuals.add(Integer.valueOf(visNum)); 212 } 213 // Enumerates all visuals that support double buffering 214 private native void getDoubleBufferVisuals(int screen); 215 216 /** 217 * Returns the default graphics configuration 218 * associated with this graphics device. 219 */ 220 @Override 221 public GraphicsConfiguration getDefaultConfiguration() { 222 if (defaultConfig == null) { 223 synchronized (configLock) { 224 makeDefaultConfiguration(); 225 } 226 } 227 return defaultConfig; 228 } 229 230 private void makeDefaultConfiguration() { 231 if (defaultConfig == null) { 232 int visNum = getConfigVisualId(0, screen); 233 if (X11GraphicsEnvironment.isGLXAvailable()) { 234 defaultConfig = GLXGraphicsConfig.getConfig(this, visNum); 235 if (X11GraphicsEnvironment.isGLXVerbose()) { 236 if (defaultConfig != null) { 237 System.out.print("OpenGL pipeline enabled"); 238 } else { 239 System.out.print("Could not enable OpenGL pipeline"); 240 } 241 System.out.println(" for default config on screen " + 242 screen); 243 } 244 } 245 if (defaultConfig == null) { 246 int depth = getConfigDepth(0, screen); 247 boolean doubleBuffer = false; 248 if (isDBESupported() && doubleBufferVisuals == null) { 249 doubleBufferVisuals = new HashSet<>(); 250 getDoubleBufferVisuals(screen); 251 doubleBuffer = 252 doubleBufferVisuals.contains(Integer.valueOf(visNum)); 253 } 254 255 if (X11GraphicsEnvironment.isXRenderAvailable()) { 256 if (X11GraphicsEnvironment.isXRenderVerbose()) { 257 System.out.println("XRender pipeline enabled"); 258 } 259 defaultConfig = XRGraphicsConfig.getConfig(this, visNum, 260 depth, getConfigColormap(0, screen), 261 doubleBuffer); 262 } else { 263 defaultConfig = X11GraphicsConfig.getConfig(this, visNum, 264 depth, getConfigColormap(0, screen), 265 doubleBuffer); 266 } 267 } 268 } 269 } 270 271 private static native void enterFullScreenExclusive(long window); 272 private static native void exitFullScreenExclusive(long window); 273 private static native boolean initXrandrExtension(); 274 private static native DisplayMode getCurrentDisplayMode(int screen); 275 private static native void enumDisplayModes(int screen, 276 ArrayList<DisplayMode> modes); 277 private static native void configDisplayMode(int screen, 278 int width, int height, 279 int displayMode); 280 private static native void resetNativeData(int screen); 281 private static native double getNativeScaleFactor(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 Thread( 442 ThreadGroupUtils.getRootThreadGroup(), r, name, 0, false); 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 @Override 488 public synchronized void displayChanged() { 489 scale = initScaleFactor(); 490 // On X11 the visuals do not change, and therefore we don't need 491 // to reset the defaultConfig, config, doubleBufferVisuals, 492 // neither do we need to reset the native data. 493 494 // pass on to all top-level windows on this screen 495 topLevels.notifyListeners(); 496 } 497 498 /** 499 * From the DisplayChangedListener interface; devices do not need 500 * to react to this event. 501 */ 502 @Override 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 public int getScaleFactor() { 516 return scale; 517 } 518 519 public int getNativeScale() { 520 isXrandrExtensionSupported(); 521 return (int)Math.round(getNativeScaleFactor(screen)); 522 } 523 524 private int initScaleFactor() { 525 526 if (SunGraphicsEnvironment.isUIScaleEnabled()) { 527 528 double debugScale = SunGraphicsEnvironment.getDebugScale(); 529 530 if (debugScale >= 1) { 531 return (int) debugScale; 532 } 533 int nativeScale = getNativeScale(); 534 return nativeScale >= 1 ? nativeScale : 1; 535 } 536 537 return 1; 538 } 539 540 /** 541 * Remove a DisplayChangeListener from this X11GraphicsDevice. 542 */ 543 public void removeDisplayChangedListener(DisplayChangedListener client) { 544 topLevels.remove(client); 545 } 546 547 public String toString() { 548 return ("X11GraphicsDevice[screen="+screen+"]"); 549 } 550 } --- EOF ---