Merge remote-tracking branch 'remotes/origin/feature-fullscreen'

This commit is contained in:
thrust26 2020-05-24 10:39:11 +02:00
commit 1fb1809049
15 changed files with 376 additions and 84 deletions

View File

@ -37,6 +37,8 @@
* Added separate positioning of launcher, emulator and debugger * Added separate positioning of launcher, emulator and debugger
* Added optional display to game refresh rate adaption in fullscreen mode
* Added option which lets default ROM path follow launcher navigation * Added option which lets default ROM path follow launcher navigation
* Added debugger 'saveaccess' function, which saves memory access counts to * Added debugger 'saveaccess' function, which saves memory access counts to

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -1377,6 +1377,13 @@
<td>Alt + Enter</td> <td>Alt + Enter</td>
<td>Cmd + Enter</td> <td>Cmd + Enter</td>
</tr> </tr>
<tr>
<td>Toggle adapting display refresh rate to game frame rate
</br>
Note: Not available for macOS.</td>
<td>Alt + r</td>
<td>Cmd + r</td>
</tr>
<tr> <tr>
<td><i>Decrease</i> overscan in fullscreen mode</td> <td><i>Decrease</i> overscan in fullscreen mode</td>
<td>Shift + PageDown</td> <td>Shift + PageDown</td>
@ -2191,7 +2198,7 @@
<tr> <tr>
<td><pre>-audio.dpc_pitch &lt;10000 - 30000&gt;</pre></td> <td><pre>-audio.dpc_pitch &lt;10000 - 30000&gt;</pre></td>
<td>Set the pitch o f Pitfall II music.</td> <td>Set the pitch of Pitfall II music.</td>
</tr> </tr>
<tr> <tr>
@ -2218,6 +2225,13 @@
aspect ratio.</td> aspect ratio.</td>
</tr> </tr>
<tr>
<td><pre>-tia.fs_refresh &lt;1|0&gt;</pre></td>
<td>While in fullscreen mode, adapt the display's refresh rate to the game's frame rate
to minimize judder.</br>
Note: Not available for macOS.</td>
</tr>
<tr> <tr>
<td><pre>-tia.fs_overscan &lt;0 - 10&gt;</pre></td> <td><pre>-tia.fs_overscan &lt;0 - 10&gt;</pre></td>
<td>Add overscan to TIA image while in fullscreen mode</td> <td>Add overscan to TIA image while in fullscreen mode</td>
@ -2943,13 +2957,15 @@
<table border="1" cellpadding="4"> <table border="1" cellpadding="4">
<tr><th>Item</th><th>Brief description</th><th>For more information,<br>see <a href="#CommandLine">CommandLine</a></th></tr> <tr><th>Item</th><th>Brief description</th><th>For more information,<br>see <a href="#CommandLine">CommandLine</a></th></tr>
<tr><td>Renderer</td><td>Use specified rendering mode</td><td>-video</td></tr> <tr><td>Renderer</td><td>Use specified rendering mode</td><td>-video</td></tr>
<tr><td>Interpolation</td><td>Interpolation of TIA image</td><td>-tia.inter</td></tr> <tr><td>Interpolation</td><td>Enable interpolation of the TIA image</td><td>-tia.inter</td></tr>
<tr><td>Zoom</td><td>Zoom level of TIA image</td><td>-tia.zoom</td></tr> <tr><td>Zoom</td><td>Zoom level of the TIA image</td><td>-tia.zoom</td></tr>
<tr><td>Fullscreen</td><td>Self-explanatory - Note that colors may slightly change. <tr><td>Fullscreen</td><td>Self-explanatory - Note that colors may slightly change.
This depends on the OS and renderer used.</td><td>-fullscreen</td></tr> This depends on the OS and renderer used.</td><td>-fullscreen</td></tr>
<tr><td>Stretch</td><td>In fullscreen mode, completely fill screen with TIA image</td><td>-tia.fs_stretch</td></tr> <tr><td>Stretch</td><td>In fullscreen mode, completely fill screen with the TIA image.</td><td>-tia.fs_stretch</td></tr>
<tr><td>Adapt display...</td><td>In fullscreen mode, adapt the display's refresh rate to the game's frame rate to minimize judder.
</br>Note: Not available for macOS.</td><td>-tia.fs_refresh</td></tr>
<tr><td>Overscan</td><td>In fullscreen mode, add overscan to the TIA image</td><td>-tia.fs_overscan</td></tr> <tr><td>Overscan</td><td>In fullscreen mode, add overscan to the TIA image</td><td>-tia.fs_overscan</td></tr>
<tr><td>V-Size adjust</td><td>Adjust height of TIA image</td><td>-tia.vsizeadjust</td></tr> <tr><td>V-Size adjust</td><td>Adjust height of the TIA image</td><td>-tia.vsizeadjust</td></tr>
</table> </table>
</td> </td>
</tr> </tr>

View File

