Fix issues with resolution switching on Linux

* Engine would always open up in Fullscreen and then switch to Windowed
   instead of just starting up as Windowed.

 * Could not change resolution because bit depth or refresh rate did not match
   * handle RefreshRate==0 as "any", just like windows

   * Remember refresh rate in combo box that enumerates all the modes and
     propagate to video mode setting instead of assuming all modes have the
     same refresh rate.

   * Report bit depth as 32 even if SDL tells us it's 24. Other parts of the
     code require it to be equal to 32, but 24 works just fine.

 * Changing from fullscreen to windowed when "Screen" was selected in
   resolution combo box (i.e. iXRes==-1) makes the window tiny (0x0 pixels).
directional-lights
Armin Burgmeier 2016-10-05 21:34:44 -07:00
parent f37eda9508
commit 561651b715
4 changed files with 53 additions and 18 deletions

View File

@ -1063,28 +1063,33 @@ void C4StartupOptionsDlg::OnGfxResComboFill(C4GUI::ComboBox_FillCB *pFiller)
pFiller->ClearEntries();
pFiller->AddEntry(LoadResStr("IDS_MNU_DEFAULTRESOLUTION"), -1);
// fill with all possible resolutions
int32_t idx = 0, iXRes, iYRes, iBitDepth;
while (Application.GetIndexedDisplayMode(idx++, &iXRes, &iYRes, &iBitDepth, NULL, Config.Graphics.Monitor))
int32_t idx = 0, iXRes, iYRes, iBitDepth, iRefreshRate;
while (Application.GetIndexedDisplayMode(idx++, &iXRes, &iYRes, &iBitDepth, &iRefreshRate, Config.Graphics.Monitor))
#ifdef _WIN32 // why only WIN32?
if (iBitDepth == C4Draw::COLOR_DEPTH)
#endif
{
StdStrBuf sGfxString = GetGfxResString(iXRes, iYRes);
if (!pFiller->FindEntry(sGfxString.getData()))
pFiller->AddEntry(sGfxString.getData(), iXRes + (uint32_t(iYRes)<<16));
pFiller->AddEntry(sGfxString.getData(), iXRes + (uint32_t(iYRes) << 12) + (uint32_t(iRefreshRate) << 24));
}
}
bool C4StartupOptionsDlg::OnGfxResComboSelChange(C4GUI::ComboBox *pForCombo, int32_t idNewSelection)
{
// get new resolution from string
int iResX=(idNewSelection & 0xffff), iResY=(uint32_t(idNewSelection) & 0xffff0000) >> 16;
int iResX=(idNewSelection & 0xfff), iResY=(uint32_t(idNewSelection) >> 12) & 0xfff, iRefreshRate = (uint32_t(idNewSelection) >> 24) & 0xff;
if (idNewSelection == -1)
{
iResX = iResY = -1;
iRefreshRate = 0;
}
// different than current?
if (iResX == Config.Graphics.ResX && iResY == Config.Graphics.ResY) return true;
if (iResX == Config.Graphics.ResX && iResY == Config.Graphics.ResY && iRefreshRate == Config.Graphics.RefreshRate) return true;
// try setting it
if (!TryNewResolution(iResX, iResY))
if (!TryNewResolution(iResX, iResY, iRefreshRate))
{
// didn't work or declined by user
return true; // do not change label, because dialog might hae been recreated!
@ -1093,9 +1098,10 @@ bool C4StartupOptionsDlg::OnGfxResComboSelChange(C4GUI::ComboBox *pForCombo, int
return true;
}
bool C4StartupOptionsDlg::TryNewResolution(int32_t iResX, int32_t iResY)
bool C4StartupOptionsDlg::TryNewResolution(int32_t iResX, int32_t iResY, int32_t iRefreshRate)
{
int32_t iOldResX = Config.Graphics.ResX, iOldResY = Config.Graphics.ResY;
int32_t iOldRefreshRate = Config.Graphics.RefreshRate;
int32_t iOldFontSize = Config.General.RXFontSize;
C4GUI::Screen *pScreen = GetScreen();
// resolution change may imply font size change
@ -1105,7 +1111,7 @@ bool C4StartupOptionsDlg::TryNewResolution(int32_t iResX, int32_t iResY)
else if (iResY > 800)
iNewFontSize = 16;
// call application to set it
if (!Application.SetVideoMode(iResX, iResY, Config.Graphics.RefreshRate, Config.Graphics.Monitor, true))
if (!Application.SetVideoMode(iResX, iResY, iRefreshRate, Config.Graphics.Monitor, !Config.Graphics.Windowed))
{
StdCopyStrBuf strChRes(LoadResStr("IDS_MNU_SWITCHRESOLUTION"));
pScreen->ShowMessage(FormatString(LoadResStr("IDS_ERR_SWITCHRES"), Application.GetLastError()).getData(), strChRes.getData(), C4GUI::Ico_Clonk, NULL);
@ -1121,17 +1127,19 @@ bool C4StartupOptionsDlg::TryNewResolution(int32_t iResX, int32_t iResY)
// Set new resolution in config before dialog recreation so that the initial combo box value is correct (#230)
Config.Graphics.ResX = iResX;
Config.Graphics.ResY = iResY;
Config.Graphics.RefreshRate = iRefreshRate;
// since the resolution was changed, everything needs to be moved around a bit
RecreateDialog(false);
// Now set old resolution again to make sure config is restored even if the program is closed during the confirmation dialog
Config.Graphics.ResX = iOldResX;
Config.Graphics.ResY = iOldResY;
Config.Graphics.RefreshRate = iOldRefreshRate;
// Show confirmation dialog
ResChangeConfirmDlg *pConfirmDlg = new ResChangeConfirmDlg();
if (!pScreen->ShowModalDlg(pConfirmDlg, true))
{
// abort: Restore screen, if this was not some program abort
if (Application.SetVideoMode(iOldResX, iOldResY, Config.Graphics.RefreshRate, Config.Graphics.Monitor, !Config.Graphics.Windowed))
if (Application.SetVideoMode(iOldResX, iOldResY, iOldRefreshRate, Config.Graphics.Monitor, !Config.Graphics.Windowed))
{
if (iNewFontSize != iOldFontSize) Application.SetGameFont(Config.General.RXFontName, iOldFontSize);
RecreateDialog(false);
@ -1142,6 +1150,7 @@ bool C4StartupOptionsDlg::TryNewResolution(int32_t iResX, int32_t iResY)
// resolution may be kept!
Config.Graphics.ResX = iResX;
Config.Graphics.ResY = iResY;
Config.Graphics.RefreshRate = iRefreshRate;
if(Config.Graphics.Windowed)
Application.SetVideoMode(Application.GetConfigWidth(), Application.GetConfigHeight(), Config.Graphics.RefreshRate, Config.Graphics.Monitor, false);
return true;

View File

@ -130,7 +130,7 @@ private:
bool OnGfxResComboSelChange(C4GUI::ComboBox *pForCombo, int32_t idNewSelection);
void OnGfxMSComboFill(C4GUI::ComboBox_FillCB *pFiller);
bool OnGfxMSComboSelChange(C4GUI::ComboBox *pForCombo, int32_t idNewSelection);
bool TryNewResolution(int32_t iResX, int32_t iResY);
bool TryNewResolution(int32_t iResX, int32_t iResY, int32_t iRefreshRate);
StdStrBuf GetGfxResString(int32_t iResX, int32_t iResY); // convert resolution to string to be displayed in resolution choice combobox
const char * GetWindowedName(int32_t mode = -1);

View File

@ -326,6 +326,16 @@ void C4AbstractApp::HandleSDLEvent(SDL_Event& e)
static int modeCount = 0;
static int bits_per_pixel(int format)
{
// C4Draw::BITS_PER_PIXEL is 32, and other parts of the code expect
// the mode's bpp to match exactly. 24 is fully compatible, so just
// pretend it's 32 in that case to adhere to the expected interface.
int bbp = SDL_BITSPERPIXEL(format);
if (bbp == 24) bbp = 32;
return bbp;
}
bool C4AbstractApp::GetIndexedDisplayMode(int32_t iIndex, int32_t *piXRes, int32_t *piYRes, int32_t *piBitDepth, int32_t *piRefreshRate, uint32_t iMonitor)
{
if (!modeCount)
@ -340,7 +350,7 @@ bool C4AbstractApp::GetIndexedDisplayMode(int32_t iIndex, int32_t *piXRes, int32
SDL_GetDisplayMode(iMonitor, iIndex, &mode);
*piXRes = mode.w;
*piYRes = mode.h;
*piBitDepth = SDL_BITSPERPIXEL(mode.format);
*piBitDepth = bits_per_pixel(mode.format);
if (piRefreshRate) *piRefreshRate = mode.refresh_rate;
return true;
}
@ -350,6 +360,21 @@ bool C4AbstractApp::SetVideoMode(int iXRes, int iYRes, unsigned int RefreshRate,
int res;
if (!fFullScreen)
{
if (iXRes == -1)
{
SDL_DisplayMode desktop_mode;
res = SDL_GetDesktopDisplayMode(iMonitor, &desktop_mode);
if (res)
{
Error(SDL_GetError());
LogF("SDL_GetDesktopDisplayMode: %s", SDL_GetError());
return false;
}
iXRes = desktop_mode.w;
iYRes = desktop_mode.h;
}
res = SDL_SetWindowFullscreen(pWindow->window, 0);
if (res)
{
@ -357,11 +382,9 @@ bool C4AbstractApp::SetVideoMode(int iXRes, int iYRes, unsigned int RefreshRate,
LogF("SDL_SetWindowFullscreen: %s", SDL_GetError());
return false;
}
if (iXRes != -1)
pWindow->SetSize(iXRes, iYRes);
C4Rect r;
pWindow->GetSize(&r);
OnResolutionChanged(r.Wdt, r.Hgt);
pWindow->SetSize(iXRes, iYRes);
OnResolutionChanged(iXRes, iYRes);
return true;
}
SDL_DisplayMode mode;
@ -384,16 +407,19 @@ bool C4AbstractApp::SetVideoMode(int iXRes, int iYRes, unsigned int RefreshRate,
OnResolutionChanged(mode.w, mode.h);
return true;
}
for (int i = 0; i < modeCount; ++i)
{
res = SDL_GetDisplayMode(iMonitor, i, &mode);
if (res)
{
Error(SDL_GetError());
LogF("SDL_GetDisplayMode: %s", SDL_GetError());
return false;
}
if (mode.w == iXRes && mode.h == iYRes && mode.refresh_rate == RefreshRate && SDL_BITSPERPIXEL(mode.format) == C4Draw::COLOR_DEPTH)
if (mode.w == iXRes && mode.h == iYRes && (RefreshRate == 0 || mode.refresh_rate == RefreshRate) && bits_per_pixel(mode.format) == C4Draw::COLOR_DEPTH)
{
res = SDL_SetWindowDisplayMode(pWindow->window, &mode);
if (res)

View File

@ -83,7 +83,7 @@ C4Window * C4Window::Init(WindowKind windowKind, C4AbstractApp * pApp, const cha
uint32_t flags = SDL_WINDOW_OPENGL;
if (windowKind == W_Fullscreen && size->Wdt == -1)
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
else if (windowKind == W_Fullscreen)
else if (windowKind == W_Fullscreen && !Config.Graphics.Windowed)
flags |= SDL_WINDOW_FULLSCREEN;
window = SDL_CreateWindow(Title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, size->Wdt, size->Hgt, flags);
if (!window)