diff --git a/src/java/org/lwjgl/opengl/LinuxDisplay.java b/src/java/org/lwjgl/opengl/LinuxDisplay.java index d7bc6b1e..d46f0085 100644 --- a/src/java/org/lwjgl/opengl/LinuxDisplay.java +++ b/src/java/org/lwjgl/opengl/LinuxDisplay.java @@ -129,7 +129,6 @@ final class LinuxDisplay implements DisplayImplementation { private DisplayMode saved_mode; private DisplayMode current_mode; - private Screen[] savedXrandrConfig; private boolean keyboard_grabbed; private boolean pointer_grabbed; @@ -483,7 +482,16 @@ final class LinuxDisplay implements DisplayImplementation { window_y = y; window_width = mode.getWidth(); window_height = mode.getHeight(); - + + // overwrite arguments x and y - superclass always uses 0,0 for fullscreen windows + // use the coordinates of XRandRs primary screen instead + // this is required to let the fullscreen window appear on the primary screen + if (mode.isFullscreenCapable() && current_displaymode_extension == XRANDR) { + Screen primaryScreen = XRandR.DisplayModetoScreen(Display.getDisplayMode()); + x = primaryScreen.xPos; + y = primaryScreen.yPos; + } + current_window = nCreateWindow(getDisplay(), getDefaultScreen(), handle, mode, current_window_mode, x, y, undecorated, parent_window, resizable); // Set the WM_CLASS hint which is needed by some WM's e.g. Gnome Shell @@ -603,12 +611,17 @@ final class LinuxDisplay implements DisplayImplementation { } private void switchDisplayModeOnTmpDisplay(DisplayMode mode) throws LWJGLException { - incDisplay(); - try { - nSwitchDisplayMode(getDisplay(), getDefaultScreen(), current_displaymode_extension, mode); - } finally { - decDisplay(); - } + if (current_displaymode_extension == XRANDR) { + // let Xrandr set the display mode + XRandR.setConfiguration(false, XRandR.DisplayModetoScreen(mode)); + } else { + incDisplay(); + try { + nSwitchDisplayMode(getDisplay(), getDefaultScreen(), current_displaymode_extension, mode); + } finally { + decDisplay(); + } + } } private static native void nSwitchDisplayMode(long display, int screen, int extension, DisplayMode mode) throws LWJGLException; @@ -625,11 +638,11 @@ final class LinuxDisplay implements DisplayImplementation { public void resetDisplayMode() { lockAWT(); try { - if( current_displaymode_extension == XRANDR && savedXrandrConfig.length > 0 ) + if( current_displaymode_extension == XRANDR ) { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { - XRandR.setConfiguration( savedXrandrConfig ); + XRandR.restoreConfiguration(); return null; } }); @@ -727,12 +740,12 @@ final class LinuxDisplay implements DisplayImplementation { throw new LWJGLException("No modes available"); switch (current_displaymode_extension) { case XRANDR: - savedXrandrConfig = AccessController.doPrivileged(new PrivilegedAction() { - public Screen[] run() { - return XRandR.getConfiguration(); + saved_mode = AccessController.doPrivileged(new PrivilegedAction() { + public DisplayMode run() { + XRandR.saveConfiguration(); + return XRandR.ScreentoDisplayMode(XRandR.getConfiguration()); } }); - saved_mode = getCurrentXRandrMode(); break; case XF86VIDMODE: saved_mode = modes[0]; @@ -929,13 +942,29 @@ final class LinuxDisplay implements DisplayImplementation { public DisplayMode[] getAvailableDisplayModes() throws LWJGLException { lockAWT(); try { - incDisplay(); - try { - DisplayMode[] modes = nGetAvailableDisplayModes(getDisplay(), getDefaultScreen(), current_displaymode_extension); - return modes; - } finally { - decDisplay(); - } + incDisplay(); + if (current_displaymode_extension == XRANDR) { + // nGetAvailableDisplayModes cannot be trusted. Use it only for bitsPerPixel + DisplayMode[] nDisplayModes = nGetAvailableDisplayModes(getDisplay(), getDefaultScreen(), current_displaymode_extension); + int bpp = 24; + if (nDisplayModes.length > 0) { + bpp = nDisplayModes[0].getBitsPerPixel(); + } + // get the resolutions and frequencys from XRandR + Screen[] resolutions = XRandR.getResolutions(XRandR.getScreenNames()[0]); + DisplayMode[] modes = new DisplayMode[resolutions.length]; + for (int i = 0; i < modes.length; i++) { + modes[i] = new DisplayMode(resolutions[i].width, resolutions[i].height, bpp, resolutions[i].freq); + } + return modes; + } else { + try { + DisplayMode[] modes = nGetAvailableDisplayModes(getDisplay(), getDefaultScreen(), current_displaymode_extension); + return modes; + } finally { + decDisplay(); + } + } } finally { unlockAWT(); } @@ -1101,11 +1130,11 @@ final class LinuxDisplay implements DisplayImplementation { if (current_window_mode == FULLSCREEN_NETWM) { nIconifyWindow(getDisplay(), getWindow(), getDefaultScreen()); try { - if( current_displaymode_extension == XRANDR && savedXrandrConfig.length > 0 ) + if( current_displaymode_extension == XRANDR ) { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { - XRandR.setConfiguration( savedXrandrConfig ); + XRandR.restoreConfiguration(); return null; } }); diff --git a/src/java/org/lwjgl/opengl/XRandR.java b/src/java/org/lwjgl/opengl/XRandR.java index 79eb0f7d..0d6a5ef9 100644 --- a/src/java/org/lwjgl/opengl/XRandR.java +++ b/src/java/org/lwjgl/opengl/XRandR.java @@ -49,6 +49,18 @@ public class XRandR { private static Screen[] current; + /** + * Either the screen marked as "primary" (if it is turned on) + * or the one with the largest (current) resolution. + */ + private static String primaryScreenIdentifier; + + /** + * Used to save the configuration of all output devices to + * restore it on exit or in case of crash. + */ + private static Screen[] savedConfiguration; + private static Map screens; private static void populate() @@ -89,7 +101,13 @@ public class XRandR name = sa[ 0 ]; // save position of this screen, will be used later when current modeline is parsed - parseScreenHeader(currentScreenPosition, "primary".equals(sa[ 2 ]) ? sa[ 3 ] : sa[ 2 ]); + if ("primary".equals(sa[ 2 ])) { + parseScreenHeader(currentScreenPosition, sa[ 3 ]); + // save primary + primaryScreenIdentifier = name; + } else { + parseScreenHeader(currentScreenPosition, sa[ 2 ]); + } } else if( Pattern.matches( "\\d*x\\d*", sa[ 0 ] ) ) { @@ -106,6 +124,17 @@ public class XRandR screens.put( name, possibles.toArray( new Screen[ possibles.size() ] ) ); current = currentList.toArray(new Screen[currentList.size()]); + + // set primary to largest screen if not set yet + if (primaryScreenIdentifier == null) { + long totalPixels = Long.MIN_VALUE; + for (Screen screen : current) { + if (1l * screen.width * screen.height > totalPixels) { + primaryScreenIdentifier = screen.name; + totalPixels = 1l * screen.width * screen.height; + } + } + } } catch( Throwable e ) { @@ -117,23 +146,33 @@ public class XRandR } /** - * @return The current screen configuration, or an empty array if - * xrandr is not supported + * @return The current screen configuration of the primary device, + * or an empty array if xrandr is not supported */ public static Screen[] getConfiguration() { populate(); + // find and return primary + for (Screen screen : current) { + if (screen.name.equals(primaryScreenIdentifier)) { + return new Screen[]{screen}; + } + } + + // problem with primary device, fall back to old behaviour return current.clone(); } /** + * @param disableOthers + * if screens not included in screens should be turned off (true) or left alone (false) * @param screens * The desired screen set, may not be null * @throws IllegalArgumentException * if no screens are specified */ - public static void setConfiguration(Screen... screens) + public static void setConfiguration(boolean disableOthers, Screen... screens) { if( screens.length == 0 ) throw new IllegalArgumentException( "Must specify at least one screen" ); @@ -141,22 +180,24 @@ public class XRandR List cmd = new ArrayList(); cmd.add( "xrandr" ); - // switch off those in the current set not in the new set - for ( Screen screen : current ) { - boolean found = false; - for ( Screen screen1 : screens ) { - if ( screen1.name.equals(screen.name) ) { - found = true; - break; - } - } + if (disableOthers) { + // switch off those in the current set not in the new set + for ( Screen screen : current ) { + boolean found = false; + for ( Screen screen1 : screens ) { + if ( screen1.name.equals(screen.name) ) { + found = true; + break; + } + } - if ( !found ) { - cmd.add("--output"); - cmd.add(screen.name); - cmd.add("--off"); - } - } + if ( !found ) { + cmd.add("--output"); + cmd.add(screen.name); + cmd.add("--off"); + } + } + } // set up new set for ( Screen screen : screens ) @@ -184,6 +225,25 @@ public class XRandR } } + /** + * Saves the current configuration for all connected display devices. + * This configuration can be restored on exit/crash by calling + * restoreConfiguration() + */ + public static void saveConfiguration() { + savedConfiguration = current.clone(); + } + + /** + * Restores the configuration for all connected display devices. + * Used on exit or in case of a crash to reset all devices. + */ + public static void restoreConfiguration() { + if (savedConfiguration != null) { + setConfiguration(true, savedConfiguration); + } + } + /** * @return the name of connected screens, or an empty array if * xrandr is not supported @@ -275,6 +335,28 @@ public class XRandR screenPosition[1] = Integer.parseInt(m.group(4)); } + static Screen DisplayModetoScreen(DisplayMode mode) { + populate(); + Screen primary = findPrimary(current); + return new Screen(primary.name, mode.getWidth(), mode.getHeight(), mode.getFrequency(), primary.xPos, primary.yPos); + } + + static DisplayMode ScreentoDisplayMode(Screen... screens) { + populate(); + Screen primary = findPrimary(screens); + return new DisplayMode(primary.width, primary.height, 24, primary.freq); + } + + private static Screen findPrimary(Screen... screens) { + for (Screen screen : screens) { + if (screen.name.equals(primaryScreenIdentifier)) { + return screen; + } + } + // fallback + return screens[0]; + } + /** * Encapsulates the configuration of a monitor. * Resolution and freq are fixed, position is mutable