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