1/*
2 * Copyright 2010, Haiku Inc.
3 * Authors:
4 *		Philippe Houdoin <phoudoin %at% haiku-os %dot% org>
5 *
6 * Distributed under the terms of the MIT License.
7 */
8
9
10#include "glutGameMode.h"
11#include "glutint.h"
12#include "glutState.h"
13
14#include <GL/glut.h>
15#include <String.h>
16#include <stdio.h>
17#include <stdlib.h>
18
19
20// GlutGameMode class
21
22GlutGameMode::GlutGameMode()
23	:
24	fActive(false),
25	fDisplayChanged(false),
26	fWidth(-1),
27	fHeight(-1),
28	fPixelDepth(-1),
29	fRefreshRate(-1),
30	fModesList(NULL),
31	fModesCount(0),
32	fGameModeWorkspace(-1),
33	fGameModeWindow(0),
34	fPreviousWindow(0)
35{
36}
37
38
39GlutGameMode::~GlutGameMode()
40{
41	free(fModesList);
42}
43
44
45status_t
46GlutGameMode::Set(const char* modeString)
47{
48    // String format: [WxH][:Bpp][@Hz]
49
50   	const char* templates[] = {
51    	"%1$ix%2$i:%3$i@%4$i",	// WxH:Bpp@Hz
52    	"%1$ix%2$i:%3$i",		// WxH:Bpp
53    	"%1$ix%2$i@%4$i",		// WxH@Hz
54    	"%1$ix%2$i",			// WxH
55    	":%3$i@%4$i",			// :Bpp@Hz
56    	":%3$i",				// :Bpp
57    	"@%4$i",				// @Hz
58    	NULL
59    };
60
61	// Find matching string format template, if any
62	for (int i=0; templates[i]; i++) {
63		// count expected arguments in this template
64		int expectedArgs = 0;
65		const char *p = templates[i];
66		while (*p) {
67			if (*p++ == '%')
68				expectedArgs++;
69		}
70
71		// printf("Trying %s pattern\n", templates[i]);
72
73		fWidth = fHeight = fPixelDepth = fRefreshRate = -1;
74
75		if (sscanf(modeString, templates[i],
76			&fWidth, &fHeight, &fPixelDepth, &fRefreshRate) == expectedArgs) {
77			// printf("match!\n");
78			return B_OK;
79		}
80	}
81
82	return B_BAD_VALUE;
83}
84
85
86bool
87GlutGameMode::IsPossible()
88{
89	display_mode* mode = _FindMatchingMode();
90	return mode != NULL;
91}
92
93
94status_t
95GlutGameMode::Enter()
96{
97	display_mode* mode = _FindMatchingMode();
98	if (!mode)
99		return B_BAD_VALUE;
100
101	BScreen screen;
102	if (!fActive) {
103		// First enter: remember this workspace original mode...
104		fGameModeWorkspace = current_workspace();
105		screen.GetMode(fGameModeWorkspace, &fOriginalMode);
106	}
107
108	// Don't make it new default mode for this workspace...
109	status_t status = screen.SetMode(fGameModeWorkspace, mode, false);
110	if (status != B_OK)
111		return status;
112
113	// Retrieve the new active display mode, which could be
114	// a sligth different than the one we asked for...
115	screen.GetMode(fGameModeWorkspace, &fCurrentMode);
116
117	if (!fGameModeWindow) {
118		// create a new window
119		fPreviousWindow = glutGetWindow();
120		fGameModeWindow = glutCreateWindow("glutGameMode");
121		if (!fGameModeWindow)
122			return Leave();
123	} else
124		// make sure it's the current window
125		glutSetWindow(fGameModeWindow);
126
127	BDirectWindow *directWindow
128		= dynamic_cast<BDirectWindow*>(gState.currentWindow->Window());
129	if (directWindow == NULL)
130		// Hum?!
131		return B_ERROR;
132
133	// Give it some useless title, except for debugging (thread name).
134	BString name;
135	name << "Game Mode " << fCurrentMode.virtual_width
136		<< "x" << fCurrentMode.virtual_height
137		<< ":" << _GetModePixelDepth(&fCurrentMode)
138		<< "@" << _GetModeRefreshRate(&fCurrentMode);
139
140	// force the game mode window to fullscreen
141	directWindow->Lock();
142	directWindow->SetTitle(name.String());
143	directWindow->SetFullScreen(true);
144	directWindow->Unlock();
145
146	fDisplayChanged = true;
147	fActive = true;
148
149	return B_OK;
150}
151
152
153status_t
154GlutGameMode::Leave()
155{
156	if (!fActive)
157		return B_OK;
158
159	if (fGameModeWorkspace < 0)
160		return B_ERROR;
161
162	if (fGameModeWindow) {
163		glutDestroyWindow(fGameModeWindow);
164		fGameModeWindow = 0;
165		if (fPreviousWindow)
166			glutSetWindow(fPreviousWindow);
167	}
168
169	if (_CompareModes(&fOriginalMode, &fCurrentMode)) {
170		// Restore original display mode
171		BScreen screen;
172		// Make restored mode the default one,
173		// as it was before entering game mode...
174		screen.SetMode(fGameModeWorkspace, &fOriginalMode, true);
175	}
176
177	fActive = false;
178	return B_OK;
179}
180
181
182display_mode*
183GlutGameMode::_FindMatchingMode()
184{
185	if (fWidth == -1 && fHeight == -1 && fPixelDepth == -1
186		&& fRefreshRate == -1)
187		// nothing to match!
188		return NULL;
189
190	if (!fModesList) {
191		// Lazy retrieval of supported modes...
192		BScreen screen;
193		if (screen.GetModeList(&fModesList, &fModesCount) == B_OK) {
194			// sort modes in decrease order (resolution, depth, frequency)
195			qsort(fModesList, fModesCount, sizeof(display_mode), _CompareModes);
196		} else {
197			// bad luck, no modes can be retrieved!
198			fModesList = NULL;
199			fModesCount = 0;
200		}
201	}
202
203	if (!fModesList)
204		return NULL;
205
206	float bestRefreshDiff = 999999;
207	int bestMode = -1;
208	for (uint32 i =0; i < fModesCount; i++) {
209
210		printf("[%" B_PRId32 "]: %d x %d, %d Bpp, %d Hz\n", i,
211			fModesList[i].virtual_width, fModesList[i].virtual_height,
212			_GetModePixelDepth(&fModesList[i]),
213			_GetModeRefreshRate(&fModesList[i]));
214
215		if (fWidth > 0 && fModesList[i].virtual_width != fWidth)
216			continue;
217
218		if (fHeight > 0 && fModesList[i].virtual_height != fHeight)
219			continue;
220
221		if (fPixelDepth > 0
222			&& _GetModePixelDepth(&fModesList[i]) != fPixelDepth)
223			continue;
224
225		float refreshDiff = fabs(_GetModeRefreshRate(&fModesList[i])
226			- fRefreshRate);
227		if (fRefreshRate > 0 && (refreshDiff > 0.006 * fRefreshRate)) {
228			// not exactly the same, but maybe the best similar mode so far?
229			if (refreshDiff < bestRefreshDiff) {
230				bestRefreshDiff = refreshDiff;
231				bestMode = i;
232			}
233			continue;
234		}
235
236		// Hey, this one match everything!
237		return &fModesList[i];
238	}
239
240	if (bestMode == -1)
241		return NULL;
242
243	return &fModesList[bestMode];
244}
245
246
247int
248GlutGameMode::_GetModePixelDepth(const display_mode* mode)
249{
250	switch (mode->space) {
251		case B_RGB32:	return 32;
252		case B_RGB24:	return 24;
253		case B_RGB16:	return 16;
254		case B_RGB15:	return 15;
255		case B_CMAP8:	return 8;
256		default:		return 0;
257	}
258}
259
260
261int
262GlutGameMode::_GetModeRefreshRate(const display_mode* mode)
263{
264	// we have to be catious as refresh rate cannot be controlled directly,
265	// so it suffers under rounding errors and hardware restrictions
266	return rint(10 * float(mode->timing.pixel_clock * 1000)
267		/ float(mode->timing.h_total * mode->timing.v_total)) / 10.0;
268}
269
270
271int
272GlutGameMode::_CompareModes(const void* _mode1, const void* _mode2)
273{
274	display_mode *mode1 = (display_mode *)_mode1;
275	display_mode *mode2 = (display_mode *)_mode2;
276
277	if (mode1->virtual_width != mode2->virtual_width)
278		return mode2->virtual_width - mode1->virtual_width;
279
280	if (mode1->virtual_height != mode2->virtual_height)
281		return mode2->virtual_height - mode1->virtual_height;
282
283	if (mode1->space != mode2->space)
284		return _GetModePixelDepth(mode2) - _GetModePixelDepth(mode1);
285
286	return _GetModeRefreshRate(mode2) - _GetModeRefreshRate(mode1);
287}
288
289
290// #pragma mark GLUT game mode API
291
292void APIENTRY
293glutGameModeString(const char* string)
294{
295	gState.gameMode.Set(string);
296}
297
298
299int APIENTRY
300glutEnterGameMode(void)
301{
302	return gState.gameMode.Enter() == B_OK;
303}
304
305
306void APIENTRY
307glutLeaveGameMode(void)
308{
309	gState.gameMode.Leave();
310}
311
312
313int APIENTRY
314glutGameModeGet(GLenum pname)
315{
316	switch( pname ) {
317    	case GLUT_GAME_MODE_ACTIVE:
318    		return gState.gameMode.IsActive();
319
320    	case GLUT_GAME_MODE_POSSIBLE:
321        	return gState.gameMode.IsPossible();
322
323    	case GLUT_GAME_MODE_WIDTH:
324			return gState.gameMode.Width();
325
326    	case GLUT_GAME_MODE_HEIGHT:
327    		return gState.gameMode.Height();
328
329	    case GLUT_GAME_MODE_PIXEL_DEPTH:
330			return gState.gameMode.PixelDepth();
331
332	    case GLUT_GAME_MODE_REFRESH_RATE:
333    		return gState.gameMode.RefreshRate();
334
335	    case GLUT_GAME_MODE_DISPLAY_CHANGED:
336    		return gState.gameMode.HasDisplayChanged();
337
338    	default:
339        	__glutWarning( "Unknown gamemode get: %d", pname );
340        	return -1;
341    }
342}
343
344
345void APIENTRY
346glutForceJoystickFunc(void)
347{
348	/*
349	Forces a joystick poll and callback.
350
351	Forces the OpenGLUT joystick code to poll your
352	joystick(s) and to call your joystick callbacks
353	with the result.  The operation completes, including
354	callbacks, before glutForceJoystickFunc() returns.
355
356    See also glutJoystickFunc()
357	*/
358
359	// TODO
360}
361