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 }