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