blob: 14ff9a5d45bf70a1aef471ae2123a7c6b36cdc7c [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
Sergey Bylokhovb9aead12015-04-17 16:54:13 +03002 * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
J. Duke319a3b92007-12-01 00:00:00 +00003 * 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
Kelly O'Hairfe008ae2010-05-25 15:58:33 -07007 * published by the Free Software Foundation. Oracle designates this
J. Duke319a3b92007-12-01 00:00:00 +00008 * particular file as subject to the "Classpath" exception as provided
Kelly O'Hairfe008ae2010-05-25 15:58:33 -07009 * by Oracle in the LICENSE file that accompanied this code.
J. Duke319a3b92007-12-01 00:00:00 +000010 *
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 *
Kelly O'Hairfe008ae2010-05-25 15:58:33 -070021 * 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.
J. Duke319a3b92007-12-01 00:00:00 +000024 */
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;
Andrew Brygin8403ec62010-04-20 11:06:47 +040035import java.security.AccessController;
36import java.security.PrivilegedAction;
J. Duke319a3b92007-12-01 00:00:00 +000037import java.util.ArrayList;
38import java.util.HashSet;
39import java.util.HashMap;
40
41import sun.java2d.opengl.GLXGraphicsConfig;
Clemens Eisserer1771e202010-05-28 11:37:44 -070042import sun.java2d.xr.XRGraphicsConfig;
J. Duke319a3b92007-12-01 00:00:00 +000043import sun.java2d.loops.SurfaceType;
44
Petr Pchelkod9bd02f2014-04-18 10:47:23 +040045import sun.awt.util.ThreadGroupUtils;
Alexander Scherbatiyd48c3fc2015-11-13 18:36:14 +040046import sun.java2d.SunGraphicsEnvironment;
Petr Pchelkocf8f2bc2014-01-15 11:53:54 +040047
J. Duke319a3b92007-12-01 00:00:00 +000048/**
49 * This is an implementation of a GraphicsDevice object for a single
50 * X11 screen.
51 *
52 * @see GraphicsEnvironment
53 * @see GraphicsConfiguration
54 */
Sergey Bylokhov8be69402015-05-16 02:37:16 +030055public final class X11GraphicsDevice extends GraphicsDevice
56 implements DisplayChangedListener {
J. Duke319a3b92007-12-01 00:00:00 +000057 int screen;
Henry Jencb91afd2014-04-28 19:05:49 -070058 HashMap<SurfaceType, Object> x11ProxyKeyMap = new HashMap<>();
J. Duke319a3b92007-12-01 00:00:00 +000059
60 private static AWTPermission fullScreenExclusivePermission;
61 private static Boolean xrandrExtSupported;
62 private final Object configLock = new Object();
63 private SunDisplayChanger topLevels = new SunDisplayChanger();
64 private DisplayMode origDisplayMode;
65 private boolean shutdownHookRegistered;
Alexander Scherbatiyd48c3fc2015-11-13 18:36:14 +040066 private final int scale;
J. Duke319a3b92007-12-01 00:00:00 +000067
68 public X11GraphicsDevice(int screennum) {
69 this.screen = screennum;
Alexander Scherbatiyd48c3fc2015-11-13 18:36:14 +040070 this.scale = initScaleFactor();
J. Duke319a3b92007-12-01 00:00:00 +000071 }
72
73 /*
74 * Initialize JNI field and method IDs for fields that may be
75 * accessed from C.
76 */
77 private static native void initIDs();
78
79 static {
80 if (!GraphicsEnvironment.isHeadless()) {
81 initIDs();
82 }
83 }
84
85 /**
86 * Returns the X11 screen of the device.
87 */
88 public int getScreen() {
89 return screen;
90 }
91
92 public Object getProxyKeyFor(SurfaceType st) {
93 synchronized (x11ProxyKeyMap) {
94 Object o = x11ProxyKeyMap.get(st);
95 if (o == null) {
96 o = new Object();
97 x11ProxyKeyMap.put(st, o);
98 }
99 return o;
100 }
101 }
102
103 /**
104 * Returns the X11 Display of this device.
105 * This method is also in MDrawingSurfaceInfo but need it here
106 * to be able to allow a GraphicsConfigTemplate to get the Display.
107 */
108 public native long getDisplay();
109
110 /**
111 * Returns the type of the graphics device.
112 * @see #TYPE_RASTER_SCREEN
113 * @see #TYPE_PRINTER
114 * @see #TYPE_IMAGE_BUFFER
115 */
116 public int getType() {
117 return TYPE_RASTER_SCREEN;
118 }
119
120 /**
121 * Returns the identification string associated with this graphics
122 * device.
123 */
124 public String getIDstring() {
125 return ":0."+screen;
126 }
127
128
129 GraphicsConfiguration[] configs;
130 GraphicsConfiguration defaultConfig;
Henry Jencb91afd2014-04-28 19:05:49 -0700131 HashSet<Integer> doubleBufferVisuals;
J. Duke319a3b92007-12-01 00:00:00 +0000132
133 /**
134 * Returns all of the graphics
135 * configurations associated with this graphics device.
136 */
137 public GraphicsConfiguration[] getConfigurations() {
138 if (configs == null) {
139 synchronized (configLock) {
140 makeConfigurations();
141 }
142 }
Andrew Bryginc26aec22009-09-10 13:52:27 +0400143 return configs.clone();
J. Duke319a3b92007-12-01 00:00:00 +0000144 }
145
146 private void makeConfigurations() {
147 if (configs == null) {
148 int i = 1; // Index 0 is always the default config
149 int num = getNumConfigs(screen);
150 GraphicsConfiguration[] ret = new GraphicsConfiguration[num];
151 if (defaultConfig == null) {
152 ret [0] = getDefaultConfiguration();
153 }
154 else {
155 ret [0] = defaultConfig;
156 }
157
158 boolean glxSupported = X11GraphicsEnvironment.isGLXAvailable();
Clemens Eisserer1771e202010-05-28 11:37:44 -0700159 boolean xrenderSupported = X11GraphicsEnvironment.isXRenderAvailable();
160
J. Duke319a3b92007-12-01 00:00:00 +0000161 boolean dbeSupported = isDBESupported();
162 if (dbeSupported && doubleBufferVisuals == null) {
Henry Jencb91afd2014-04-28 19:05:49 -0700163 doubleBufferVisuals = new HashSet<>();
J. Duke319a3b92007-12-01 00:00:00 +0000164 getDoubleBufferVisuals(screen);
165 }
166 for ( ; i < num; i++) {
167 int visNum = getConfigVisualId(i, screen);
168 int depth = getConfigDepth (i, screen);
169 if (glxSupported) {
170 ret[i] = GLXGraphicsConfig.getConfig(this, visNum);
171 }
172 if (ret[i] == null) {
173 boolean doubleBuffer =
174 (dbeSupported &&
Andrei Dmitriev0e366512008-04-07 14:53:51 +0400175 doubleBufferVisuals.contains(Integer.valueOf(visNum)));
Clemens Eisserer1771e202010-05-28 11:37:44 -0700176
177 if (xrenderSupported) {
178 ret[i] = XRGraphicsConfig.getConfig(this, visNum, depth, getConfigColormap(i, screen),
179 doubleBuffer);
180 } else {
181 ret[i] = X11GraphicsConfig.getConfig(this, visNum, depth,
182 getConfigColormap(i, screen),
183 doubleBuffer);
184 }
J. Duke319a3b92007-12-01 00:00:00 +0000185 }
186 }
187 configs = ret;
188 }
189 }
190
191 /*
192 * Returns the number of X11 visuals representable as an
193 * X11GraphicsConfig object.
194 */
195 public native int getNumConfigs(int screen);
196
197 /*
198 * Returns the visualid for the given index of graphics configurations.
199 */
200 public native int getConfigVisualId (int index, int screen);
201 /*
202 * Returns the depth for the given index of graphics configurations.
203 */
Sergey Bylokhov8be69402015-05-16 02:37:16 +0300204 private native int getConfigDepth(int index, int screen);
J. Duke319a3b92007-12-01 00:00:00 +0000205
206 /*
207 * Returns the colormap for the given index of graphics configurations.
208 */
Sergey Bylokhov8be69402015-05-16 02:37:16 +0300209 private native int getConfigColormap(int index, int screen);
J. Duke319a3b92007-12-01 00:00:00 +0000210
211 // Whether or not double-buffering extension is supported
Sergey Bylokhov8be69402015-05-16 02:37:16 +0300212 static native boolean isDBESupported();
J. Duke319a3b92007-12-01 00:00:00 +0000213 // Callback for adding a new double buffer visual into our set
214 private void addDoubleBufferVisual(int visNum) {
Andrei Dmitriev0e366512008-04-07 14:53:51 +0400215 doubleBufferVisuals.add(Integer.valueOf(visNum));
J. Duke319a3b92007-12-01 00:00:00 +0000216 }
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) {
Henry Jencb91afd2014-04-28 19:05:49 -0700252 doubleBufferVisuals = new HashSet<>();
J. Duke319a3b92007-12-01 00:00:00 +0000253 getDoubleBufferVisuals(screen);
254 doubleBuffer =
Andrei Dmitriev0e366512008-04-07 14:53:51 +0400255 doubleBufferVisuals.contains(Integer.valueOf(visNum));
J. Duke319a3b92007-12-01 00:00:00 +0000256 }
Clemens Eisserer1771e202010-05-28 11:37:44 -0700257
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 }
J. Duke319a3b92007-12-01 00:00:00 +0000270 }
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);
Alexander Scherbatiyd48c3fc2015-11-13 18:36:14 +0400284 private static native int getNativeScaleFactor(int screen);
J. Duke319a3b92007-12-01 00:00:00 +0000285
286 /**
287 * Returns true only if:
288 * - the Xrandr extension is present
289 * - the necessary Xrandr functions were loaded successfully
J. Duke319a3b92007-12-01 00:00:00 +0000290 */
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() {
Alexander Zvegintsev70a2e792015-04-29 12:54:36 +0300301 boolean fsAvailable = isXrandrExtensionSupported();
J. Duke319a3b92007-12-01 00:00:00 +0000302 if (fsAvailable) {
303 SecurityManager security = System.getSecurityManager();
304 if (security != null) {
305 if (fullScreenExclusivePermission == null) {
306 fullScreenExclusivePermission =
307 new AWTPermission("fullScreenExclusive");
308 }
309 try {
310 security.checkPermission(fullScreenExclusivePermission);
311 } catch (SecurityException e) {
312 return false;
313 }
314 }
315 }
316 return fsAvailable;
317 }
318
319 @Override
320 public boolean isDisplayChangeSupported() {
Alexander Zvegintsev3d7db7c2015-06-22 15:47:44 +0300321 return (isFullScreenSupported()
Alexander Zvegintsev4c121562015-07-23 15:12:32 +0300322 && (getFullScreenWindow() != null)
Alexander Zvegintsev3d7db7c2015-06-22 15:47:44 +0300323 && !((X11GraphicsEnvironment) GraphicsEnvironment
324 .getLocalGraphicsEnvironment()).runningXinerama());
J. Duke319a3b92007-12-01 00:00:00 +0000325 }
326
327 private static void enterFullScreenExclusive(Window w) {
Sergey Bylokhovb9aead12015-04-17 16:54:13 +0300328 X11ComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(w);
J. Duke319a3b92007-12-01 00:00:00 +0000329 if (peer != null) {
Alexander Zvegintsev70a2e792015-04-29 12:54:36 +0300330 enterFullScreenExclusive(peer.getWindow());
Anthony Petrov370b3a92009-10-14 16:19:46 +0400331 peer.setFullScreenExclusiveModeState(true);
J. Duke319a3b92007-12-01 00:00:00 +0000332 }
333 }
334
335 private static void exitFullScreenExclusive(Window w) {
Sergey Bylokhovb9aead12015-04-17 16:54:13 +0300336 X11ComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(w);
J. Duke319a3b92007-12-01 00:00:00 +0000337 if (peer != null) {
Anthony Petrov370b3a92009-10-14 16:19:46 +0400338 peer.setFullScreenExclusiveModeState(false);
Alexander Zvegintsev70a2e792015-04-29 12:54:36 +0300339 exitFullScreenExclusive(peer.getWindow());
J. Duke319a3b92007-12-01 00:00:00 +0000340 }
341 }
342
343 @Override
344 public synchronized void setFullScreenWindow(Window w) {
345 Window old = getFullScreenWindow();
346 if (w == old) {
347 return;
348 }
349
350 boolean fsSupported = isFullScreenSupported();
351 if (fsSupported && old != null) {
352 // enter windowed mode (and restore original display mode)
353 exitFullScreenExclusive(old);
Alexander Zvegintsev3d7db7c2015-06-22 15:47:44 +0300354 if (isDisplayChangeSupported()) {
355 setDisplayMode(origDisplayMode);
356 }
J. Duke319a3b92007-12-01 00:00:00 +0000357 }
358
359 super.setFullScreenWindow(w);
360
361 if (fsSupported && w != null) {
362 // save original display mode
363 if (origDisplayMode == null) {
364 origDisplayMode = getDisplayMode();
365 }
366
367 // enter fullscreen mode
368 enterFullScreenExclusive(w);
369 }
370 }
371
372 private DisplayMode getDefaultDisplayMode() {
373 GraphicsConfiguration gc = getDefaultConfiguration();
374 Rectangle r = gc.getBounds();
375 return new DisplayMode(r.width, r.height,
376 DisplayMode.BIT_DEPTH_MULTI,
377 DisplayMode.REFRESH_RATE_UNKNOWN);
378 }
379
380 @Override
381 public synchronized DisplayMode getDisplayMode() {
382 if (isFullScreenSupported()) {
Phil Race4c0f9a12015-04-20 10:39:30 -0700383 DisplayMode mode = getCurrentDisplayMode(screen);
384 if (mode == null) {
385 mode = getDefaultDisplayMode();
386 }
387 return mode;
J. Duke319a3b92007-12-01 00:00:00 +0000388 } else {
389 if (origDisplayMode == null) {
390 origDisplayMode = getDefaultDisplayMode();
391 }
392 return origDisplayMode;
393 }
394 }
395
396 @Override
397 public synchronized DisplayMode[] getDisplayModes() {
398 if (!isFullScreenSupported()) {
399 return super.getDisplayModes();
400 }
401 ArrayList<DisplayMode> modes = new ArrayList<DisplayMode>();
402 enumDisplayModes(screen, modes);
403 DisplayMode[] retArray = new DisplayMode[modes.size()];
404 return modes.toArray(retArray);
405 }
406
407 @Override
408 public synchronized void setDisplayMode(DisplayMode dm) {
409 if (!isDisplayChangeSupported()) {
410 super.setDisplayMode(dm);
411 return;
412 }
413 Window w = getFullScreenWindow();
414 if (w == null) {
415 throw new IllegalStateException("Must be in fullscreen mode " +
416 "in order to set display mode");
417 }
Dmitri Trembovetskie4c9db92008-07-18 10:48:44 -0700418 if (getDisplayMode().equals(dm)) {
419 return;
420 }
J. Duke319a3b92007-12-01 00:00:00 +0000421 if (dm == null ||
422 (dm = getMatchingDisplayMode(dm)) == null)
423 {
424 throw new IllegalArgumentException("Invalid display mode");
425 }
426
427 if (!shutdownHookRegistered) {
428 // register a shutdown hook so that we return to the
429 // original DisplayMode when the VM exits (if the application
430 // is already in the original DisplayMode at that time, this
431 // hook will have no effect)
432 shutdownHookRegistered = true;
Petr Pchelkocf8f2bc2014-01-15 11:53:54 +0400433 PrivilegedAction<Void> a = () -> {
Petr Pchelkocf8f2bc2014-01-15 11:53:54 +0400434 Runnable r = () -> {
435 Window old = getFullScreenWindow();
436 if (old != null) {
437 exitFullScreenExclusive(old);
Alexander Zvegintsev3d7db7c2015-06-22 15:47:44 +0300438 if (isDisplayChangeSupported()) {
439 setDisplayMode(origDisplayMode);
440 }
J. Duke319a3b92007-12-01 00:00:00 +0000441 }
Petr Pchelkocf8f2bc2014-01-15 11:53:54 +0400442 };
Sergey Bylokhov78119062015-04-03 17:17:36 +0300443 String name = "Display-Change-Shutdown-Thread-" + screen;
Phil Race01b34482016-04-04 14:22:07 -0700444 Thread t = new Thread(
445 ThreadGroupUtils.getRootThreadGroup(), r, name, 0, false);
Petr Pchelkocf8f2bc2014-01-15 11:53:54 +0400446 t.setContextClassLoader(null);
447 Runtime.getRuntime().addShutdownHook(t);
448 return null;
J. Duke319a3b92007-12-01 00:00:00 +0000449 };
Andrew Brygin8403ec62010-04-20 11:06:47 +0400450 AccessController.doPrivileged(a);
J. Duke319a3b92007-12-01 00:00:00 +0000451 }
452
453 // switch to the new DisplayMode
454 configDisplayMode(screen,
455 dm.getWidth(), dm.getHeight(),
456 dm.getRefreshRate());
457
458 // update bounds of the fullscreen window
459 w.setBounds(0, 0, dm.getWidth(), dm.getHeight());
460
461 // configDisplayMode() is synchronous, so the display change will be
462 // complete by the time we get here (and it is therefore safe to call
463 // displayChanged() now)
464 ((X11GraphicsEnvironment)
465 GraphicsEnvironment.getLocalGraphicsEnvironment()).displayChanged();
466 }
467
468 private synchronized DisplayMode getMatchingDisplayMode(DisplayMode dm) {
469 if (!isDisplayChangeSupported()) {
470 return null;
471 }
472 DisplayMode[] modes = getDisplayModes();
473 for (DisplayMode mode : modes) {
474 if (dm.equals(mode) ||
475 (dm.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN &&
476 dm.getWidth() == mode.getWidth() &&
477 dm.getHeight() == mode.getHeight() &&
478 dm.getBitDepth() == mode.getBitDepth()))
479 {
480 return mode;
481 }
482 }
483 return null;
484 }
485
486 /**
487 * From the DisplayChangedListener interface; called from
488 * X11GraphicsEnvironment when the display mode has been changed.
489 */
490 public synchronized void displayChanged() {
Anthony Petrov248bde32011-11-09 13:43:39 +0300491 // On X11 the visuals do not change, and therefore we don't need
492 // to reset the defaultConfig, config, doubleBufferVisuals,
493 // neither do we need to reset the native data.
J. Duke319a3b92007-12-01 00:00:00 +0000494
495 // pass on to all top-level windows on this screen
496 topLevels.notifyListeners();
497 }
498
499 /**
500 * From the DisplayChangedListener interface; devices do not need
501 * to react to this event.
502 */
503 public void paletteChanged() {
504 }
505
506 /**
507 * Add a DisplayChangeListener to be notified when the display settings
508 * are changed. Typically, only top-level containers need to be added
509 * to X11GraphicsDevice.
510 */
511 public void addDisplayChangedListener(DisplayChangedListener client) {
512 topLevels.add(client);
513 }
514
Alexander Scherbatiyd48c3fc2015-11-13 18:36:14 +0400515 public int getScaleFactor() {
516 return scale;
517 }
518
519 private int initScaleFactor() {
520
521 if (SunGraphicsEnvironment.isUIScaleEnabled()) {
522
523 double debugScale = SunGraphicsEnvironment.getDebugScale();
524
525 if (debugScale >= 1) {
526 return (int) debugScale;
527 }
528
529 int nativeScale = getNativeScaleFactor(screen);
530 return nativeScale >= 1 ? nativeScale : 1;
531 }
532
533 return 1;
534 }
535
J. Duke319a3b92007-12-01 00:00:00 +0000536 /**
537 * Remove a DisplayChangeListener from this X11GraphicsDevice.
538 */
539 public void removeDisplayChangedListener(DisplayChangedListener client) {
540 topLevels.remove(client);
541 }
542
543 public String toString() {
544 return ("X11GraphicsDevice[screen="+screen+"]");
545 }
546}