001: /*
002: * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.awt;
027:
028: import java.awt.AWTPermission;
029: import java.awt.GraphicsDevice;
030: import java.awt.GraphicsConfiguration;
031: import java.awt.GraphicsEnvironment;
032: import java.awt.DisplayMode;
033: import java.awt.Frame;
034: import java.awt.Rectangle;
035: import java.awt.Window;
036: import java.awt.image.ColorModel;
037: import java.util.ArrayList;
038: import java.util.Vector;
039: import java.awt.peer.WindowPeer;
040: import sun.awt.windows.WWindowPeer;
041: import sun.java2d.d3d.D3DContext;
042: import sun.java2d.opengl.WGLGraphicsConfig;
043: import sun.java2d.windows.WindowsFlags;
044:
045: /**
046: * This is an implementation of a GraphicsDevice object for a single
047: * Win32 screen.
048: *
049: * @see GraphicsEnvironment
050: * @see GraphicsConfiguration
051: * @version 10 Feb 1997
052: */
053: public class Win32GraphicsDevice extends GraphicsDevice implements
054: DisplayChangedListener {
055: int screen;
056: ColorModel dynamicColorModel; // updated with dev changes
057: ColorModel colorModel; // static for device
058: GraphicsConfiguration[] configs;
059: GraphicsConfiguration defaultConfig;
060: boolean offscreenAccelerationEnabled = true;
061: private D3DContext d3dContext;
062:
063: private final String idString;
064: private final String descString;
065: // Note that we do not synchronize access to this variable - it doesn't
066: // really matter if a thread does an operation on graphics device which is
067: // about to become invalid (or already become) - we are prepared to deal
068: // with this on the native level.
069: private boolean valid;
070:
071: // keep track of top-level windows on this display
072: private SunDisplayChanger topLevels = new SunDisplayChanger();
073: private static boolean pfDisabled;
074: private static AWTPermission fullScreenExclusivePermission;
075: private Rectangle ownerWindowedModeBounds = null;
076: // the original display mode we had before entering the fullscreen
077: // mode
078: private DisplayMode defaultDisplayMode;
079:
080: static {
081:
082: // 4455041 - Even when ddraw is disabled, ddraw.dll is loaded when
083: // pixel format calls are made. This causes problems when a Java app
084: // is run as an NT service. To prevent the loading of ddraw.dll
085: // completely, sun.awt.nopixfmt should be set as well. Apps which use
086: // OpenGL w/ Java probably don't want to set this.
087: String nopixfmt = (String) java.security.AccessController
088: .doPrivileged(new sun.security.action.GetPropertyAction(
089: "sun.awt.nopixfmt"));
090: pfDisabled = (nopixfmt != null);
091: initIDs();
092: }
093:
094: private static native void initIDs();
095:
096: /**
097: * Acceleration can be disabled due to capabilities of the display
098: * device discovered during ddraw initialization. This is not the
099: * same as isDDEnabledOnDevice(), which returns false when ddraw
100: * was disabled by the user or had problems initializing.
101: */
102: public boolean isOffscreenAccelerationEnabled() {
103: return offscreenAccelerationEnabled;
104: }
105:
106: native void initDevice(int screen);
107:
108: public Win32GraphicsDevice(int screennum) {
109: this .screen = screennum;
110: // we cache the strings because we want toString() and getIDstring
111: // to reflect the original screen number (which may change if the
112: // device is removed)
113: idString = "\\Display" + screen;
114: descString = "Win32GraphicsDevice[screen=" + screen;
115: valid = true;
116:
117: initDevice(screennum);
118: }
119:
120: /**
121: * Returns the type of the graphics device.
122: * @see #TYPE_RASTER_SCREEN
123: * @see #TYPE_PRINTER
124: * @see #TYPE_IMAGE_BUFFER
125: */
126: public int getType() {
127: return TYPE_RASTER_SCREEN;
128: }
129:
130: /**
131: * Returns the Win32 screen of the device.
132: */
133: public int getScreen() {
134: return screen;
135: }
136:
137: /**
138: * Returns whether this is a valid devicie. Device can become
139: * invalid as a result of device removal event.
140: */
141: boolean isValid() {
142: return valid;
143: }
144:
145: /**
146: * Called from native code when the device was removed.
147: *
148: * @param defaultScreen the current default screen
149: */
150: void invalidate(int defaultScreen) {
151: valid = false;
152: screen = defaultScreen;
153: }
154:
155: /**
156: * Returns the identification string associated with this graphics
157: * device.
158: */
159: public String getIDstring() {
160: return idString;
161: }
162:
163: /**
164: * Returns all of the graphics
165: * configurations associated with this graphics device.
166: */
167: public GraphicsConfiguration[] getConfigurations() {
168: if (configs == null) {
169: if (WindowsFlags.isOGLEnabled() && isDefaultDevice()) {
170: defaultConfig = getDefaultConfiguration();
171: if (defaultConfig != null) {
172: configs = new GraphicsConfiguration[1];
173: configs[0] = defaultConfig;
174: return configs;
175: }
176: }
177:
178: int max = getMaxConfigs(screen);
179: int defaultPixID = getDefaultPixID(screen);
180: Vector v = new Vector(max);
181: if (defaultPixID == 0) {
182: // Workaround for failing GDI calls, or if DirectDraw
183: // is disabled
184: defaultConfig = Win32GraphicsConfig.getConfig(this ,
185: defaultPixID);
186: v.addElement(defaultConfig);
187: } else {
188: for (int i = 1; i <= max; i++) {
189: if (isPixFmtSupported(i, screen)) {
190: if (i == defaultPixID) {
191: defaultConfig = Win32GraphicsConfig
192: .getConfig(this , i);
193: v.addElement(defaultConfig);
194: } else {
195: v.addElement(Win32GraphicsConfig.getConfig(
196: this , i));
197: }
198: }
199: }
200: }
201: configs = new GraphicsConfiguration[v.size()];
202: v.copyInto(configs);
203: }
204: return configs;
205: }
206:
207: /**
208: * Returns the maximum number of graphics configurations available, or 1
209: * if PixelFormat calls fail or are disabled.
210: * This number is less than or equal to the number of graphics
211: * configurations supported.
212: */
213: protected int getMaxConfigs(int screen) {
214: if (pfDisabled) {
215: return 1;
216: } else {
217: return getMaxConfigsImpl(screen);
218: }
219: }
220:
221: private native int getMaxConfigsImpl(int screen);
222:
223: /**
224: * Returns whether or not the PixelFormat indicated by index is
225: * supported. Supported PixelFormats support drawing to a Window
226: * (PFD_DRAW_TO_WINDOW), support GDI (PFD_SUPPORT_GDI), and in the
227: * case of an 8-bit format (cColorBits <= 8) uses indexed colors
228: * (iPixelType == PFD_TYPE_COLORINDEX).
229: * We use the index 0 to indicate that PixelFormat calls don't work, or
230: * are disabled. Do not call this function with an index of 0.
231: * @param index a PixelFormat index
232: */
233: protected native boolean isPixFmtSupported(int index, int screen);
234:
235: /**
236: * Returns the PixelFormatID of the default graphics configuration
237: * associated with this graphics device, or 0 if PixelFormats calls fail or
238: * are disabled.
239: */
240: protected int getDefaultPixID(int screen) {
241: if (pfDisabled) {
242: return 0;
243: } else {
244: return getDefaultPixIDImpl(screen);
245: }
246: }
247:
248: /**
249: * Returns the default PixelFormat ID from GDI. Do not call if PixelFormats
250: * are disabled.
251: */
252: private native int getDefaultPixIDImpl(int screen);
253:
254: /**
255: * Returns the default graphics configuration
256: * associated with this graphics device.
257: */
258: public GraphicsConfiguration getDefaultConfiguration() {
259: if (defaultConfig == null) {
260: // first try to create a WGLGraphicsConfig if OGL is enabled
261: // REMIND: the WGL code does not yet work properly in multimon
262: // situations, so we will fallback on GDI if we are not on the
263: // default device...
264: if (WindowsFlags.isOGLEnabled() && isDefaultDevice()) {
265: int defPixID = WGLGraphicsConfig
266: .getDefaultPixFmt(screen);
267: defaultConfig = WGLGraphicsConfig.getConfig(this ,
268: defPixID);
269: if (WindowsFlags.isOGLVerbose()) {
270: if (defaultConfig != null) {
271: System.out.print("OpenGL pipeline enabled");
272: } else {
273: System.out
274: .print("Could not enable OpenGL pipeline");
275: }
276: System.out.println(" for default config on screen "
277: + screen);
278: }
279: }
280:
281: // Fix for 4669614. Most apps are not concerned with PixelFormats,
282: // yet we ALWAYS used them for determining ColorModels and such.
283: // By passing in 0 as the PixelFormatID here, we signal that
284: // PixelFormats should not be used, thus avoid loading the opengl
285: // library. Apps concerned with PixelFormats can still use
286: // GraphicsConfiguration.getConfigurations().
287: // Note that calling native pixel format functions tends to cause
288: // problems between those functions (which are OpenGL-related)
289: // and our use of DirectX. For example, some Matrox boards will
290: // crash or hang calling these functions when any app is running
291: // in DirectX fullscreen mode. So avoiding these calls unless
292: // absolutely necessary is preferable.
293: if (defaultConfig == null) {
294: defaultConfig = Win32GraphicsConfig.getConfig(this , 0);
295: }
296: }
297: return defaultConfig;
298: }
299:
300: public String toString() {
301: return valid ? descString + "]" : descString + ", removed]";
302: }
303:
304: /**
305: * Returns true if this is the default GraphicsDevice for the
306: * GraphicsEnvironment.
307: */
308: private boolean isDefaultDevice() {
309: return (this == GraphicsEnvironment
310: .getLocalGraphicsEnvironment().getDefaultScreenDevice());
311: }
312:
313: private native boolean isDDEnabledOnDeviceNative(int screen);
314:
315: public D3DContext getD3DContext() {
316: if (d3dContext == null) {
317: d3dContext = new D3DContext(this );
318: }
319: return d3dContext;
320: }
321:
322: public boolean isDDEnabledOnDevice() {
323: return (WindowsFlags.isDDEnabled() && isValid() && isDDEnabledOnDeviceNative(screen));
324: }
325:
326: public boolean isD3DEnabledOnDevice() {
327: // The conditions under which we enable the D3D pipeline for the device:
328: // - d3d is not disabled via a flag
329: // - either d3d is forced via property or we're in fullscreen mode
330: // - the hardware/drivers meet our requirements
331: return (WindowsFlags.isD3DEnabled()
332: && isValid()
333: && (WindowsFlags.isD3DSet() || getFullScreenWindow() != null) && ((getD3DContext()
334: .getDeviceCaps() & D3DContext.J2D_D3D_ENABLED_OK) != 0));
335: }
336:
337: private static boolean isFSExclusiveModeAllowed() {
338: SecurityManager security = System.getSecurityManager();
339: if (security != null) {
340: if (fullScreenExclusivePermission == null) {
341: fullScreenExclusivePermission = new AWTPermission(
342: "fullScreenExclusive");
343: }
344: try {
345: security.checkPermission(fullScreenExclusivePermission);
346: } catch (SecurityException e) {
347: return false;
348: }
349: }
350: return true;
351: }
352:
353: /**
354: * We support the exclusive fullscreen mode in both ddraw and
355: * noddraw modes, so we return true unless we're not allowed to use
356: * fullscreen mode.
357: */
358: public boolean isFullScreenSupported() {
359: return isFSExclusiveModeAllowed();
360: }
361:
362: /**
363: * Return the owning Frame for a given Window. Used in setFSWindow below
364: * to set the properties of the owning Frame when a Window goes
365: * into fullscreen mode.
366: */
367: private Frame getToplevelOwner(Window w) {
368: Window owner = w;
369: while (owner != null) {
370: owner = owner.getOwner();
371: if (owner instanceof Frame) {
372: return (Frame) owner;
373: }
374: }
375: // Should not get here, but return something intelligent just in case
376: return null;
377: }
378:
379: public synchronized void setFullScreenWindow(Window w) {
380: Window old = getFullScreenWindow();
381: if (w == old) {
382: return;
383: }
384: if (!isFullScreenSupported()) {
385: super .setFullScreenWindow(w);
386: return;
387: }
388:
389: // Enter windowed mode.
390: if (old != null) {
391: // restore the original display mode
392: if (defaultDisplayMode != null) {
393: setDisplayMode(defaultDisplayMode);
394: // we set the default display mode to null here
395: // because the default mode could change during
396: // the life of the application (user can change it through
397: // the desktop properties dialog, for example), so
398: // we need to record it every time prior to
399: // entering the fullscreen mode.
400: defaultDisplayMode = null;
401: }
402: WWindowPeer peer = (WWindowPeer) old.getPeer();
403: if (peer != null) {
404: synchronized (peer) {
405: peer.destroyBuffers();
406: exitFullScreenExclusive(isDDEnabledOnDevice(),
407: screen, peer);
408: }
409: }
410: /**
411: * Bug 4933099: There is some funny-business to deal with when this
412: * method is called with a Window instead of a Frame. See 4836744
413: * for more information on this. One side-effect of our workaround
414: * for the problem is that the owning Frame of a Window may end
415: * up getting resized during the fullscreen process. When we
416: * return from fullscreen mode, we should resize the Frame to
417: * its original size (just like the Window is being resized
418: * to its original size in GraphicsDevice).
419: */
420: if (!(old instanceof Frame)) {
421: Frame owner = getToplevelOwner(old);
422: if (owner != null && ownerWindowedModeBounds != null) {
423: owner.setBounds(ownerWindowedModeBounds);
424: }
425: ownerWindowedModeBounds = null;
426: }
427: }
428: super .setFullScreenWindow(w);
429: if (w != null) {
430: // always record the default display mode prior to going
431: // fullscreen
432: defaultDisplayMode = getDisplayMode();
433: // Bug 4933099
434: if (!(w instanceof Frame)) {
435: Frame owner = getToplevelOwner(w);
436: if (owner != null) {
437: ownerWindowedModeBounds = owner.getBounds();
438: // These will get set for the native window in
439: // any case. Set them here so that resetting them
440: // later actually does the right thing
441: owner.setBounds(w.getBounds());
442: }
443: }
444: // Enter full screen exclusive mode.
445: WWindowPeer peer = (WWindowPeer) w.getPeer();
446: synchronized (peer) {
447: enterFullScreenExclusive(isDDEnabledOnDevice(), screen,
448: peer);
449: // Note: removed replaceSurfaceData() call because
450: // changing the window size or making it visible
451: // will cause this anyway, and both of these events happen
452: // as part of switching into fullscreen mode.
453: }
454:
455: // fix for 4868278
456: peer.updateGC();
457: peer.resetTargetGC();
458: }
459: }
460:
461: // Entering and exiting full-screen mode are done within a
462: // tree-lock and should never lock on any resources which are
463: // required by other threads which may have them and may require
464: // the tree-lock.
465: private native void enterFullScreenExclusive(boolean useDD,
466: int screen, WindowPeer w);
467:
468: private native void exitFullScreenExclusive(boolean useDD,
469: int screen, WindowPeer w);
470:
471: public boolean isDisplayChangeSupported() {
472: return (isFullScreenSupported() && getFullScreenWindow() != null);
473: }
474:
475: public synchronized void setDisplayMode(DisplayMode dm) {
476: if (!isDisplayChangeSupported()) {
477: super .setDisplayMode(dm);
478: return;
479: }
480: if (dm == null || (dm = getMatchingDisplayMode(dm)) == null) {
481: throw new IllegalArgumentException("Invalid display mode");
482: }
483: if (getDisplayMode().equals(dm)) {
484: return;
485: }
486: Window w = getFullScreenWindow();
487: if (w != null) {
488: WWindowPeer peer = (WWindowPeer) w.getPeer();
489: configDisplayMode(screen, peer, dm.getWidth(), dm
490: .getHeight(), dm.getBitDepth(), dm.getRefreshRate());
491: // resize the fullscreen window to the dimensions of the new
492: // display mode
493: Rectangle screenBounds = getDefaultConfiguration()
494: .getBounds();
495: w.setBounds(screenBounds.x, screenBounds.y, dm.getWidth(),
496: dm.getHeight());
497: // Note: no call to replaceSurfaceData is required here since
498: // replacement will be caused by an upcoming display change event
499: } else {
500: throw new IllegalStateException(
501: "Must be in fullscreen mode "
502: + "in order to set display mode");
503: }
504: }
505:
506: private native DisplayMode getCurrentDisplayMode(int screen);
507:
508: private native void configDisplayMode(int screen, WindowPeer w,
509: int width, int height, int bitDepth, int refreshRate);
510:
511: private native void enumDisplayModes(int screen, ArrayList modes);
512:
513: // This function is only available if DirectDraw is enabled, otherwise we
514: // have to do the work the hard way (enumerating all of the display modes
515: // and checking each one)
516: private native boolean isDisplayModeAvailable(int screen,
517: int width, int height, int bitDepth, int refreshRate);
518:
519: public synchronized DisplayMode getDisplayMode() {
520: DisplayMode res = getCurrentDisplayMode(screen);
521: return res;
522: }
523:
524: public synchronized DisplayMode[] getDisplayModes() {
525: ArrayList modes = new ArrayList();
526: enumDisplayModes(screen, modes);
527: int listSize = modes.size();
528: DisplayMode[] retArray = new DisplayMode[listSize];
529: for (int i = 0; i < listSize; i++) {
530: retArray[i] = (DisplayMode) modes.get(i);
531: }
532: return retArray;
533: }
534:
535: private synchronized DisplayMode getMatchingDisplayMode(
536: DisplayMode dm) {
537: if (!isDisplayChangeSupported()) {
538: return null;
539: }
540: if (isDDEnabledOnDevice()) {
541: return isDisplayModeAvailable(screen, dm.getWidth(), dm
542: .getHeight(), dm.getBitDepth(), dm.getRefreshRate()) ? dm
543: : null;
544: } else {
545: // The function isDisplayModeAvailable is only available if
546: // DirectDraw is enabled, otherwise we have to do the work the
547: // hard way (enumerating all of the display modes
548: // and checking each one)
549: DisplayMode[] modes = getDisplayModes();
550: for (DisplayMode mode : modes) {
551: if (dm.equals(mode)
552: || (dm.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN
553: && dm.getWidth() == mode.getWidth()
554: && dm.getHeight() == mode.getHeight() && dm
555: .getBitDepth() == mode.getBitDepth())) {
556: return mode;
557: }
558: }
559: return null;
560: }
561: }
562:
563: /*
564: * From the DisplayChangeListener interface.
565: * Called from Win32GraphicsEnvironment when the display settings have
566: * changed.
567: */
568: public void displayChanged() {
569: d3dContext = null;
570: dynamicColorModel = null;
571: defaultConfig = null;
572: configs = null;
573: // pass on to all top-level windows on this display
574: topLevels.notifyListeners();
575: }
576:
577: /**
578: * Part of the DisplayChangedListener interface: devices
579: * do not need to react to this event
580: */
581: public void paletteChanged() {
582: }
583:
584: /*
585: * Add a DisplayChangeListener to be notified when the display settings
586: * are changed. Typically, only top-level containers need to be added
587: * to Win32GraphicsDevice.
588: */
589: public void addDisplayChangedListener(DisplayChangedListener client) {
590: topLevels.add(client);
591: }
592:
593: /*
594: * Remove a DisplayChangeListener from this Win32GraphicsDevice
595: */
596: public void removeDisplayChangedListener(
597: DisplayChangedListener client) {
598: topLevels.remove(client);
599: }
600:
601: /**
602: * Creates and returns the color model associated with this device
603: */
604: private native ColorModel makeColorModel(int screen, boolean dynamic);
605:
606: /**
607: * Returns a dynamic ColorModel which is updated when there
608: * are any changes (e.g., palette changes) in the device
609: */
610: public ColorModel getDynamicColorModel() {
611: if (dynamicColorModel == null) {
612: dynamicColorModel = makeColorModel(screen, true);
613: }
614: return dynamicColorModel;
615: }
616:
617: /**
618: * Returns the non-dynamic ColorModel associated with this device
619: */
620: public ColorModel getColorModel() {
621: if (colorModel == null) {
622: colorModel = makeColorModel(screen, false);
623: }
624: return colorModel;
625: }
626:
627: private native int getDeviceMemoryNative(int screen);
628:
629: /**
630: * Returns number of bytes available in VRAM on this device.
631: */
632: public int getAvailableAcceleratedMemory() {
633: if (getDefaultConfiguration() instanceof WGLGraphicsConfig) {
634: // when OGL is enabled, there is no way to determine the amount
635: // of accelerated memory, so just return the default value
636: return super.getAvailableAcceleratedMemory();
637: }
638: return getDeviceMemoryNative(screen);
639: }
640: }
|