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     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 = (WWindowPeer)old.getPeer();
 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 = (WWindowPeer)w.getPeer();
 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 = (WWindowPeer)w.getPeer();
 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 }