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