xref: /haiku/src/libs/glut/glutGameMode.cpp (revision 4a062f23c6736cf29b37c631aa615918a35f8c2e)
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 
GlutGameMode()22 GlutGameMode::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 
~GlutGameMode()39 GlutGameMode::~GlutGameMode()
40 {
41 	free(fModesList);
42 }
43 
44 
45 status_t
Set(const char * modeString)46 GlutGameMode::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 
86 bool
IsPossible()87 GlutGameMode::IsPossible()
88 {
89 	display_mode* mode = _FindMatchingMode();
90 	return mode != NULL;
91 }
92 
93 
94 status_t
Enter()95 GlutGameMode::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 
153 status_t
Leave()154 GlutGameMode::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 
182 display_mode*
_FindMatchingMode()183 GlutGameMode::_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 
247 int
_GetModePixelDepth(const display_mode * mode)248 GlutGameMode::_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 
261 int
_GetModeRefreshRate(const display_mode * mode)262 GlutGameMode::_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 
271 int
_CompareModes(const void * _mode1,const void * _mode2)272 GlutGameMode::_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 
292 void APIENTRY
glutGameModeString(const char * string)293 glutGameModeString(const char* string)
294 {
295 	gState.gameMode.Set(string);
296 }
297 
298 
299 int APIENTRY
glutEnterGameMode(void)300 glutEnterGameMode(void)
301 {
302 	return gState.gameMode.Enter() == B_OK;
303 }
304 
305 
306 void APIENTRY
glutLeaveGameMode(void)307 glutLeaveGameMode(void)
308 {
309 	gState.gameMode.Leave();
310 }
311 
312 
313 int APIENTRY
glutGameModeGet(GLenum pname)314 glutGameModeGet(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 
345 void APIENTRY
glutForceJoystickFunc(void)346 glutForceJoystickFunc(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