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