source: Daodan/src/Daodan_GL.c@ 866

Last change on this file since 866 was 838, checked in by alloc, 12 years ago

Daodan:

  • Fixes #40
  • Moved configuration parsing (ini, command line) into its own file
File size: 11.6 KB
Line 
1#include <windows.h>
2#include <math.h>
3
4#include "Oni.h"
5#include "Daodan_Config.h"
6#include "Daodan_Utility.h"
7#include <GL/gl.h>
8#include <GL/glu.h>
9#include "Daodan_Win32.h"
10
11#include "Daodan_GL.h"
12#include "Oni_GL.h"
13
14static const M3tDisplayMode daodan_reslist[] =
15{
16 { 640, 480, 0, 0 },
17 { 720, 480, 0, 0 },
18 { 720, 576, 0, 0 },
19 { 768, 480, 0, 0 },
20 { 800, 480, 0, 0 },
21 { 800, 600, 0, 0 },
22 { 852, 480, 0, 0 },
23 { 856, 480, 0, 0 },
24 { 960, 540, 0, 0 },
25 { 960, 720, 0, 0 },
26 { 1024, 576, 0, 0 },
27 { 1024, 600, 0, 0 },
28 { 1024, 640, 0, 0 },
29 { 1024, 768, 0, 0 },
30 { 1152, 768, 0, 0 },
31 { 1152, 864, 0, 0 },
32 { 1280, 720, 0, 0 },
33 { 1280, 768, 0, 0 },
34 { 1280, 800, 0, 0 },
35 { 1280, 960, 0, 0 },
36 { 1280, 1024, 0, 0 },
37 { 1366, 768, 0, 0 },
38 { 1400, 1050, 0, 0 },
39 { 1440, 900, 0, 0 },
40 { 1600, 900, 0, 0 },
41 { 1600, 1200, 0, 0 },
42 { 1920, 1080, 0, 0 },
43 { 1920, 1200, 0, 0 },
44 { 1920, 1440, 0, 0 },
45};
46
47static DWORD window_style, window_exstyle;
48
49// HACK: use additional device entries to store display modes. It would give us
50// 67 mode slots total (far more than enough). I absolutely have no idea where
51// Rossy got his 104 (it would take up to 0x660 bytes while the whole GL state
52// is only 0x63c bytes). Maybe it was just octal (67 + 1).
53// This hack would break (crash!) "m3_display_list" script command.
54#define DD_MAX_MODES ((offsetof(M3tDrawEngineCaps,__unknown) - \
55 offsetof(M3tDrawEngineCaps,DisplayDevices) - \
56 offsetof(M3tDisplayDevice,Modes)) / sizeof(M3tDisplayMode))
57
58// Former daodan_resdepths.
59#define DD_MIN_DEPTH 16
60
61unsigned short ONICALL DD_GLrEnumerateDisplayModes(M3tDisplayMode* modes)
62{
63 unsigned int vmodes = 0;
64 unsigned int screen_x = GetSystemMetrics(SM_CXSCREEN);
65 unsigned int screen_y = GetSystemMetrics(SM_CYSCREEN);
66
67 unsigned int i;
68 signed int j;
69
70 DDrStartupMessage("Daodan: Listing display modes");
71
72 memset(modes, 0, sizeof(M3tDisplayMode) * DD_MAX_MODES);
73
74 if (M3gResolutionSwitch)
75 {
76 // Enumerate in -switch mode. "67 slots ought to be enough for anybody".
77
78 DEVMODE dm;
79
80 dm.dmSize = sizeof(dm);
81 dm.dmDriverExtra = 0;
82
83 for (i = 0; EnumDisplaySettings(NULL, i, &dm); ++i)
84 {
85 if (dm.dmBitsPerPel < DD_MIN_DEPTH || dm.dmPelsWidth < 640 || dm.dmPelsHeight < 480)
86 continue;
87
88 // Already exists? Search backwards as modes are sorted most of the times
89 for (j = vmodes - 1; j >= 0; --j)
90 if (modes[j].Width == dm.dmPelsWidth && modes[j].Height == dm.dmPelsHeight &&
91 modes[j].Depth == dm.dmBitsPerPel)
92 break;
93
94 if (j >= 0)
95 continue; // We've found a match.
96
97 modes[vmodes].Width = dm.dmPelsWidth;
98 modes[vmodes].Height = dm.dmPelsHeight;
99 modes[vmodes].Depth = dm.dmBitsPerPel;
100
101 if (++vmodes >= DD_MAX_MODES)
102 break;
103 }
104 }
105 else
106 {
107 // In -noswitch we put predefined window sizes which don't overlap
108 // deskbar(s) plus one "native" fullscreen mode.
109
110 unsigned int workarea_width, workarea_height, frame_width, frame_height;
111 DWORD style, exstyle;
112 RECT rc;
113
114 if (SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0))
115 {
116 workarea_width = rc.right - rc.left;
117 workarea_height = rc.bottom - rc.top;
118 }
119 else
120 {
121 workarea_width = screen_x;
122 workarea_height = screen_y;
123 }
124
125 style = (DWORD) GetWindowLongPtr(ONgPlatformData.Window, GWL_STYLE);
126 exstyle = (DWORD) GetWindowLongPtr(ONgPlatformData.Window, GWL_EXSTYLE);
127
128 // Calculate additional width and height for window borders. Don't
129 // bother with system metrics. Let Windows calculate this.
130 rc.left = rc.top = 0;
131 rc.right = rc.bottom = 300;
132
133 if (AdjustWindowRectEx(&rc, style, FALSE, exstyle))
134 {
135 frame_width = rc.right - rc.left - 300;
136 frame_height = rc.bottom - rc.top - 300;
137 }
138 else
139 {
140 frame_width = 0;
141 frame_height = 0;
142 }
143
144 for (i = 0; i < sizeof(daodan_reslist) / sizeof(daodan_reslist[0]); ++i)
145 {
146 // Don't check the mode which has the same rect as screen. We would
147 // add it later as a special case.
148 if (daodan_reslist[i].Width == screen_x && daodan_reslist[i].Height == screen_y)
149 continue;
150
151 if (daodan_reslist[i].Width + frame_width <= workarea_width &&
152 daodan_reslist[i].Height + frame_height <= workarea_height)
153 {
154 modes[vmodes] = daodan_reslist[i];
155 modes[vmodes].Depth = GLgInitialMode.dmBitsPerPel;
156
157 if (++vmodes >= DD_MAX_MODES)
158 {
159 --vmodes; // Remove the last mode to make room for "fullscreen" mode.
160 break;
161 }
162 }
163 }
164
165 modes[vmodes].Width = GLgInitialMode.dmPelsWidth;
166 modes[vmodes].Height = GLgInitialMode.dmPelsHeight;
167 modes[vmodes].Depth = GLgInitialMode.dmBitsPerPel;
168 ++vmodes;
169 }
170
171 DDrStartupMessage("Daodan: %u modes available:", vmodes);
172 for (i = 0; i < vmodes; ++i)
173 DDrStartupMessage("Daodan: %ux%ux%u", modes[i].Width, modes[i].Height, modes[i].Depth);
174
175 return vmodes;
176}
177
178// Sets a new display mode (if it is somehow different from a current mode).
179// NOTE: signature for this function was changed to simplify code.
180UUtBool DD_GLrPlatform_SetDisplayMode(M3tDisplayMode* mode)
181{
182 if (mode->Height < 480)
183 return UUcFalse;
184
185 if (M3gResolutionSwitch)
186 {
187 DEVMODE new_mode, cur_mode;
188
189 cur_mode.dmSize = sizeof(cur_mode);
190 cur_mode.dmDriverExtra = 0;
191
192 // We don't need this check. Windows does this too (see CDS_RESET).
193 if (!EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &cur_mode) ||
194 cur_mode.dmPelsWidth != mode->Width || cur_mode.dmPelsHeight != mode->Height ||
195 cur_mode.dmBitsPerPel != mode->Depth)
196 {
197 new_mode.dmSize = sizeof(new_mode);
198 new_mode.dmFields = DM_BITSPERPEL | DM_PELSHEIGHT | DM_PELSWIDTH;
199 new_mode.dmPelsWidth = mode->Width;
200 new_mode.dmPelsHeight = mode->Height;
201 new_mode.dmBitsPerPel = mode->Depth;
202
203 if (ChangeDisplaySettings(&new_mode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
204 return UUcFalse;
205 }
206
207 // We didn't change window size in DD_GLrPlatform_Initialize so we need
208 // to change it here.
209 SetWindowPos(ONgPlatformData.Window, NULL, 0, 0, mode->Width, mode->Height,
210 SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOZORDER);
211
212 return UUcTrue;
213 }
214 else
215 {
216 unsigned screen_x, screen_y;
217 DWORD style, exstyle, new_style, new_exstyle;
218 DWORD flags;
219 RECT rc, workarea_rc;
220 POINT pt;
221
222 screen_x = GetSystemMetrics(SM_CXSCREEN);
223 screen_y = GetSystemMetrics(SM_CYSCREEN);
224
225 GetClientRect(ONgPlatformData.Window, &rc);
226
227 // Don't do anything if the mode was not changed.
228 if (rc.right == mode->Width && rc.bottom == mode->Height)
229 return UUcTrue;
230
231 style = (DWORD) GetWindowLongPtr(ONgPlatformData.Window, GWL_STYLE);
232 exstyle = (DWORD) GetWindowLongPtr(ONgPlatformData.Window, GWL_EXSTYLE);
233 flags = SWP_NOACTIVATE | SWP_NOZORDER;
234
235 // Remember initial window style to correctly restore from fullscreen.
236 if (window_style == 0)
237 {
238 window_style = style;
239 window_exstyle = exstyle;
240 }
241
242 if (mode->Width == screen_x && mode->Height == screen_y)
243 {
244 // "Fullscreen" mode.
245 new_exstyle = exstyle & ~(WS_EX_CLIENTEDGE | WS_EX_DLGMODALFRAME | WS_EX_STATICEDGE | WS_EX_WINDOWEDGE);
246 new_style = style & ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME | WS_DLGFRAME);
247 new_style = new_style | WS_POPUP;
248 rc.left = 0;
249 rc.top = 0;
250 rc.right = mode->Width;
251 rc.bottom = mode->Height;
252 }
253 else
254 {
255 if (opt_border)
256 {
257 pt.x = rc.left;
258 pt.y = rc.top;
259 ClientToScreen(ONgPlatformData.Window, &pt);
260 }
261 else
262 {
263 pt.x = screen_x / 2 - mode->Width / 2;
264 pt.y = screen_y / 2 - mode->Height / 2;
265 }
266
267 new_exstyle = window_exstyle;
268 new_style = window_style;
269 rc.left = pt.x;
270 rc.top = pt.y;
271 rc.right = rc.left + mode->Width;
272 rc.bottom = rc.top + mode->Height;
273
274 AdjustWindowRectEx(&rc, new_style, FALSE, new_exstyle);
275
276 // Convert to width and height.
277 rc.right -= rc.left;
278 rc.bottom -= rc.top;
279
280 if (SystemParametersInfo(SPI_GETWORKAREA, 0, &workarea_rc, 0))
281 {
282 // We try to keep window position, but we should prevent window
283 // from going off screen.
284
285 if (rc.left + rc.right > workarea_rc.right)
286 rc.left = workarea_rc.right - rc.right;
287 if (rc.top + rc.bottom > workarea_rc.bottom)
288 rc.top = workarea_rc.bottom - rc.bottom;
289
290 // Titlebar should always be visible.
291
292 if (rc.left < workarea_rc.left)
293 rc.left = workarea_rc.left;
294 if (rc.top < workarea_rc.top)
295 rc.top = workarea_rc.top;
296 }
297 }
298
299 if (new_style != style)
300 {
301 SetWindowLongPtr(ONgPlatformData.Window, GWL_STYLE, (LONG_PTR) new_style);
302 flags |= SWP_FRAMECHANGED | SWP_DRAWFRAME;
303 }
304
305 if (new_exstyle != exstyle)
306 {
307 SetWindowLongPtr(ONgPlatformData.Window, GWL_EXSTYLE, (LONG_PTR) new_exstyle);
308 flags |= SWP_FRAMECHANGED | SWP_DRAWFRAME;
309 }
310
311 SetWindowPos(ONgPlatformData.Window, NULL, rc.left, rc.top, rc.right, rc.bottom, flags);
312 return UUcTrue;
313 }
314}
315
316static void ONICALL DD_GLiGamma_Restore(void)
317{
318 if (opt_gamma)
319 {
320 if (gl_api->wglSetDeviceGammaRamp3DFX)
321 gl_api->wglSetDeviceGammaRamp3DFX(gl->hDC, GLgInitialGammaRamp);
322 else
323 SetDeviceGammaRamp(gl->hDC, GLgInitialGammaRamp);
324 }
325}
326
327static void ONICALL DD_GLiGamma_Initialize(void)
328{
329 if (opt_gamma)
330 {
331 if (gl_api->wglSetDeviceGammaRamp3DFX)
332 {
333 UUrStartupMessage("Daodan: Using 3dfx gamma adjustment");
334 GLgGammaRampValid = gl_api->wglGetDeviceGammaRamp3DFX(gl->hDC, GLgInitialGammaRamp);
335 }
336 else
337 {
338 UUrStartupMessage("Daodan: Using Windows gamma adjustment");
339 GLgGammaRampValid = GetDeviceGammaRamp(gl->hDC, GLgInitialGammaRamp);
340 }
341
342 M3rSetGamma(ONrPersist_GetGamma());
343 }
344 else
345 {
346 GLgGammaRampValid = FALSE;
347 }
348}
349
350// Disposes OpenGL engine. Called once.
351void ONICALL DD_GLrPlatform_Dispose(void)
352{
353 DEVMODE dm;
354
355 DD_GLiGamma_Restore();
356
357 gl_api->wglMakeCurrent(NULL, NULL);
358 gl_api->wglDeleteContext(gl->hGLRC);
359 ReleaseDC(ONgPlatformData.Window, gl->hDC);
360
361 // Restore initial display mode if it does not match current mode.
362
363 dm.dmSize = sizeof(dm);
364 dm.dmDriverExtra = 0;
365
366 if (!EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm) ||
367 dm.dmPelsWidth != GLgInitialMode.dmPelsWidth ||
368 dm.dmPelsHeight != GLgInitialMode.dmPelsHeight ||
369 dm.dmBitsPerPel != GLgInitialMode.dmBitsPerPel)
370 {
371 ChangeDisplaySettings(&GLgInitialMode, 0);
372 }
373
374 // (skipping SetWindowPos as it only adds flickering)
375 gl_unload_library();
376}
377
378// Initializes (and re-initializes) OpenGL.
379UUtBool ONICALL DD_GLrPlatform_Initialize(void)
380{
381 static const M3tDisplayMode FallbackMode = { 640, 480, 16, 0 };
382
383 if (!DD_GLrPlatform_SetDisplayMode(&gl->DisplayMode))
384 {
385 gl->DisplayMode = FallbackMode;
386
387 if (!DD_GLrPlatform_SetDisplayMode(&gl->DisplayMode))
388 {
389 goto exit_err;
390 }
391 }
392
393 // (DD_GLrPlatform_SetDisplayMode updates a window rectangle for us)
394
395 if (!gl->hDC && !(gl->hDC = GetDC(ONgPlatformData.Window)))
396 {
397 goto exit_err;
398 }
399
400 if (!M3gResolutionSwitch && opt_gamma)
401 {
402 UUrStartupMessage("Daodan: Ignoring gamma setting due to windowed mode");
403 opt_gamma = false;
404 }
405
406 DD_GLiGamma_Initialize();
407
408 // This creates a rendering context too.
409 if (!gl_platform_set_pixel_format(gl->hDC))
410 {
411 if (gl->DisplayMode.Depth != 16)
412 {
413 gl->DisplayMode.Depth = 16;
414 if (!DD_GLrPlatform_SetDisplayMode(&gl->DisplayMode))
415 goto exit_err;
416
417 if (!gl_platform_set_pixel_format(gl->hDC))
418 goto exit_err;
419 }
420 }
421
422 return UUcTrue;
423
424exit_err:
425 AUrMessageBox(1, "Daodan: Failed to initialize OpenGL contexts; Oni will now exit.");
426 exit(0);
427 return 0;
428}
Note: See TracBrowser for help on using the repository browser.