1524 lines
49 KiB
C++
1524 lines
49 KiB
C++
/***************************************************************************
|
|
screen.cpp -- screen init
|
|
-------------------
|
|
created : Fri Aug 13 22:29:56 CEST 1999
|
|
copyright : (C) 1999, 2004 by Eric Espie, Bernhard Wymann
|
|
email : torcs@free.fr
|
|
version : $Id$
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
/** @file
|
|
Screen management.
|
|
@author <a href=mailto:torcs@free.fr>Eric Espie</a>
|
|
@version $Id$
|
|
@ingroup screen
|
|
*/
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <cmath>
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
|
|
#ifdef WIN32
|
|
#include <windows.h>
|
|
#include <process.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <SDL.h>
|
|
#include <SDL_video.h>
|
|
|
|
#include <portability.h>
|
|
|
|
#include "tgfclient.h"
|
|
#include "gui.h"
|
|
|
|
#include "glfeatures.h"
|
|
|
|
// The resizable properties.
|
|
static bool GfScrResizable = false;
|
|
|
|
// The resizable functions.
|
|
bool GfScrInitSDL2();
|
|
bool GfScrGetResizable();
|
|
void GfScrSetFullscreen(bool bFullScreen = true);
|
|
void gfScrShutdown();
|
|
void gfScrBaseOpenGLSetup();
|
|
void gfScrOpenGlFeatures();
|
|
bool GfScrCreateMenuWindow();
|
|
bool GfScrValidateWindowPosition(int X, int Y);
|
|
void GfScrInitialWindowedPosition();
|
|
void gfScrSaveWindowState();
|
|
bool gfScrAAOpenGLSetup();
|
|
void gfScrDisableResizable();
|
|
bool GfscrAllowMultiFullScreens();
|
|
SDL_Rect GetMultiFullScreenBounds();
|
|
|
|
|
|
// The screen properties.
|
|
static int GfScrWidth;
|
|
static int GfScrHeight;
|
|
static int GfViewWidth;
|
|
static int GfViewHeight;
|
|
static int GfScrCenX;
|
|
static int GfScrCenY;
|
|
static SDL_GLContext GLContext = NULL;
|
|
|
|
static int GfScrNumDisplays = 0;
|
|
static int GfScrStartDisplayId = 0;
|
|
|
|
// The screen surface.
|
|
static SDL_Surface *PScreenSurface = NULL;
|
|
|
|
/* Default list of screen color depths (bits per pixel, alpha included) in case
|
|
something went wrong during hardware / driver capabilities detection */
|
|
static int ADefScreenColorDepths[] = { 16, 24, 32 };
|
|
static const int NDefScreenColorDepths =
|
|
sizeof(ADefScreenColorDepths) / sizeof(ADefScreenColorDepths[0]);
|
|
|
|
/* Default list of screen sizes ("resolutions") in case
|
|
something went wrong during hardware / driver capabilities detection */
|
|
static tScreenSize ADefScreenSizes[] =
|
|
{
|
|
{ 320, 200 },
|
|
{ 320, 240 },
|
|
{ 400, 300 },
|
|
{ 416, 312 },
|
|
{ 480, 360 },
|
|
{ 512, 384 },
|
|
{ 576, 384 },
|
|
{ 576, 432 },
|
|
{ 640, 384 },
|
|
{ 640, 400 },
|
|
{ 640, 480 },
|
|
{ 640, 512 },
|
|
{ 700, 525 },
|
|
{ 720, 450 },
|
|
{ 800, 512 },
|
|
{ 800, 600 },
|
|
{ 832, 624 },
|
|
{ 840, 525 },
|
|
{ 896, 672 },
|
|
{ 928, 696 },
|
|
{ 960, 600 },
|
|
{ 960, 720 },
|
|
{ 1024, 600 },
|
|
{ 1024, 768 },
|
|
{ 1152, 768 },
|
|
{ 1152, 864 },
|
|
{ 1280, 600 },
|
|
{ 1280, 720 },
|
|
{ 1280, 768 },
|
|
{ 1280, 800 },
|
|
{ 1280, 960 },
|
|
{ 1280, 1024 },
|
|
{ 1366, 768 },
|
|
{ 1400, 1050 },
|
|
{ 1440, 900 },
|
|
{ 1600, 900 },
|
|
{ 1600, 1024 },
|
|
{ 1680, 1050 },
|
|
{ 1792, 1344 },
|
|
{ 1800, 1440 },
|
|
{ 1920, 1080 },
|
|
{ 1920, 1200 },
|
|
{ 2560, 1080 },
|
|
{ 2560, 1440 },
|
|
{ 3840, 2160 }
|
|
};
|
|
static const int NDefScreenSizes =
|
|
sizeof(ADefScreenSizes) / sizeof(ADefScreenSizes[0]);
|
|
|
|
/** Get a list of (common?) window sizes (pixels)
|
|
@ingroup screen
|
|
@return ScreenSizeVector of sizes
|
|
*/
|
|
ScreenSizeVector GfScrGetWindowSizes()
|
|
{
|
|
ScreenSizeVector vecSizes;
|
|
for(int i = 0; i < NDefScreenSizes; i++)
|
|
{
|
|
vecSizes.push_back(ADefScreenSizes[i]);
|
|
}
|
|
ScreenSizeVector custSizes = GfScrGetCustomWindowSizes();
|
|
|
|
for (unsigned i = 0; i < custSizes.size(); i++)
|
|
{
|
|
vecSizes.push_back(custSizes[i]);
|
|
}
|
|
return vecSizes;
|
|
}
|
|
|
|
/** Get any custom screen / window sizes (pixels) from screen.xml
|
|
@ingroup screen
|
|
@param none
|
|
@return ScreenSizeVector of custom sizes
|
|
*/
|
|
ScreenSizeVector GfScrGetCustomWindowSizes()
|
|
{
|
|
ScreenSizeVector vecSizes;
|
|
|
|
void* hparmScreen =
|
|
GfParmReadFileLocal(GFSCR_CONF_FILE, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
|
|
|
|
if (GfParmExistsSection(hparmScreen, GFSCR_SECT_WIN_MODES))
|
|
{
|
|
tScreenSize last;
|
|
last.width = 0;
|
|
last.height = 0;
|
|
|
|
GfParmListSeekFirst(hparmScreen, GFSCR_SECT_WIN_MODES);
|
|
do
|
|
{
|
|
last.width = GfParmGetCurNum(hparmScreen, GFSCR_SECT_WIN_MODES, GFSCR_ATT_WIN_X, NULL, 0);
|
|
last.height = GfParmGetCurNum(hparmScreen, GFSCR_SECT_WIN_MODES, GFSCR_ATT_WIN_Y, NULL, 0);
|
|
|
|
if ((last.height != 0) && (last.width != 0))
|
|
vecSizes.push_back(last);
|
|
}
|
|
while (GfParmListSeekNext(hparmScreen, GFSCR_SECT_WIN_MODES) == 0);
|
|
}
|
|
return vecSizes;
|
|
}
|
|
|
|
/** Get the supported screen / window sizes (pixels) for the current display mode.
|
|
@ingroup screen
|
|
@param nDisplayIndex Index of the display from which to get the sizes
|
|
@return ScreenSizeVector of detected supported sizes
|
|
*/
|
|
ScreenSizeVector GfScrGetSupportedSizes(int nDisplayIndex)
|
|
{
|
|
/* Build list of available screen sizes */
|
|
int numModes;
|
|
SDL_DisplayMode mode;
|
|
SDL_Rect bounds;
|
|
ScreenSizeVector vecSizes;
|
|
tScreenSize last;
|
|
last.width = 0;
|
|
last.height = 0;
|
|
last.refresh_rate = 0;
|
|
|
|
bounds.w = 0;
|
|
bounds.h = 0;
|
|
|
|
// make sure nDisplayIndex is valid (less than Number of displays)
|
|
if(nDisplayIndex < GfScrGetAttachedDisplays())
|
|
{
|
|
if(SDL_GetDesktopDisplayMode(nDisplayIndex, &mode) == 0)
|
|
{
|
|
bounds.w = mode.w;
|
|
bounds.h = mode.h;
|
|
GfLogInfo("Display %d : %d x %d x %d @ %d hz\n", nDisplayIndex + 1, mode.w,mode.h,SDL_BITSPERPIXEL(mode.format),mode.refresh_rate);
|
|
}
|
|
else
|
|
{
|
|
GfLogError("Could not get the Display mode for Display %d \n", nDisplayIndex + 1);
|
|
bounds.w = 0;
|
|
bounds.h = 0;
|
|
}
|
|
|
|
numModes = SDL_GetNumDisplayModes(nDisplayIndex);
|
|
GfLogInfo("Display %d : modes available %d\n", nDisplayIndex + 1, numModes);
|
|
|
|
for(int i = 0; i < numModes; i++)
|
|
{
|
|
if(SDL_GetDisplayMode(nDisplayIndex, i, &mode) == 0)
|
|
{
|
|
// Don't allow duplicate entries
|
|
if((mode.w != last.width) || (mode.h != last.height))
|
|
{
|
|
GfLogDebug(" %d x %d x %d @ %d hz\n",mode.w,mode.h,SDL_BITSPERPIXEL(mode.format),mode.refresh_rate);
|
|
last.width = mode.w;
|
|
last.height = mode.h;
|
|
last.refresh_rate = mode.refresh_rate;
|
|
vecSizes.push_back(last);
|
|
}
|
|
}
|
|
}
|
|
// Reverse the order for combobox GUI
|
|
std::reverse(vecSizes.begin(), vecSizes.end());
|
|
}
|
|
else
|
|
{
|
|
GfLogError("Invalid Display index passed to GfScrGetSupportedSizes()\n");
|
|
}
|
|
|
|
if(vecSizes.empty())
|
|
{
|
|
GfLogInfo("No supported sizes for Display .\n");
|
|
|
|
// Desperation stick the Display Bounds into the vector
|
|
last.width = bounds.w;
|
|
last.height = bounds.h;
|
|
last.refresh_rate = mode.refresh_rate;
|
|
vecSizes.push_back(last);
|
|
}
|
|
|
|
return vecSizes;
|
|
}
|
|
|
|
/** Get the screen dimensions
|
|
@ingroup screen
|
|
@param nDisplayIndex Index of the display from which to get the size
|
|
@return tScreenSize containing the current size
|
|
*/
|
|
tScreenSize GfScrGetCurrentDisplaySize(int nDisplayIndex)
|
|
{
|
|
tScreenSize size;
|
|
|
|
size.width = 0;
|
|
size.height = 0;
|
|
size.refresh_rate = 0;
|
|
|
|
SDL_DisplayMode mode;
|
|
if(SDL_GetCurrentDisplayMode(nDisplayIndex, &mode) == 0)
|
|
{
|
|
size.width = mode.w;
|
|
size.height = mode.h;
|
|
size.refresh_rate = mode.refresh_rate;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
/** Get the default / fallback screen / window color depths (bits per pixels, alpha included).
|
|
@ingroup screen
|
|
@param pnColorDepths Address of number of default sizes (output).
|
|
@return Array of detected supported sizes (static data, never free).
|
|
*/
|
|
int* GfScrGetDefaultColorDepths(int* pnColorDepths)
|
|
{
|
|
*pnColorDepths = NDefScreenColorDepths;
|
|
|
|
return ADefScreenColorDepths;
|
|
}
|
|
|
|
/** Get the supported color depths as supported by the underlying hardware/driver.
|
|
@ingroup screen
|
|
@param pnDepths Address of number of detected color depths (output)
|
|
@return Array of detected supported color depths (allocated on the heap, must use free at the end)
|
|
*/
|
|
int* GfScrGetSupportedColorDepths(int* pnDepths)
|
|
{
|
|
// Need to completely re-write this function
|
|
*pnDepths = NDefScreenColorDepths;
|
|
return ADefScreenColorDepths;
|
|
}
|
|
|
|
static void gfScrReshapeViewport(int width, int height)
|
|
{
|
|
GfViewWidth = GfScrWidth = width;
|
|
GfViewHeight = GfScrHeight = height;
|
|
GfScrCenX = width / 2;
|
|
GfScrCenY = height / 2;
|
|
|
|
glViewport((width-GfViewWidth)/2, (height-GfViewHeight)/2, GfViewWidth, GfViewHeight);
|
|
glMatrixMode(GL_PROJECTION );
|
|
glLoadIdentity();
|
|
glOrtho(0.0, 640.0, 0.0, 480.0, -1.0, 1.0);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
}
|
|
|
|
static void gfuiInitialWindowedPosition(int displayId, SDL_Window* window )
|
|
{
|
|
int top = 0, left = 0, bottom = 0, right = 0, x = 0, y = 0;
|
|
SDL_Rect rect;
|
|
SDL_GetDisplayBounds(displayId, &rect);
|
|
|
|
SDL_GetWindowPosition(window, &x, &y);
|
|
|
|
SDL_GetWindowBordersSize(window, &top, &left, &bottom, &right);
|
|
|
|
if(y < rect.y + top)
|
|
y = rect.y + top;
|
|
if(x < rect.x)
|
|
x = rect.x;
|
|
|
|
SDL_SetWindowPosition(window, x, y);
|
|
}
|
|
|
|
SDL_Surface* gfScrCreateWindow(int nWinWidth, int nWinHeight, int nTotalDepth,int bfVideoMode)
|
|
{
|
|
if(GfuiWindow)
|
|
{
|
|
SDL_DestroyWindow(GfuiWindow);
|
|
GfuiWindow = NULL;
|
|
}
|
|
if(PScreenSurface)
|
|
{
|
|
SDL_FreeSurface(PScreenSurface);
|
|
PScreenSurface = NULL;
|
|
}
|
|
// Set window/icon captions
|
|
std::ostringstream ossCaption;
|
|
ossCaption << GfuiApp().name() << ' ' << GfuiApp().version();
|
|
|
|
GfuiWindow = SDL_CreateWindow(ossCaption.str().c_str(),
|
|
SDL_WINDOWPOS_CENTERED_DISPLAY(GfScrStartDisplayId), SDL_WINDOWPOS_CENTERED_DISPLAY(GfScrStartDisplayId),
|
|
nWinWidth, nWinHeight, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
|
|
|
|
|
|
#if !defined(__APPLE__)
|
|
// Set window icon (MUST be a 32x32 icon for Windows, and with black pixels as alpha ones,
|
|
// as BMP doesn't support transparency).
|
|
std::ostringstream ossIconFilename;
|
|
ossIconFilename << GfDataDir() << "data/icons/icon.bmp";
|
|
SDL_Surface* surfIcon = SDL_LoadBMP(ossIconFilename.str().c_str());
|
|
if (surfIcon)
|
|
{
|
|
SDL_SetColorKey(surfIcon, SDL_TRUE, SDL_MapRGB(surfIcon->format, 0, 0, 0));
|
|
SDL_SetWindowIcon(GfuiWindow, surfIcon);
|
|
SDL_FreeSurface(surfIcon);
|
|
}
|
|
#endif
|
|
/* Create OpenGL context */
|
|
GLContext = SDL_GL_CreateContext(GfuiWindow);
|
|
|
|
// If specified, try best possible settings.
|
|
PScreenSurface = SDL_CreateRGBSurface(0, nWinWidth, nWinHeight, nTotalDepth,
|
|
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
|
|
0x00FF0000, 0x0000FF00, 0x000000FF,
|
|
#else
|
|
0x000000FF, 0x0000FF00, 0x00FF0000,
|
|
#endif
|
|
0x00000000);
|
|
|
|
if (bfVideoMode & SDL_WINDOW_FULLSCREEN)
|
|
{
|
|
SDL_Rect bounds;
|
|
|
|
/* Work around SDL2 bug */
|
|
if (SDL_GetDisplayBounds(GfScrStartDisplayId, &bounds) == 0) {
|
|
if (bounds.w == nWinWidth && bounds.h == nWinHeight)
|
|
SDL_SetWindowFullscreen(GfuiWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
else SDL_SetWindowFullscreen(GfuiWindow, SDL_WINDOW_FULLSCREEN);
|
|
} else SDL_SetWindowFullscreen(GfuiWindow, SDL_WINDOW_FULLSCREEN);
|
|
}
|
|
return PScreenSurface;
|
|
}
|
|
|
|
int GfScrGetAttachedDisplays()
|
|
{
|
|
int nDisplays = SDL_GetNumVideoDisplays();
|
|
return nDisplays;
|
|
}
|
|
|
|
bool GfScrInitSDL2(int nWinWidth, int nWinHeight, int nFullScreen)
|
|
{
|
|
#ifdef __APPLE__
|
|
// Version d'OpenGL
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
|
#else
|
|
// Version d'OpenGL
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
|
#endif
|
|
|
|
// Prepare video mode.
|
|
int bfVideoMode = SDL_WINDOW_OPENGL;
|
|
|
|
// Initialize SDL video subsystem (and exit if not supported).
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
|
|
{
|
|
GfLogError("Couldn't initialize SDL audio/video sub-system (%s)\n", SDL_GetError());
|
|
return false;
|
|
}
|
|
#if ((SDL_MAJOR_VERSION >= 2) && (SDL_PATCHLEVEL >= 5))
|
|
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
|
|
#endif
|
|
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
|
|
|
|
GfScrNumDisplays = GfScrGetAttachedDisplays();
|
|
|
|
// Get selected frame buffer specs from config file
|
|
// 1) Load the config file
|
|
void* hparmScreen =
|
|
GfParmReadFileLocal(GFSCR_CONF_FILE, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
|
|
|
|
// 2) Check / update test state of any 'in-test' specs.
|
|
if (GfParmExistsSection(hparmScreen, GFSCR_SECT_INTESTPROPS))
|
|
{
|
|
// Remove the 'in-test' specs if the test failed (we are still in the 'in progress'
|
|
// test state because the game crashed during the test).
|
|
if (std::string(GfParmGetStr(hparmScreen, GFSCR_SECT_INTESTPROPS, GFSCR_ATT_TESTSTATE,
|
|
GFSCR_VAL_INPROGRESS)) == GFSCR_VAL_INPROGRESS)
|
|
{
|
|
GfLogInfo("Reverting to last validated screen specs, as last test failed.\n");
|
|
GfParmRemoveSection(hparmScreen, GFSCR_SECT_INTESTPROPS);
|
|
}
|
|
|
|
// If the test has not yet been done, mark it as 'in progress'
|
|
else
|
|
{
|
|
GfLogInfo("Testing new screen specs : let's see what's happening ...\n");
|
|
GfParmSetStr(hparmScreen, GFSCR_SECT_INTESTPROPS, GFSCR_ATT_TESTSTATE,
|
|
GFSCR_VAL_INPROGRESS);
|
|
}
|
|
|
|
// Write the config file to disk (in case the forthcoming test makes the game crash,
|
|
// or in order the Options / Display menu shows the actual current settings).
|
|
GfParmWriteFile(NULL, hparmScreen, "Screen");
|
|
}
|
|
|
|
// 3) Select the 'in-test' specs if present, otherwise the 'validated' ones.
|
|
const char* pszScrPropSec =
|
|
GfParmExistsSection(hparmScreen, GFSCR_SECT_INTESTPROPS)
|
|
? GFSCR_SECT_INTESTPROPS : GFSCR_SECT_VALIDPROPS;
|
|
|
|
// 4) Get/Read the specs.
|
|
if (nWinWidth < 0)
|
|
nWinWidth =
|
|
(int)GfParmGetNum(hparmScreen, pszScrPropSec, GFSCR_ATT_WIN_X, (char*)NULL, 800);
|
|
if (nWinHeight < 0)
|
|
nWinHeight =
|
|
(int)GfParmGetNum(hparmScreen, pszScrPropSec, GFSCR_ATT_WIN_Y, (char*)NULL, 600);
|
|
int nTotalDepth =
|
|
(int)GfParmGetNum(hparmScreen, pszScrPropSec, GFSCR_ATT_BPP, (char*)NULL, 32);
|
|
GfScrStartDisplayId =
|
|
(int)GfParmGetNum(hparmScreen, pszScrPropSec, GFSCR_ATT_STARTUPDISPLAY, (char*)NULL, 0);
|
|
if(GfScrStartDisplayId >= GfScrNumDisplays)
|
|
{
|
|
GfScrStartDisplayId = 0;
|
|
}
|
|
|
|
double maxRefreshRate = GfParmGetNum(hparmScreen, pszScrPropSec, GFSCR_ATT_MAXREFRESH, NULL, 50);
|
|
|
|
GfuiApp().eventLoop().setMaxRefreshRate(maxRefreshRate);
|
|
|
|
bool bFullScreen;
|
|
if (nFullScreen < 0)
|
|
bFullScreen =
|
|
std::string(GfParmGetStr(hparmScreen, pszScrPropSec, GFSCR_ATT_FSCR, GFSCR_VAL_NO))
|
|
== GFSCR_VAL_YES;
|
|
else
|
|
bFullScreen = nFullScreen ? true : false;
|
|
|
|
if(bFullScreen)
|
|
bfVideoMode |= SDL_WINDOW_FULLSCREEN;
|
|
|
|
//* TODO : move and re-implement these?
|
|
bool bAlphaChannel =
|
|
std::string(GfParmGetStr(hparmScreen, pszScrPropSec, GFSCR_ATT_ALPHACHANNEL,
|
|
GFSCR_VAL_YES))
|
|
== GFSCR_VAL_YES;
|
|
|
|
bool bBumpMap =
|
|
std::string(GfParmGetStr(hparmScreen, pszScrPropSec, GFSCR_ATT_BUMPMAPPING,
|
|
GFSCR_VAL_NO))
|
|
== GFSCR_VAL_YES;
|
|
|
|
int nAniFilt =
|
|
(int)GfParmGetNum(hparmScreen, pszScrPropSec, GFSCR_ATT_ANISOTROPICFILTERING, (char*)NULL, 0);
|
|
|
|
bool bStereo =
|
|
std::string(GfParmGetStr(hparmScreen, pszScrPropSec, GFSCR_ATT_STEREOVISION,
|
|
GFSCR_VAL_NO))
|
|
== GFSCR_VAL_YES;
|
|
bool bTryBestVInitMode =
|
|
std::string(GfParmGetStr(hparmScreen, pszScrPropSec, GFSCR_ATT_VINIT,
|
|
GFSCR_VAL_VINIT_BEST))
|
|
== GFSCR_VAL_VINIT_BEST;
|
|
|
|
|
|
// TODO ?
|
|
// Add new values to the config OpenGL Major and Minor
|
|
// and setup GL Major/Minor before window creation
|
|
// SDL_GL_SetSwapInterval(1) for for vsync (may have to go AFTER window creation)
|
|
|
|
if (bTryBestVInitMode)
|
|
{
|
|
GfLogInfo("Trying 'best possible mode' for video initialization.\n");
|
|
|
|
// Detect best supported features for the specified frame buffer specs.
|
|
// Warning: Restarts the game if the frame buffer specs changed since last call.
|
|
// If specified and possible, setup the best possible settings.
|
|
if (GfglFeatures::self().checkBestSupport(nWinWidth, nWinHeight, nTotalDepth,
|
|
bAlphaChannel, bFullScreen, bBumpMap, bStereo,nAniFilt,hparmScreen))
|
|
{
|
|
// Load Open GL user settings from the config file.
|
|
GfglFeatures::self().loadSelection();
|
|
|
|
// Setup the video mode parameters.
|
|
const int nColorDepth =
|
|
GfglFeatures::self().getSelected(GfglFeatures::ColorDepth);
|
|
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, nColorDepth/3);
|
|
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, nColorDepth/3);
|
|
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, nColorDepth/3);
|
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, nColorDepth);
|
|
|
|
const int nAlphaDepth =
|
|
GfglFeatures::self().getSelected(GfglFeatures::AlphaDepth);
|
|
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, nAlphaDepth);
|
|
|
|
const int nDoubleBuffer =
|
|
GfglFeatures::self().isSelected(GfglFeatures::DoubleBuffer) ? 1 : 0;
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, nDoubleBuffer);
|
|
|
|
const int nMultiSampling =
|
|
GfglFeatures::self().isSelected(GfglFeatures::MultiSampling) ? 1 : 0;
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, nMultiSampling);
|
|
if (nMultiSampling)
|
|
{
|
|
const int nMaxMultiSamples =
|
|
GfglFeatures::self().getSelected(GfglFeatures::MultiSamplingSamples);
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, nMaxMultiSamples);
|
|
}
|
|
|
|
const int nStereoVision =
|
|
GfglFeatures::self().isSelected(GfglFeatures::StereoVision) ? 1 : 0;
|
|
SDL_GL_SetAttribute(SDL_GL_STEREO, nStereoVision);
|
|
|
|
// Try the video mode with these parameters : should always work
|
|
// (unless you downgraded you hardware / OS and didn't clear your config file).
|
|
PScreenSurface = gfScrCreateWindow(nWinWidth, nWinHeight, nTotalDepth,bfVideoMode);
|
|
}
|
|
|
|
// If best mode not supported, or test actually failed,
|
|
// revert to a supported mode (restart the game).
|
|
if (!PScreenSurface)
|
|
{
|
|
GfLogWarning("Failed to setup best supported video mode "
|
|
"whereas previously detected !\n");
|
|
GfLogWarning("Tip: You should remove your %s%s file and restart,\n",
|
|
GfLocalDir(), GFSCR_CONF_FILE);
|
|
GfLogWarning(" if something changed in your OS"
|
|
" or video hardware/driver configuration.\n");
|
|
|
|
// If testing new screen specs, remember that the test failed
|
|
// in order to revert to the previous validated specs on restart.
|
|
if (std::string(pszScrPropSec) == GFSCR_SECT_INTESTPROPS)
|
|
{
|
|
GfParmSetStr(hparmScreen, pszScrPropSec, GFSCR_ATT_TESTSTATE,
|
|
GFSCR_VAL_FAILED);
|
|
}
|
|
|
|
// Force compatible video init. mode if not testing a new video mode.
|
|
else
|
|
{
|
|
GfLogWarning("Falling back to a more compatible default mode ...\n");
|
|
GfParmSetStr(hparmScreen, pszScrPropSec, GFSCR_ATT_VINIT,
|
|
GFSCR_VAL_VINIT_COMPATIBLE);
|
|
}
|
|
GfParmWriteFile(NULL, hparmScreen, "Screen");
|
|
GfParmReleaseHandle(hparmScreen);
|
|
|
|
// And restart the game.
|
|
GfuiApp().restart(); // Never returns.
|
|
}
|
|
}
|
|
|
|
// Video initialization with generic compatible settings.
|
|
if (!PScreenSurface)
|
|
{
|
|
GfLogInfo("Trying 'default compatible' mode for video initialization.\n");
|
|
|
|
// cancel StereoVision
|
|
SDL_GL_SetAttribute(SDL_GL_STEREO, 0);
|
|
|
|
//SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
|
|
//SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 8);
|
|
|
|
PScreenSurface = gfScrCreateWindow(nWinWidth, nWinHeight, nTotalDepth,bfVideoMode);
|
|
if (!PScreenSurface)
|
|
GfLogTrace("Can't get a %s%dx%dx%d compatible video mode\n",
|
|
bFullScreen ? "full-screen " : "", nWinWidth, nWinHeight, nTotalDepth);
|
|
}
|
|
|
|
// Failed : Try and remove the full-screen requirement if present ...
|
|
if (!PScreenSurface && bFullScreen)
|
|
{
|
|
bfVideoMode &= ~SDL_WINDOW_FULLSCREEN;
|
|
PScreenSurface = gfScrCreateWindow(nWinWidth, nWinHeight, nTotalDepth,bfVideoMode);
|
|
if (!PScreenSurface)
|
|
GfLogTrace("Can't get a non-full-screen %dx%dx%d compatible video mode\n",
|
|
nWinWidth, nWinHeight, nTotalDepth);
|
|
|
|
// Update screen specs.
|
|
GfParmSetStr(hparmScreen, pszScrPropSec, GFSCR_ATT_FSCR, GFSCR_VAL_NO);
|
|
GfParmWriteFile(NULL, hparmScreen, "Screen");
|
|
}
|
|
|
|
// Failed : Try with a lower fallback size : should be supported everywhere ...
|
|
if (!PScreenSurface)
|
|
{
|
|
nWinWidth = ADefScreenSizes[0].width;
|
|
nWinHeight = ADefScreenSizes[0].height;
|
|
PScreenSurface = gfScrCreateWindow(nWinWidth, nWinHeight, nTotalDepth,bfVideoMode);
|
|
if (!PScreenSurface)
|
|
GfLogTrace("Can't get a %dx%dx%d compatible video mode\n",
|
|
nWinWidth, nWinHeight, nTotalDepth);
|
|
|
|
// Update screen specs.
|
|
GfParmSetNum(hparmScreen, pszScrPropSec, GFSCR_ATT_WIN_X, 0, (tdble)nWinWidth);
|
|
GfParmSetNum(hparmScreen, pszScrPropSec, GFSCR_ATT_WIN_Y, 0, (tdble)nWinHeight);
|
|
GfParmWriteFile(NULL, hparmScreen, "Screen");
|
|
}
|
|
|
|
// Failed : Try with a lower fallback color depth : should be supported everywhere ...
|
|
if (!PScreenSurface)
|
|
{
|
|
nTotalDepth = ADefScreenColorDepths[0];
|
|
PScreenSurface = gfScrCreateWindow(nWinWidth, nWinHeight, nTotalDepth,bfVideoMode);
|
|
if (!PScreenSurface)
|
|
GfLogTrace("Can't get a %dx%dx%d compatible video mode\n",
|
|
nWinWidth, nWinHeight, nTotalDepth);
|
|
|
|
// Update screen specs.
|
|
GfParmSetNum(hparmScreen, pszScrPropSec, GFSCR_ATT_BPP, 0, (tdble)nTotalDepth);
|
|
GfParmWriteFile(NULL, hparmScreen, "Screen");
|
|
}
|
|
|
|
// Close the config file.
|
|
GfParmReleaseHandle(hparmScreen);
|
|
|
|
// Failed : No way ... no more ideas !
|
|
if (!PScreenSurface)
|
|
{
|
|
GfLogError("Unable to get any compatible video mode"
|
|
" (fallback resolution / color depth not supported) : giving up !\n\n");
|
|
return false;
|
|
}
|
|
|
|
// If we get here, that's because we succeeded in getting a valid video mode :-)
|
|
|
|
// If 'compatible mode' selected, detect only standard Open GL features
|
|
// and load OpenGL settings from the config file.
|
|
if (!bTryBestVInitMode)
|
|
{
|
|
GfglFeatures::self().detectStandardSupport();
|
|
GfglFeatures::self().dumpSupport();
|
|
GfglFeatures::self().loadSelection();
|
|
if (GfglFeatures::self().isSupported(GfglFeatures::MultiSampling))
|
|
{
|
|
bool MultiSamplingWasSelected =
|
|
GfglFeatures::self().isSelected(GfglFeatures::MultiSampling);
|
|
int MultiSamplingSamples =
|
|
GfglFeatures::self().getSelected(GfglFeatures::MultiSamplingSamples);
|
|
if(MultiSamplingWasSelected)
|
|
{
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, MultiSamplingSamples);
|
|
}
|
|
else
|
|
{
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save view geometry and screen center.
|
|
GfViewWidth = nWinWidth;
|
|
GfViewHeight = nWinHeight;
|
|
GfScrCenX = nWinWidth / 2;
|
|
GfScrCenY = nWinHeight / 2;
|
|
|
|
// Report about selected SDL video mode.
|
|
GfLogInfo("Selected SDL video mode :\n");
|
|
GfLogInfo(" Full screen : %s\n", (bfVideoMode & SDL_WINDOW_FULLSCREEN) ? "Yes" : "No");
|
|
GfLogInfo(" Size : %dx%d\n", nWinWidth, nWinHeight);
|
|
GfLogInfo(" Color depth : %d bits\n", nTotalDepth);
|
|
GfLogInfo(" Max. refresh rate : %f\n", maxRefreshRate);
|
|
|
|
// Report about underlying hardware (needs a running frame buffer).
|
|
GfglFeatures::self().dumpHardwareInfo();
|
|
|
|
if(GfuiWindow)
|
|
{
|
|
SDL_SetWindowPosition(GfuiWindow, SDL_WINDOWPOS_CENTERED_DISPLAY(GfScrStartDisplayId), SDL_WINDOWPOS_CENTERED_DISPLAY(GfScrStartDisplayId));
|
|
SDL_ShowWindow(GfuiWindow);
|
|
SDL_RestoreWindow(GfuiWindow);
|
|
}
|
|
|
|
// Position Window if not full screen
|
|
if (!(bfVideoMode & SDL_WINDOW_FULLSCREEN))
|
|
{
|
|
gfuiInitialWindowedPosition(GfScrStartDisplayId, GfuiWindow);
|
|
}
|
|
|
|
|
|
// Initialize the Open GL viewport.
|
|
gfScrReshapeViewport(nWinWidth, nWinHeight);
|
|
|
|
// Setup the event loop about the new display.
|
|
GfuiApp().eventLoop().setReshapeCB(gfScrReshapeViewport);
|
|
GfuiApp().eventLoop().postRedisplay();
|
|
return true;
|
|
}
|
|
bool GfScrInit(int nWinWidth, int nWinHeight, int nFullScreen)
|
|
{
|
|
GfScrResizable = GfScrGetResizable();
|
|
if (GfScrResizable)
|
|
return GfScrInitSDL2();
|
|
else
|
|
return GfScrInitSDL2(nWinWidth,nWinHeight,nFullScreen);
|
|
}
|
|
|
|
/** Shutdown the screen
|
|
@ingroup screen
|
|
@return none
|
|
*/
|
|
void GfScrShutdown(void)
|
|
{
|
|
if (GfScrResizable)
|
|
{
|
|
return gfScrShutdown();
|
|
}
|
|
|
|
GfLogTrace("Shutting down screen.\n");
|
|
|
|
SDL_GL_MakeCurrent(GfuiWindow,GLContext);
|
|
SDL_GL_DeleteContext(GLContext);
|
|
GLContext = NULL;
|
|
SDL_DestroyWindow(GfuiWindow);
|
|
GfuiWindow = NULL;
|
|
|
|
// Shutdown SDL video sub-system.
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
|
|
|
|
// If there's an 'in-test' screen properties section in the config file,
|
|
// * if the test state is 'to do', do nothing (will be taken care of in next GfScrInit),
|
|
// * if the test state is 'in progress', validate the new screen properties,
|
|
// * if the test state is 'failed', revert to the validated screen properties.
|
|
void* hparmScreen = GfParmReadFileLocal(GFSCR_CONF_FILE, GFPARM_RMODE_STD);
|
|
|
|
if (GfParmExistsSection(hparmScreen, GFSCR_SECT_INTESTPROPS))
|
|
{
|
|
if (std::string(GfParmGetStr(hparmScreen, GFSCR_SECT_INTESTPROPS, GFSCR_ATT_TESTSTATE,
|
|
GFSCR_VAL_INPROGRESS)) == GFSCR_VAL_INPROGRESS)
|
|
{
|
|
GfLogInfo("Validating new screen specs (test was successful).\n");
|
|
|
|
// Copy the 'in test' props to the 'validated' ones.
|
|
GfParmSetNum(hparmScreen, GFSCR_SECT_VALIDPROPS, GFSCR_ATT_WIN_X, 0,
|
|
GfParmGetNum(hparmScreen, GFSCR_SECT_INTESTPROPS, GFSCR_ATT_WIN_X, 0, 800));
|
|
GfParmSetNum(hparmScreen, GFSCR_SECT_VALIDPROPS, GFSCR_ATT_WIN_Y, 0,
|
|
GfParmGetNum(hparmScreen, GFSCR_SECT_INTESTPROPS, GFSCR_ATT_WIN_Y, 0, 600));
|
|
GfParmSetNum(hparmScreen, GFSCR_SECT_VALIDPROPS, GFSCR_ATT_BPP, 0,
|
|
GfParmGetNum(hparmScreen, GFSCR_SECT_INTESTPROPS, GFSCR_ATT_BPP, 0, 32));
|
|
GfParmSetNum(hparmScreen, GFSCR_SECT_VALIDPROPS, GFSCR_ATT_STARTUPDISPLAY, 0,
|
|
GfParmGetNum(hparmScreen, GFSCR_SECT_INTESTPROPS, GFSCR_ATT_STARTUPDISPLAY, 0, 0));
|
|
GfParmSetStr(hparmScreen, GFSCR_SECT_VALIDPROPS, GFSCR_ATT_VDETECT,
|
|
GfParmGetStr(hparmScreen, GFSCR_SECT_INTESTPROPS, GFSCR_ATT_VDETECT, GFSCR_VAL_VDETECT_AUTO));
|
|
GfParmSetNum(hparmScreen, GFSCR_SECT_VALIDPROPS, GFSCR_ATT_MAXREFRESH, 0,
|
|
GfParmGetNum(hparmScreen, GFSCR_SECT_INTESTPROPS, GFSCR_ATT_MAXREFRESH, 0, 50));
|
|
const char* pszVInitMode =
|
|
GfParmGetStr(hparmScreen, GFSCR_SECT_INTESTPROPS, GFSCR_ATT_VINIT,
|
|
GFSCR_VAL_VINIT_COMPATIBLE);
|
|
GfParmSetStr(hparmScreen, GFSCR_SECT_VALIDPROPS, GFSCR_ATT_VINIT, pszVInitMode);
|
|
GfParmSetStr(hparmScreen, GFSCR_SECT_VALIDPROPS, GFSCR_ATT_FSCR,
|
|
GfParmGetStr(hparmScreen, GFSCR_SECT_INTESTPROPS, GFSCR_ATT_FSCR, GFSCR_VAL_NO));
|
|
// Store OpenGL settings if best video init mode selected
|
|
// (because loadSelection can changed them).
|
|
if (std::string(pszVInitMode) == GFSCR_VAL_VINIT_BEST)
|
|
GfglFeatures::self().storeSelection(hparmScreen);
|
|
}
|
|
else if (std::string(GfParmGetStr(hparmScreen, GFSCR_SECT_INTESTPROPS, GFSCR_ATT_TESTSTATE,
|
|
GFSCR_VAL_INPROGRESS)) == GFSCR_VAL_FAILED)
|
|
{
|
|
GfLogInfo("Canceling new screen specs, back to old ones (test failed).\n");
|
|
}
|
|
|
|
|
|
if (std::string(GfParmGetStr(hparmScreen, GFSCR_SECT_INTESTPROPS, GFSCR_ATT_TESTSTATE,
|
|
GFSCR_VAL_INPROGRESS)) != GFSCR_VAL_TODO)
|
|
{
|
|
// Remove the 'in-test' section.
|
|
GfParmRemoveSection(hparmScreen, GFSCR_SECT_INTESTPROPS);
|
|
|
|
// Write the screen config file to disk.
|
|
GfParmWriteFile(NULL, hparmScreen, "Screen");
|
|
}
|
|
else
|
|
{
|
|
GfLogInfo("New screen specs will be tested when restarting.\n");
|
|
}
|
|
}
|
|
|
|
// Release screen config params file.
|
|
GfParmReleaseHandle(hparmScreen);
|
|
}
|
|
|
|
|
|
/** Get the screen and viewport sizes.
|
|
@ingroup screen
|
|
@param scrw address of screen with
|
|
@param scrh address of screen height
|
|
@param vieww address of viewport with
|
|
@param viewh address of viewport height
|
|
@return none
|
|
*/
|
|
void GfScrGetSize(int *scrW, int *scrH, int *viewW, int *viewH)
|
|
{
|
|
*scrW = GfScrWidth;
|
|
*scrH = GfScrHeight;
|
|
*viewW = GfViewWidth;
|
|
*viewH = GfViewHeight;
|
|
}
|
|
|
|
bool GfScrToggleFullScreen()
|
|
{
|
|
Uint32 flags = SDL_GetWindowFlags(GfuiWindow);
|
|
|
|
if ((flags & SDL_WINDOW_FULLSCREEN) || (flags & SDL_WINDOW_FULLSCREEN_DESKTOP)) {
|
|
SDL_SetWindowFullscreen(GfuiWindow, 0);
|
|
return false;
|
|
} else {
|
|
SDL_Rect bounds;
|
|
|
|
/* Work around SDL2 bug */
|
|
if (SDL_GetDisplayBounds(GfScrStartDisplayId, &bounds) == 0) {
|
|
if (bounds.w == GfScrWidth && bounds.h == GfScrHeight)
|
|
SDL_SetWindowFullscreen(GfuiWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
else SDL_SetWindowFullscreen(GfuiWindow, SDL_WINDOW_FULLSCREEN);
|
|
} else SDL_SetWindowFullscreen(GfuiWindow, SDL_WINDOW_FULLSCREEN);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/** Capture screen pixels into an RGB buffer (caller must free the here-allocated buffer).
|
|
@ingroup screen
|
|
@param scrw address of screen with
|
|
@param scrh address of screen height
|
|
@return none
|
|
*/
|
|
unsigned char* GfScrCapture(int* viewW, int *viewH)
|
|
{
|
|
unsigned char *img;
|
|
int sW, sH;
|
|
|
|
GfScrGetSize(&sW, &sH, viewW, viewH);
|
|
img = (unsigned char*)malloc((*viewW) * (*viewH) * 3);
|
|
if (img)
|
|
{
|
|
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
|
glReadBuffer(GL_FRONT);
|
|
glReadPixels((sW-(*viewW))/2, (sH-(*viewH))/2, *viewW, *viewH,
|
|
GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)img);
|
|
}
|
|
|
|
return img;
|
|
}
|
|
|
|
/** Capture screen pixels into a PNG file
|
|
@ingroup screen
|
|
@param filename filename of the png file
|
|
@return 0 Ok
|
|
<br>-1 Error
|
|
*/
|
|
int GfScrCaptureAsPNG(const char *filename)
|
|
{
|
|
int viewW, viewH;
|
|
|
|
// Capture screen to an RGB image (in memory) (and measure elapsed time).
|
|
const double dCaptureBeginTime = GfTimeClock();
|
|
|
|
unsigned char* img = GfScrCapture(&viewW, &viewH);
|
|
|
|
const double dCaptureEndTime = GfTimeClock();
|
|
|
|
const double dCaptureDuration = dCaptureEndTime - dCaptureBeginTime;
|
|
|
|
// Write RGB image to the PNG file (and measure elapsed time).
|
|
const int nStatus = GfTexWriteImageToPNG(img, filename, viewW, viewH);
|
|
|
|
const double dFileWriteDuration = GfTimeClock() - dCaptureEndTime;
|
|
|
|
// Free the image buffer.
|
|
if (img)
|
|
free(img);
|
|
|
|
if (!nStatus)
|
|
GfLogTrace("Captured screen to %s (capture=%.3f s, PNG=%.3f s)\n",
|
|
filename, dCaptureDuration, dFileWriteDuration);
|
|
else
|
|
GfLogError("Failed to capture screen to %s\n", filename);
|
|
|
|
return nStatus;
|
|
}
|
|
|
|
SDL_Window* GfScrGetMainWindow()
|
|
{
|
|
return GfuiWindow;
|
|
}
|
|
|
|
// ===========
|
|
bool GfScrUsingResizableWindow()
|
|
{
|
|
return GfScrResizable;
|
|
}
|
|
|
|
bool GfScrGetResizable()
|
|
{
|
|
GfScrResizable = false;
|
|
// Read from screen.xml
|
|
void* hparmScreen = GfParmReadFileLocal(GFSCR_CONF_FILE, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
|
|
|
|
const char *resizable;
|
|
resizable = GfParmGetStr(hparmScreen, GFSCR_SECT_WINDOWPROPS, GFSCR_ATT_RESIZABLE, GFSCR_VAL_NO);
|
|
if (strcmp(resizable, GFSCR_VAL_YES) == 0)
|
|
{
|
|
GfScrResizable = true;
|
|
}
|
|
// Close the config file.
|
|
GfParmReleaseHandle(hparmScreen);
|
|
|
|
return GfScrResizable;
|
|
}
|
|
|
|
bool gfScrAAOpenGLSetup()
|
|
{
|
|
bool bSupported = false;
|
|
// TODO read this from config
|
|
int samplesWanted = 8;
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
|
|
|
|
SDL_Window* testWindow = SDL_CreateWindow("AA test",
|
|
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
|
640, 480,
|
|
SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
|
|
|
if(testWindow)
|
|
{
|
|
SDL_GLContext context = 0;
|
|
context = SDL_GL_CreateContext(testWindow);
|
|
if(context)
|
|
{
|
|
int buffers = -1;
|
|
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
|
|
if(buffers)
|
|
{
|
|
int samples = -1;
|
|
glGetIntegerv(GL_MAX_SAMPLES_EXT, &samples);
|
|
if(samples > 0)
|
|
{
|
|
bSupported = true;
|
|
if(samples > samplesWanted)
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, samplesWanted);
|
|
else
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, samples);
|
|
}
|
|
}
|
|
SDL_GL_DeleteContext(context);
|
|
context = NULL;
|
|
}
|
|
else
|
|
{
|
|
GfLogTrace("Unable to create an OpenGL AA test context: SDL Error: %s\n", SDL_GetError());
|
|
}
|
|
SDL_DestroyWindow(testWindow);
|
|
testWindow = NULL;
|
|
}
|
|
else
|
|
{
|
|
GfLogTrace("Unable to create an OpenGL AA test window: SDL Error: %s\n", SDL_GetError());
|
|
}
|
|
|
|
if(bSupported == false)
|
|
{
|
|
GfLogTrace("Disabling Anti-aliasing\n");
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
|
|
}
|
|
|
|
|
|
return bSupported;
|
|
}
|
|
|
|
void gfScrBaseOpenGLSetup()
|
|
{
|
|
|
|
// Will need for multi-window support
|
|
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
|
|
|
|
// Setup Anti-aliasing (if available)
|
|
gfScrAAOpenGLSetup();
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
|
|
//SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
|
|
|
|
#if ((SDL_MAJOR_VERSION >= 2) && (SDL_PATCHLEVEL >= 5))
|
|
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
|
|
#endif
|
|
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
|
|
}
|
|
|
|
bool GfScrCreateMenuWindow()
|
|
{
|
|
SDL_Rect bounds;
|
|
SDL_GetDisplayBounds(GfScrStartDisplayId, &bounds);
|
|
|
|
float percentage = 0.90;
|
|
|
|
// Set window/icon captions
|
|
std::ostringstream ossCaption;
|
|
ossCaption << GfuiApp().name() << " " << GfuiApp().version();
|
|
|
|
GfuiWindow = SDL_CreateWindow(ossCaption.str().c_str(),
|
|
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
|
bounds.w * percentage,
|
|
bounds.h * percentage,
|
|
SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
|
|
|
if (!GfuiWindow)
|
|
{
|
|
GfLogError("Unable to create an OpenGL window: SDL Error: %s\n", SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
#if !defined(__APPLE__)
|
|
// Set window icon (MUST be a 32x32 icon for Windows, and with black pixels as alpha ones,
|
|
// as BMP doesn't support transparency).
|
|
std::ostringstream ossIconFilename;
|
|
ossIconFilename << GfDataDir() << "data/icons/icon.bmp";
|
|
SDL_Surface* surfIcon = SDL_LoadBMP(ossIconFilename.str().c_str());
|
|
if (surfIcon)
|
|
{
|
|
SDL_SetColorKey(surfIcon, SDL_TRUE, SDL_MapRGB(surfIcon->format, 0, 0, 0));
|
|
SDL_SetWindowIcon(GfuiWindow, surfIcon);
|
|
SDL_FreeSurface(surfIcon);
|
|
}
|
|
#endif
|
|
// Create OpenGL context
|
|
GLContext = SDL_GL_CreateContext(GfuiWindow);
|
|
|
|
int doublebuffer = -1;
|
|
int shared = -1;
|
|
int samples = -1;
|
|
|
|
SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &doublebuffer);
|
|
SDL_GL_GetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, &shared);
|
|
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples);
|
|
|
|
if ((!GLContext) || (!doublebuffer)) // || (!shared))
|
|
{
|
|
GfLogError("Unable to create an OpenGL context: SDL Error: %s\n", SDL_GetError());
|
|
GfLogError("\t GLContext = %p\n",GLContext);
|
|
GfLogError("\t doublebuffer = %d\n",doublebuffer);
|
|
GfLogError("\t shared = %d\n",shared);
|
|
GfLogError("\t samples = %d\n",samples);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
SDL_GL_MakeCurrent(GfuiWindow,GLContext);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void gfScrOpenGlFeatures()
|
|
{
|
|
GfglFeatures::self().detectStandardSupport();
|
|
GfglFeatures::self().dumpSupport();
|
|
|
|
if (SDL_GL_ExtensionSupported("GL_EXT_texture_filter_anisotropic"))
|
|
GfglFeatures::self().select(GfglFeatures::AnisotropicFiltering, 2);
|
|
else
|
|
GfglFeatures::self().select(GfglFeatures::AnisotropicFiltering, GfglFeatures::InvalidInt);
|
|
|
|
if (SDL_GL_ExtensionSupported("GL_ARB_multitexture"))
|
|
{
|
|
int nValue = 0;
|
|
GfglFeatures::self().select(GfglFeatures::MultiTexturing, true);
|
|
glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &nValue);
|
|
GfglFeatures::self().select(GfglFeatures::MultiTexturingUnits, nValue);
|
|
}
|
|
else
|
|
{
|
|
GfglFeatures::self().select(GfglFeatures::MultiTexturing, false);
|
|
GfglFeatures::self().select(GfglFeatures::MultiTexturingUnits, 1);
|
|
}
|
|
if(SDL_GL_ExtensionSupported("GL_ARB_texture_compression"))
|
|
{
|
|
int nValue = 0;
|
|
glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB, &nValue);
|
|
if(nValue)
|
|
GfglFeatures::self().select(GfglFeatures::TextureCompression, true);
|
|
}
|
|
else
|
|
{
|
|
GfglFeatures::self().select(GfglFeatures::TextureCompression, false);
|
|
}
|
|
int maxTex = 0;
|
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTex);
|
|
if (maxTex > 16384) // Max in-game supported value (must be consistent with openglconfig.cpp)
|
|
maxTex = 16384;
|
|
GfglFeatures::self().select(GfglFeatures::TextureMaxSize, maxTex);
|
|
}
|
|
|
|
bool GfScrValidateWindowPosition(int X, int Y)
|
|
{
|
|
SDL_Rect rect;
|
|
int numDisplays = SDL_GetNumVideoDisplays();
|
|
|
|
for(int i = 0;i < numDisplays;i++)
|
|
{
|
|
if(SDL_GetDisplayBounds(i,&rect) == 0)
|
|
{
|
|
if(X >= rect.x && X < rect.x + rect.w && Y >= rect.y && Y < rect.y + rect.h)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void GfScrInitialWindowedPosition()
|
|
{
|
|
int x = SDL_WINDOWPOS_UNDEFINED;
|
|
int y = SDL_WINDOWPOS_UNDEFINED;
|
|
int w = 800;
|
|
int h = 600;
|
|
int full = 0;
|
|
int max = 0;
|
|
|
|
void* hparmScreen =
|
|
GfParmReadFileLocal(GFSCR_CONF_FILE, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
|
|
if (GfParmExistsSection(hparmScreen, GFSCR_SECT_WINDOWPROPS))
|
|
{
|
|
|
|
x = (int)GfParmGetNum(hparmScreen, GFSCR_SECT_WINDOWPROPS, GFSCR_ATT_WIN_LEFT, (char*)NULL, x);
|
|
y = (int)GfParmGetNum(hparmScreen, GFSCR_SECT_WINDOWPROPS, GFSCR_ATT_WIN_TOP, (char*)NULL, y);
|
|
w = (int)GfParmGetNum(hparmScreen, GFSCR_SECT_WINDOWPROPS, GFSCR_ATT_WIN_X, (char*)NULL, w);
|
|
h = (int)GfParmGetNum(hparmScreen, GFSCR_SECT_WINDOWPROPS, GFSCR_ATT_WIN_Y, (char*)NULL, h);
|
|
full = (int)GfParmGetNum(hparmScreen, GFSCR_SECT_WINDOWPROPS, GFSCR_ATT_FULLSCREEN, (char*)NULL, full);
|
|
max = (int)GfParmGetNum(hparmScreen, GFSCR_SECT_WINDOWPROPS, GFSCR_ATT_MAXIMIZED, (char*)NULL, max);
|
|
}
|
|
GfParmReleaseHandle(hparmScreen);
|
|
|
|
// Make sure these are valid
|
|
if(GfScrValidateWindowPosition(x, y) == false)
|
|
{
|
|
SDL_Rect rect;
|
|
if(SDL_GetDisplayBounds(0,&rect) == 0)
|
|
{
|
|
w = rect.w * 0.9;
|
|
h = rect.h * 0.9;
|
|
}
|
|
x = SDL_WINDOWPOS_UNDEFINED;
|
|
y = SDL_WINDOWPOS_UNDEFINED;
|
|
}
|
|
|
|
SDL_SetWindowPosition(GfuiWindow, x, y);
|
|
SDL_SetWindowSize(GfuiWindow, w, h);
|
|
|
|
if(max)
|
|
{
|
|
SDL_MaximizeWindow(GfuiWindow);
|
|
}
|
|
|
|
if(full == 1)
|
|
{
|
|
SDL_SetWindowFullscreen(GfuiWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
}
|
|
else if(full == 2)
|
|
{
|
|
GfScrToggleMultiFullScreens(NULL);
|
|
}
|
|
}
|
|
|
|
bool GfScrInitSDL2()
|
|
{
|
|
GfLogTrace("Initializing resizable screen.\n");
|
|
// Initialize SDL video subsystem (and exit if not supported).
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
|
|
{
|
|
GfLogError("Couldn't initialize SDL audio/video sub-system (%s)\n", SDL_GetError());
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GfuiApp().name().c_str(),
|
|
"SDL2 initialization failed.\nPlease verify that all prerequistes are installed.\n", NULL);
|
|
return false;
|
|
}
|
|
|
|
// Get system info
|
|
|
|
// Read saved info
|
|
|
|
// Setup OpenGL
|
|
gfScrBaseOpenGLSetup();
|
|
|
|
// Create Window
|
|
if(GfScrCreateMenuWindow())
|
|
{
|
|
// Only needed for ssggraph
|
|
gfScrOpenGlFeatures();
|
|
|
|
GfScrInitialWindowedPosition();
|
|
|
|
SDL_ShowWindow(GfuiWindow);
|
|
|
|
// Initialize the Open GL viewport.
|
|
SDL_GetWindowSize(GfuiWindow, &GfScrWidth, &GfScrHeight);
|
|
gfScrReshapeViewport(GfScrWidth, GfScrHeight);
|
|
|
|
// Setup the event loop about the new display.
|
|
GfuiApp().eventLoop().setReshapeCB(gfScrReshapeViewport);
|
|
GfuiApp().eventLoop().postRedisplay();
|
|
|
|
double maxRefreshRate = 50;
|
|
void* hparmScreen =
|
|
GfParmReadFileLocal(GFSCR_CONF_FILE, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
|
|
|
|
if (hparmScreen)
|
|
{
|
|
maxRefreshRate = GfParmGetNum(hparmScreen,
|
|
GFSCR_SECT_VALIDPROPS, GFSCR_ATT_MAXREFRESH, NULL, maxRefreshRate);
|
|
GfParmReleaseHandle(hparmScreen);
|
|
}
|
|
|
|
GfuiApp().eventLoop().setMaxRefreshRate(maxRefreshRate);
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
gfScrDisableResizable();
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GfuiApp().name().c_str(),
|
|
"Unable to create a resizable openGL window.\nThe Display Mode has been reset.", NULL);
|
|
GfuiApp().restart();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void GfScrSetFullscreen(bool bFullScreen /* = true */)
|
|
{
|
|
if(bFullScreen == false)
|
|
{
|
|
SDL_SetWindowFullscreen(GfuiWindow, 0);
|
|
}
|
|
else
|
|
{
|
|
SDL_SetWindowFullscreen(GfuiWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
}
|
|
}
|
|
|
|
void GfScrToggleFullScreen(void* unused)
|
|
{
|
|
Uint32 flags = SDL_GetWindowFlags(GfuiWindow);
|
|
|
|
if ((flags & SDL_WINDOW_FULLSCREEN) || (flags & SDL_WINDOW_FULLSCREEN_DESKTOP))
|
|
{
|
|
GfScrSetFullscreen(false);
|
|
}
|
|
else
|
|
{
|
|
if (flags & SDL_WINDOW_BORDERLESS) // we are MultiFullScreen
|
|
{
|
|
GfScrToggleMultiFullScreens(NULL);
|
|
}
|
|
GfScrSetFullscreen(true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
SDL_Rect GetMultiFullScreenBounds()
|
|
{
|
|
SDL_Rect bounds;
|
|
SDL_Rect maxBounds;
|
|
int nDisplays = SDL_GetNumVideoDisplays();
|
|
for(int i = 0;i < nDisplays;i++)
|
|
{
|
|
if(SDL_GetDisplayBounds(i, &bounds) == 0)
|
|
{
|
|
if(i == 0)
|
|
{
|
|
maxBounds = bounds;
|
|
}
|
|
else
|
|
{
|
|
if(bounds.x < maxBounds.x)
|
|
maxBounds.x = bounds.x;
|
|
|
|
maxBounds.w += bounds.w;
|
|
}
|
|
}
|
|
}
|
|
return maxBounds;
|
|
}
|
|
|
|
bool GfscrAllowMultiFullScreens()
|
|
{
|
|
bool bRet = false;
|
|
SDL_Rect bounds;
|
|
|
|
int nDisplays = SDL_GetNumVideoDisplays();
|
|
if(nDisplays > 1)
|
|
{
|
|
int height = 0;
|
|
int top = 0;
|
|
for(int i = 0;i < nDisplays;i++)
|
|
{
|
|
if(SDL_GetDisplayBounds(i, &bounds) == 0)
|
|
{
|
|
if(i == 0)
|
|
{
|
|
top = bounds.y;
|
|
height = bounds.h;
|
|
bRet = true;
|
|
continue;
|
|
}
|
|
if((bounds.h != height) || (bounds.y != top))
|
|
{
|
|
bRet = false;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bRet = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
void GfScrToggleMultiFullScreens(void* unused)
|
|
{
|
|
static int restoreX = 0;
|
|
static int restoreY = 0;
|
|
static int restoreW = 800;
|
|
static int restoreH = 600;
|
|
Uint32 flags = SDL_GetWindowFlags(GfuiWindow);
|
|
|
|
if (flags & SDL_WINDOW_BORDERLESS) // we are MultiFullScreen
|
|
{
|
|
SDL_SetWindowBordered(GfuiWindow, SDL_TRUE);
|
|
SDL_SetWindowPosition(GfuiWindow, restoreX, restoreY);
|
|
SDL_SetWindowSize(GfuiWindow, restoreW, restoreH);
|
|
}
|
|
else if(GfscrAllowMultiFullScreens()) // NOT in Full-multiscreen
|
|
{
|
|
if ((flags & SDL_WINDOW_FULLSCREEN) || (flags & SDL_WINDOW_FULLSCREEN_DESKTOP))
|
|
{
|
|
GfScrSetFullscreen(false);
|
|
}
|
|
|
|
SDL_GetWindowPosition(GfuiWindow, &restoreX, &restoreY);
|
|
SDL_GetWindowSize(GfuiWindow, &restoreW, &restoreH);
|
|
|
|
SDL_SetWindowBordered(GfuiWindow, SDL_FALSE);
|
|
SDL_Rect bounds = GetMultiFullScreenBounds();
|
|
|
|
if(!SDL_RectEmpty(&bounds))
|
|
{
|
|
SDL_SetWindowPosition(GfuiWindow,bounds.x, bounds.y);
|
|
SDL_SetWindowSize(GfuiWindow, bounds.w, bounds.h);
|
|
}
|
|
else
|
|
{
|
|
GfLogError("GetMultiFullScreenBounds() returned an empty rectangle.\n");
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
void gfScrSaveWindowState()
|
|
{
|
|
GfLogTrace("Saving resizable window state.\n");
|
|
|
|
// TODO this has some problems on Linux
|
|
int x = 0;
|
|
int y = 0;
|
|
int w = 0;
|
|
int h = 0;
|
|
int full = 0;
|
|
int max = 0;
|
|
|
|
int dispIndex = SDL_GetWindowDisplayIndex(GfuiWindow);
|
|
|
|
Uint32 flags = SDL_GetWindowFlags(GfuiWindow);
|
|
if (flags & SDL_WINDOW_BORDERLESS) // we are MultiFullScreen
|
|
{
|
|
full = 2;
|
|
GfScrToggleMultiFullScreens(NULL);
|
|
}
|
|
if ((flags & SDL_WINDOW_FULLSCREEN) || (flags & SDL_WINDOW_FULLSCREEN_DESKTOP))
|
|
{
|
|
full = 1;
|
|
SDL_SetWindowFullscreen(GfuiWindow, 0);
|
|
}
|
|
|
|
flags = SDL_GetWindowFlags(GfuiWindow);
|
|
if (flags & SDL_WINDOW_MAXIMIZED)
|
|
{
|
|
max = 1;
|
|
SDL_RestoreWindow(GfuiWindow);
|
|
}
|
|
else if (flags & SDL_WINDOW_MINIMIZED)
|
|
{
|
|
SDL_RestoreWindow(GfuiWindow);
|
|
}
|
|
SDL_GetWindowPosition(GfuiWindow, &x, &y);
|
|
SDL_GetWindowSize(GfuiWindow, &w, &h);
|
|
|
|
void* hparmScreen =
|
|
GfParmReadFileLocal(GFSCR_CONF_FILE, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
|
|
if (GfParmExistsSection(hparmScreen, GFSCR_SECT_WINDOWPROPS))
|
|
{
|
|
GfParmSetNum(hparmScreen, GFSCR_SECT_WINDOWPROPS, GFSCR_ATT_STARTUPDISPLAY, (char*)NULL, dispIndex);
|
|
GfParmSetNum(hparmScreen, GFSCR_SECT_WINDOWPROPS, GFSCR_ATT_FULLSCREEN, (char*)NULL, full);
|
|
GfParmSetNum(hparmScreen, GFSCR_SECT_WINDOWPROPS, GFSCR_ATT_MAXIMIZED, (char*)NULL, max);
|
|
GfParmSetNum(hparmScreen, GFSCR_SECT_WINDOWPROPS, GFSCR_ATT_WIN_LEFT, (char*)NULL, x);
|
|
GfParmSetNum(hparmScreen, GFSCR_SECT_WINDOWPROPS, GFSCR_ATT_WIN_TOP, (char*)NULL, y);
|
|
GfParmSetNum(hparmScreen, GFSCR_SECT_WINDOWPROPS, GFSCR_ATT_WIN_X, (char*)NULL, w);
|
|
GfParmSetNum(hparmScreen, GFSCR_SECT_WINDOWPROPS, GFSCR_ATT_WIN_Y, (char*)NULL, h);
|
|
}
|
|
|
|
// Write and release screen config params file.
|
|
GfParmWriteFile(NULL, hparmScreen, "Screen");
|
|
GfParmReleaseHandle(hparmScreen);
|
|
}
|
|
|
|
void gfScrShutdown()
|
|
{
|
|
GfLogTrace("Shutting down resizable screen.\n");
|
|
|
|
SDL_GL_MakeCurrent(GfuiWindow,GLContext);
|
|
|
|
// save the window state and position
|
|
gfScrSaveWindowState();
|
|
|
|
SDL_GL_DeleteContext(GLContext);
|
|
GLContext = NULL;
|
|
SDL_DestroyWindow(GfuiWindow);
|
|
GfuiWindow = NULL;
|
|
|
|
// Shutdown SDL video sub-system.
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
|
|
}
|
|
|
|
void gfScrDisableResizable()
|
|
{
|
|
void* hparmScreen =
|
|
GfParmReadFileLocal(GFSCR_CONF_FILE, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
|
|
if(hparmScreen)
|
|
{
|
|
if (GfParmExistsSection(hparmScreen, GFSCR_SECT_WINDOWPROPS))
|
|
{
|
|
GfParmSetStr(hparmScreen, GFSCR_SECT_WINDOWPROPS, GFSCR_ATT_RESIZABLE, GFSCR_VAL_NO);
|
|
}
|
|
|
|
// Write and release screen config params file.
|
|
GfParmWriteFile(NULL, hparmScreen, "Screen");
|
|
GfParmReleaseHandle(hparmScreen);
|
|
}
|
|
} |