com.jogamp.newt.MonitorMode Maven / Gradle / Ivy
/**
* Copyright 2013 JogAmp Community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of JogAmp Community.
*/
package com.jogamp.newt;
import java.util.Comparator;
import javax.media.nativewindow.util.DimensionImmutable;
import javax.media.nativewindow.util.RectangleImmutable;
import javax.media.nativewindow.util.SurfaceSize;
import com.jogamp.newt.util.MonitorModeUtil;
/**
* Immutable MonitorMode Class, consisting of it's read only components:
*
* - nativeId
* - {@link SizeAndRRate}, consist out of non rotated {@link #getSurfaceSize() surface size}, {@link #getRefreshRate() refresh rate} and {@link #getFlags() flags}.
* rotation
, measured counter clockwise (CCW)
*
*
* Aquire and filter MonitorMode
*
* - {@link MonitorDevice} Selection:
*
* - A List of all {@link MonitorDevice}s is accessible via {@link Screen#getMonitorDevices()}.
* - The main monitor used by a windows is accessible via {@link Window#getMainMonitor()}.
* - The main monitor covering an arbitrary rectnagle is accessible via {@link Screen#getMainMonitor(RectangleImmutable)}.
*
* - The current MonitorMode can be obtained via {@link MonitorDevice#getCurrentMode()}.
* - The original MonitorMode can be obtained via {@link MonitorDevice#getOriginalMode()}.
* - {@link MonitorMode} Filtering:
*
* - A {@link MonitorDevice}'s MonitorModes is accessible via {@link MonitorDevice#getSupportedModes()}.
* - You may utilize {@link MonitorModeUtil} to filter and select a desired MonitorMode.
*
*
*
*
* Changing MonitorMode
*
* - Use {@link MonitorDevice#setCurrentMode(MonitorMode)}
* to change the current MonitorMode for all {@link Screen}s referenced via the {@link Screen#getFQName() full qualified name (FQN)}.
* - The {@link MonitorDevice#getOriginalMode() original mode} is restored when
*
* - the last FQN referenced Screen closes.
* - the JVM shuts down.
*
*
*
* Example for changing the MonitorMode:
*
// Pick the monitor:
// Either the one used by a window ..
MonitorDevice monitor = window.getMainMonitor();
// Or arbitrary from the list ..
List allMonitor = getMonitorDevices();
MonitorDevice monitor = allMonitor.get(0);
// Current and original modes ..
MonitorMode mmCurrent = monitor.queryCurrentMode();
MonitorMode mmOrig = monitor.getOriginalMode();
// Target resolution
Dimension res = new Dimension(800, 600);
// Target refresh rate shall be similar to current one ..
float freq = mmCurrent.getRefreshRate();
// Target rotation shall be similar to current one
int rot = mmCurrent.getRotation();
// Filter criterias sequential out of all available MonitorMode of the chosen MonitorDevice
List monitorModes = monitor.getSupportedModes();
monitorModes = MonitorModeUtil.filterByFlags(monitorModes, 0); // no interlace, double-scan etc
monitorModes = MonitorModeUtil.filterByRotation(monitorModes, rot);
monitorModes = MonitorModeUtil.filterByResolution(monitorModes, res);
monitorModes = MonitorModeUtil.filterByRate(monitorModes, freq);
monitorModes = MonitorModeUtil.getHighestAvailableBpp(monitorModes);
// pick 1st one and set to current ..
MonitorMode mm = monitorModes.get(0);
monitor.setCurrentMode(mm);
*
*/
public class MonitorMode implements Comparable {
/** Comparator for 2 {@link MonitorMode}s, following comparison order as described in {@link MonitorMode#compareTo(MonitorMode)}, returning the ascending order. */
public static final Comparator monitorModeComparator = new Comparator() {
@Override
public int compare(MonitorMode mm1, MonitorMode mm2) {
return mm1.compareTo(mm2);
} };
/** Comparator for 2 {@link MonitorMode}s, following comparison order as described in {@link MonitorMode#compareTo(MonitorMode)}, returning the descending order. */
public static final Comparator monitorModeComparatorInv = new Comparator() {
@Override
public int compare(MonitorMode mm1, MonitorMode mm2) {
return mm2.compareTo(mm1);
} };
/**
* Immutable surfaceSize, flags and refreshRate Class, consisting of it's read only components:
*
* - nativeId
* - {@link SurfaceSize} surface memory size
* flags
* refresh rate
*
*/
public static class SizeAndRRate implements Comparable {
/** Non rotated surface size */
public final SurfaceSize surfaceSize;
/** Mode bitfield flags, i.e. {@link #FLAG_DOUBLESCAN}, {@link #FLAG_INTERLACE}, .. */
public final int flags;
/** Vertical refresh rate */
public final float refreshRate;
public final int hashCode;
public SizeAndRRate(SurfaceSize surfaceSize, float refreshRate, int flags) {
if(null==surfaceSize) {
throw new IllegalArgumentException("surfaceSize must be set ("+surfaceSize+")");
}
this.surfaceSize=surfaceSize;
this.flags = flags;
this.refreshRate=refreshRate;
this.hashCode = getHashCode();
}
private final static String STR_INTERLACE = "Interlace";
private final static String STR_DOUBLESCAN = "DoubleScan";
private final static String STR_SEP = ", ";
public static final StringBuilder flags2String(int flags) {
final StringBuilder sb = new StringBuilder();
boolean sp = false;
if( 0 != ( flags & FLAG_INTERLACE ) ) {
sb.append(STR_INTERLACE);
sp = true;
}
if( 0 != ( flags & FLAG_DOUBLESCAN ) ) {
if( sp ) {
sb.append(STR_SEP);
}
sb.append(STR_DOUBLESCAN);
sp = true;
}
return sb;
}
@Override
public final String toString() {
return surfaceSize+" @ "+refreshRate+" Hz, flags ["+flags2String(flags).toString()+"]";
}
/**
*
* Compares {@link SurfaceSize#compareTo(SurfaceSize) surfaceSize} 1st, then {@link #flags}, then {@link #refreshRate}.
*
*
* Flags are compared as follows:
*
* NONE > DOUBLESCAN > INTERLACE
*
*
*
* Refresh rate differences of < 0.01 are considered equal (epsilon).
*
* {@inheritDoc}
*/
@Override
public int compareTo(final SizeAndRRate sszr) {
final int rssz = surfaceSize.compareTo(sszr.surfaceSize);
if( 0 != rssz ) {
return rssz;
}
final int tflags = 0 == flags ? Integer.MAX_VALUE : flags; // normalize NONE
final int xflags = 0 == sszr.flags ? Integer.MAX_VALUE : sszr.flags; // normalize NONE
if( tflags == xflags ) {
final float refreshEpsilon = 0.01f; // reasonable sorting granularity of refresh rate
final float drate = refreshRate - sszr.refreshRate;
if( Math.abs(drate) < refreshEpsilon ) {
return 0;
} else if( drate > refreshEpsilon ) {
return 1;
} else {
return -1;
}
} else {
if(tflags > xflags) {
return 1;
} else if(tflags < xflags) {
return -1;
}
return 0;
}
}
/**
* Tests equality of two {@link SizeAndRRate} objects
* by evaluating equality of it's components:
*
* surfaceSize
* refreshRate
* flags
*
*/
@Override
public final boolean equals(Object obj) {
if (this == obj) { return true; }
if (obj instanceof SizeAndRRate) {
final SizeAndRRate p = (SizeAndRRate)obj;
return surfaceSize.equals(p.surfaceSize) &&
flags == p.flags &&
refreshRate == p.refreshRate ;
}
return false;
}
/**
* Returns a combined hash code of it's elements:
*
* surfaceSize
* flags
* refreshRate
*
*/
@Override
public final int hashCode() {
return hashCode;
}
private final int getHashCode() {
// 31 * x == (x << 5) - x
int hash = 31 + surfaceSize.hashCode();
hash = ((hash << 5) - hash) + flags;
hash = ((hash << 5) - hash) + (int)(refreshRate*100.0f);
return hash;
}
}
/** zero rotation, compared to normal settings */
public static final int ROTATE_0 = 0;
/** 90 degrees CCW rotation */
public static final int ROTATE_90 = 90;
/** 180 degrees CCW rotation */
public static final int ROTATE_180 = 180;
/** 270 degrees CCW rotation */
public static final int ROTATE_270 = 270;
/** Frame is split into two fields. See {@link #getFlags()}. */
public static final int FLAG_INTERLACE = 1 << 0;
/** Lines are doubled. See {@link #getFlags()}. */
public static final int FLAG_DOUBLESCAN = 1 << 1;
/** The immutable native Id of this instance, which may not be unique. */
private final int nativeId;
private final SizeAndRRate sizeAndRRate;
private final int rotation;
private final int hashCode;
public static boolean isRotationValid(int rotation) {
return rotation == MonitorMode.ROTATE_0 || rotation == MonitorMode.ROTATE_90 ||
rotation == MonitorMode.ROTATE_180 || rotation == MonitorMode.ROTATE_270 ;
}
/**
* @param sizeAndRRate the surface size and refresh rate mode
* @param rotation the screen rotation, measured counter clockwise (CCW)
*/
public MonitorMode(int nativeId, SizeAndRRate sizeAndRRate, int rotation) {
if ( !isRotationValid(rotation) ) {
throw new RuntimeException("invalid rotation: "+rotation);
}
this.nativeId = nativeId;
this.sizeAndRRate = sizeAndRRate;
this.rotation = rotation;
this.hashCode = getHashCode();
}
/**
* Creates a user instance w/o {@link #getId() identity} to filter our matching modes w/ identity.
*
* See {@link com.jogamp.newt.util.MonitorModeUtil} for filter utilities.
*
* @param surfaceSize
* @param refreshRate
* @param flags
* @param rotation
*/
public MonitorMode(SurfaceSize surfaceSize, float refreshRate, int flags, int rotation) {
this(0, new SizeAndRRate(surfaceSize, refreshRate, flags), rotation);
}
/** @return the immutable native Id of this mode, may not be unique, may be 0. */
public final int getId() { return nativeId; }
/** Returns the surfaceSize and refreshRate instance. */
public final SizeAndRRate getSizeAndRRate() {
return sizeAndRRate;
}
/** Returns the unrotated {@link SurfaceSize} */
public final SurfaceSize getSurfaceSize() {
return sizeAndRRate.surfaceSize;
}
/** Returns the vertical refresh rate. */
public final float getRefreshRate() {
return sizeAndRRate.refreshRate;
}
/** Returns bitfield w/ flags, i.e. {@link #FLAG_DOUBLESCAN}, {@link #FLAG_INTERLACE}, .. */
public final int getFlags() {
return sizeAndRRate.flags;
}
/** Returns the CCW rotation of this mode */
public final int getRotation() {
return rotation;
}
/** Returns the rotated screen width,
* derived from getMonitorMode().getSurfaceSize().getResolution()
* and getRotation()
*/
public final int getRotatedWidth() {
return getRotatedWH(true);
}
/** Returns the rotated screen height,
* derived from getMonitorMode().getSurfaceSize().getResolution()
* and getRotation()
*/
public final int getRotatedHeight() {
return getRotatedWH(false);
}
@Override
public final String toString() {
return "[Id "+Display.toHexString(nativeId)+", " + sizeAndRRate + ", " + rotation + " degr]";
}
/**
*
* Compares {@link SizeAndRRate#compareTo(SizeAndRRate) sizeAndRRate} 1st, then {@link #rotation}.
*
*
* Rotation is compared inverted, i.e. 360 - rotation
,
* so the lowest rotation reflects a higher value.
*
*
* Order of comparing MonitorMode:
*
* - resolution
* - bits per pixel
* - flags
* - refresh rate
* - rotation
*
*
* {@inheritDoc}
*/
@Override
public int compareTo(final MonitorMode mm) {
final int c = sizeAndRRate.compareTo(mm.sizeAndRRate);
if( 0 != c ) {
return c;
}
final int trot = 360 - rotation; // normalize rotation
final int xrot = 360 - mm.rotation; // normalize rotation
if(trot > xrot) {
return 1;
} else if(trot < xrot) {
return -1;
}
return 0;
}
/**
* Tests equality of two {@link MonitorMode} objects
* by evaluating equality of it's components:
*
* nativeId
* sizeAndRRate
* rotation
*
*/
@Override
public final boolean equals(Object obj) {
if (this == obj) { return true; }
if (obj instanceof MonitorMode) {
MonitorMode sm = (MonitorMode)obj;
return sm.nativeId == this.nativeId &&
sm.sizeAndRRate.equals(sizeAndRRate) &&
sm.rotation == this.rotation ;
}
return false;
}
/**
* Returns a combined hash code of it's elements:
*
* nativeId
* sizeAndRRate
* rotation
*
*/
@Override
public final int hashCode() {
return hashCode;
}
private final int getHashCode() {
// 31 * x == (x << 5) - x
int hash = 31 + getId();
hash = ((hash << 5) - hash) + sizeAndRRate.hashCode();
hash = ((hash << 5) - hash) + getRotation();
return hash;
}
private final int getRotatedWH(boolean width) {
final DimensionImmutable d = sizeAndRRate.surfaceSize.getResolution();
final boolean swap = MonitorMode.ROTATE_90 == rotation || MonitorMode.ROTATE_270 == rotation ;
if ( ( width && swap ) || ( !width && !swap ) ) {
return d.getHeight();
}
return d.getWidth();
}
}