1 /*
2  * Copyright (c) 2002-2008 LWJGL Project
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'LWJGL' nor the names of
17  *   its contributors may be used to endorse or promote products derived
18  *   from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package org.lwjgl.util;
33 
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Comparator;
37 
38 import org.lwjgl.LWJGLException;
39 import org.lwjgl.LWJGLUtil;
40 import org.lwjgl.opengl.DisplayMode;
41 
42 /**
43  * Display initialization utility, that can be used to find display modes and pick
44  * one for you based on your criteria.
45  * @author $Author$
46  * @version $Revision$
47  * $Id$
48  */
49 public final class Display {
50 
51 	private static final boolean DEBUG = false;
52 
53 	/**
54 	 * Determine the available display modes that match the specified minimum and maximum criteria.
55 	 * If any given criterium is specified as -1 then it is ignored.
56 	 *
57 	 * @param minWidth the minimum display resolution in pixels
58 	 * @param minHeight the minimum display resolution in pixels
59 	 * @param maxWidth the maximum display resolution in pixels
60 	 * @param maxHeight the maximum display resolution in pixels
61 	 * @param minBPP the minimum bit depth per pixel
62 	 * @param maxBPP the maximum bit depth per pixel
63 	 * @param minFreq the minimum display frequency in Hz
64 	 * @param maxFreq the maximum display frequency in Hz
65 	 * @return an array of matching display modes
66 	 */
getAvailableDisplayModes(int minWidth, int minHeight, int maxWidth, int maxHeight, int minBPP, int maxBPP, int minFreq, int maxFreq)67 	public static DisplayMode[] getAvailableDisplayModes(int minWidth, int minHeight, int maxWidth, int maxHeight, int minBPP, int maxBPP,
68 		int minFreq, int maxFreq) throws LWJGLException
69 	{
70 		// First get the available display modes
71 		DisplayMode[] modes = org.lwjgl.opengl.Display.getAvailableDisplayModes();
72 
73 		if (LWJGLUtil.DEBUG || DEBUG) {
74 			System.out.println("Available screen modes:");
75 			for ( DisplayMode mode : modes ) {
76 				System.out.println(mode);
77 			}
78 		}
79 
80 		ArrayList<DisplayMode> matches = new ArrayList<DisplayMode>(modes.length);
81 
82 		for (int i = 0; i < modes.length; i ++) {
83 			assert modes[i] != null : ""+i+" "+modes.length;
84 			if (minWidth != -1 && modes[i].getWidth() < minWidth)
85 				continue;
86 			if (maxWidth != -1 && modes[i].getWidth() > maxWidth)
87 				continue;
88 			if (minHeight != -1 && modes[i].getHeight() < minHeight)
89 				continue;
90 			if (maxHeight != -1 && modes[i].getHeight() > maxHeight)
91 				continue;
92 			if (minBPP != -1 && modes[i].getBitsPerPixel() < minBPP)
93 				continue;
94 			if (maxBPP != -1 && modes[i].getBitsPerPixel() > maxBPP)
95 				continue;
96 			//if (modes[i].bpp == 24)
97 			//	continue;
98 			if (modes[i].getFrequency() != 0) {
99 				if (minFreq != -1 && modes[i].getFrequency() < minFreq)
100 					continue;
101 				if (maxFreq != -1 && modes[i].getFrequency() > maxFreq)
102 					continue;
103 			}
104 			matches.add(modes[i]);
105 		}
106 
107 		DisplayMode[] ret = new DisplayMode[matches.size()];
108 		matches.toArray(ret);
109 		if (LWJGLUtil.DEBUG && DEBUG) {
110 			System.out.println("Filtered screen modes:");
111 			for ( DisplayMode mode : ret ) {
112 				System.out.println(mode);
113 			}
114 		}
115 
116 		return ret;
117 	}
118 
119 	/**
120 	 * Create the display by choosing from a list of display modes based on an order of preference.
121 	 * You must supply a list of allowable display modes, probably by calling getAvailableDisplayModes(),
122 	 * and an array with the order in which you would like them sorted in descending order.
123 	 * This method attempts to create the topmost display mode; if that fails, it will try the next one,
124 	 * and so on, until there are no modes left. If no mode is set at the end, an exception is thrown.
125 	 * @param dm a list of display modes to choose from
126 	 * @param param the names of the DisplayMode fields in the order in which you would like them sorted.
127 	 * @return the chosen display mode
128 	 * @throws NoSuchFieldException if one of the params is not a field in DisplayMode
129 	 * @throws Exception if no display mode could be set
130 	 * @see org.lwjgl.opengl.DisplayMode
131 	 */
setDisplayMode(DisplayMode[] dm, final String[] param)132 	public static DisplayMode setDisplayMode(DisplayMode[] dm, final String[] param) throws Exception {
133 
134 		class FieldAccessor {
135 			final String fieldName;
136 			final int order;
137 			final int preferred;
138 			final boolean usePreferred;
139 			FieldAccessor(String fieldName, int order, int preferred, boolean usePreferred) {
140 				this.fieldName = fieldName;
141 				this.order = order;
142 				this.preferred = preferred;
143 				this.usePreferred = usePreferred;
144 			}
145 			int getInt(DisplayMode mode) {
146 				if ("width".equals(fieldName)) {
147 					return mode.getWidth();
148 				}
149 				if ("height".equals(fieldName)) {
150 					return mode.getHeight();
151 				}
152 				if ("freq".equals(fieldName)) {
153 					return mode.getFrequency();
154 				}
155 				if ("bpp".equals(fieldName)) {
156 					return mode.getBitsPerPixel();
157 				}
158 				throw new IllegalArgumentException("Unknown field "+fieldName);
159 			}
160 		}
161 
162 		class Sorter implements Comparator<DisplayMode> {
163 
164 			final FieldAccessor[] accessors;
165 
166 			Sorter() {
167 				accessors = new FieldAccessor[param.length];
168 				for (int i = 0; i < accessors.length; i ++) {
169 					int idx = param[i].indexOf('=');
170 					if (idx > 0) {
171 						accessors[i] = new FieldAccessor(param[i].substring(0, idx), 0, Integer.parseInt(param[i].substring(idx + 1, param[i].length())), true);
172 					} else if (param[i].charAt(0) == '-') {
173 						accessors[i] = new FieldAccessor(param[i].substring(1), -1, 0, false);
174 					} else {
175 						accessors[i] = new FieldAccessor(param[i], 1, 0, false);
176 					}
177 				}
178 			}
179 
180 			/**
181 			 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
182 			 */
183 			public int compare(DisplayMode dm1, DisplayMode dm2) {
184 				for ( FieldAccessor accessor : accessors ) {
185 					int f1 = accessor.getInt(dm1);
186 					int f2 = accessor.getInt(dm2);
187 
188 					if ( accessor.usePreferred && f1 != f2 ) {
189 						if ( f1 == accessor.preferred )
190 							return -1;
191 						else if ( f2 == accessor.preferred )
192 							return 1;
193 						else {
194 							// Score according to the difference between the values
195 							int absf1 = Math.abs(f1 - accessor.preferred);
196 							int absf2 = Math.abs(f2 - accessor.preferred);
197 							if ( absf1 < absf2 )
198 								return -1;
199 							else if ( absf1 > absf2 )
200 								return 1;
201 							else
202 								continue;
203 						}
204 					} else if ( f1 < f2 )
205 						return accessor.order;
206 					else if ( f1 == f2 )
207 						continue;
208 					else
209 						return -accessor.order;
210 				}
211 
212 				return 0;
213 			}
214 		}
215 
216 		// Sort the display modes
217 		Arrays.sort(dm, new Sorter());
218 
219 		// Try them out in the appropriate order
220 		if (LWJGLUtil.DEBUG || DEBUG) {
221 			System.out.println("Sorted display modes:");
222 			for ( DisplayMode aDm : dm ) {
223 				System.out.println(aDm);
224 			}
225 		}
226 		for ( DisplayMode aDm : dm ) {
227 			try {
228 				if ( LWJGLUtil.DEBUG || DEBUG )
229 					System.out.println("Attempting to set displaymode: " + aDm);
230 				org.lwjgl.opengl.Display.setDisplayMode(aDm);
231 				return aDm;
232 			} catch (Exception e) {
233 				if ( LWJGLUtil.DEBUG || DEBUG ) {
234 					System.out.println("Failed to set display mode to " + aDm);
235 					e.printStackTrace();
236 				}
237 			}
238 		}
239 
240 		throw new Exception("Failed to set display mode.");
241 	}
242 
243 }
244