2010-03-08 22:29:32 +01:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2002-2010 LWJGL Project
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
|
* modification, are permitted provided that the following conditions are
|
|
|
|
|
* met:
|
|
|
|
|
*
|
|
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
|
*
|
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
|
*
|
|
|
|
|
* * Neither the name of 'LWJGL' nor the names of
|
|
|
|
|
* its contributors may be used to endorse or promote products derived
|
|
|
|
|
* from this software without specific prior written permission.
|
|
|
|
|
*
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
|
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
|
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package org.lwjgl.opengl;
|
|
|
|
|
|
|
|
|
|
import java.io.BufferedReader;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.io.InputStreamReader;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
2010-05-20 20:11:47 +02:00
|
|
|
import java.util.regex.Matcher;
|
2010-03-08 22:29:32 +01:00
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Utility for working with the xrandr commmand-line utility. Assumes
|
|
|
|
|
* xrandr v1.2 or higher.
|
|
|
|
|
*
|
|
|
|
|
* @author ryanm
|
|
|
|
|
*/
|
|
|
|
|
public class XRandR {
|
|
|
|
|
|
|
|
|
|
private static Screen[] current;
|
|
|
|
|
|
|
|
|
|
private static Map /* <String, Screen[]> */screens;
|
|
|
|
|
|
|
|
|
|
private static void populate() {
|
|
|
|
|
if (screens == null) {
|
|
|
|
|
screens = new HashMap/* <String, Screen[]> */();
|
|
|
|
|
|
|
|
|
|
// ProcessBuilder pb = new ProcessBuilder( "xrandr", "-q" );
|
|
|
|
|
// pb.redirectErrorStream();
|
|
|
|
|
try {
|
|
|
|
|
// Process p= pb.start();
|
|
|
|
|
Process p = Runtime.getRuntime().exec(new String[] { "xrandr", "-q"});
|
|
|
|
|
|
|
|
|
|
List/* <Screen> */currentList = new ArrayList/* <Screen> */();
|
|
|
|
|
List/* <Screen> */possibles = new ArrayList/* <Screen> */();
|
|
|
|
|
String name = null;
|
|
|
|
|
|
|
|
|
|
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
|
|
|
|
String line;
|
|
|
|
|
while ((line = br.readLine()) != null) {
|
|
|
|
|
line = line.trim();
|
|
|
|
|
String[] sa = line.split("\\s+");
|
|
|
|
|
|
|
|
|
|
if (sa[1].equals("connected")) {
|
|
|
|
|
// found a new screen block
|
|
|
|
|
if (name != null) {
|
|
|
|
|
screens.put(name, possibles.toArray(new Screen[possibles.size()]));
|
|
|
|
|
possibles.clear();
|
|
|
|
|
}
|
|
|
|
|
name = sa[0];
|
|
|
|
|
|
|
|
|
|
// record the current config
|
2010-05-20 20:11:47 +02:00
|
|
|
parseScreen(currentList, name, sa[2]);
|
2010-03-08 22:29:32 +01:00
|
|
|
} else if (Pattern.matches("\\d*x\\d*", sa[0])) {
|
|
|
|
|
// found a new mode line
|
2010-05-20 20:11:47 +02:00
|
|
|
parseScreen(possibles, name, sa[0]);
|
2010-03-08 22:29:32 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
screens.put(name, possibles.toArray(new Screen[possibles.size()]));
|
|
|
|
|
|
|
|
|
|
current = (Screen[]) currentList.toArray(new Screen[currentList.size()]);
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return The current screen configuration, or an empty array if
|
|
|
|
|
* xrandr is not supported
|
|
|
|
|
*/
|
|
|
|
|
public static Screen[] getConfiguration() {
|
2010-04-22 20:32:46 +02:00
|
|
|
populate();
|
2010-03-08 22:29:32 +01:00
|
|
|
|
|
|
|
|
return (Screen[]) current.clone();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param screens
|
|
|
|
|
* The desired screen set, may not be <code>null</code>
|
|
|
|
|
*/
|
2010-04-22 20:32:46 +02:00
|
|
|
public static void setConfiguration(Screen[]/* ... */screens) {
|
2010-03-08 22:29:32 +01:00
|
|
|
if (screens.length == 0) {
|
|
|
|
|
throw new IllegalArgumentException("Must specify at least one screen");
|
|
|
|
|
}
|
2010-04-22 20:32:46 +02:00
|
|
|
|
2010-03-08 22:29:32 +01:00
|
|
|
List/* <String> */cmd = new ArrayList/* <String> */();
|
|
|
|
|
cmd.add("xrandr");
|
|
|
|
|
|
|
|
|
|
// switch off those in the current set not in the new set
|
|
|
|
|
for (int i = 0; i < current.length; i++) {
|
|
|
|
|
boolean found = false;
|
|
|
|
|
for (int j = 0; j < screens.length; j++) {
|
|
|
|
|
if (screens[j].name.equals(current[i].name)) {
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
|
cmd.add("--output");
|
|
|
|
|
cmd.add(current[i].name);
|
|
|
|
|
cmd.add("--off");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set up new set
|
|
|
|
|
for (int i = 0; i < screens.length; i++) {
|
|
|
|
|
screens[i].getArgs(cmd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// ProcessBuilder pb = new ProcessBuilder( cmd );
|
|
|
|
|
// pb.redirectErrorStream();
|
|
|
|
|
// Process p = pb.start();
|
|
|
|
|
Process p = Runtime.getRuntime().exec((String[]) cmd.toArray(new String[cmd.size()]));
|
|
|
|
|
// no output is expected, but check anyway
|
|
|
|
|
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
|
|
|
|
String line;
|
|
|
|
|
while ((line = br.readLine()) != null) {
|
|
|
|
|
System.out.println(line);
|
|
|
|
|
}
|
|
|
|
|
current = screens;
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
2010-04-22 20:32:46 +02:00
|
|
|
|
2010-03-08 22:29:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return the name of connected screens, or an empty array if
|
|
|
|
|
* xrandr is not supported
|
|
|
|
|
*/
|
|
|
|
|
public static String[] getScreenNames() {
|
2010-04-22 20:32:46 +02:00
|
|
|
populate();
|
2010-03-08 22:29:32 +01:00
|
|
|
return (String[]) screens.keySet().toArray(new String[screens.size()]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param name
|
|
|
|
|
* @return the possible resolutions of the named screen, or
|
|
|
|
|
* <code>null</code> if there is no such screen
|
|
|
|
|
*/
|
|
|
|
|
public static Screen[] getResolutions(String name) {
|
2010-04-22 20:32:46 +02:00
|
|
|
populate();
|
2010-03-08 22:29:32 +01:00
|
|
|
// clone the array to prevent held copies being altered
|
|
|
|
|
return (Screen[]) ((Screen[]) screens.get(name)).clone();
|
|
|
|
|
}
|
2010-05-20 20:11:47 +02:00
|
|
|
|
|
|
|
|
private static final Pattern SCREEN_PATTERN1 = Pattern.compile("^(\\d+)x(\\d+)\\+(\\d+)\\+(\\d+)$");
|
|
|
|
|
private static final Pattern SCREEN_PATTERN2 = Pattern.compile("^(\\d+)x(\\d+)$");
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Parses a screen configuration and adds it to the list if it's valid.
|
|
|
|
|
*
|
|
|
|
|
* @param list
|
|
|
|
|
* the list to add the Screen to if it's valid
|
|
|
|
|
* @param name
|
|
|
|
|
* the name of this screen
|
|
|
|
|
* @param conf
|
|
|
|
|
* config string, format either widthxheight or
|
|
|
|
|
* widthxheight+xPos+yPos
|
|
|
|
|
*/
|
|
|
|
|
private static void parseScreen(List /* <Screen> */ list, String name, String what) {
|
|
|
|
|
Matcher m = SCREEN_PATTERN1.matcher(what);
|
|
|
|
|
if(!m.matches()) {
|
|
|
|
|
m = SCREEN_PATTERN2.matcher(what);
|
|
|
|
|
if(!m.matches()) {
|
|
|
|
|
System.out.println("Did not match: " + what);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
int width = Integer.parseInt(m.group(1));
|
|
|
|
|
int height = Integer.parseInt(m.group(2));
|
|
|
|
|
int xpos, ypos;
|
|
|
|
|
if(m.groupCount() > 3) {
|
|
|
|
|
xpos = Integer.parseInt(m.group(3));
|
|
|
|
|
ypos = Integer.parseInt(m.group(4));
|
|
|
|
|
} else {
|
|
|
|
|
xpos = 0;
|
|
|
|
|
ypos = 0;
|
|
|
|
|
}
|
|
|
|
|
list.add(new Screen(name, width, height, xpos, ypos));
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-08 22:29:32 +01:00
|
|
|
/**
|
|
|
|
|
* Encapsulates the configuration of a monitor. Resolution is
|
|
|
|
|
* fixed, position is mutable
|
|
|
|
|
*
|
|
|
|
|
* @author ryanm
|
|
|
|
|
*/
|
|
|
|
|
public static class Screen implements Cloneable {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Name for this output
|
|
|
|
|
*/
|
|
|
|
|
public final String name;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Width in pixels
|
|
|
|
|
*/
|
|
|
|
|
public final int width;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Height in pixels
|
|
|
|
|
*/
|
|
|
|
|
public final int height;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Position on the x-axis, in pixels
|
|
|
|
|
*/
|
|
|
|
|
public int xPos = 0;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Position on the y-axis, in pixels
|
|
|
|
|
*/
|
|
|
|
|
public int yPos = 0;
|
|
|
|
|
|
2010-05-20 20:11:47 +02:00
|
|
|
private Screen(String name, int width, int height, int xPos, int yPos) {
|
2010-03-08 22:29:32 +01:00
|
|
|
this.name = name;
|
2010-05-20 20:11:47 +02:00
|
|
|
this.width = width;
|
|
|
|
|
this.height = height;
|
|
|
|
|
this.xPos = xPos;
|
|
|
|
|
this.yPos = yPos;
|
2010-03-08 22:29:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void getArgs(List/* <String> */argList) {
|
|
|
|
|
argList.add("--output");
|
|
|
|
|
argList.add(name);
|
|
|
|
|
argList.add("--mode");
|
|
|
|
|
argList.add(width + "x" + height);
|
|
|
|
|
argList.add("--pos");
|
|
|
|
|
argList.add(xPos + "x" + yPos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// @Override
|
|
|
|
|
public String toString() {
|
|
|
|
|
return name + " " + width + "x" + height + " @ " + xPos + "x" + yPos;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-04-22 20:32:46 +02:00
|
|
|
}
|