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.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 private 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 public synchronized void setFullScreenWindow(Window w) { 333 Window old = getFullScreenWindow(); 334 if (w == old) { 335 return; 336 } 337 if (!isFullScreenSupported()) { 338 super.setFullScreenWindow(w); 339 return; 340 } 341 342 // Enter windowed mode. 343 if (old != null) { 344 // restore the original display mode 345 if (defaultDisplayMode != null) { 346 setDisplayMode(defaultDisplayMode); 347 // we set the default display mode to null here 348 // because the default mode could change during 349 // the life of the application (user can change it through 350 // the desktop properties dialog, for example), so 351 // we need to record it every time prior to 352 // entering the fullscreen mode. 353 defaultDisplayMode = null; 354 } 355 WWindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(old); 356 if (peer != null) { 357 peer.setFullScreenExclusiveModeState(false); 358 // we used to destroy the buffers on exiting fs mode, this 359 // is no longer needed since fs change will cause a surface 360 // data replacement 361 synchronized(peer) { 362 exitFullScreenExclusive(screen, peer); 363 } 364 } 365 removeFSWindowListener(old); 366 } 367 super.setFullScreenWindow(w); 368 if (w != null) { 369 // always record the default display mode prior to going 370 // fullscreen 371 defaultDisplayMode = getDisplayMode(); 372 addFSWindowListener(w); 373 // Enter full screen exclusive mode. 374 WWindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(w); 375 if (peer != null) { 376 synchronized(peer) { 377 enterFullScreenExclusive(screen, peer); 378 // Note: removed replaceSurfaceData() call because 379 // changing the window size or making it visible 380 // will cause this anyway, and both of these events happen 381 // as part of switching into fullscreen mode. 382 } 383 peer.setFullScreenExclusiveModeState(true); 384 } 385 386 // fix for 4868278 387 peer.updateGC(); 388 } 389 } 390 391 // Entering and exiting full-screen mode are done within a 392 // tree-lock and should never lock on any resources which are 393 // required by other threads which may have them and may require 394 // the tree-lock. 395 // REMIND: in the future these methods may need to become protected so that 396 // subclasses could override them and use appropriate api other than GDI 397 // for implementing these functions. 398 protected native void enterFullScreenExclusive(int screen, WindowPeer w); 399 protected native void exitFullScreenExclusive(int screen, WindowPeer w); 400 401 @Override 402 public boolean isDisplayChangeSupported() { 403 return (isFullScreenSupported() && getFullScreenWindow() != null); 404 } 405 406 @Override 407 public synchronized void setDisplayMode(DisplayMode dm) { 408 if (!isDisplayChangeSupported()) { 409 super.setDisplayMode(dm); 410 return; 411 } 412 if (dm == null || (dm = getMatchingDisplayMode(dm)) == null) { 413 throw new IllegalArgumentException("Invalid display mode"); 414 } 415 if (getDisplayMode().equals(dm)) { 416 return; 417 } 418 Window w = getFullScreenWindow(); 419 if (w != null) { 420 WWindowPeer peer = AWTAccessor.getComponentAccessor().getPeer(w); 421 configDisplayMode(screen, peer, dm.getWidth(), dm.getHeight(), 422 dm.getBitDepth(), dm.getRefreshRate()); 423 // resize the fullscreen window to the dimensions of the new 424 // display mode 425 Rectangle screenBounds = getDefaultConfiguration().getBounds(); 426 w.setBounds(screenBounds.x, screenBounds.y, 427 dm.getWidth(), dm.getHeight()); 428 // Note: no call to replaceSurfaceData is required here since 429 // replacement will be caused by an upcoming display change event 430 } else { 431 throw new IllegalStateException("Must be in fullscreen mode " + 432 "in order to set display mode"); 433 } 434 } 435 436 protected native DisplayMode getCurrentDisplayMode(int screen); 437 protected native void configDisplayMode(int screen, WindowPeer w, int width, 438 int height, int bitDepth, 439 int refreshRate); 440 protected native void enumDisplayModes(int screen, ArrayList<DisplayMode> modes); 441 442 @Override 443 public synchronized DisplayMode getDisplayMode() { 444 DisplayMode res = getCurrentDisplayMode(screen); 445 return res; 446 } 447 448 @Override 449 public synchronized DisplayMode[] getDisplayModes() { 450 ArrayList<DisplayMode> modes = new ArrayList<>(); 451 enumDisplayModes(screen, modes); 452 int listSize = modes.size(); 453 DisplayMode[] retArray = new DisplayMode[listSize]; 454 for (int i = 0; i < listSize; i++) { 455 retArray[i] = modes.get(i); 456 } 457 return retArray; 458 } 459 460 protected synchronized DisplayMode getMatchingDisplayMode(DisplayMode dm) { 461 if (!isDisplayChangeSupported()) { 462 return null; 463 } 464 DisplayMode[] modes = getDisplayModes(); 465 for (DisplayMode mode : modes) { 466 if (dm.equals(mode) || 467 (dm.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN && 468 dm.getWidth() == mode.getWidth() && 469 dm.getHeight() == mode.getHeight() && 470 dm.getBitDepth() == mode.getBitDepth())) 471 { 472 return mode; 473 } 474 } 475 return null; 476 } 477 478 /* 479 * From the DisplayChangeListener interface. 480 * Called from Win32GraphicsEnvironment when the display settings have 481 * changed. 482 */ 483 public void displayChanged() { 484 dynamicColorModel = null; 485 defaultConfig = null; 486 configs = null; 487 // pass on to all top-level windows on this display 488 topLevels.notifyListeners(); 489 } 490 491 /** 492 * Part of the DisplayChangedListener interface: devices 493 * do not need to react to this event 494 */ 495 public void paletteChanged() { 496 } 497 498 /* 499 * Add a DisplayChangeListener to be notified when the display settings 500 * are changed. Typically, only top-level containers need to be added 501 * to Win32GraphicsDevice. 502 */ 503 public void addDisplayChangedListener(DisplayChangedListener client) { 504 topLevels.add(client); 505 } 506 507 /* 508 * Remove a DisplayChangeListener from this Win32GraphicsDevice 509 */ 510 public void removeDisplayChangedListener(DisplayChangedListener client) { 511 topLevels.remove(client); 512 } 513 514 /** 515 * Creates and returns the color model associated with this device 516 */ 517 private native ColorModel makeColorModel (int screen, 518 boolean dynamic); 519 520 /** 521 * Returns a dynamic ColorModel which is updated when there 522 * are any changes (e.g., palette changes) in the device 523 */ 524 public ColorModel getDynamicColorModel() { 525 if (dynamicColorModel == null) { 526 dynamicColorModel = makeColorModel(screen, true); 527 } 528 return dynamicColorModel; 529 } 530 531 /** 532 * Returns the non-dynamic ColorModel associated with this device 533 */ 534 public ColorModel getColorModel() { 535 if (colorModel == null) { 536 colorModel = makeColorModel(screen, false); 537 } 538 return colorModel; 539 } 540 541 /** 542 * WindowAdapter class responsible for de/iconifying full-screen window 543 * of this device. 544 * 545 * The listener restores the default display mode when window is iconified 546 * and sets it back to the one set by the user on de-iconification. 547 */ 548 private static class Win32FSWindowAdapter extends WindowAdapter { 549 private Win32GraphicsDevice device; 550 private DisplayMode dm; 551 552 Win32FSWindowAdapter(Win32GraphicsDevice device) { 553 this.device = device; 554 } 555 556 private void setFSWindowsState(Window other, int state) { 557 GraphicsDevice gds[] = 558 GraphicsEnvironment.getLocalGraphicsEnvironment(). 559 getScreenDevices(); 560 // check if the de/activation was caused by other 561 // fs window and ignore the event if that's the case 562 if (other != null) { 563 for (GraphicsDevice gd : gds) { 564 if (other == gd.getFullScreenWindow()) { 565 return; 566 } 567 } 568 } 569 // otherwise apply state to all fullscreen windows 570 for (GraphicsDevice gd : gds) { 571 Window fsw = gd.getFullScreenWindow(); 572 if (fsw instanceof Frame) { 573 ((Frame)fsw).setExtendedState(state); 574 } 575 } 576 } 577 578 @Override 579 public void windowDeactivated(WindowEvent e) { 580 setFSWindowsState(e.getOppositeWindow(), Frame.ICONIFIED); 581 } 582 583 @Override 584 public void windowActivated(WindowEvent e) { 585 setFSWindowsState(e.getOppositeWindow(), Frame.NORMAL); 586 } 587 588 @Override 589 public void windowIconified(WindowEvent e) { 590 // restore the default display mode for this device 591 DisplayMode ddm = device.defaultDisplayMode; 592 if (ddm != null) { 593 dm = device.getDisplayMode(); 594 device.setDisplayMode(ddm); 595 } 596 } 597 598 @Override 599 public void windowDeiconified(WindowEvent e) { 600 // restore the user-set display mode for this device 601 if (dm != null) { 602 device.setDisplayMode(dm); 603 dm = null; 604 } 605 } 606 } 607 608 /** 609 * Adds a WindowListener to be used as 610 * activation/deactivation listener for the current full-screen window. 611 * 612 * @param w full-screen window 613 */ 614 protected void addFSWindowListener(final Window w) { 615 // Note: even though we create a listener for Window instances of 616 // fs windows they will not receive window events. 617 fsWindowListener = new Win32FSWindowAdapter(this); 618 619 // Fix for 6709453. Using invokeLater to avoid listening 620 // for the events already posted to the queue. 621 EventQueue.invokeLater(new Runnable() { 622 public void run() { 623 w.addWindowListener(fsWindowListener); 624 } 625 }); 626 } 627 628 /** 629 * Removes the fs window listener. 630 * 631 * @param w full-screen window 632 */ 633 protected void removeFSWindowListener(Window w) { 634 w.removeWindowListener(fsWindowListener); 635 fsWindowListener = null; 636 } 637 }