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