From bbd5dd572de70baebf6466488606fe4b5e9baab9 Mon Sep 17 00:00:00 2001 From: Philippe Houdoin Date: Tue, 3 Aug 2010 17:34:37 +0000 Subject: [PATCH] Experimental GLUT Game Mode support. Still several issues with non-32bits color spaces. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37878 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/libs/mesa/glut/glutGameMode.cpp | 286 +++++++++++++++++++++++++--- src/libs/mesa/glut/glutGameMode.h | 41 +++- src/libs/mesa/glut/glutState.h | 3 +- 3 files changed, 295 insertions(+), 35 deletions(-) diff --git a/src/libs/mesa/glut/glutGameMode.cpp b/src/libs/mesa/glut/glutGameMode.cpp index 1554c39788..4822ed1c6e 100644 --- a/src/libs/mesa/glut/glutGameMode.cpp +++ b/src/libs/mesa/glut/glutGameMode.cpp @@ -14,72 +14,300 @@ #include -void APIENTRY -glutGameModeString(const char *string) +// GlutGameMode class + +GlutGameMode::GlutGameMode() + : + fActive(false), + fDisplayChanged(false), + fWidth(-1), + fHeight(-1), + fPixelDepth(-1), + fRefreshRate(-1), + fModesList(NULL), + fModesCount(0), + fGameModeWorkspace(-1) { - // TODO +} + + +GlutGameMode::~GlutGameMode() +{ + free(fModesList); +} + + +status_t +GlutGameMode::Set(const char* modeString) +{ + // String format: [WxH][:Bpp][@Hz] + + const char* templates[] = { + "%1$ix%2$i:%3$i@%4$i", // WxH:Bpp@Hz + "%1$ix%2$i:%3$i", // WxH:Bpp + "%1$ix%2$i@%4$i", // WxH@Hz + "%1$ix%2$i", // WxH + ":%3$i@%4$i", // :Bpp@Hz + ":%3$i", // :Bpp + "@%4$i", // @Hz + NULL + }; + + // Find matching string format template, if any + for (int i=0; templates[i]; i++) { + // count expected arguments in this template + int expectedArgs = 0; + const char *p = templates[i]; + while (*p) { + if (*p++ == '%') + expectedArgs++; + } + + // printf("Trying %s pattern\n", templates[i]); + + fWidth = fHeight = fPixelDepth = fRefreshRate = -1; + + if (sscanf(modeString, templates[i], + &fWidth, &fHeight, &fPixelDepth, &fRefreshRate) == expectedArgs) { + // printf("match!\n"); + return B_OK; + } + } + + return B_BAD_VALUE; +} + + +bool +GlutGameMode::IsPossible() +{ + display_mode* mode = _FindMatchingMode(); + return mode != NULL; +} + + +status_t +GlutGameMode::Enter() +{ + display_mode* mode = _FindMatchingMode(); + if (!mode) + return B_BAD_VALUE; + + BScreen screen; + if (!fActive) { + // First enter: remember this workspace original mode... + fGameModeWorkspace = current_workspace(); + screen.GetMode(fGameModeWorkspace, &fOriginalMode); + } + + // Don't make it new default mode for this workspace... + status_t status = screen.SetMode(fGameModeWorkspace, mode, false); + if (status != B_OK) + return status; + + // Retrieve the new active display mode, which could be + // a sligth different than the one we asked for... + screen.GetMode(fGameModeWorkspace, &fCurrentMode); + + // create a new window in full screen + glutCreateWindow("glutGameMode"); + BDirectWindow *directWindow + = dynamic_cast(gState.currentWindow->Window()); + if (directWindow == NULL) + // Hum?! + return B_ERROR; + + directWindow->SetFullScreen(true); + + fDisplayChanged = true; + fActive = true; + + return B_OK; +} + + +status_t +GlutGameMode::Leave() +{ + if (!fActive) + return B_OK; + + if (fGameModeWorkspace < 0) + return B_ERROR; + + if (_CompareModes(&fOriginalMode, &fCurrentMode)) { + // Restore original display mode + BScreen screen; + // Make restored mode the default one, + // as it was before entering game mode... + screen.SetMode(fGameModeWorkspace, &fOriginalMode, true); + } + + fActive = false; + return B_OK; +} + + +display_mode* +GlutGameMode::_FindMatchingMode() +{ + if (fWidth == -1 && fHeight == -1 && fPixelDepth == -1 + && fRefreshRate == -1) + // nothing to match! + return NULL; + + if (!fModesList) { + // Lazy retrieval of supported modes... + BScreen screen; + if (screen.GetModeList(&fModesList, &fModesCount) == B_OK) { + // sort modes + qsort(fModesList, fModesCount, sizeof(display_mode), _CompareModes); + } else { + // bad luck, no modes can be retrieved! + fModesList = NULL; + fModesCount = 0; + } + } + + if (!fModesList) + return NULL; + + float bestRefreshDiff = 999999; + int bestMode = -1; + for (uint32 i =0; i < fModesCount; i++) { + + printf("[%ld]: %d x %d, %d Bpp, %d Hz\n", i, + fModesList[i].virtual_width, fModesList[i].virtual_height, + _GetModePixelDepth(&fModesList[i]), + _GetModeRefreshRate(&fModesList[i])); + + if (fWidth > 0 && fModesList[i].virtual_width != fWidth) + continue; + + if (fHeight > 0 && fModesList[i].virtual_height != fHeight) + continue; + + if (fPixelDepth > 0 + && _GetModePixelDepth(&fModesList[i]) != fPixelDepth) + continue; + + float refreshDiff = fabs(_GetModeRefreshRate(&fModesList[i]) + - fRefreshRate); + if (fRefreshRate > 0 && (refreshDiff > 0.006 * fRefreshRate)) { + // not exactly the same, but maybe the best similar mode so far? + if (refreshDiff < bestRefreshDiff) { + bestRefreshDiff = refreshDiff; + bestMode = i; + } + continue; + } + + // Hey, this one match everything! + return &fModesList[i]; + } + + if (bestMode == -1) + return NULL; + + return &fModesList[bestMode]; +} + + +int +GlutGameMode::_GetModePixelDepth(const display_mode* mode) +{ + switch (mode->space) { + case B_RGB32: return 32; + case B_RGB24: return 24; + case B_RGB16: return 16; + case B_RGB15: return 15; + case B_CMAP8: return 8; + default: return 0; + } +} + + +int +GlutGameMode::_GetModeRefreshRate(const display_mode* mode) +{ + // we have to be catious as refresh rate cannot be controlled directly, + // so it suffers under rounding errors and hardware restrictions + return rint(10 * float(mode->timing.pixel_clock * 1000) + / float(mode->timing.h_total * mode->timing.v_total)) / 10.0; +} + + +int +GlutGameMode::_CompareModes(const void* _mode1, const void* _mode2) +{ + display_mode *mode1 = (display_mode *)_mode1; + display_mode *mode2 = (display_mode *)_mode2; + + if (mode1->virtual_width != mode2->virtual_width) + return mode1->virtual_width - mode2->virtual_width; + + if (mode1->virtual_height != mode2->virtual_height) + return mode1->virtual_height - mode2->virtual_height; + + if (mode1->space != mode2->space) + return _GetModePixelDepth(mode1) - _GetModePixelDepth(mode2); + + return _GetModeRefreshRate(mode1) - _GetModeRefreshRate(mode2); +} + + +// #pragma mark GLUT game mode API + +void APIENTRY +glutGameModeString(const char* string) +{ + gState.gameMode.Set(string); } int APIENTRY glutEnterGameMode(void) { - // TODO - return 0; + return gState.gameMode.Enter() == B_OK; } void APIENTRY glutLeaveGameMode(void) { - // TODO + gState.gameMode.Leave(); } int APIENTRY glutGameModeGet(GLenum pname) { - int ret = -1; - switch( pname ) { case GLUT_GAME_MODE_ACTIVE: - ret = gState.gameMode != NULL; - break; + return gState.gameMode.IsActive(); case GLUT_GAME_MODE_POSSIBLE: - // TODO - break; + return gState.gameMode.IsPossible(); case GLUT_GAME_MODE_WIDTH: - if (gState.gameMode) - ret = gState.gameMode->width; - break; + return gState.gameMode.Width(); case GLUT_GAME_MODE_HEIGHT: - if (gState.gameMode) - ret = gState.gameMode->height; - break; + return gState.gameMode.Height(); case GLUT_GAME_MODE_PIXEL_DEPTH: - if (gState.gameMode) - ret = gState.gameMode->pixelDepth; - break; + return gState.gameMode.PixelDepth(); case GLUT_GAME_MODE_REFRESH_RATE: - if (gState.gameMode) - ret = gState.gameMode->refreshRate; - break; + return gState.gameMode.RefreshRate(); case GLUT_GAME_MODE_DISPLAY_CHANGED: - // TODO - break; + return gState.gameMode.HasDisplayChanged(); default: __glutWarning( "Unknown gamemode get: %d", pname ); - break; + return -1; } - - return ret; } diff --git a/src/libs/mesa/glut/glutGameMode.h b/src/libs/mesa/glut/glutGameMode.h index 86a7884c82..196654ff09 100644 --- a/src/libs/mesa/glut/glutGameMode.h +++ b/src/libs/mesa/glut/glutGameMode.h @@ -7,6 +7,7 @@ */ #include +#include /*! All information needed for game mode and @@ -14,8 +15,40 @@ */ class GlutGameMode { public: - int width; - int height; - int pixelDepth; - int refreshRate; + GlutGameMode(); + virtual ~GlutGameMode(); + + status_t Set(const char* modeString); + + status_t Enter(); + status_t Leave(); + + bool IsActive() const { return fActive; } + bool IsPossible(); + bool HasDisplayChanged() const { return fDisplayChanged; } + + int Width() const { return fWidth; } + int Height() const { return fHeight; } + int PixelDepth() const { return fPixelDepth; } + int RefreshRate() const { return fRefreshRate; } + +private: + display_mode* _FindMatchingMode(); + + static int _CompareModes(const void* mode1, const void* mode2); + static int _GetModeRefreshRate(const display_mode* mode); + static int _GetModePixelDepth(const display_mode* mode); + + bool fActive; + bool fDisplayChanged; + int fWidth; + int fHeight; + int fPixelDepth; + int fRefreshRate; + display_mode* fModesList; + uint32 fModesCount; + display_mode fOriginalMode; + int fGameModeWorkspace; + display_mode fCurrentMode; + }; diff --git a/src/libs/mesa/glut/glutState.h b/src/libs/mesa/glut/glutState.h index 0c10f17f55..2de114f724 100644 --- a/src/libs/mesa/glut/glutState.h +++ b/src/libs/mesa/glut/glutState.h @@ -47,7 +47,7 @@ struct GlutState { int modifierKeys; // only valid during keyboard callback int keyRepeatMode; // global repeat - GlutGameMode* gameMode; + GlutGameMode gameMode; bool debug; // call glGetError bool quitAll; // quit @@ -67,7 +67,6 @@ struct GlutState { menuStatus = 0; modifierKeys = ~0; keyRepeatMode = GLUT_KEY_REPEAT_DEFAULT; - gameMode = NULL; debug = quitAll = false; } };