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.GraphicsDevice; 30 import java.awt.GraphicsConfiguration; 31 import java.awt.GraphicsEnvironment; 32 import java.awt.DisplayMode; 33 import java.awt.EventQueue; 34 import java.awt.Frame; 35 import java.awt.Rectangle; 36 import java.awt.Window; 37 import java.awt.event.WindowAdapter; 38 import java.awt.event.WindowEvent; 39 import java.awt.event.WindowListener; 40 import java.awt.geom.Point2D; 41 import java.awt.image.ColorModel; 42 import java.util.ArrayList; 43 import java.util.Vector; 44 import java.awt.peer.WindowPeer; 45 import java.security.AccessController; 46 import sun.awt.windows.WWindowPeer; 47 import sun.java2d.SunGraphicsEnvironment; 48 import sun.java2d.opengl.WGLGraphicsConfig; 49 import sun.java2d.windows.WindowsFlags; 50 import sun.security.action.GetPropertyAction; 51 import static sun.awt.Win32GraphicsEnvironment.debugScaleX; 52 import static sun.awt.Win32GraphicsEnvironment.debugScaleY; 53 54 /** 55 * This is an implementation of a GraphicsDevice object for a single 56 * Win32 screen. 57 * 58 * @see GraphicsEnvironment 59 * @see GraphicsConfiguration 60 */ 61 public class Win32GraphicsDevice extends GraphicsDevice implements 62 DisplayChangedListener { 63 int screen; 64 ColorModel dynamicColorModel; // updated with dev changes 65 ColorModel colorModel; // static for device 66 protected GraphicsConfiguration[] configs; 67 protected GraphicsConfiguration defaultConfig; 68 69 private final String idString; 70 protected String descString; 71 // Note that we do not synchronize access to this variable - it doesn't 72 // really matter if a thread does an operation on graphics device which is 73 // about to become invalid (or already become) - we are prepared to deal 74 // with this on the native level. 75 private boolean valid; 76 77 // keep track of top-level windows on this display 78 private SunDisplayChanger topLevels = new SunDisplayChanger(); 79 // REMIND: we may disable the use of pixel formats for some accelerated 80 // pipelines which are mutually exclusive with opengl, for which 81 // pixel formats were added in the first place 82 protected static boolean pfDisabled; 83 private static AWTPermission fullScreenExclusivePermission; 84 // the original display mode we had before entering the fullscreen 85 // mode 86 private DisplayMode defaultDisplayMode; 87 // activation/deactivation listener for the full-screen window 88 private WindowListener fsWindowListener; 89 90 private float scaleX = 1.0f; 91 private float scaleY = 1.0f; 92 93 static { 94 95 // 4455041 - Even when ddraw is disabled, ddraw.dll is loaded when 96 // pixel format calls are made. This causes problems when a Java app 97 // is run as an NT service. To prevent the loading of ddraw.dll 98 // completely, sun.awt.nopixfmt should be set as well. Apps which use 99 // OpenGL w/ Java probably don't want to set this. 100 String nopixfmt = java.security.AccessController.doPrivileged( 101 new sun.security.action.GetPropertyAction("sun.awt.nopixfmt")); 102 pfDisabled = (nopixfmt != null); 103 initIDs(); 104 } 105 106 private static native void initIDs(); 107 108 native void initDevice(int screen); 109 native void initNativeScale(int screen); 110 native void setNativeScale(int screen, float scaleX, float scaleY); 111 native float getNativeScaleX(int screen); 112 native float getNativeScaleY(int screen); 113 114 public Win32GraphicsDevice(int screennum) { 115 this.screen = screennum; 116 // we cache the strings because we want toString() and getIDstring 117 // to reflect the original screen number (which may change if the 118 // device is removed) 119 idString = "\\Display"+screen; 120 // REMIND: may be should use class name? 121 descString = "Win32GraphicsDevice[screen=" + screen; 122 valid = true; 123 124 initDevice(screennum); 125 initScaleFactors(); 126 } 127 128 /** 129 * Returns the type of the graphics device. 130 * @see #TYPE_RASTER_SCREEN 131 * @see #TYPE_PRINTER 132 * @see #TYPE_IMAGE_BUFFER 133 */ 134 public int getType() { 135 return TYPE_RASTER_SCREEN; 136 } 137 138 /** 139 * Returns the Win32 screen of the device. 140 */ 141 public int getScreen() { 142 return screen; 143 } 144 145 public float getDefaultScaleX() { 146 return scaleX; 147 } 148 149 public float getDefaultScaleY() { 150 return scaleY; 151 } 152 153 private void initScaleFactors() { 154 if (SunGraphicsEnvironment.isUIScaleEnabled()) { 155 if (debugScaleX > 0 && debugScaleY > 0) { 156 scaleX = debugScaleX; 157 scaleY = debugScaleY; 158 setNativeScale(screen, scaleX, scaleY); 159 } else { 160 initNativeScale(screen); 161 scaleX = getNativeScaleX(screen); 162 scaleY = getNativeScaleY(screen); 163 } 164 } 165 } 166 167 /** 168 * Returns whether this is a valid devicie. Device can become 169 * invalid as a result of device removal event. 170 */ 171 public boolean isValid() { 172 return valid; 173 } 174 175 /** 176 * Called from native code when the device was removed. 177 * 178 * @param defaultScreen the current default screen 179 */ 180 protected void invalidate(int defaultScreen) { 181 valid = false; 182 screen = defaultScreen; 183 } 184 185 /** 186 * Returns the identification string associated with this graphics 187 * device. 188 */ 189 public String getIDstring() { 190 return idString; 191 } 192 193 194 /** 195 * Returns all of the graphics 196 * configurations associated with this graphics device. 197 */ 198 public GraphicsConfiguration[] getConfigurations() { 199 if (configs==null) { 200 if (WindowsFlags.isOGLEnabled() && isDefaultDevice()) { 201 defaultConfig = getDefaultConfiguration(); 202 if (defaultConfig != null) { 203 configs = new GraphicsConfiguration[1]; 204 configs[0] = defaultConfig; 205 return configs.clone(); 206 } 207 } 208 209 int max = getMaxConfigs(screen); 210 int defaultPixID = getDefaultPixID(screen); 211 Vector<GraphicsConfiguration> v = new Vector<>( max ); 212 if (defaultPixID == 0) { 213 // Workaround for failing GDI calls 214 defaultConfig = Win32GraphicsConfig.getConfig(this, 215 defaultPixID); 216 v.addElement(defaultConfig); 217 } 218 else { 219 for (int i = 1; i <= max; i++) { 220 if (isPixFmtSupported(i, screen)) { 221 if (i == defaultPixID) { 222 defaultConfig = Win32GraphicsConfig.getConfig( 223 this, i); 224 v.addElement(defaultConfig); 225 } 226 else { 227 v.addElement(Win32GraphicsConfig.getConfig( 228 this, i)); 229 } 230 } 231 } 232 } 233 configs = new GraphicsConfiguration[v.size()]; 234 v.copyInto(configs); 235 } 236 return configs.clone(); 237 } 238 239 /** 240 * Returns the maximum number of graphics configurations available, or 1 241 * if PixelFormat calls fail or are disabled. 242 * This number is less than or equal to the number of graphics 243 * configurations supported. 244 */ 245 protected int getMaxConfigs(int screen) { 246 if (pfDisabled) { 247 return 1; 248 } else { 249 return getMaxConfigsImpl(screen); 250 } 251 } 252 253 private native int getMaxConfigsImpl(int screen); 254 255 /** 256 * Returns whether or not the PixelFormat indicated by index is 257 * supported. Supported PixelFormats support drawing to a Window 258 * (PFD_DRAW_TO_WINDOW), support GDI (PFD_SUPPORT_GDI), and in the 259 * case of an 8-bit format (cColorBits <= 8) uses indexed colors 260 * (iPixelType == PFD_TYPE_COLORINDEX). 261 * We use the index 0 to indicate that PixelFormat calls don't work, or 262 * are disabled. Do not call this function with an index of 0. 263 * @param index a PixelFormat index 264 */ 265 private native boolean isPixFmtSupported(int index, int screen); 266 267 /** 268 * Returns the PixelFormatID of the default graphics configuration 269 * associated with this graphics device, or 0 if PixelFormats calls fail or 270 * are disabled. 271 */ 272 protected int getDefaultPixID(int screen) { 273 if (pfDisabled) { 274 return 0; 275 } else { 276 return getDefaultPixIDImpl(screen); 277 } 278 } 279 280 /** 281 * Returns the default PixelFormat ID from GDI. Do not call if PixelFormats 282 * are disabled. 283 */ 284 private native int getDefaultPixIDImpl(int screen); 285 286 /** 287 * Returns the default graphics configuration 288 * associated with this graphics device. 289 */ 290 public GraphicsConfiguration getDefaultConfiguration() { 291 if (defaultConfig == null) { 292 // first try to create a WGLGraphicsConfig if OGL is enabled 293 // REMIND: the WGL code does not yet work properly in multimon 294 // situations, so we will fallback on GDI if we are not on the 295 // default device... 296 if (WindowsFlags.isOGLEnabled() && isDefaultDevice()) { 297 int defPixID = WGLGraphicsConfig.getDefaultPixFmt(screen); 298 defaultConfig = WGLGraphicsConfig.getConfig(this, defPixID); 299 if (WindowsFlags.isOGLVerbose()) { 300 if (defaultConfig != null) { 301 System.out.print("OpenGL pipeline enabled"); 302 } else { 303 System.out.print("Could not enable OpenGL pipeline"); 304 } 305 System.out.println(" for default config on screen " + 306 screen); 307 } 308 } 309 310 // Fix for 4669614. Most apps are not concerned with PixelFormats, 311 // yet we ALWAYS used them for determining ColorModels and such. 312 // By passing in 0 as the PixelFormatID here, we signal that 313 // PixelFormats should not be used, thus avoid loading the opengl 314 // library. Apps concerned with PixelFormats can still use 315 // GraphicsConfiguration.getConfigurations(). 316 // Note that calling native pixel format functions tends to cause 317 // problems between those functions (which are OpenGL-related) 318 // and our use of DirectX. For example, some Matrox boards will 319 // crash or hang calling these functions when any app is running 320 // in DirectX fullscreen mode. So avoiding these calls unless 321 // absolutely necessary is preferable. 322 if (defaultConfig == null) { 323 defaultConfig = Win32GraphicsConfig.getConfig(this, 0); 324 } 325 } 326 return defaultConfig; 327 } 328 329 public String toString() { 330 return valid ? descString + "]" : descString + ", removed]"; 331 } 332 333 /** 334 * Returns true if this is the default GraphicsDevice for the 335 * GraphicsEnvironment. 336 */ 337 private boolean isDefaultDevice() { 338 return (this == 339 GraphicsEnvironment. 340 getLocalGraphicsEnvironment().getDefaultScreenDevice()); 341 } 342 343 private static boolean isFSExclusiveModeAllowed() { 344 SecurityManager security = System.getSecurityManager(); 345 if (security != null) { 346 if (fullScreenExclusivePermission == null) { 347 fullScreenExclusivePermission = 348 new AWTPermission("fullScreenExclusive"); 349 } 350 try { 351 security.checkPermission(fullScreenExclusivePermission); 352 } catch (SecurityException e) { 353 return false; 354 } 355 } 356 return true; 357 } 358 359 /** 360 * returns true unless we're not allowed to use fullscreen mode. 361 */ 362 @Override 363 public boolean isFullScreenSupported() { 364 return isFSExclusiveModeAllowed(); 365 } 366 367 @Override 368 public synchronized void setFullScreenWindow(Window w) { 369 Window old = getFullScreenWindow(); 370 if (w == old) { 371 return; 372 } 373 if (!isFullScreenSupported()) { 374 super.setFullScreenWindow(w); 375 return; 376 } 377 378 // Enter windowed mode. 379 if (old != null) { 380 // restore the original display mode 381 if (defaultDisplayMode != null) { 382 setDisplayMode(defaultDisplayMode); 383 // we set the default display mode to null here 384 // because the default mode could change during 385 // the life of the application (user can change it through 386 // the desktop properties dialog, for example), so 387 // we need to record it every time prior to 388 // entering the fullscreen mode. 389 defaultDisplayMode = null; 390 } 391 WWindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(old); 392 if (peer != null) { 393 peer.setFullScreenExclusiveModeState(false); 394 // we used to destroy the buffers on exiting fs mode, this 395 // is no longer needed since fs change will cause a surface 396 // data replacement 397 synchronized(peer) { 398 exitFullScreenExclusive(screen, peer); 399 } 400 } 401 removeFSWindowListener(old); 402 } 403 super.setFullScreenWindow(w); 404 if (w != null) { 405 // always record the default display mode prior to going 406 // fullscreen 407 defaultDisplayMode = getDisplayMode(); 408 addFSWindowListener(w); 409 // Enter full screen exclusive mode. 410 WWindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(w); 411 if (peer != null) { 412 synchronized(peer) { 413 enterFullScreenExclusive(screen, peer); 414 // Note: removed replaceSurfaceData() call because 415 // changing the window size or making it visible 416 // will cause this anyway, and both of these events happen 417 // as part of switching into fullscreen mode. 418 } 419 peer.setFullScreenExclusiveModeState(true); 420 } 421 422 // fix for 4868278 423 peer.updateGC(); 424 } 425 } 426 427 // Entering and exiting full-screen mode are done within a 428 // tree-lock and should never lock on any resources which are 429 // required by other threads which may have them and may require 430 // the tree-lock. 431 // REMIND: in the future these methods may need to become protected so that 432 // subclasses could override them and use appropriate api other than GDI 433 // for implementing these functions. 434 protected native void enterFullScreenExclusive(int screen, WindowPeer w); 435 protected native void exitFullScreenExclusive(int screen, WindowPeer w); 436 437 @Override 438 public boolean isDisplayChangeSupported() { 439 return (isFullScreenSupported() && getFullScreenWindow() != null); 440 } 441 442 @Override 443 public synchronized void setDisplayMode(DisplayMode dm) { 444 if (!isDisplayChangeSupported()) { 445 super.setDisplayMode(dm); 446 return; 447 } 448 if (dm == null || (dm = getMatchingDisplayMode(dm)) == null) { 449 throw new IllegalArgumentException("Invalid display mode"); 450 } 451 if (getDisplayMode().equals(dm)) { 452 return; 453 } 454 Window w = getFullScreenWindow(); 455 if (w != null) { 456 WWindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(w); 457 configDisplayMode(screen, peer, dm.getWidth(), dm.getHeight(), 458 dm.getBitDepth(), dm.getRefreshRate()); 459 // resize the fullscreen window to the dimensions of the new 460 // display mode 461 Rectangle screenBounds = getDefaultConfiguration().getBounds(); 462 w.setBounds(screenBounds.x, screenBounds.y, 463 dm.getWidth(), dm.getHeight()); 464 // Note: no call to replaceSurfaceData is required here since 465 // replacement will be caused by an upcoming display change event 466 } else { 467 throw new IllegalStateException("Must be in fullscreen mode " + 468 "in order to set display mode"); 469 } 470 } 471 472 protected native DisplayMode getCurrentDisplayMode(int screen); 473 protected native void configDisplayMode(int screen, WindowPeer w, int width, 474 int height, int bitDepth, 475 int refreshRate); 476 protected native void enumDisplayModes(int screen, ArrayList<DisplayMode> modes); 477 478 @Override 479 public synchronized DisplayMode getDisplayMode() { 480 DisplayMode res = getCurrentDisplayMode(screen); 481 return res; 482 } 483 484 @Override 485 public synchronized DisplayMode[] getDisplayModes() { 486 ArrayList<DisplayMode> modes = new ArrayList<>(); 487 enumDisplayModes(screen, modes); 488 int listSize = modes.size(); 489 DisplayMode[] retArray = new DisplayMode[listSize]; 490 for (int i = 0; i < listSize; i++) { 491 retArray[i] = modes.get(i); 492 } 493 return retArray; 494 } 495 496 protected synchronized DisplayMode getMatchingDisplayMode(DisplayMode dm) { 497 if (!isDisplayChangeSupported()) { 498 return null; 499 } 500 DisplayMode[] modes = getDisplayModes(); 501 for (DisplayMode mode : modes) { 502 if (dm.equals(mode) || 503 (dm.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN && 504 dm.getWidth() == mode.getWidth() && 505 dm.getHeight() == mode.getHeight() && 506 dm.getBitDepth() == mode.getBitDepth())) 507 { 508 return mode; 509 } 510 } 511 return null; 512 } 513 514 /* 515 * From the DisplayChangeListener interface. 516 * Called from Win32GraphicsEnvironment when the display settings have 517 * changed. 518 */ 519 public void displayChanged() { 520 dynamicColorModel = null; 521 defaultConfig = null; 522 configs = null; 523 // pass on to all top-level windows on this display 524 topLevels.notifyListeners(); 525 initScaleFactors(); 526 } 527 528 /** 529 * Part of the DisplayChangedListener interface: devices 530 * do not need to react to this event 531 */ 532 public void paletteChanged() { 533 } 534 535 /* 536 * Add a DisplayChangeListener to be notified when the display settings 537 * are changed. Typically, only top-level containers need to be added 538 * to Win32GraphicsDevice. 539 */ 540 public void addDisplayChangedListener(DisplayChangedListener client) { 541 topLevels.add(client); 542 } 543 544 /* 545 * Remove a DisplayChangeListener from this Win32GraphicsDevice 546 */ 547 public void removeDisplayChangedListener(DisplayChangedListener client) { 548 topLevels.remove(client); 549 } 550 551 /** 552 * Creates and returns the color model associated with this device 553 */ 554 private native ColorModel makeColorModel (int screen, 555 boolean dynamic); 556 557 /** 558 * Returns a dynamic ColorModel which is updated when there 559 * are any changes (e.g., palette changes) in the device 560 */ 561 public ColorModel getDynamicColorModel() { 562 if (dynamicColorModel == null) { 563 dynamicColorModel = makeColorModel(screen, true); 564 } 565 return dynamicColorModel; 566 } 567 568 /** 569 * Returns the non-dynamic ColorModel associated with this device 570 */ 571 public ColorModel getColorModel() { 572 if (colorModel == null) { 573 colorModel = makeColorModel(screen, false); 574 } 575 return colorModel; 576 } 577 578 /** 579 * WindowAdapter class responsible for de/iconifying full-screen window 580 * of this device. 581 * 582 * The listener restores the default display mode when window is iconified 583 * and sets it back to the one set by the user on de-iconification. 584 */ 585 private static class Win32FSWindowAdapter extends WindowAdapter { 586 private Win32GraphicsDevice device; 587 private DisplayMode dm; 588 589 Win32FSWindowAdapter(Win32GraphicsDevice device) { 590 this.device = device; 591 } 592 593 private void setFSWindowsState(Window other, int state) { 594 GraphicsDevice gds[] = 595 GraphicsEnvironment.getLocalGraphicsEnvironment(). 596 getScreenDevices(); 597 // check if the de/activation was caused by other 598 // fs window and ignore the event if that's the case 599 if (other != null) { 600 for (GraphicsDevice gd : gds) { 601 if (other == gd.getFullScreenWindow()) { 602 return; 603 } 604 } 605 } 606 // otherwise apply state to all fullscreen windows 607 for (GraphicsDevice gd : gds) { 608 Window fsw = gd.getFullScreenWindow(); 609 if (fsw instanceof Frame) { 610 ((Frame)fsw).setExtendedState(state); 611 } 612 } 613 } 614 615 @Override 616 public void windowDeactivated(WindowEvent e) { 617 setFSWindowsState(e.getOppositeWindow(), Frame.ICONIFIED); 618 } 619 620 @Override 621 public void windowActivated(WindowEvent e) { 622 setFSWindowsState(e.getOppositeWindow(), Frame.NORMAL); 623 } 624 625 @Override 626 public void windowIconified(WindowEvent e) { 627 // restore the default display mode for this device 628 DisplayMode ddm = device.defaultDisplayMode; 629 if (ddm != null) { 630 dm = device.getDisplayMode(); 631 device.setDisplayMode(ddm); 632 } 633 } 634 635 @Override 636 public void windowDeiconified(WindowEvent e) { 637 // restore the user-set display mode for this device 638 if (dm != null) { 639 device.setDisplayMode(dm); 640 dm = null; 641 } 642 } 643 } 644 645 /** 646 * Adds a WindowListener to be used as 647 * activation/deactivation listener for the current full-screen window. 648 * 649 * @param w full-screen window 650 */ 651 protected void addFSWindowListener(final Window w) { 652 // Note: even though we create a listener for Window instances of 653 // fs windows they will not receive window events. 654 fsWindowListener = new Win32FSWindowAdapter(this); 655 656 // Fix for 6709453. Using invokeLater to avoid listening 657 // for the events already posted to the queue. 658 EventQueue.invokeLater(new Runnable() { 659 public void run() { 660 w.addWindowListener(fsWindowListener); 661 } 662 }); 663 } 664 665 /** 666 * Removes the fs window listener. 667 * 668 * @param w full-screen window 669 */ 670 protected void removeFSWindowListener(Window w) { 671 w.removeWindowListener(fsWindowListener); 672 fsWindowListener = null; 673 } 674 }