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