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