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 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 39 GlutGameMode::~GlutGameMode() 40 { 41 free(fModesList); 42 } 43 44 45 status_t 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 87 GlutGameMode::IsPossible() 88 { 89 display_mode* mode = _FindMatchingMode(); 90 return mode != NULL; 91 } 92 93 94 status_t 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 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* 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("[%ld]: %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 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 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 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 293 glutGameModeString(const char* string) 294 { 295 gState.gameMode.Set(string); 296 } 297 298 299 int APIENTRY 300 glutEnterGameMode(void) 301 { 302 return gState.gameMode.Enter() == B_OK; 303 } 304 305 306 void APIENTRY 307 glutLeaveGameMode(void) 308 { 309 gState.gameMode.Leave(); 310 } 311 312 313 int APIENTRY 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 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