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