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