mirror of https://github.com/stella-emu/stella.git
Merge remote-tracking branch 'remotes/origin/feature-fullscreen'
This commit is contained in:
commit
1fb1809049
|
@ -37,6 +37,8 @@
|
|||
|
||||
* 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 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 |
|
@ -1377,6 +1377,13 @@
|
|||
<td>Alt + Enter</td>
|
||||
<td>Cmd + Enter</td>
|
||||
</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>
|
||||
<td><i>Decrease</i> overscan in fullscreen mode</td>
|
||||
<td>Shift + PageDown</td>
|
||||
|
@ -2191,7 +2198,7 @@
|
|||
|
||||
<tr>
|
||||
<td><pre>-audio.dpc_pitch <10000 - 30000></pre></td>
|
||||
<td>Set the pitch o f Pitfall II music.</td>
|
||||
<td>Set the pitch of Pitfall II music.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
@ -2218,6 +2225,13 @@
|
|||
aspect ratio.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><pre>-tia.fs_refresh <1|0></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>
|
||||
<td><pre>-tia.fs_overscan <0 - 10></pre></td>
|
||||
<td>Add overscan to TIA image while in fullscreen mode</td>
|
||||
|
@ -2943,13 +2957,15 @@
|
|||
<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><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>Zoom</td><td>Zoom level of TIA image</td><td>-tia.zoom</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 the TIA image</td><td>-tia.zoom</td></tr>
|
||||
<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>
|
||||
<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>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>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "SDL_lib.hxx"
|
||||
#include "bspf.hxx"
|
||||
#include "Logger.hxx"
|
||||
|
@ -99,19 +101,32 @@ void FrameBufferSDL2::queryHardware(vector<Common::Size>& fullscreenRes,
|
|||
int numModes = SDL_GetNumDisplayModes(i);
|
||||
ostringstream s;
|
||||
|
||||
s << "Supported video modes for display " << i << ":";
|
||||
Logger::debug(s.str());
|
||||
s << "Supported video modes (" << numModes << ") for display " << i << ":";
|
||||
|
||||
string lastRes = "";
|
||||
|
||||
for (int m = 0; m < numModes; m++)
|
||||
{
|
||||
SDL_DisplayMode mode;
|
||||
ostringstream res;
|
||||
|
||||
SDL_GetDisplayMode(i, m, &mode);
|
||||
s.str("");
|
||||
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)
|
||||
s << " (active)";
|
||||
Logger::debug(s.str());
|
||||
res << std::setw(4) << mode.w << "x" << std::setw(4) << mode.h;
|
||||
|
||||
if(lastRes != res.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
|
||||
|
@ -218,21 +233,14 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
|
|||
if(SDL_WasInit(SDL_INIT_VIDEO) == 0)
|
||||
return false;
|
||||
|
||||
// TODO: On multiple displays, switching from centered mode, does not respect
|
||||
// current window's display (which many not be centered anymore)
|
||||
const bool fullScreen = mode.fsIndex != -1;
|
||||
bool forceCreateRenderer = false;
|
||||
|
||||
// Get windowed window's last display
|
||||
Int32 displayIndex = std::min(myNumDisplays, myOSystem.settings().getInt(getDisplayKey()));
|
||||
// Get windowed window's last position
|
||||
myWindowedPos = myOSystem.settings().getPoint(getPositionKey());
|
||||
|
||||
// Always recreate renderer (some systems need this)
|
||||
if(myRenderer)
|
||||
{
|
||||
SDL_DestroyRenderer(myRenderer);
|
||||
myRenderer = nullptr;
|
||||
}
|
||||
|
||||
int posX, posY;
|
||||
|
||||
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);
|
||||
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
|
||||
// keep the same handle
|
||||
// Problem is, doing so on other platforms results in flickering when
|
||||
// toggling fullscreen windowed mode
|
||||
// So we have a special case for macOS
|
||||
#ifndef BSPF_MACOS
|
||||
#ifdef ADAPTABLE_REFRESH_SUPPORT
|
||||
SDL_DisplayMode adaptedSdlMode;
|
||||
const bool shouldAdapt = fullScreen && myOSystem.settings().getBool("tia.fs_refresh")
|
||||
&& gameRefreshRate()
|
||||
// take care of 59.94 Hz
|
||||
&& 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,
|
||||
// as it's not necessary, and causes flashing in fullscreen mode
|
||||
if(myWindow)
|
||||
{
|
||||
int d = SDL_GetWindowDisplayIndex(myWindow);
|
||||
const int d = SDL_GetWindowDisplayIndex(myWindow);
|
||||
int 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);
|
||||
myWindow = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if(myWindow)
|
||||
{
|
||||
// Even though window size stayed the same, the title may have changed
|
||||
SDL_SetWindowTitle(myWindow, title.c_str());
|
||||
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
|
||||
{
|
||||
forceCreateRenderer = true;
|
||||
myWindow = SDL_CreateWindow(title.c_str(), posX, posY,
|
||||
mode.screen.w, mode.screen.h, flags);
|
||||
if(myWindow == nullptr)
|
||||
|
@ -312,31 +316,133 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
|
|||
Logger::error(msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
const string& video = myOSystem.settings().getString("video"); // Render hint
|
||||
SDL_RendererInfo renderInfo;
|
||||
|
||||
if(myOSystem.settings().getBool("vsync")
|
||||
&& !myOSystem.settings().getBool("turbo")) // V'synced blits option
|
||||
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();
|
||||
determineDimensions();
|
||||
|
||||
if(myRenderer == nullptr)
|
||||
if(recreate)
|
||||
{
|
||||
string msg = "ERROR: Unable to create SDL renderer: " + string(SDL_GetError());
|
||||
Logger::error(msg);
|
||||
return false;
|
||||
//cerr << "Create new renderer for buffer type #" << int(myBufferType) << endl;
|
||||
if(myRenderer)
|
||||
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();
|
||||
|
||||
SDL_RendererInfo renderinfo;
|
||||
|
||||
if(SDL_GetRendererInfo(myRenderer, &renderinfo) >= 0)
|
||||
myOSystem.settings().setValue("video", renderinfo.name);
|
||||
|
||||
|
@ -404,6 +510,36 @@ bool FrameBufferSDL2::fullScreen() const
|
|||
#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()
|
||||
{
|
||||
|
@ -416,10 +552,9 @@ void FrameBufferSDL2::renderToScreen()
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void FrameBufferSDL2::setWindowIcon()
|
||||
{
|
||||
ASSERT_MAIN_THREAD;
|
||||
|
||||
#if !defined(BSPF_MACOS) && !defined(RETRON77)
|
||||
#include "stella_icon.hxx"
|
||||
ASSERT_MAIN_THREAD;
|
||||
|
||||
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(stella_icon, 32, 32, 32,
|
||||
32 * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000);
|
||||
|
|
|
@ -181,6 +181,25 @@ class FrameBufferSDL2 : public FrameBuffer
|
|||
*/
|
||||
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.
|
||||
|
||||
|
@ -233,6 +252,16 @@ class FrameBufferSDL2 : public FrameBuffer
|
|||
*/
|
||||
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:
|
||||
// The SDL video buffer
|
||||
SDL_Window* myWindow{nullptr};
|
||||
|
|
|
@ -467,6 +467,7 @@ PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultCommo
|
|||
{Event::SoundToggle, KBDK_RIGHTBRACKET, KBDM_CTRL},
|
||||
|
||||
{Event::ToggleFullScreen, KBDK_RETURN, MOD3},
|
||||
{Event::ToggleAdaptRefresh, KBDK_R, MOD3},
|
||||
{Event::OverscanDecrease, KBDK_PAGEDOWN, KBDM_SHIFT},
|
||||
{Event::OverscanIncrease, KBDK_PAGEUP, KBDM_SHIFT},
|
||||
//{Event::VidmodeStd, KBDK_1, MOD3},
|
||||
|
|
|
@ -101,6 +101,12 @@ static const string EmptyString("");
|
|||
#undef PAGE_SIZE
|
||||
#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
|
||||
{
|
||||
static constexpr float PI_f = 3.141592653589793238462643383279502884F;
|
||||
|
|
|
@ -123,6 +123,7 @@ class Event
|
|||
ToggleFrameStats, ToggleSAPortOrder, ExitGame,
|
||||
// add new events from here to avoid that user remapped events get overwritten
|
||||
SettingDecrease, SettingIncrease, PreviousSetting, NextSetting,
|
||||
ToggleAdaptRefresh,
|
||||
|
||||
LastType
|
||||
};
|
||||
|
|
|
@ -350,17 +350,25 @@ AdjustFunction EventHandler::cycleAdjustSetting(int direction)
|
|||
myOSystem.settings().getString("palette") == PaletteHandler::SETTING_CUSTOM;
|
||||
const bool isCustomFilter =
|
||||
myOSystem.settings().getInt("tv.filter") == int(NTSCFilter::Preset::CUSTOM);
|
||||
bool repeat;
|
||||
|
||||
do
|
||||
{
|
||||
myAdjustSetting =
|
||||
AdjustSetting(BSPF::clampw(int(myAdjustSetting) + direction, 0, int(AdjustSetting::MAX_ADJ)));
|
||||
// skip currently non-relevant adjustments
|
||||
} while((myAdjustSetting == AdjustSetting::OVERSCAN && !isFullScreen)
|
||||
|| (myAdjustSetting == AdjustSetting::PALETTE_PHASE && !isCustomPalette)
|
||||
|| (myAdjustSetting >= AdjustSetting::NTSC_SHARPNESS
|
||||
&& myAdjustSetting <= AdjustSetting::NTSC_BLEEDING
|
||||
&& !isCustomFilter));
|
||||
repeat = (myAdjustSetting == AdjustSetting::OVERSCAN && !isFullScreen)
|
||||
#ifdef ADAPTABLE_REFRESH_SUPPORT
|
||||
|| (myAdjustSetting == AdjustSetting::ADAPT_REFRESH && !isFullScreen)
|
||||
#endif
|
||||
|| (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);
|
||||
}
|
||||
|
@ -376,6 +384,9 @@ AdjustFunction EventHandler::getAdjustSetting(AdjustSetting setting)
|
|||
std::bind(&Sound::adjustVolume, &myOSystem.sound(), _1),
|
||||
std::bind(&FrameBuffer::selectVidMode, &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(&Console::selectFormat, &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;
|
||||
|
||||
#ifdef ADAPTABLE_REFRESH_SUPPORT
|
||||
case Event::ToggleAdaptRefresh:
|
||||
if(pressed && !repeated)
|
||||
{
|
||||
myOSystem.frameBuffer().toggleAdaptRefresh();
|
||||
myAdjustSetting = AdjustSetting::ADAPT_REFRESH;
|
||||
myAdjustActive = true;
|
||||
}
|
||||
return;
|
||||
#endif
|
||||
|
||||
case Event::OverscanDecrease:
|
||||
if(pressed)
|
||||
{
|
||||
|
@ -2218,6 +2240,9 @@ EventHandler::EmulActionList EventHandler::ourEmulActionList = { {
|
|||
{ Event::KeyboardOnePound, "P1 Keyboard #", "" },
|
||||
// Video
|
||||
{ Event::ToggleFullScreen, "Toggle fullscreen", "" },
|
||||
#ifdef ADAPTABLE_REFRESH_SUPPORT
|
||||
{ Event::ToggleAdaptRefresh, "Toggle fullscreen refresh rate adapt", "" },
|
||||
#endif
|
||||
{ Event::OverscanDecrease, "Decrease overscan in fullscreen mode", "" },
|
||||
{ Event::OverscanIncrease, "Increase overscan in fullscreen mode", "" },
|
||||
{ Event::VidmodeDecrease, "Previous zoom level", "" },
|
||||
|
@ -2361,7 +2386,7 @@ const Event::EventSet EventHandler::MiscEvents = {
|
|||
const Event::EventSet EventHandler::AudioVideoEvents = {
|
||||
Event::VolumeDecrease, Event::VolumeIncrease, Event::SoundToggle,
|
||||
Event::VidmodeDecrease, Event::VidmodeIncrease,
|
||||
Event::ToggleFullScreen,
|
||||
Event::ToggleFullScreen, Event::ToggleAdaptRefresh,
|
||||
Event::OverscanDecrease, Event::OverscanIncrease,
|
||||
Event::FormatDecrease, Event::FormatIncrease,
|
||||
Event::VCenterDecrease, Event::VCenterIncrease,
|
||||
|
|
|
@ -398,6 +398,9 @@ class EventHandler
|
|||
VOLUME,
|
||||
ZOOM,
|
||||
FULLSCREEN,
|
||||
#ifdef ADAPTABLE_REFRESH_SUPPORT
|
||||
ADAPT_REFRESH,
|
||||
#endif
|
||||
OVERSCAN,
|
||||
TVFORMAT,
|
||||
VCENTER,
|
||||
|
@ -517,7 +520,12 @@ class EventHandler
|
|||
#else
|
||||
PNG_SIZE = 0,
|
||||
#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
|
||||
;
|
||||
|
||||
|
|
|
@ -959,6 +959,7 @@ void FrameBuffer::setFullscreen(bool enable)
|
|||
default:
|
||||
return;
|
||||
}
|
||||
saveCurrentWindowPosition();
|
||||
|
||||
// Changing the video mode can take some time, during which the last
|
||||
// sound played may get 'stuck'
|
||||
|
@ -993,9 +994,49 @@ void FrameBuffer::toggleFullscreen(bool toggle)
|
|||
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -81,6 +81,13 @@ class FrameBuffer
|
|||
}
|
||||
};
|
||||
|
||||
struct DisplayMode
|
||||
{
|
||||
uInt32 display;
|
||||
Common::Size size;
|
||||
uInt32 refresh_rate;
|
||||
};
|
||||
|
||||
enum class BufferType {
|
||||
None,
|
||||
Launcher,
|
||||
|
@ -262,6 +269,13 @@ class FrameBuffer
|
|||
*/
|
||||
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.
|
||||
|
||||
|
@ -439,6 +453,7 @@ class FrameBuffer
|
|||
virtual int scaleY(int y) const { return y; }
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
This method is called to query and initialize the video hardware
|
||||
for desktop and fullscreen resolution information. Since several
|
||||
|
@ -510,6 +525,11 @@ class FrameBuffer
|
|||
*/
|
||||
virtual string about() const = 0;
|
||||
|
||||
/**
|
||||
Retrieve the current display's refresh rate
|
||||
*/
|
||||
virtual int refreshRate() const { return 0; }
|
||||
|
||||
protected:
|
||||
// The parent system for the framebuffer
|
||||
OSystem& myOSystem;
|
||||
|
|
|
@ -52,6 +52,7 @@ Settings::Settings()
|
|||
setPermanent("tia.zoom", "3");
|
||||
setPermanent("fullscreen", "false");
|
||||
setPermanent("tia.fs_stretch", "false");
|
||||
setPermanent("tia.fs_refresh", "false");
|
||||
setPermanent("tia.fs_overscan", "0");
|
||||
setPermanent("tia.vsizeadjust", 0);
|
||||
setPermanent("tia.dbgcolors", "roygpb");
|
||||
|
@ -441,6 +442,7 @@ void Settings::usage() const
|
|||
<< " -tia.inter <1|0> Enable interpolated (smooth) scaling for TIA\n"
|
||||
<< " image\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.dbgcolors <string> Debug colors to use for each object (see manual\n"
|
||||
<< " for description)\n"
|
||||
|
|
|
@ -83,14 +83,6 @@ VideoAudioDialog::VideoAudioDialog(OSystem& osystem, DialogContainer& parent,
|
|||
addTVEffectsTab();
|
||||
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
|
||||
WidgetArray wid;
|
||||
addDefaultsOKCancelBGroup(wid, _font);
|
||||
|
@ -149,26 +141,29 @@ void VideoAudioDialog::addDisplayTab()
|
|||
wid.push_back(myFullscreen);
|
||||
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
|
||||
myUseStretch = new CheckboxWidget(myTab, _font, xpos + INDENT, ypos + 1, "Stretch");
|
||||
wid.push_back(myUseStretch);
|
||||
|
||||
#ifdef ADAPTABLE_REFRESH_SUPPORT
|
||||
// Adapt refresh rate
|
||||
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
|
||||
ypos += lineHeight + VGAP;
|
||||
myTVOverscan = new SliderWidget(myTab, _font, xpos + INDENT, ypos - 1, swidth, lineHeight,
|
||||
"Overscan", lwidth - INDENT, kOverscanChanged, fontWidth * 3, "%");
|
||||
myTVOverscan->setMinValue(0); myTVOverscan->setMaxValue(10);
|
||||
myTVOverscan->setTickmarkIntervals(2);
|
||||
wid.push_back(myTVOverscan);
|
||||
ypos += lineHeight + VGAP;
|
||||
|
||||
// Vertical size
|
||||
ypos += lineHeight + VGAP;
|
||||
myVSizeAdjust =
|
||||
new SliderWidget(myTab, _font, xpos, ypos-1, swidth, lineHeight,
|
||||
"V-Size adjust", lwidth, kVSizeChanged, fontWidth * 7, "%", 0, true);
|
||||
|
@ -176,6 +171,7 @@ void VideoAudioDialog::addDisplayTab()
|
|||
myVSizeAdjust->setTickmarkIntervals(2);
|
||||
wid.push_back(myVSizeAdjust);
|
||||
|
||||
|
||||
// Add items for tab 0
|
||||
addToFocusList(wid, myTab, tabID);
|
||||
}
|
||||
|
@ -480,10 +476,12 @@ void VideoAudioDialog::loadConfig()
|
|||
|
||||
// Fullscreen
|
||||
myFullscreen->setState(instance().settings().getBool("fullscreen"));
|
||||
/*string mode = instance().settings().getString("fullscreenmode");
|
||||
myFullScreenMode->setSelected(mode);*/
|
||||
// Fullscreen stretch setting
|
||||
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
|
||||
myTVOverscan->setValue(instance().settings().getInt("tia.fs_overscan"));
|
||||
handleFullScreenChange();
|
||||
|
@ -595,6 +593,10 @@ void VideoAudioDialog::saveConfig()
|
|||
instance().settings().setValue("fullscreen", myFullscreen->getState());
|
||||
// Fullscreen stretch setting
|
||||
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
|
||||
instance().settings().setValue("tia.fs_overscan", myTVOverscan->getValueLabel());
|
||||
|
||||
|
@ -611,7 +613,6 @@ void VideoAudioDialog::saveConfig()
|
|||
|
||||
// Note: Palette values are saved directly when changed!
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// TV Effects tab
|
||||
// TV Mode
|
||||
|
@ -706,8 +707,10 @@ void VideoAudioDialog::setDefaults()
|
|||
myTIAInterpolate->setState(false);
|
||||
// screen size
|
||||
myFullscreen->setState(false);
|
||||
//myFullScreenMode->setSelectedIndex(0);
|
||||
myUseStretch->setState(false);
|
||||
#ifdef ADAPTABLE_REFRESH_SUPPORT
|
||||
myRefreshAdapt->setState(false);
|
||||
#endif
|
||||
myTVOverscan->setValue(0);
|
||||
myTIAZoom->setValue(300);
|
||||
myVSizeAdjust->setValue(0);
|
||||
|
@ -833,6 +836,9 @@ void VideoAudioDialog::handleFullScreenChange()
|
|||
{
|
||||
bool enable = myFullscreen->getState();
|
||||
myUseStretch->setEnabled(enable);
|
||||
#ifdef ADAPTABLE_REFRESH_SUPPORT
|
||||
myRefreshAdapt->setEnabled(enable);
|
||||
#endif
|
||||
myTVOverscan->setEnabled(enable);
|
||||
}
|
||||
|
||||
|
|
|
@ -71,9 +71,9 @@ class VideoAudioDialog : public Dialog
|
|||
PopUpWidget* myRenderer{nullptr};
|
||||
CheckboxWidget* myTIAInterpolate{nullptr};
|
||||
CheckboxWidget* myFullscreen{nullptr};
|
||||
//PopUpWidget* myFullScreenMode;
|
||||
CheckboxWidget* myUseStretch{nullptr};
|
||||
SliderWidget* myTVOverscan{nullptr};
|
||||
CheckboxWidget* myRefreshAdapt{nullptr};
|
||||
SliderWidget* myTIAZoom{nullptr};
|
||||
SliderWidget* myVSizeAdjust{nullptr};
|
||||
|
||||
|
|
Loading…
Reference in New Issue