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