Win32GraphicsDevice.java revision 13220:a8e9ad77ac81
1177633Sdfr/*
2177633Sdfr * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
3177633Sdfr * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4177633Sdfr *
5177633Sdfr * This code is free software; you can redistribute it and/or modify it
6177633Sdfr * under the terms of the GNU General Public License version 2 only, as
7177633Sdfr * published by the Free Software Foundation.  Oracle designates this
8177633Sdfr * particular file as subject to the "Classpath" exception as provided
9177633Sdfr * by Oracle in the LICENSE file that accompanied this code.
10177633Sdfr *
11177633Sdfr * This code is distributed in the hope that it will be useful, but WITHOUT
12177633Sdfr * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13177633Sdfr * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14177633Sdfr * version 2 for more details (a copy is included in the LICENSE file that
15177633Sdfr * accompanied this code).
16177633Sdfr *
17177633Sdfr * You should have received a copy of the GNU General Public License version
18177633Sdfr * 2 along with this work; if not, write to the Free Software Foundation,
19177633Sdfr * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20177633Sdfr *
21177633Sdfr * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22177633Sdfr * or visit www.oracle.com if you need additional information or have any
23177633Sdfr * questions.
24177633Sdfr */
25177633Sdfr
26177633Sdfrpackage sun.awt;
27177633Sdfr
28177633Sdfrimport java.awt.AWTPermission;
29177633Sdfrimport java.awt.GraphicsDevice;
30177633Sdfrimport java.awt.GraphicsConfiguration;
31177633Sdfrimport java.awt.GraphicsEnvironment;
32177633Sdfrimport java.awt.DisplayMode;
33177633Sdfrimport java.awt.EventQueue;
34177633Sdfrimport java.awt.Frame;
35177633Sdfrimport java.awt.Rectangle;
36177633Sdfrimport java.awt.Window;
37177633Sdfrimport java.awt.event.WindowAdapter;
38177633Sdfrimport java.awt.event.WindowEvent;
39177633Sdfrimport java.awt.event.WindowListener;
40177633Sdfrimport java.awt.geom.Point2D;
41177633Sdfrimport java.awt.image.ColorModel;
42177633Sdfrimport java.util.ArrayList;
43177633Sdfrimport java.util.Vector;
44177633Sdfrimport java.awt.peer.WindowPeer;
45177633Sdfrimport java.security.AccessController;
46177633Sdfrimport sun.awt.windows.WWindowPeer;
47177633Sdfrimport sun.java2d.SunGraphicsEnvironment;
48177633Sdfrimport sun.java2d.opengl.WGLGraphicsConfig;
49177633Sdfrimport sun.java2d.windows.WindowsFlags;
50177633Sdfrimport sun.security.action.GetPropertyAction;
51177633Sdfrimport static sun.awt.Win32GraphicsEnvironment.debugScaleX;
52177633Sdfrimport static sun.awt.Win32GraphicsEnvironment.debugScaleY;
53177633Sdfr
54177633Sdfr/**
55177633Sdfr * This is an implementation of a GraphicsDevice object for a single
56177633Sdfr * Win32 screen.
57177633Sdfr *
58177633Sdfr * @see GraphicsEnvironment
59177633Sdfr * @see GraphicsConfiguration
60177633Sdfr */
61177633Sdfrpublic class Win32GraphicsDevice extends GraphicsDevice implements
62177633Sdfr DisplayChangedListener {
63177633Sdfr    int screen;
64177633Sdfr    ColorModel dynamicColorModel;   // updated with dev changes
65177633Sdfr    ColorModel colorModel;          // static for device
66177633Sdfr    protected GraphicsConfiguration[] configs;
67177633Sdfr    protected GraphicsConfiguration defaultConfig;
68177633Sdfr
69192971Skmacy    private final String idString;
70192971Skmacy    protected String descString;
71177633Sdfr    // Note that we do not synchronize access to this variable - it doesn't
72177633Sdfr    // really matter if a thread does an operation on graphics device which is
73177633Sdfr    // about to become invalid (or already become) - we are prepared to deal
74177633Sdfr    // with this on the native level.
75177633Sdfr    private boolean valid;
76177633Sdfr
77177633Sdfr    // keep track of top-level windows on this display
78177633Sdfr    private SunDisplayChanger topLevels = new SunDisplayChanger();
79192971Skmacy    // REMIND: we may disable the use of pixel formats for some accelerated
80192971Skmacy    // pipelines which are mutually exclusive with opengl, for which
81177633Sdfr    // pixel formats were added in the first place
82177633Sdfr    protected static boolean pfDisabled;
83177633Sdfr    private static AWTPermission fullScreenExclusivePermission;
84177633Sdfr    // the original display mode we had before entering the fullscreen
85177633Sdfr    // mode
86177633Sdfr    private DisplayMode defaultDisplayMode;
87177633Sdfr    // activation/deactivation listener for the full-screen window
88177633Sdfr    private WindowListener fsWindowListener;
89177633Sdfr
90177633Sdfr    private float scaleX;
91192971Skmacy    private float scaleY;
92192971Skmacy
93177633Sdfr    static {
94177633Sdfr
95177633Sdfr        // 4455041 - Even when ddraw is disabled, ddraw.dll is loaded when
96177633Sdfr        // pixel format calls are made.  This causes problems when a Java app
97177633Sdfr        // is run as an NT service.  To prevent the loading of ddraw.dll
98177633Sdfr        // completely, sun.awt.nopixfmt should be set as well.  Apps which use
99177633Sdfr        // OpenGL w/ Java probably don't want to set this.
100177633Sdfr        String nopixfmt = java.security.AccessController.doPrivileged(
101177633Sdfr            new sun.security.action.GetPropertyAction("sun.awt.nopixfmt"));
102177633Sdfr        pfDisabled = (nopixfmt != null);
103177633Sdfr        initIDs();
104177633Sdfr    }
105177633Sdfr
106177633Sdfr    private static native void initIDs();
107177633Sdfr
108177633Sdfr    native void initDevice(int screen);
109177633Sdfr    native void initNativeScale(int screen);
110177633Sdfr    native void setNativeScale(int screen, float scaleX, float scaleY);
111177633Sdfr    native float getNativeScaleX(int screen);
112177633Sdfr    native float getNativeScaleY(int screen);
113177633Sdfr
114177633Sdfr    public Win32GraphicsDevice(int screennum) {
115177633Sdfr        this.screen = screennum;
116177633Sdfr        // we cache the strings because we want toString() and getIDstring
117177633Sdfr        // to reflect the original screen number (which may change if the
118177633Sdfr        // device is removed)
119177633Sdfr        idString = "\\Display"+screen;
120177633Sdfr        // REMIND: may be should use class name?
121177633Sdfr        descString = "Win32GraphicsDevice[screen=" + screen;
122177633Sdfr        valid = true;
123177633Sdfr
124177633Sdfr        initDevice(screennum);
125177633Sdfr        initScaleFactors();
126177633Sdfr    }
127177633Sdfr
128177633Sdfr    /**
129177633Sdfr     * Returns the type of the graphics device.
130177633Sdfr     * @see #TYPE_RASTER_SCREEN
131177633Sdfr     * @see #TYPE_PRINTER
132177633Sdfr     * @see #TYPE_IMAGE_BUFFER
133177633Sdfr     */
134177633Sdfr    public int getType() {
135177633Sdfr        return TYPE_RASTER_SCREEN;
136177633Sdfr    }
137177633Sdfr
138177633Sdfr    /**
139177633Sdfr     * Returns the Win32 screen of the device.
140177633Sdfr     */
141177633Sdfr    public int getScreen() {
142177633Sdfr        return screen;
143177633Sdfr    }
144177633Sdfr
145177633Sdfr    public float getDefaultScaleX() {
146177633Sdfr        return scaleX;
147177633Sdfr    }
148177633Sdfr
149177633Sdfr    public float getDefaultScaleY() {
150177633Sdfr        return scaleY;
151177633Sdfr    }
152177633Sdfr
153177633Sdfr    private void initScaleFactors() {
154177633Sdfr        if (SunGraphicsEnvironment.isUIScaleEnabled()) {
155177633Sdfr            if (debugScaleX > 0 && debugScaleY > 0) {
156177633Sdfr                scaleX = debugScaleX;
157177633Sdfr                scaleY = debugScaleY;
158177633Sdfr                setNativeScale(screen, scaleX, scaleY);
159177633Sdfr            } else {
160177633Sdfr                initNativeScale(screen);
161177633Sdfr                scaleX = getNativeScaleX(screen);
162177633Sdfr                scaleY = getNativeScaleY(screen);
163177633Sdfr            }
164177633Sdfr        } else {
165177633Sdfr            scaleX = 1;
166177633Sdfr            scaleY = 1;
167177633Sdfr        }
168177633Sdfr    }
169177633Sdfr
170177633Sdfr    /**
171177633Sdfr     * Returns whether this is a valid devicie. Device can become
172177633Sdfr     * invalid as a result of device removal event.
173177633Sdfr     */
174177633Sdfr    public boolean isValid() {
175177633Sdfr        return valid;
176177633Sdfr    }
177177633Sdfr
178177633Sdfr    /**
179177633Sdfr     * Called from native code when the device was removed.
180177633Sdfr     *
181177633Sdfr     * @param defaultScreen the current default screen
182177633Sdfr     */
183177633Sdfr    protected void invalidate(int defaultScreen) {
184177633Sdfr        valid = false;
185177633Sdfr        screen = defaultScreen;
186177633Sdfr    }
187177633Sdfr
188177633Sdfr    /**
189177633Sdfr     * Returns the identification string associated with this graphics
190177633Sdfr     * device.
191177633Sdfr     */
192177633Sdfr    public String getIDstring() {
193177633Sdfr        return idString;
194177633Sdfr    }
195177633Sdfr
196177633Sdfr
197177633Sdfr    /**
198177633Sdfr     * Returns all of the graphics
199177633Sdfr     * configurations associated with this graphics device.
200177633Sdfr     */
201177633Sdfr    public GraphicsConfiguration[] getConfigurations() {
202177633Sdfr        if (configs==null) {
203177633Sdfr            if (WindowsFlags.isOGLEnabled() && isDefaultDevice()) {
204177633Sdfr                defaultConfig = getDefaultConfiguration();
205177633Sdfr                if (defaultConfig != null) {
206177633Sdfr                    configs = new GraphicsConfiguration[1];
207177633Sdfr                    configs[0] = defaultConfig;
208177633Sdfr                    return configs.clone();
209177633Sdfr                }
210177633Sdfr            }
211177633Sdfr
212177633Sdfr            int max = getMaxConfigs(screen);
213177633Sdfr            int defaultPixID = getDefaultPixID(screen);
214177633Sdfr            Vector<GraphicsConfiguration> v = new Vector<>( max );
215177633Sdfr            if (defaultPixID == 0) {
216177633Sdfr                // Workaround for failing GDI calls
217309503Sngie                defaultConfig = Win32GraphicsConfig.getConfig(this,
218177633Sdfr                                                              defaultPixID);
219177633Sdfr                v.addElement(defaultConfig);
220177633Sdfr            }
221177633Sdfr            else {
222177633Sdfr                for (int i = 1; i <= max; i++) {
223177633Sdfr                    if (isPixFmtSupported(i, screen)) {
224177633Sdfr                        if (i == defaultPixID) {
225177633Sdfr                            defaultConfig = Win32GraphicsConfig.getConfig(
226177633Sdfr                             this, i);
227177633Sdfr                            v.addElement(defaultConfig);
228177633Sdfr                        }
229177633Sdfr                        else {
230177633Sdfr                            v.addElement(Win32GraphicsConfig.getConfig(
231177633Sdfr                             this, i));
232177633Sdfr                        }
233177633Sdfr                    }
234192971Skmacy                }
235192971Skmacy            }
236192971Skmacy            configs = new GraphicsConfiguration[v.size()];
237192971Skmacy            v.copyInto(configs);
238192971Skmacy        }
239192971Skmacy        return configs.clone();
240192971Skmacy    }
241192971Skmacy
242192971Skmacy    /**
243192971Skmacy     * Returns the maximum number of graphics configurations available, or 1
244192971Skmacy     * if PixelFormat calls fail or are disabled.
245192971Skmacy     * This number is less than or equal to the number of graphics
246192971Skmacy     * configurations supported.
247192971Skmacy     */
248192971Skmacy    protected int getMaxConfigs(int screen) {
249192971Skmacy        if (pfDisabled) {
250192971Skmacy            return 1;
251192971Skmacy        } else {
252192971Skmacy            return getMaxConfigsImpl(screen);
253192971Skmacy        }
254192971Skmacy    }
255192971Skmacy
256192971Skmacy    private native int getMaxConfigsImpl(int screen);
257192971Skmacy
258192971Skmacy    /**
259192971Skmacy     * Returns whether or not the PixelFormat indicated by index is
260192971Skmacy     * supported.  Supported PixelFormats support drawing to a Window
261192971Skmacy     * (PFD_DRAW_TO_WINDOW), support GDI (PFD_SUPPORT_GDI), and in the
262192971Skmacy     * case of an 8-bit format (cColorBits <= 8) uses indexed colors
263192971Skmacy     * (iPixelType == PFD_TYPE_COLORINDEX).
264192971Skmacy     * We use the index 0 to indicate that PixelFormat calls don't work, or
265192971Skmacy     * are disabled.  Do not call this function with an index of 0.
266192971Skmacy     * @param index a PixelFormat index
267192971Skmacy     */
268192971Skmacy    private native boolean isPixFmtSupported(int index, int screen);
269192971Skmacy
270192971Skmacy    /**
271192971Skmacy     * Returns the PixelFormatID of the default graphics configuration
272192971Skmacy     * associated with this graphics device, or 0 if PixelFormats calls fail or
273192971Skmacy     * are disabled.
274192971Skmacy     */
275192971Skmacy    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     */
287    private native int getDefaultPixIDImpl(int screen);
288
289    /**
290     * Returns the default graphics configuration
291     * associated with this graphics device.
292     */
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
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     */
340    private boolean isDefaultDevice() {
341        return (this ==
342                GraphicsEnvironment.
343                    getLocalGraphicsEnvironment().getDefaultScreenDevice());
344    }
345
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
366    public boolean isFullScreenSupported() {
367        return isFSExclusiveModeAllowed();
368    }
369
370    @Override
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.
437    protected native void enterFullScreenExclusive(int screen, WindowPeer w);
438    protected native void exitFullScreenExclusive(int screen, WindowPeer w);
439
440    @Override
441    public boolean isDisplayChangeSupported() {
442        return (isFullScreenSupported() && getFullScreenWindow() != null);
443    }
444
445    @Override
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
475    protected native DisplayMode getCurrentDisplayMode(int screen);
476    protected native void configDisplayMode(int screen, WindowPeer w, int width,
477                                          int height, int bitDepth,
478                                          int refreshRate);
479    protected native void enumDisplayModes(int screen, ArrayList<DisplayMode> modes);
480
481    @Override
482    public synchronized DisplayMode getDisplayMode() {
483        DisplayMode res = getCurrentDisplayMode(screen);
484        return res;
485    }
486
487    @Override
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
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     */
522    public void displayChanged() {
523        dynamicColorModel = null;
524        defaultConfig = null;
525        configs = null;
526        // pass on to all top-level windows on this display
527        topLevels.notifyListeners();
528        initScaleFactors();
529    }
530
531    /**
532     * Part of the DisplayChangedListener interface: devices
533     * do not need to react to this event
534     */
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     */
543    public void addDisplayChangedListener(DisplayChangedListener client) {
544        topLevels.add(client);
545    }
546
547    /*
548     * Remove a DisplayChangeListener from this Win32GraphicsDevice
549     */
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     */
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     */
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     */
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
592        Win32FSWindowAdapter(Win32GraphicsDevice device) {
593            this.device = device;
594        }
595
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
619        public void windowDeactivated(WindowEvent e) {
620            setFSWindowsState(e.getOppositeWindow(), Frame.ICONIFIED);
621        }
622
623        @Override
624        public void windowActivated(WindowEvent e) {
625            setFSWindowsState(e.getOppositeWindow(), Frame.NORMAL);
626        }
627
628        @Override
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
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     */
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     */
673    protected void removeFSWindowListener(Window w) {
674        w.removeWindowListener(fsWindowListener);
675        fsWindowListener = null;
676    }
677}
678