@ -15,6 +15,8 @@
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================ //============================================================================
#include <cmath>
#include "SDL_lib.hxx" #include "SDL_lib.hxx"
#include "bspf.hxx" #include "bspf.hxx"
#include "Logger.hxx" #include "Logger.hxx"
@ -99,19 +101,32 @@ void FrameBufferSDL2::queryHardware(vector<Common::Size>& fullscreenRes,
int numModes = SDL_GetNumDisplayModes(i); int numModes = SDL_GetNumDisplayModes(i);
ostringstream s; ostringstream s;
s << "Supported video modes for display " << i << ":"; s << "Supported video modes (" << numModes << ") for display " << i << ":";
Logger::debug(s.str());
string lastRes = "";
for (int m = 0; m < numModes; m++) for (int m = 0; m < numModes; m++)
{ {
SDL_DisplayMode mode; SDL_DisplayMode mode;
ostringstream res;
SDL_GetDisplayMode(i, m, &mode); SDL_GetDisplayMode(i, m, &mode);
s.str(""); res << std::setw(4) << mode.w << "x" << std::setw(4) << mode.h;
s << " " << m << ": " << mode.w << "x" << mode.h << "@" << mode.refresh_rate << "Hz";
if (mode.w == display.w && mode.h == display.h && mode.refresh_rate == display.refresh_rate) if(lastRes != res.str())
s << " (active)"; {
Logger::debug(s.str()); Logger::debug(s.str());
s.str("");
lastRes = res.str();
s << lastRes << ": ";
}
s << mode.refresh_rate << "Hz";
if(mode.w == display.w && mode.h == display.h && mode.refresh_rate == display.refresh_rate)
s << "* ";
else
s << " ";
} }
Logger::debug(s.str());
} }
// Now get the maximum windowed desktop resolution // Now get the maximum windowed desktop resolution
@ -218,21 +233,14 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
if(SDL_WasInit(SDL_INIT_VIDEO) == 0) if(SDL_WasInit(SDL_INIT_VIDEO) == 0)
return false; return false;
// TODO: On multiple displays, switching from centered mode, does not respect const bool fullScreen = mode.fsIndex != -1;
// current window's display (which many not be centered anymore) bool forceCreateRenderer = false;
// Get windowed window's last display // Get windowed window's last display
Int32 displayIndex = std::min(myNumDisplays, myOSystem.settings().getInt(getDisplayKey())); Int32 displayIndex = std::min(myNumDisplays, myOSystem.settings().getInt(getDisplayKey()));
// Get windowed window's last position // Get windowed window's last position
myWindowedPos = myOSystem.settings().getPoint(getPositionKey()); myWindowedPos = myOSystem.settings().getPoint(getPositionKey());
// Always recreate renderer (some systems need this)
if(myRenderer)
{
SDL_DestroyRenderer(myRenderer);
myRenderer = nullptr;
}
int posX, posY; int posX, posY;
myCenter = myOSystem.settings().getBool("center"); myCenter = myOSystem.settings().getBool("center");
@ -261,49 +269,45 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
posX = BSPF::clamp(posX, x0 - Int32(mode.screen.w) + 50, x1 - 50); posX = BSPF::clamp(posX, x0 - Int32(mode.screen.w) + 50, x1 - 50);
posY = BSPF::clamp(posY, y0 + 50, y1 - 50); posY = BSPF::clamp(posY, y0 + 50, y1 - 50);
} }
uInt32 flags = mode.fsIndex != -1 ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0;
flags |= SDL_WINDOW_ALLOW_HIGHDPI;
// macOS seems to have issues with destroying the window, and wants to #ifdef ADAPTABLE_REFRESH_SUPPORT
// keep the same handle SDL_DisplayMode adaptedSdlMode;
// Problem is, doing so on other platforms results in flickering when const bool shouldAdapt = fullScreen && myOSystem.settings().getBool("tia.fs_refresh")
// toggling fullscreen windowed mode && gameRefreshRate()
// So we have a special case for macOS // take care of 59.94 Hz
#ifndef BSPF_MACOS && refreshRate() % gameRefreshRate() != 0 && refreshRate() % (gameRefreshRate() - 1) != 0;
const bool adaptRefresh = shouldAdapt && adaptRefreshRate(displayIndex, adaptedSdlMode);
#else
const bool adaptRefresh = false;
#endif
const uInt32 flags = SDL_WINDOW_ALLOW_HIGHDPI
| (fullScreen ? adaptRefresh ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
// Don't re-create the window if its display and size hasn't changed, // Don't re-create the window if its display and size hasn't changed,
// as it's not necessary, and causes flashing in fullscreen mode // as it's not necessary, and causes flashing in fullscreen mode
if(myWindow) if(myWindow)
{ {
int d = SDL_GetWindowDisplayIndex(myWindow); const int d = SDL_GetWindowDisplayIndex(myWindow);
int w, h; int w, h;
SDL_GetWindowSize(myWindow, &w, &h); SDL_GetWindowSize(myWindow, &w, &h);
if(d != displayIndex || uInt32(w) != mode.screen.w || uInt32(h) != mode.screen.h) if(d != displayIndex || uInt32(w) != mode.screen.w || uInt32(h) != mode.screen.h
|| adaptRefresh)
{ {
SDL_DestroyWindow(myWindow); SDL_DestroyWindow(myWindow);
myWindow = nullptr; myWindow = nullptr;
} }
} }
if(myWindow) if(myWindow)
{ {
// Even though window size stayed the same, the title may have changed // Even though window size stayed the same, the title may have changed
SDL_SetWindowTitle(myWindow, title.c_str()); SDL_SetWindowTitle(myWindow, title.c_str());
SDL_SetWindowPosition(myWindow, posX, posY); SDL_SetWindowPosition(myWindow, posX, posY);
} }
#else
// macOS wants to *never* re-create the window
// This sometimes results in the window being resized *after* it's displayed,
// but at least the code works and doesn't crash
if(myWindow)
{
SDL_SetWindowFullscreen(myWindow, flags);
SDL_SetWindowSize(myWindow, mode.screen.w, mode.screen.h);
SDL_SetWindowPosition(myWindow, posX, posY);
SDL_SetWindowTitle(myWindow, title.c_str());
}
#endif
else else
{ {
forceCreateRenderer = true;
myWindow = SDL_CreateWindow(title.c_str(), posX, posY, myWindow = SDL_CreateWindow(title.c_str(), posX, posY,
mode.screen.w, mode.screen.h, flags); mode.screen.w, mode.screen.h, flags);
if(myWindow == nullptr) if(myWindow == nullptr)
@ -312,31 +316,133 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
Logger::error(msg); Logger::error(msg);
return false; return false;
} }
setWindowIcon(); setWindowIcon();
} }
#ifdef ADAPTABLE_REFRESH_SUPPORT
if(adaptRefresh)
{
// Switch to mode for adapted refresh rate
if(SDL_SetWindowDisplayMode(myWindow, &adaptedSdlMode) != 0)
{
Logger::error("ERROR: Display refresh rate change failed");
}
else
{
ostringstream msg;
msg << "Display refresh rate changed to " << adaptedSdlMode.refresh_rate << " Hz";
Logger::info(msg.str());
}
}
#endif
return createRenderer(forceCreateRenderer);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferSDL2::adaptRefreshRate(Int32 displayIndex, SDL_DisplayMode& adaptedSdlMode)
{
SDL_DisplayMode sdlMode;
if(SDL_GetCurrentDisplayMode(displayIndex, &sdlMode) != 0)
{
Logger::error("ERROR: Display mode could not be retrieved");
return false;
}
const int currentRefreshRate = sdlMode.refresh_rate;
const int wantedRefreshRate = gameRefreshRate();
// Take care of rounded refresh rates (e.g. 59.94 Hz)
float factor = std::min(float(currentRefreshRate) / wantedRefreshRate,
float(currentRefreshRate) / (wantedRefreshRate - 1));
// Calculate difference taking care of integer factors (e.g. 100/120)
float bestDiff = std::abs(factor - std::round(factor)) / factor;
bool adapt = false;
// Display refresh rate should be an integer factor of the game's refresh rate
// Note: Modes are scanned with size being first priority,
// therefore the size will never change.
// Check for integer factors 1 (60/50 Hz) and 2 (120/100 Hz)
for(int m = 1; m <= 2; ++m)
{
SDL_DisplayMode closestSdlMode;
sdlMode.refresh_rate = wantedRefreshRate * m;
if(SDL_GetClosestDisplayMode(displayIndex, &sdlMode, &closestSdlMode) == nullptr)
{
Logger::error("ERROR: Closest display mode could not be retrieved");
return adapt;
}
factor = std::min(float(sdlMode.refresh_rate) / sdlMode.refresh_rate,
float(sdlMode.refresh_rate) / (sdlMode.refresh_rate - 1));
const float diff = std::abs(factor - std::round(factor)) / factor;
if(diff < bestDiff)
{
bestDiff = diff;
adaptedSdlMode = closestSdlMode;
adapt = true;
}
}
//cerr << "refresh rate adapt ";
//if(adapt)
// cerr << "required (" << currentRefreshRate << " Hz -> " << adaptedSdlMode.refresh_rate << " Hz)";
//else
// cerr << "not required/possible";
//cerr << endl;
// Only change if the display supports a better refresh rate
return adapt;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferSDL2::createRenderer(bool force)
{
// A new renderer is only created when necessary:
// - new myWindow (force = true)
// - no renderer existing
// - different renderer flags
// - different renderer name
bool recreate = force || myRenderer == nullptr;
uInt32 renderFlags = SDL_RENDERER_ACCELERATED; uInt32 renderFlags = SDL_RENDERER_ACCELERATED;
const string& video = myOSystem.settings().getString("video"); // Render hint
SDL_RendererInfo renderInfo;
if(myOSystem.settings().getBool("vsync") if(myOSystem.settings().getBool("vsync")
&& !myOSystem.settings().getBool("turbo")) // V'synced blits option && !myOSystem.settings().getBool("turbo")) // V'synced blits option
renderFlags |= SDL_RENDERER_PRESENTVSYNC; renderFlags |= SDL_RENDERER_PRESENTVSYNC;
const string& video = myOSystem.settings().getString("video"); // Render hint
if(video != "")
SDL_SetHint(SDL_HINT_RENDER_DRIVER, video.c_str());
myRenderer = SDL_CreateRenderer(myWindow, -1, renderFlags); // check renderer flags and name
recreate |= (SDL_GetRendererInfo(myRenderer, &renderInfo) != 0)
|| ((renderInfo.flags & (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC)) != renderFlags
|| (video != renderInfo.name));
detectFeatures(); if(recreate)
determineDimensions();
if(myRenderer == nullptr)
{ {
string msg = "ERROR: Unable to create SDL renderer: " + string(SDL_GetError()); //cerr << "Create new renderer for buffer type #" << int(myBufferType) << endl;
Logger::error(msg); if(myRenderer)
return false; SDL_DestroyRenderer(myRenderer);
if(video != "")
SDL_SetHint(SDL_HINT_RENDER_DRIVER, video.c_str());
myRenderer = SDL_CreateRenderer(myWindow, -1, renderFlags);
detectFeatures();
determineDimensions();
if(myRenderer == nullptr)
{
string msg = "ERROR: Unable to create SDL renderer: " + string(SDL_GetError());
Logger::error(msg);
return false;
}
} }
clear(); clear();
SDL_RendererInfo renderinfo; SDL_RendererInfo renderinfo;
if(SDL_GetRendererInfo(myRenderer, &renderinfo) >= 0) if(SDL_GetRendererInfo(myRenderer, &renderinfo) >= 0)
myOSystem.settings().setValue("video", renderinfo.name); myOSystem.settings().setValue("video", renderinfo.name);
@ -404,6 +510,36 @@ bool FrameBufferSDL2::fullScreen() const
#endif #endif
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int FrameBufferSDL2::refreshRate() const
{
ASSERT_MAIN_THREAD;
const uInt32 displayIndex = SDL_GetWindowDisplayIndex(myWindow);
SDL_DisplayMode sdlMode;
if(SDL_GetCurrentDisplayMode(displayIndex, &sdlMode) == 0)
return sdlMode.refresh_rate;
if(myWindow != nullptr)
Logger::error("Could not retrieve current display mode");
return 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int FrameBufferSDL2::gameRefreshRate() const
{
if(myOSystem.hasConsole())
{
const string format = myOSystem.console().getFormatString();
const bool isNtsc = format == "NTSC" || format == "PAL60" || format == "SECAM60";
return isNtsc ? 60 : 50; // The code will take care of 59/49 Hz
}
return 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL2::renderToScreen() void FrameBufferSDL2::renderToScreen()
{ {
@ -416,10 +552,9 @@ void FrameBufferSDL2::renderToScreen()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL2::setWindowIcon() void FrameBufferSDL2::setWindowIcon()
{ {
ASSERT_MAIN_THREAD;
#if !defined(BSPF_MACOS) && !defined(RETRON77) #if !defined(BSPF_MACOS) && !defined(RETRON77)
#include "stella_icon.hxx" #include "stella_icon.hxx"
ASSERT_MAIN_THREAD;
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(stella_icon, 32, 32, 32, SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(stella_icon, 32, 32, 32,
32 * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000); 32 * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000);

View File

@ -181,6 +181,25 @@ class FrameBufferSDL2 : public FrameBuffer
*/ */
bool setVideoMode(const string& title, const VideoMode& mode) override; bool setVideoMode(const string& title, const VideoMode& mode) override;
/**
Checks if the display refresh rate should be adapted to game refresh rate in (real) fullscreen mode
@param displayIndex The display which should be checked
@param adaptedSdlMode The best matching mode if the refresh rate should be changed
@return True if the refresh rate should be changed
*/
bool adaptRefreshRate(Int32 displayIndex, SDL_DisplayMode& adaptedSdlMode);
/**
Create a new renderer if required
@param force If true, force new renderer creation
@return False on any errors, else true
*/
bool createRenderer(bool force);
/** /**
This method is called to create a surface with the given attributes. This method is called to create a surface with the given attributes.
@ -233,6 +252,16 @@ class FrameBufferSDL2 : public FrameBuffer
*/ */
void determineDimensions(); void determineDimensions();
/**
Retrieve the current display's refresh rate, or 0 if no window
*/
int refreshRate() const override;
/**
Retrieve the current game's refresh rate, or 60 if no game
*/
int gameRefreshRate() const;
private: private:
// The SDL video buffer // The SDL video buffer
SDL_Window* myWindow{nullptr}; SDL_Window* myWindow{nullptr};

View File

@ -467,6 +467,7 @@ PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultCommo
{Event::SoundToggle, KBDK_RIGHTBRACKET, KBDM_CTRL}, {Event::SoundToggle, KBDK_RIGHTBRACKET, KBDM_CTRL},
{Event::ToggleFullScreen, KBDK_RETURN, MOD3}, {Event::ToggleFullScreen, KBDK_RETURN, MOD3},
{Event::ToggleAdaptRefresh, KBDK_R, MOD3},
{Event::OverscanDecrease, KBDK_PAGEDOWN, KBDM_SHIFT}, {Event::OverscanDecrease, KBDK_PAGEDOWN, KBDM_SHIFT},
{Event::OverscanIncrease, KBDK_PAGEUP, KBDM_SHIFT}, {Event::OverscanIncrease, KBDK_PAGEUP, KBDM_SHIFT},
//{Event::VidmodeStd, KBDK_1, MOD3}, //{Event::VidmodeStd, KBDK_1, MOD3},

View File

@ -101,6 +101,12 @@ static const string EmptyString("");
#undef PAGE_SIZE #undef PAGE_SIZE
#undef PAGE_MASK #undef PAGE_MASK
// Adaptable refresh is currently not available on MacOS
// In the future, this may expand to other systems
#if !defined(BSPF_MACOS)
#define ADAPTABLE_REFRESH_SUPPORT
#endif
namespace BSPF namespace BSPF
{ {
static constexpr float PI_f = 3.141592653589793238462643383279502884F; static constexpr float PI_f = 3.141592653589793238462643383279502884F;

View File

@ -123,6 +123,7 @@ class Event
ToggleFrameStats, ToggleSAPortOrder, ExitGame, ToggleFrameStats, ToggleSAPortOrder, ExitGame,
// add new events from here to avoid that user remapped events get overwritten // add new events from here to avoid that user remapped events get overwritten
SettingDecrease, SettingIncrease, PreviousSetting, NextSetting, SettingDecrease, SettingIncrease, PreviousSetting, NextSetting,
ToggleAdaptRefresh,
LastType LastType
}; };

View File

@ -350,17 +350,25 @@ AdjustFunction EventHandler::cycleAdjustSetting(int direction)
myOSystem.settings().getString("palette") == PaletteHandler::SETTING_CUSTOM; myOSystem.settings().getString("palette") == PaletteHandler::SETTING_CUSTOM;
const bool isCustomFilter = const bool isCustomFilter =
myOSystem.settings().getInt("tv.filter") == int(NTSCFilter::Preset::CUSTOM); myOSystem.settings().getInt("tv.filter") == int(NTSCFilter::Preset::CUSTOM);
bool repeat;
do do
{ {
myAdjustSetting = myAdjustSetting =
AdjustSetting(BSPF::clampw(int(myAdjustSetting) + direction, 0, int(AdjustSetting::MAX_ADJ))); AdjustSetting(BSPF::clampw(int(myAdjustSetting) + direction, 0, int(AdjustSetting::MAX_ADJ)));
// skip currently non-relevant adjustments // skip currently non-relevant adjustments
} while((myAdjustSetting == AdjustSetting::OVERSCAN && !isFullScreen) repeat = (myAdjustSetting == AdjustSetting::OVERSCAN && !isFullScreen)
|| (myAdjustSetting == AdjustSetting::PALETTE_PHASE && !isCustomPalette) #ifdef ADAPTABLE_REFRESH_SUPPORT
|| (myAdjustSetting >= AdjustSetting::NTSC_SHARPNESS || (myAdjustSetting == AdjustSetting::ADAPT_REFRESH && !isFullScreen)
&& myAdjustSetting <= AdjustSetting::NTSC_BLEEDING #endif
&& !isCustomFilter)); || (myAdjustSetting == AdjustSetting::PALETTE_PHASE && !isCustomPalette)
|| (myAdjustSetting >= AdjustSetting::NTSC_SHARPNESS
&& myAdjustSetting <= AdjustSetting::NTSC_BLEEDING
&& !isCustomFilter);
// avoid endless loop
if(repeat && !direction)
direction = 1;
} while(repeat);
return getAdjustSetting(myAdjustSetting); return getAdjustSetting(myAdjustSetting);
} }
@ -376,6 +384,9 @@ AdjustFunction EventHandler::getAdjustSetting(AdjustSetting setting)
std::bind(&Sound::adjustVolume, &myOSystem.sound(), _1), std::bind(&Sound::adjustVolume, &myOSystem.sound(), _1),
std::bind(&FrameBuffer::selectVidMode, &myOSystem.frameBuffer(), _1), std::bind(&FrameBuffer::selectVidMode, &myOSystem.frameBuffer(), _1),
std::bind(&FrameBuffer::toggleFullscreen, &myOSystem.frameBuffer(), _1), std::bind(&FrameBuffer::toggleFullscreen, &myOSystem.frameBuffer(), _1),
#ifdef ADAPTABLE_REFRESH_SUPPORT
std::bind(&FrameBuffer::toggleAdaptRefresh, &myOSystem.frameBuffer(), _1),
#endif
std::bind(&FrameBuffer::changeOverscan, &myOSystem.frameBuffer(), _1), std::bind(&FrameBuffer::changeOverscan, &myOSystem.frameBuffer(), _1),
std::bind(&Console::selectFormat, &myOSystem.console(), _1), std::bind(&Console::selectFormat, &myOSystem.console(), _1),
std::bind(&Console::changeVerticalCenter, &myOSystem.console(), _1), std::bind(&Console::changeVerticalCenter, &myOSystem.console(), _1),
@ -658,6 +669,17 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated)
} }
return; return;
#ifdef ADAPTABLE_REFRESH_SUPPORT
case Event::ToggleAdaptRefresh:
if(pressed && !repeated)
{
myOSystem.frameBuffer().toggleAdaptRefresh();
myAdjustSetting = AdjustSetting::ADAPT_REFRESH;
myAdjustActive = true;
}
return;
#endif
case Event::OverscanDecrease: case Event::OverscanDecrease:
if(pressed) if(pressed)
{ {
@ -2218,6 +2240,9 @@ EventHandler::EmulActionList EventHandler::ourEmulActionList = { {
{ Event::KeyboardOnePound, "P1 Keyboard #", "" }, { Event::KeyboardOnePound, "P1 Keyboard #", "" },
// Video // Video
{ Event::ToggleFullScreen, "Toggle fullscreen", "" }, { Event::ToggleFullScreen, "Toggle fullscreen", "" },
#ifdef ADAPTABLE_REFRESH_SUPPORT
{ Event::ToggleAdaptRefresh, "Toggle fullscreen refresh rate adapt", "" },
#endif
{ Event::OverscanDecrease, "Decrease overscan in fullscreen mode", "" }, { Event::OverscanDecrease, "Decrease overscan in fullscreen mode", "" },
{ Event::OverscanIncrease, "Increase overscan in fullscreen mode", "" }, { Event::OverscanIncrease, "Increase overscan in fullscreen mode", "" },
{ Event::VidmodeDecrease, "Previous zoom level", "" }, { Event::VidmodeDecrease, "Previous zoom level", "" },
@ -2361,7 +2386,7 @@ const Event::EventSet EventHandler::MiscEvents = {
const Event::EventSet EventHandler::AudioVideoEvents = { const Event::EventSet EventHandler::AudioVideoEvents = {
Event::VolumeDecrease, Event::VolumeIncrease, Event::SoundToggle, Event::VolumeDecrease, Event::VolumeIncrease, Event::SoundToggle,
Event::VidmodeDecrease, Event::VidmodeIncrease, Event::VidmodeDecrease, Event::VidmodeIncrease,
Event::ToggleFullScreen, Event::ToggleFullScreen, Event::ToggleAdaptRefresh,
Event::OverscanDecrease, Event::OverscanIncrease, Event::OverscanDecrease, Event::OverscanIncrease,
Event::FormatDecrease, Event::FormatIncrease, Event::FormatDecrease, Event::FormatIncrease,
Event::VCenterDecrease, Event::VCenterIncrease, Event::VCenterDecrease, Event::VCenterIncrease,

View File

@ -398,6 +398,9 @@ class EventHandler
VOLUME, VOLUME,
ZOOM, ZOOM,
FULLSCREEN, FULLSCREEN,
#ifdef ADAPTABLE_REFRESH_SUPPORT
ADAPT_REFRESH,
#endif
OVERSCAN, OVERSCAN,
TVFORMAT, TVFORMAT,
VCENTER, VCENTER,
@ -517,7 +520,12 @@ class EventHandler
#else #else
PNG_SIZE = 0, PNG_SIZE = 0,
#endif #endif
EMUL_ACTIONLIST_SIZE = 156 + PNG_SIZE + COMBO_SIZE, #ifdef ADAPTABLE_REFRESH_SUPPORT
REFRESH_SIZE = 1,
#else
REFRESH_SIZE = 0,
#endif
EMUL_ACTIONLIST_SIZE = 156 + PNG_SIZE + COMBO_SIZE + REFRESH_SIZE,
MENU_ACTIONLIST_SIZE = 18 MENU_ACTIONLIST_SIZE = 18
; ;

View File

@ -959,6 +959,7 @@ void FrameBuffer::setFullscreen(bool enable)
default: default:
return; return;
} }
saveCurrentWindowPosition();
// Changing the video mode can take some time, during which the last // Changing the video mode can take some time, during which the last
// sound played may get 'stuck' // sound played may get 'stuck'
@ -993,9 +994,49 @@ void FrameBuffer::toggleFullscreen(bool toggle)
setFullscreen(isFullscreen); setFullscreen(isFullscreen);
showMessage(string("Fullscreen ") + (isFullscreen ? "enabled" : "disabled")); if(myBufferType == BufferType::Emulator)
{
ostringstream msg;
msg << "Fullscreen ";
if(isFullscreen)
msg << "enabled (" << refreshRate() << " Hz)";
else
msg << "disabled";
showMessage(msg.str());
}
} }
#ifdef ADAPTABLE_REFRESH_SUPPORT
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::toggleAdaptRefresh(bool toggle)
{
bool isAdaptRefresh = myOSystem.settings().getInt("tia.fs_refresh");
if(toggle)
isAdaptRefresh = !isAdaptRefresh;
if(myBufferType == BufferType::Emulator)
{
if(toggle)
{
myOSystem.settings().setValue("tia.fs_refresh", isAdaptRefresh);
// issue a complete framebuffer re-initialization
myOSystem.createFrameBuffer();
}
ostringstream msg;
msg << "Adapt refresh rate ";
msg << (isAdaptRefresh ? "enabled" : "disabled");
msg << " (" << refreshRate() << " Hz)";
showMessage(msg.str());
}
}
#endif
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::changeOverscan(int direction) void FrameBuffer::changeOverscan(int direction)
{ {

View File

@ -81,6 +81,13 @@ class FrameBuffer
} }
}; };
struct DisplayMode
{
uInt32 display;
Common::Size size;
uInt32 refresh_rate;
};
enum class BufferType { enum class BufferType {
None, None,
Launcher, Launcher,
@ -262,6 +269,13 @@ class FrameBuffer
*/ */
void toggleFullscreen(bool toggle = true); void toggleFullscreen(bool toggle = true);
#ifdef ADAPTABLE_REFRESH_SUPPORT
/**
Toggles between adapt fullscreen refresh rate on and off.
*/
void FrameBuffer::toggleAdaptRefresh(bool toggle = true);
#endif
/** /**
Changes the fullscreen overscan. Changes the fullscreen overscan.
@ -439,6 +453,7 @@ class FrameBuffer
virtual int scaleY(int y) const { return y; } virtual int scaleY(int y) const { return y; }
protected: protected:
/** /**
This method is called to query and initialize the video hardware This method is called to query and initialize the video hardware
for desktop and fullscreen resolution information. Since several for desktop and fullscreen resolution information. Since several
@ -510,6 +525,11 @@ class FrameBuffer
*/ */
virtual string about() const = 0; virtual string about() const = 0;
/**
Retrieve the current display's refresh rate
*/
virtual int refreshRate() const { return 0; }
protected: protected:
// The parent system for the framebuffer // The parent system for the framebuffer
OSystem& myOSystem; OSystem& myOSystem;

View File

@ -52,6 +52,7 @@ Settings::Settings()
setPermanent("tia.zoom", "3"); setPermanent("tia.zoom", "3");
setPermanent("fullscreen", "false"); setPermanent("fullscreen", "false");
setPermanent("tia.fs_stretch", "false"); setPermanent("tia.fs_stretch", "false");
setPermanent("tia.fs_refresh", "false");
setPermanent("tia.fs_overscan", "0"); setPermanent("tia.fs_overscan", "0");
setPermanent("tia.vsizeadjust", 0); setPermanent("tia.vsizeadjust", 0);
setPermanent("tia.dbgcolors", "roygpb"); setPermanent("tia.dbgcolors", "roygpb");
@ -441,6 +442,7 @@ void Settings::usage() const
<< " -tia.inter <1|0> Enable interpolated (smooth) scaling for TIA\n" << " -tia.inter <1|0> Enable interpolated (smooth) scaling for TIA\n"
<< " image\n" << " image\n"
<< " -tia.fs_stretch <1|0> Stretch TIA image to fill fullscreen mode\n" << " -tia.fs_stretch <1|0> Stretch TIA image to fill fullscreen mode\n"
<< " -tia.fs_refresh <1|0> Try to adapt display refresh rate to game's FPS\n"
<< " -tia.fs_overscan <0-10> Add overscan to TIA image in fullscreen mode\n" << " -tia.fs_overscan <0-10> Add overscan to TIA image in fullscreen mode\n"
<< " -tia.dbgcolors <string> Debug colors to use for each object (see manual\n" << " -tia.dbgcolors <string> Debug colors to use for each object (see manual\n"
<< " for description)\n" << " for description)\n"

View File

@ -83,14 +83,6 @@ VideoAudioDialog::VideoAudioDialog(OSystem& osystem, DialogContainer& parent,
addTVEffectsTab(); addTVEffectsTab();
addAudioTab(); addAudioTab();
//const int req_w = std::max(myFastSCBios->getRight(), myCloneBad->getRight()) + HBORDER + 1;
//const int req_h = _th + VGAP * 3
// + std::max(myUseVSync->getBottom(), myTVScanIntense->getBottom())
// + buttonHeight + VBORDER * 2;
//// Set real dimensions
//setSize(req_w, req_h, max_w, max_h);
// Add Defaults, OK and Cancel buttons // Add Defaults, OK and Cancel buttons
WidgetArray wid; WidgetArray wid;
addDefaultsOKCancelBGroup(wid, _font); addDefaultsOKCancelBGroup(wid, _font);
@ -149,26 +141,29 @@ void VideoAudioDialog::addDisplayTab()
wid.push_back(myFullscreen); wid.push_back(myFullscreen);
ypos += lineHeight + VGAP; ypos += lineHeight + VGAP;
/*pwidth = font.getStringWidth("0: 3840x2860@120Hz");
myFullScreenMode = new PopUpWidget(myTab, font, xpos + INDENT + 2, ypos, pwidth, lineHeight,
instance().frameBuffer().supportedScreenModes(), "Mode ");
wid.push_back(myFullScreenMode);
ypos += lineHeight + VGAP;*/
// FS stretch // FS stretch
myUseStretch = new CheckboxWidget(myTab, _font, xpos + INDENT, ypos + 1, "Stretch"); myUseStretch = new CheckboxWidget(myTab, _font, xpos + INDENT, ypos + 1, "Stretch");
wid.push_back(myUseStretch); wid.push_back(myUseStretch);
#ifdef ADAPTABLE_REFRESH_SUPPORT
// Adapt refresh rate
ypos += lineHeight + VGAP; ypos += lineHeight + VGAP;
myRefreshAdapt = new CheckboxWidget(myTab, _font, xpos + INDENT, ypos + 1, "Adapt display refresh rate");
wid.push_back(myRefreshAdapt);
#else
myRefreshAdapt = nullptr;
#endif
// FS overscan // FS overscan
ypos += lineHeight + VGAP;
myTVOverscan = new SliderWidget(myTab, _font, xpos + INDENT, ypos - 1, swidth, lineHeight, myTVOverscan = new SliderWidget(myTab, _font, xpos + INDENT, ypos - 1, swidth, lineHeight,
"Overscan", lwidth - INDENT, kOverscanChanged, fontWidth * 3, "%"); "Overscan", lwidth - INDENT, kOverscanChanged, fontWidth * 3, "%");
myTVOverscan->setMinValue(0); myTVOverscan->setMaxValue(10); myTVOverscan->setMinValue(0); myTVOverscan->setMaxValue(10);
myTVOverscan->setTickmarkIntervals(2); myTVOverscan->setTickmarkIntervals(2);
wid.push_back(myTVOverscan); wid.push_back(myTVOverscan);
ypos += lineHeight + VGAP;
// Vertical size // Vertical size
ypos += lineHeight + VGAP;
myVSizeAdjust = myVSizeAdjust =
new SliderWidget(myTab, _font, xpos, ypos-1, swidth, lineHeight, new SliderWidget(myTab, _font, xpos, ypos-1, swidth, lineHeight,
"V-Size adjust", lwidth, kVSizeChanged, fontWidth * 7, "%", 0, true); "V-Size adjust", lwidth, kVSizeChanged, fontWidth * 7, "%", 0, true);
@ -176,6 +171,7 @@ void VideoAudioDialog::addDisplayTab()
myVSizeAdjust->setTickmarkIntervals(2); myVSizeAdjust->setTickmarkIntervals(2);
wid.push_back(myVSizeAdjust); wid.push_back(myVSizeAdjust);
// Add items for tab 0 // Add items for tab 0
addToFocusList(wid, myTab, tabID); addToFocusList(wid, myTab, tabID);
} }
@ -480,10 +476,12 @@ void VideoAudioDialog::loadConfig()
// Fullscreen // Fullscreen
myFullscreen->setState(instance().settings().getBool("fullscreen")); myFullscreen->setState(instance().settings().getBool("fullscreen"));
/*string mode = instance().settings().getString("fullscreenmode");
myFullScreenMode->setSelected(mode);*/
// Fullscreen stretch setting // Fullscreen stretch setting
myUseStretch->setState(instance().settings().getBool("tia.fs_stretch")); myUseStretch->setState(instance().settings().getBool("tia.fs_stretch"));
#ifdef ADAPTABLE_REFRESH_SUPPORT
// Adapt refresh rate
myRefreshAdapt->setState(instance().settings().getBool("tia.fs_refresh"));
#endif
// Fullscreen overscan setting // Fullscreen overscan setting
myTVOverscan->setValue(instance().settings().getInt("tia.fs_overscan")); myTVOverscan->setValue(instance().settings().getInt("tia.fs_overscan"));
handleFullScreenChange(); handleFullScreenChange();
@ -595,6 +593,10 @@ void VideoAudioDialog::saveConfig()
instance().settings().setValue("fullscreen", myFullscreen->getState()); instance().settings().setValue("fullscreen", myFullscreen->getState());
// Fullscreen stretch setting // Fullscreen stretch setting
instance().settings().setValue("tia.fs_stretch", myUseStretch->getState()); instance().settings().setValue("tia.fs_stretch", myUseStretch->getState());
#ifdef ADAPTABLE_REFRESH_SUPPORT
// Adapt refresh rate
instance().settings().setValue("tia.fs_refresh", myRefreshAdapt->getState());
#endif
// Fullscreen overscan // Fullscreen overscan
instance().settings().setValue("tia.fs_overscan", myTVOverscan->getValueLabel()); instance().settings().setValue("tia.fs_overscan", myTVOverscan->getValueLabel());
@ -611,7 +613,6 @@ void VideoAudioDialog::saveConfig()
// Note: Palette values are saved directly when changed! // Note: Palette values are saved directly when changed!
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// TV Effects tab // TV Effects tab
// TV Mode // TV Mode
@ -706,8 +707,10 @@ void VideoAudioDialog::setDefaults()
myTIAInterpolate->setState(false); myTIAInterpolate->setState(false);
// screen size // screen size
myFullscreen->setState(false); myFullscreen->setState(false);
//myFullScreenMode->setSelectedIndex(0);
myUseStretch->setState(false); myUseStretch->setState(false);
#ifdef ADAPTABLE_REFRESH_SUPPORT
myRefreshAdapt->setState(false);
#endif
myTVOverscan->setValue(0); myTVOverscan->setValue(0);
myTIAZoom->setValue(300); myTIAZoom->setValue(300);
myVSizeAdjust->setValue(0); myVSizeAdjust->setValue(0);
@ -833,6 +836,9 @@ void VideoAudioDialog::handleFullScreenChange()
{ {
bool enable = myFullscreen->getState(); bool enable = myFullscreen->getState();
myUseStretch->setEnabled(enable); myUseStretch->setEnabled(enable);
#ifdef ADAPTABLE_REFRESH_SUPPORT
myRefreshAdapt->setEnabled(enable);
#endif
myTVOverscan->setEnabled(enable); myTVOverscan->setEnabled(enable);
} }

View File

@ -71,9 +71,9 @@ class VideoAudioDialog : public Dialog
PopUpWidget* myRenderer{nullptr}; PopUpWidget* myRenderer{nullptr};
CheckboxWidget* myTIAInterpolate{nullptr}; CheckboxWidget* myTIAInterpolate{nullptr};
CheckboxWidget* myFullscreen{nullptr}; CheckboxWidget* myFullscreen{nullptr};
//PopUpWidget* myFullScreenMode;
CheckboxWidget* myUseStretch{nullptr}; CheckboxWidget* myUseStretch{nullptr};
SliderWidget* myTVOverscan{nullptr}; SliderWidget* myTVOverscan{nullptr};
CheckboxWidget* myRefreshAdapt{nullptr};
SliderWidget* myTIAZoom{nullptr}; SliderWidget* myTIAZoom{nullptr};
SliderWidget* myVSizeAdjust{nullptr}; SliderWidget* myVSizeAdjust{nullptr};