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