1 //----------------------------------------------------------------------------
2 //  EDGE Resolution Handling
3 //----------------------------------------------------------------------------
4 //
5 //  Copyright (c) 1999-2009  The EDGE Team.
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 //  Based on the DOOM source code, released by Id Software under the
20 //  following copyright:
21 //
22 //    Copyright (C) 1993-1996 by id Software, Inc.
23 //
24 //----------------------------------------------------------------------------
25 
26 #include "i_defs.h"
27 #include "i_defs_gl.h"
28 
29 #include <limits.h>
30 
31 #include <vector>
32 #include <algorithm>
33 
34 #include "r_modes.h"
35 #include "r_gldefs.h"
36 #include "r_colormap.h"
37 #include "am_map.h"
38 #include "r_image.h"
39 #include "r_units.h"
40 #include "r_draw.h"
41 #include "r_wipe.h"
42 
43 // Globals
44 int SCREENWIDTH;
45 int SCREENHEIGHT;
46 int SCREENBITS;
47 bool FULLSCREEN;
48 
49 
50 static std::vector<scrmode_c *> screen_modes;
51 
52 
R_DepthIsEquivalent(int depth1,int depth2)53 bool R_DepthIsEquivalent(int depth1, int depth2)
54 {
55 	if (depth1 == depth2)
56 		return true;
57 
58 	if (MIN(depth1,depth2) == 15 && MAX(depth1,depth2) == 16)
59 		return true;
60 
61 	if (MIN(depth1,depth2) == 24 && MAX(depth1,depth2) == 32)
62 		return true;
63 
64 	return false;
65 }
66 
SizeDiff(int w1,int h1,int w2,int h2)67 static int SizeDiff(int w1, int h1, int w2, int h2)
68 {
69 	return (w1 * 10000 + h1) - (w2 * 10000 + h2);
70 }
71 
72 
R_FindResolution(int w,int h,int depth,bool full)73 scrmode_c *R_FindResolution(int w, int h, int depth, bool full)
74 {
75 	for (int i = 0; i < (int)screen_modes.size(); i++)
76 	{
77 		scrmode_c *cur = screen_modes[i];
78 
79 		if (cur->width == w && cur->height == h &&
80 			R_DepthIsEquivalent(cur->depth, depth) &&
81 			cur->full == full)
82 		{
83 			return cur;
84 		}
85 	}
86 
87 	return NULL;
88 }
89 
90 
91 //
92 // R_AddResolution
93 //
94 // Adds a resolution to the scrmodes list. This is used so we can
95 // select it within the video options menu.
96 //
R_AddResolution(scrmode_c * mode)97 void R_AddResolution(scrmode_c *mode)
98 {
99     scrmode_c *exist = R_FindResolution(mode->width, mode->height,
100 							mode->depth, mode->full);
101 	if (exist)
102 	{
103 		if (mode->depth != exist->depth)
104 		{
105 			// depth is different but equivalent.  Update current
106 			// member in list, giving preference to power-of-two.
107 			if (mode->depth == 16 or mode->depth == 32)
108 				exist->depth = mode->depth;
109 		}
110 
111 		return;
112 	}
113 
114 	screen_modes.push_back(new scrmode_c(*mode));
115 }
116 
117 
R_DumpResList(void)118 void R_DumpResList(void)
119 {
120     I_Printf("Available Resolutions:\n");
121 
122 	for (int i = 0; i < (int)screen_modes.size(); i++)
123 	{
124 		scrmode_c *cur = screen_modes[i];
125 
126 		if (i > 0 && (i % 3) == 0)
127 			I_Printf("\n");
128 
129         I_Printf("  %4dx%4d @ %02d %s",
130                  cur->width, cur->height, cur->depth,
131                  cur->full ? "FS " : "win");
132 	}
133 
134 	I_Printf("\n");
135 }
136 
R_IncrementResolution(scrmode_c * mode,int what,int dir)137 bool R_IncrementResolution(scrmode_c *mode, int what, int dir)
138 {
139 	// Algorithm:
140 	//   for RESINC_Depth and RESINC_Full, we simply toggle the
141 	//   value in question (depth or full), and find the mode
142 	//   with matching depth/full and the closest size.
143 	//
144 	//   for RESINC_Size, we find modes with matching depth/full
145 	//   and the *next* closest size (ignoring the same size or
146 	//   sizes that are in opposite direction to 'dir' param).
147 
148 	SYS_ASSERT(dir == 1 || dir == -1);
149 
150 	int depth = mode->depth;
151 	bool full = mode->full;
152 
153 	if (what == RESINC_Depth)
154 		depth = (depth < 20) ? 32 : 16;
155 
156 	if (what == RESINC_Full)
157 		full = !full;
158 
159 	scrmode_c *best = NULL;
160 	int best_diff = (1 << 30);
161 
162 	for (int i = 0; i < (int)screen_modes.size(); i++)
163 	{
164 		scrmode_c *cur = screen_modes[i];
165 
166 		if (! R_DepthIsEquivalent(cur->depth, depth))
167 			continue;
168 
169 		if (cur->full != full)
170 			continue;
171 
172 		int diff = SizeDiff(cur->width, cur->height, mode->width, mode->height);
173 
174 		if (what == RESINC_Size)
175 		{
176 			if (diff * dir <= 0)
177 				continue;
178 		}
179 
180 		diff = ABS(diff);
181 
182 		if (diff < best_diff)
183 		{
184 			best_diff = diff;
185 			best = cur;
186 
187 			if (diff == 0)
188 				break;
189 		}
190 	}
191 
192 	if (best)
193 	{
194 		mode->width  = best->width;
195 		mode->height = best->height;
196 		mode->depth  = best->depth;
197 		mode->full   = best->full;
198 
199 		return true;
200 	}
201 
202 	return false;
203 }
204 
205 
206 //----------------------------------------------------------------------------
207 
208 
R_SoftInitResolution(void)209 void R_SoftInitResolution(void)
210 {
211 	L_WriteDebug("R_SoftInitResolution...\n");
212 
213 	RGL_NewScreenSize(SCREENWIDTH, SCREENHEIGHT, SCREENBITS);
214 
215 	// -ES- 1999/08/29 Fixes the garbage palettes, and the blank 16-bit console
216 	V_SetPalette(PALETTE_NORMAL, 0);
217 	V_ColourNewFrame();
218 
219 	// re-initialise various bits of GL state
220 	RGL_SoftInit();
221 	RGL_SoftInitUnits();	// -ACB- 2004/02/15 Needed to sort vars lost in res change
222 
223 	L_WriteDebug("-  returning true.\n");
224 
225 	return ;
226 }
227 
228 
DoExecuteChangeResolution(scrmode_c * mode)229 static bool DoExecuteChangeResolution(scrmode_c *mode)
230 {
231 	RGL_StopWipe();  // delete any wipe texture too
232 
233 	W_DeleteAllImages();
234 
235 	bool was_ok = I_SetScreenSize(mode);
236 
237 	if (! was_ok)
238 		return false;
239 
240 	SCREENWIDTH  = mode->width;
241 	SCREENHEIGHT = mode->height;
242 	SCREENBITS   = mode->depth;
243 	FULLSCREEN   = mode->full;
244 
245 	// gfx card doesn't like to switch too rapidly
246 	I_Sleep(250);
247 	I_Sleep(250);
248 
249 	return true;
250 }
251 
252 
253 struct Compare_Res_pred
254 {
operator ()Compare_Res_pred255 	inline bool operator() (const scrmode_c * A, const scrmode_c * B) const
256 	{
257 		if (A->full != B->full)
258 		{
259 			return FULLSCREEN ? (A->full > B->full) : (A->full < B->full);
260 		}
261 
262 		if (! R_DepthIsEquivalent(A->depth, B->depth))
263 		{
264 			int a_equiv = (A->depth < 20) ? 16 : 32;
265 			int b_equiv = (B->depth < 20) ? 16 : 32;
266 
267 			return R_DepthIsEquivalent(SCREENBITS, 16) ?
268 				(a_equiv < b_equiv) : (a_equiv > b_equiv);
269 		}
270 
271 		if (A->width != B->width)
272 		{
273 			int a_diff_w = ABS(SCREENWIDTH - A->width);
274 			int b_diff_w = ABS(SCREENWIDTH - B->width);
275 
276 			return (a_diff_w < b_diff_w);
277 		}
278 		else
279 		{
280 			int a_diff_h = ABS(SCREENHEIGHT - A->height);
281 			int b_diff_h = ABS(SCREENHEIGHT - B->height);
282 
283 			return (a_diff_h < b_diff_h);
284 		}
285 	}
286 };
287 
288 
R_InitialResolution(void)289 void R_InitialResolution(void)
290 {
291 	L_WriteDebug("R_InitialResolution...\n");
292 
293 	scrmode_c mode;
294 
295 	mode.width  = SCREENWIDTH;
296 	mode.height = SCREENHEIGHT;
297 	mode.depth  = SCREENBITS;
298 	mode.full   = FULLSCREEN;
299 
300     if (DoExecuteChangeResolution(&mode))
301 	{
302 		// this mode worked, make sure it's in the list
303 		R_AddResolution(&mode);
304 		return;
305 	}
306 
307 	L_WriteDebug("- Looking for another mode to try...\n");
308 
309 	// sort modes into a good order, choosing sizes near the
310 	// request size first, and different depths/fullness last.
311 
312 	std::sort(screen_modes.begin(), screen_modes.end(),
313 			  Compare_Res_pred());
314 
315 	for (int i = 0; i < (int)screen_modes.size(); i++)
316 	{
317 		if (DoExecuteChangeResolution(screen_modes[i]))
318 			return;
319 	}
320 
321     // FOOBAR!
322 	I_Error("Unable to set any resolutions!");
323 }
324 
325 
R_ChangeResolution(scrmode_c * mode)326 bool R_ChangeResolution(scrmode_c *mode)
327 {
328 	L_WriteDebug("R_ChangeResolution...\n");
329 
330     if (DoExecuteChangeResolution(mode))
331 		return true;
332 
333 	L_WriteDebug("- Failed : switching back...\n");
334 
335 	scrmode_c old_mode;
336 
337 	old_mode.width  = SCREENWIDTH;
338 	old_mode.height = SCREENHEIGHT;
339 	old_mode.depth  = SCREENBITS;
340 	old_mode.full   = FULLSCREEN;
341 
342     if (DoExecuteChangeResolution(&old_mode))
343 		return false;
344 
345 	// This ain't good - current and previous resolutions do not work.
346 	I_Error("Switch back to old resolution failed!\n");
347 	return false; /* NOT REACHED */
348 }
349 
350 
351 //--- editor settings ---
352 // vi:ts=4:sw=4:noexpandtab
353