001: /*
002: * Copyright 1997-2007 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.DisplayMode;
030: import java.awt.GraphicsEnvironment;
031: import java.awt.GraphicsDevice;
032: import java.awt.GraphicsConfiguration;
033: import java.awt.Rectangle;
034: import java.awt.Window;
035: import java.util.ArrayList;
036: import java.util.HashSet;
037: import java.util.HashMap;
038:
039: import sun.java2d.opengl.GLXGraphicsConfig;
040: import sun.java2d.loops.SurfaceType;
041:
042: /**
043: * This is an implementation of a GraphicsDevice object for a single
044: * X11 screen.
045: *
046: * @see GraphicsEnvironment
047: * @see GraphicsConfiguration
048: * @version 10 Feb 1997
049: */
050: public class X11GraphicsDevice extends GraphicsDevice implements
051: DisplayChangedListener {
052: int screen;
053: HashMap x11ProxyKeyMap = new HashMap();
054:
055: private static AWTPermission fullScreenExclusivePermission;
056: private static Boolean xrandrExtSupported;
057: private final Object configLock = new Object();
058: private SunDisplayChanger topLevels = new SunDisplayChanger();
059: private DisplayMode origDisplayMode;
060: private boolean shutdownHookRegistered;
061:
062: public X11GraphicsDevice(int screennum) {
063: this .screen = screennum;
064: }
065:
066: /*
067: * Initialize JNI field and method IDs for fields that may be
068: * accessed from C.
069: */
070: private static native void initIDs();
071:
072: static {
073: if (!GraphicsEnvironment.isHeadless()) {
074: initIDs();
075: }
076: }
077:
078: /**
079: * Returns the X11 screen of the device.
080: */
081: public int getScreen() {
082: return screen;
083: }
084:
085: public Object getProxyKeyFor(SurfaceType st) {
086: synchronized (x11ProxyKeyMap) {
087: Object o = x11ProxyKeyMap.get(st);
088: if (o == null) {
089: o = new Object();
090: x11ProxyKeyMap.put(st, o);
091: }
092: return o;
093: }
094: }
095:
096: /**
097: * Returns the X11 Display of this device.
098: * This method is also in MDrawingSurfaceInfo but need it here
099: * to be able to allow a GraphicsConfigTemplate to get the Display.
100: */
101: public native long getDisplay();
102:
103: /**
104: * Returns the type of the graphics device.
105: * @see #TYPE_RASTER_SCREEN
106: * @see #TYPE_PRINTER
107: * @see #TYPE_IMAGE_BUFFER
108: */
109: public int getType() {
110: return TYPE_RASTER_SCREEN;
111: }
112:
113: /**
114: * Returns the identification string associated with this graphics
115: * device.
116: */
117: public String getIDstring() {
118: return ":0." + screen;
119: }
120:
121: GraphicsConfiguration[] configs;
122: GraphicsConfiguration defaultConfig;
123: HashSet doubleBufferVisuals;
124:
125: /**
126: * Returns all of the graphics
127: * configurations associated with this graphics device.
128: */
129: public GraphicsConfiguration[] getConfigurations() {
130: if (configs == null) {
131: synchronized (configLock) {
132: makeConfigurations();
133: }
134: }
135: return configs;
136: }
137:
138: private void makeConfigurations() {
139: if (configs == null) {
140: int i = 1; // Index 0 is always the default config
141: int num = getNumConfigs(screen);
142: GraphicsConfiguration[] ret = new GraphicsConfiguration[num];
143: if (defaultConfig == null) {
144: ret[0] = getDefaultConfiguration();
145: } else {
146: ret[0] = defaultConfig;
147: }
148:
149: boolean glxSupported = X11GraphicsEnvironment
150: .isGLXAvailable();
151: boolean dbeSupported = isDBESupported();
152: if (dbeSupported && doubleBufferVisuals == null) {
153: doubleBufferVisuals = new HashSet();
154: getDoubleBufferVisuals(screen);
155: }
156: for (; i < num; i++) {
157: int visNum = getConfigVisualId(i, screen);
158: int depth = getConfigDepth(i, screen);
159: if (glxSupported) {
160: ret[i] = GLXGraphicsConfig.getConfig(this , visNum);
161: }
162: if (ret[i] == null) {
163: boolean doubleBuffer = (dbeSupported && doubleBufferVisuals
164: .contains(new Integer(visNum)));
165: ret[i] = X11GraphicsConfig.getConfig(this , visNum,
166: depth, getConfigColormap(i, screen),
167: doubleBuffer);
168: }
169: }
170: configs = ret;
171: }
172: }
173:
174: /*
175: * Returns the number of X11 visuals representable as an
176: * X11GraphicsConfig object.
177: */
178: public native int getNumConfigs(int screen);
179:
180: /*
181: * Returns the visualid for the given index of graphics configurations.
182: */
183: public native int getConfigVisualId(int index, int screen);
184:
185: /*
186: * Returns the depth for the given index of graphics configurations.
187: */
188: public native int getConfigDepth(int index, int screen);
189:
190: /*
191: * Returns the colormap for the given index of graphics configurations.
192: */
193: public native int getConfigColormap(int index, int screen);
194:
195: // Whether or not double-buffering extension is supported
196: public static native boolean isDBESupported();
197:
198: // Callback for adding a new double buffer visual into our set
199: private void addDoubleBufferVisual(int visNum) {
200: doubleBufferVisuals.add(new Integer(visNum));
201: }
202:
203: // Enumerates all visuals that support double buffering
204: private native void getDoubleBufferVisuals(int screen);
205:
206: /**
207: * Returns the default graphics configuration
208: * associated with this graphics device.
209: */
210: public GraphicsConfiguration getDefaultConfiguration() {
211: if (defaultConfig == null) {
212: synchronized (configLock) {
213: makeDefaultConfiguration();
214: }
215: }
216: return defaultConfig;
217: }
218:
219: private void makeDefaultConfiguration() {
220: if (defaultConfig == null) {
221: int visNum = getConfigVisualId(0, screen);
222: if (X11GraphicsEnvironment.isGLXAvailable()) {
223: defaultConfig = GLXGraphicsConfig.getConfig(this ,
224: visNum);
225: if (X11GraphicsEnvironment.isGLXVerbose()) {
226: if (defaultConfig != null) {
227: System.out.print("OpenGL pipeline enabled");
228: } else {
229: System.out
230: .print("Could not enable OpenGL pipeline");
231: }
232: System.out.println(" for default config on screen "
233: + screen);
234: }
235: }
236: if (defaultConfig == null) {
237: int depth = getConfigDepth(0, screen);
238: boolean doubleBuffer = false;
239: if (isDBESupported() && doubleBufferVisuals == null) {
240: doubleBufferVisuals = new HashSet();
241: getDoubleBufferVisuals(screen);
242: doubleBuffer = doubleBufferVisuals
243: .contains(new Integer(visNum));
244: }
245: defaultConfig = X11GraphicsConfig.getConfig(this ,
246: visNum, depth, getConfigColormap(0, screen),
247: doubleBuffer);
248: }
249: }
250: }
251:
252: private static native void enterFullScreenExclusive(long window);
253:
254: private static native void exitFullScreenExclusive(long window);
255:
256: private static native boolean initXrandrExtension();
257:
258: private static native DisplayMode getCurrentDisplayMode(int screen);
259:
260: private static native void enumDisplayModes(int screen,
261: ArrayList<DisplayMode> modes);
262:
263: private static native void configDisplayMode(int screen, int width,
264: int height, int displayMode);
265:
266: private static native void resetNativeData(int screen);
267:
268: /**
269: * Returns true only if:
270: * - the Xrandr extension is present
271: * - the necessary Xrandr functions were loaded successfully
272: * - XINERAMA is not enabled
273: */
274: private static synchronized boolean isXrandrExtensionSupported() {
275: if (xrandrExtSupported == null) {
276: xrandrExtSupported = Boolean.valueOf(initXrandrExtension());
277: }
278: return xrandrExtSupported.booleanValue();
279: }
280:
281: @Override
282: public boolean isFullScreenSupported() {
283: // REMIND: for now we will only allow fullscreen exclusive mode
284: // on the primary screen; we could change this behavior slightly
285: // in the future by allowing only one screen to be in fullscreen
286: // exclusive mode at any given time...
287: boolean fsAvailable = (screen == 0)
288: && isXrandrExtensionSupported();
289: if (fsAvailable) {
290: SecurityManager security = System.getSecurityManager();
291: if (security != null) {
292: if (fullScreenExclusivePermission == null) {
293: fullScreenExclusivePermission = new AWTPermission(
294: "fullScreenExclusive");
295: }
296: try {
297: security
298: .checkPermission(fullScreenExclusivePermission);
299: } catch (SecurityException e) {
300: return false;
301: }
302: }
303: }
304: return fsAvailable;
305: }
306:
307: @Override
308: public boolean isDisplayChangeSupported() {
309: return (isFullScreenSupported() && (getFullScreenWindow() != null));
310: }
311:
312: private static void enterFullScreenExclusive(Window w) {
313: X11ComponentPeer peer = (X11ComponentPeer) w.getPeer();
314: if (peer != null) {
315: enterFullScreenExclusive(peer.getContentWindow());
316: }
317: }
318:
319: private static void exitFullScreenExclusive(Window w) {
320: X11ComponentPeer peer = (X11ComponentPeer) w.getPeer();
321: if (peer != null) {
322: exitFullScreenExclusive(peer.getContentWindow());
323: }
324: }
325:
326: @Override
327: public synchronized void setFullScreenWindow(Window w) {
328: Window old = getFullScreenWindow();
329: if (w == old) {
330: return;
331: }
332:
333: boolean fsSupported = isFullScreenSupported();
334: if (fsSupported && old != null) {
335: // enter windowed mode (and restore original display mode)
336: exitFullScreenExclusive(old);
337: setDisplayMode(origDisplayMode);
338: }
339:
340: super .setFullScreenWindow(w);
341:
342: if (fsSupported && w != null) {
343: // save original display mode
344: if (origDisplayMode == null) {
345: origDisplayMode = getDisplayMode();
346: }
347:
348: // enter fullscreen mode
349: enterFullScreenExclusive(w);
350: }
351: }
352:
353: private DisplayMode getDefaultDisplayMode() {
354: GraphicsConfiguration gc = getDefaultConfiguration();
355: Rectangle r = gc.getBounds();
356: return new DisplayMode(r.width, r.height,
357: DisplayMode.BIT_DEPTH_MULTI,
358: DisplayMode.REFRESH_RATE_UNKNOWN);
359: }
360:
361: @Override
362: public synchronized DisplayMode getDisplayMode() {
363: if (isFullScreenSupported()) {
364: return getCurrentDisplayMode(screen);
365: } else {
366: if (origDisplayMode == null) {
367: origDisplayMode = getDefaultDisplayMode();
368: }
369: return origDisplayMode;
370: }
371: }
372:
373: @Override
374: public synchronized DisplayMode[] getDisplayModes() {
375: if (!isFullScreenSupported()) {
376: return super .getDisplayModes();
377: }
378: ArrayList<DisplayMode> modes = new ArrayList<DisplayMode>();
379: enumDisplayModes(screen, modes);
380: DisplayMode[] retArray = new DisplayMode[modes.size()];
381: return modes.toArray(retArray);
382: }
383:
384: @Override
385: public synchronized void setDisplayMode(DisplayMode dm) {
386: if (!isDisplayChangeSupported()) {
387: super .setDisplayMode(dm);
388: return;
389: }
390: Window w = getFullScreenWindow();
391: if (w == null) {
392: throw new IllegalStateException(
393: "Must be in fullscreen mode "
394: + "in order to set display mode");
395: }
396: if (dm == null || (dm = getMatchingDisplayMode(dm)) == null) {
397: throw new IllegalArgumentException("Invalid display mode");
398: }
399:
400: if (!shutdownHookRegistered) {
401: // register a shutdown hook so that we return to the
402: // original DisplayMode when the VM exits (if the application
403: // is already in the original DisplayMode at that time, this
404: // hook will have no effect)
405: shutdownHookRegistered = true;
406: Runnable r = new Runnable() {
407: public void run() {
408: Window old = getFullScreenWindow();
409: if (old != null) {
410: exitFullScreenExclusive(old);
411: setDisplayMode(origDisplayMode);
412: }
413: }
414: };
415: Thread t = new Thread(r, "Display-Change-Shutdown-Thread-"
416: + screen);
417: Runtime.getRuntime().addShutdownHook(t);
418: }
419:
420: // switch to the new DisplayMode
421: configDisplayMode(screen, dm.getWidth(), dm.getHeight(), dm
422: .getRefreshRate());
423:
424: // update bounds of the fullscreen window
425: w.setBounds(0, 0, dm.getWidth(), dm.getHeight());
426:
427: // configDisplayMode() is synchronous, so the display change will be
428: // complete by the time we get here (and it is therefore safe to call
429: // displayChanged() now)
430: ((X11GraphicsEnvironment) GraphicsEnvironment
431: .getLocalGraphicsEnvironment()).displayChanged();
432: }
433:
434: private synchronized DisplayMode getMatchingDisplayMode(
435: DisplayMode dm) {
436: if (!isDisplayChangeSupported()) {
437: return null;
438: }
439: DisplayMode[] modes = getDisplayModes();
440: for (DisplayMode mode : modes) {
441: if (dm.equals(mode)
442: || (dm.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN
443: && dm.getWidth() == mode.getWidth()
444: && dm.getHeight() == mode.getHeight() && dm
445: .getBitDepth() == mode.getBitDepth())) {
446: return mode;
447: }
448: }
449: return null;
450: }
451:
452: /**
453: * From the DisplayChangedListener interface; called from
454: * X11GraphicsEnvironment when the display mode has been changed.
455: */
456: public synchronized void displayChanged() {
457: // reset the list of configs (and default config)
458: defaultConfig = null;
459: configs = null;
460: doubleBufferVisuals = null;
461:
462: // reset the native data structures associated with this device (they
463: // will be reinitialized when the GraphicsConfigs are configured)
464: resetNativeData(screen);
465:
466: // pass on to all top-level windows on this screen
467: topLevels.notifyListeners();
468: }
469:
470: /**
471: * From the DisplayChangedListener interface; devices do not need
472: * to react to this event.
473: */
474: public void paletteChanged() {
475: }
476:
477: /**
478: * Add a DisplayChangeListener to be notified when the display settings
479: * are changed. Typically, only top-level containers need to be added
480: * to X11GraphicsDevice.
481: */
482: public void addDisplayChangedListener(DisplayChangedListener client) {
483: topLevels.add(client);
484: }
485:
486: /**
487: * Remove a DisplayChangeListener from this X11GraphicsDevice.
488: */
489: public void removeDisplayChangedListener(
490: DisplayChangedListener client) {
491: topLevels.remove(client);
492: }
493:
494: public String toString() {
495: return ("X11GraphicsDevice[screen=" + screen + "]");
496: }
497: }
|