stella/src/emucore/FrameBuffer.cxx

1556 lines
50 KiB
C++
Raw Normal View History

//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#include "bspf.hxx"
#include "Logger.hxx"
#include "Console.hxx"
#include "EventHandler.hxx"
#include "Event.hxx"
#include "OSystem.hxx"
#include "Settings.hxx"
#include "TIA.hxx"
#include "Sound.hxx"
#include "FBSurface.hxx"
#include "TIASurface.hxx"
#include "FrameBuffer.hxx"
#ifdef DEBUGGER_SUPPORT
#include "Debugger.hxx"
#endif
#ifdef GUI_SUPPORT
#include "Font.hxx"
#include "StellaFont.hxx"
#include "ConsoleMediumFont.hxx"
#include "ConsoleMediumBFont.hxx"
#include "StellaMediumFont.hxx"
#include "StellaLargeFont.hxx"
#include "Stella12x24tFont.hxx"
#include "Stella14x28tFont.hxx"
#include "Stella16x32tFont.hxx"
#include "ConsoleFont.hxx"
#include "ConsoleBFont.hxx"
#include "Launcher.hxx"
#include "Menu.hxx"
#include "CommandMenu.hxx"
2020-01-19 14:45:32 +00:00
#include "MessageMenu.hxx"
#include "TimeMachine.hxx"
#endif
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBuffer::FrameBuffer(OSystem& osystem)
: myOSystem(osystem)
{
}
2017-11-17 17:00:17 +00:00
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBuffer::~FrameBuffer()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBuffer::initialize()
{
// Get desktop resolution and supported renderers
vector<Common::Size> windowedDisplays;
queryHardware(myFullscreenDisplays, windowedDisplays, myRenderers);
uInt32 query_w = windowedDisplays[0].w, query_h = windowedDisplays[0].h;
// Check the 'maxres' setting, which is an undocumented developer feature
// that specifies the desktop size (not normally set)
const Common::Size& s = myOSystem.settings().getSize("maxres");
if(s.valid())
{
query_w = s.w;
query_h = s.h;
}
// Various parts of the codebase assume a minimum screen size
myAbsDesktopSize.w = std::max(query_w, FBMinimum::Width);
myAbsDesktopSize.h = std::max(query_h, FBMinimum::Height);
myDesktopSize = myAbsDesktopSize;
// Check for HiDPI mode (is it activated, and can we use it?)
2019-05-13 17:04:39 +00:00
myHiDPIAllowed = ((myAbsDesktopSize.w / 2) >= FBMinimum::Width) &&
((myAbsDesktopSize.h / 2) >= FBMinimum::Height);
myHiDPIEnabled = myHiDPIAllowed && myOSystem.settings().getBool("hidpi");
// In HiDPI mode, the desktop resolution is essentially halved
// Later, the output is scaled and rendered in 2x mode
if(hidpiEnabled())
{
myDesktopSize.w = myAbsDesktopSize.w / hidpiScaleFactor();
myDesktopSize.h = myAbsDesktopSize.h / hidpiScaleFactor();
}
#ifdef GUI_SUPPORT
setupFonts();
#endif
// Determine possible TIA windowed zoom levels
myTIAMaxZoom = maxZoomForScreen(
TIAConstants::viewableWidth, TIAConstants::viewableHeight,
myAbsDesktopSize.w, myAbsDesktopSize.h);
setUIPalette();
myGrabMouse = myOSystem.settings().getBool("grabmouse");
// Create a TIA surface; we need it for rendering TIA images
myTIASurface = make_unique<TIASurface>(myOSystem);
return true;
}
#ifdef GUI_SUPPORT
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::setupFonts()
{
////////////////////////////////////////////////////////////////////
// Create fonts to draw text
// NOTE: the logic determining appropriate font sizes is done here,
// so that the UI classes can just use the font they expect,
// and not worry about it
// This logic should also take into account the size of the
// framebuffer, and try to be intelligent about font sizes
// We can probably add ifdefs to take care of corner cases,
// but that means we've failed to abstract it enough ...
////////////////////////////////////////////////////////////////////
// This font is used in a variety of situations when a really small
// font is needed; we let the specific widget/dialog decide when to
// use it
mySmallFont = make_unique<GUI::Font>(GUI::stellaDesc); // 6x10
if(myOSystem.settings().getBool("minimal_ui"))
2020-03-16 08:26:45 +00:00
{
// The general font used in all UI elements
myFont = make_unique<GUI::Font>(GUI::stella12x24tDesc); // 12x24
2020-03-16 08:26:45 +00:00
// The info font used in all UI elements
myInfoFont = make_unique<GUI::Font>(GUI::stellaLargeDesc); // 10x20
}
else
2020-03-16 08:26:45 +00:00
{
const int NUM_FONTS = 7;
FontDesc FONT_DESC[NUM_FONTS] = {GUI::consoleDesc, GUI::consoleMediumDesc, GUI::stellaMediumDesc,
GUI::stellaLargeDesc, GUI::stella12x24tDesc, GUI::stella14x28tDesc, GUI::stella16x32tDesc};
const string& dialogFont = myOSystem.settings().getString("dialogfont");
FontDesc fd = getFontDesc(dialogFont);
// The general font used in all UI elements
myFont = make_unique<GUI::Font>(fd); // default: 9x18
// The info font used in all UI elements,
// automatically determined aiming for 1 / 1.4 (~= 18 / 13) size
int fontIdx = 0;
for(int i = 0; i < NUM_FONTS; ++i)
{
if(fd.height <= FONT_DESC[i].height * 1.4)
{
fontIdx = i;
break;
}
}
myInfoFont = make_unique<GUI::Font>(FONT_DESC[fontIdx]); // default 8x13
// Determine minimal zoom level based on the default font
// So what fits with default font should fit for any font.
// However, we have to make sure all Dialogs are sized using the fontsize.
int zoom_h = (fd.height * 4 * 2) / GUI::stellaMediumDesc.height;
int zoom_w = (fd.maxwidth * 4 * 2) / GUI::stellaMediumDesc.maxwidth;
myTIAMinZoom = std::max(std::max(zoom_w, zoom_h) / 4.F, 2.F); // round to 25% steps, >= 200%
2020-03-16 08:26:45 +00:00
}
// The font used by the ROM launcher
const string& lf = myOSystem.settings().getString("launcherfont");
2018-08-12 21:01:43 +00:00
myLauncherFont = make_unique<GUI::Font>(getFontDesc(lf)); // 8x13
}
2018-08-12 21:01:43 +00:00
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FontDesc FrameBuffer::getFontDesc(const string& name) const
{
if(name == "small")
return GUI::consoleBDesc; // 8x13
else if(name == "low_medium")
return GUI::consoleMediumBDesc; // 9x15
else if(name == "medium")
return GUI::stellaMediumDesc; // 9x18
else if(name == "large" || name == "large10")
return GUI::stellaLargeDesc; // 10x20
else if(name == "large12")
return GUI::stella12x24tDesc; // 12x24
else if(name == "large14")
return GUI::stella14x28tDesc; // 14x28
else // "large16"
return GUI::stella16x32tDesc; // 16x32
2018-08-12 21:01:43 +00:00
}
#endif
2018-08-12 21:01:43 +00:00
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBInitStatus FrameBuffer::createDisplay(const string& title, BufferType type,
uInt32 width, uInt32 height,
bool honourHiDPI)
{
// always save, maybe only the mode of the window has changed
saveCurrentWindowPosition();
myBufferType = type;
++myInitializedCount;
myScreenTitle = title;
// In HiDPI mode, all created displays must be scaled by 2x
if(honourHiDPI && hidpiEnabled())
{
width *= hidpiScaleFactor();
height *= hidpiScaleFactor();
}
// A 'windowed' system is defined as one where the window size can be
// larger than the screen size, as there's some sort of window manager
// that takes care of it (all current desktop systems fall in this category)
// However, some systems have no concept of windowing, and have hard limits
// on how large a window can be (ie, the size of the 'desktop' is the
// absolute upper limit on window size)
//
// If the WINDOWED_SUPPORT macro is defined, we treat the system as the
// former type; if not, as the latter type
bool useFullscreen = false;
#ifdef WINDOWED_SUPPORT
// We assume that a desktop of at least minimum acceptable size means that
// we're running on a 'large' system, and the window size requirements
// can be relaxed
// Otherwise, we treat the system as if WINDOWED_SUPPORT is not defined
if(myDesktopSize.w < FBMinimum::Width && myDesktopSize.h < FBMinimum::Height &&
2018-01-15 13:44:09 +00:00
(myDesktopSize.w < width || myDesktopSize.h < height))
return FBInitStatus::FailTooLarge;
useFullscreen = myOSystem.settings().getBool("fullscreen");
#else
// Make sure this mode is even possible
// We only really need to worry about it in non-windowed environments,
// where requesting a window that's too large will probably cause a crash
if(myDesktopSize.w < width || myDesktopSize.h < height)
return FBInitStatus::FailTooLarge;
2019-03-31 18:39:14 +00:00
useFullscreen = true;
#endif
// Set the available video modes for this framebuffer
setAvailableVidModes(width, height);
// Initialize video subsystem (make sure we get a valid mode)
string pre_about = about();
const FrameBuffer::VideoMode& mode = getSavedVidMode(useFullscreen);
if(width <= mode.screen.w && height <= mode.screen.h)
{
// Changing the video mode can take some time, during which the last
// sound played may get 'stuck'
// So we mute the sound until the operation completes
bool oldMuteState = myOSystem.sound().mute(true);
if(setVideoMode(myScreenTitle, mode))
{
myImageRect = mode.image;
myScreenSize = mode.screen;
myScreenRect = Common::Rect(mode.screen);
// Inform TIA surface about new mode
if(myOSystem.eventHandler().state() != EventHandlerState::LAUNCHER &&
myOSystem.eventHandler().state() != EventHandlerState::DEBUGGER)
myTIASurface->initialize(myOSystem.console(), mode);
// Did we get the requested fullscreen state?
myOSystem.settings().setValue("fullscreen", fullScreen());
resetSurfaces();
setCursorState();
myOSystem.sound().mute(oldMuteState);
}
else
{
Logger::error("ERROR: Couldn't initialize video subsystem");
return FBInitStatus::FailNotSupported;
}
}
else
return FBInitStatus::FailTooLarge;
#ifdef GUI_SUPPORT
// Erase any messages from a previous run
myMsg.counter = 0;
// Create surfaces for TIA statistics and general messages
const GUI::Font& f = hidpiEnabled() ? infoFont() : font();
2017-12-19 15:56:01 +00:00
myStatsMsg.color = kColorInfo;
myStatsMsg.w = f.getMaxCharWidth() * 40 + 3;
myStatsMsg.h = (f.getFontHeight() + 2) * 3;
if(!myStatsMsg.surface)
2018-01-15 13:44:09 +00:00
{
myStatsMsg.surface = allocateSurface(myStatsMsg.w, myStatsMsg.h);
2018-01-15 13:44:09 +00:00
myStatsMsg.surface->attributes().blending = true;
myStatsMsg.surface->attributes().blendalpha = 92; //aligned with TimeMachineDialog
2018-01-15 13:44:09 +00:00
myStatsMsg.surface->applyAttributes();
}
if(!myMsg.surface)
{
const int fontWidth = font().getMaxCharWidth(),
HBORDER = fontWidth * 1.25 / 2.0;
myMsg.surface = allocateSurface(fontWidth * MESSAGE_WIDTH + HBORDER * 2,
font().getFontHeight() * 1.5);
}
#endif
// Print initial usage message, but only print it later if the status has changed
if(myInitializedCount == 1)
{
Logger::info(about());
}
else
{
string post_about = about();
if(post_about != pre_about)
Logger::info(post_about);
}
return FBInitStatus::Success;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::update(bool force)
{
// Onscreen messages are a special case and require different handling than
// other objects; they aren't UI dialogs in the normal sense nor are they
// TIA images, and they need to be rendered on top of everything
// The logic is split in two pieces:
// - at the top of ::update(), to determine whether underlying dialogs
// need to be force-redrawn
// - at the bottom of ::update(), to actually draw them (this must come
// last, since they are always drawn on top of everything else).
// Full rendering is required when messages are enabled
2019-12-25 16:37:03 +00:00
force = force || myMsg.counter >= 0;
// Detect when a message has been turned off; one last redraw is required
// in this case, to draw over the area that the message occupied
if(myMsg.counter == 0)
myMsg.counter = -1;
switch(myOSystem.eventHandler().state())
{
case EventHandlerState::NONE:
case EventHandlerState::EMULATION:
// Do nothing; emulation mode is handled separately (see below)
return;
case EventHandlerState::PAUSE:
{
// Show a pause message immediately and then every 7 seconds
if(myPausedCount-- <= 0)
{
2017-12-15 17:10:40 +00:00
myPausedCount = uInt32(7 * myOSystem.frameRate());
showMessage("Paused", MessagePosition::MiddleCenter);
}
if(force)
myTIASurface->render();
break; // EventHandlerState::PAUSE
}
#ifdef GUI_SUPPORT
case EventHandlerState::OPTIONSMENU:
{
2019-12-25 16:37:03 +00:00
force = force || myOSystem.menu().needsRedraw();
if(force)
{
clear();
myTIASurface->render();
myOSystem.menu().draw(force);
}
break; // EventHandlerState::OPTIONSMENU
}
case EventHandlerState::CMDMENU:
{
2019-12-25 16:37:03 +00:00
force = force || myOSystem.commandMenu().needsRedraw();
if(force)
{
clear();
myTIASurface->render();
myOSystem.commandMenu().draw(force);
}
break; // EventHandlerState::CMDMENU
}
2020-01-19 14:45:32 +00:00
case EventHandlerState::MESSAGEMENU:
{
force = force || myOSystem.messageMenu().needsRedraw();
if (force)
{
clear();
myTIASurface->render();
myOSystem.messageMenu().draw(force);
}
break; // EventHandlerState::MESSAGEMENU
}
case EventHandlerState::TIMEMACHINE:
{
2019-12-25 16:37:03 +00:00
force = force || myOSystem.timeMachine().needsRedraw();
if(force)
{
clear();
myTIASurface->render();
myOSystem.timeMachine().draw(force);
}
break; // EventHandlerState::TIMEMACHINE
}
case EventHandlerState::LAUNCHER:
{
2019-12-25 16:37:03 +00:00
force = force || myOSystem.launcher().needsRedraw();
if(force)
{
clear();
myOSystem.launcher().draw(force);
}
break; // EventHandlerState::LAUNCHER
}
#endif
#ifdef DEBUGGER_SUPPORT
case EventHandlerState::DEBUGGER:
{
2019-12-25 16:37:03 +00:00
force = force || myOSystem.debugger().needsRedraw();
if(force)
{
clear();
myOSystem.debugger().draw(force);
}
break; // EventHandlerState::DEBUGGER
}
#endif
default:
break;
}
// Draw any pending messages
// The logic here determines whether to draw the message
// If the message is to be disabled, logic inside the draw method
// indicates that, and then the code at the top of this method sees
// the change and redraws everything
if(myMsg.enabled)
drawMessage();
// Push buffers to screen only when necessary
if(force)
renderToScreen();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::updateInEmulationMode(float framesPerSecond)
{
// Update method that is specifically tailored to emulation mode
// Typically called from a thread, so it needs to be separate from
// the normal update() method
//
// We don't worry about selective rendering here; the rendering
// always happens at the full framerate
clear(); // TODO - test this: it may cause slowdowns on older systems
myTIASurface->render();
// Show frame statistics
if(myStatsMsg.enabled)
drawFrameStats(framesPerSecond);
myLastScanlines = myOSystem.console().tia().frameBufferScanlinesLastFrame();
myPausedCount = 0;
// Draw any pending messages
if(myMsg.enabled)
drawMessage();
// Push buffers to screen
renderToScreen();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::showMessage(const string& message, MessagePosition position,
bool force)
{
#ifdef GUI_SUPPORT
// Only show messages if they've been enabled
if(myMsg.surface == nullptr || !(force || myOSystem.settings().getBool("uimessages")))
return;
const int fontWidth = font().getMaxCharWidth(),
fontHeight = font().getFontHeight();
const int VBORDER = fontHeight / 4;
const int HBORDER = fontWidth * 1.25 / 2.0;
myMsg.counter = uInt32(myOSystem.frameRate()) * 2; // Show message for 2 seconds
if(myMsg.counter == 0)
myMsg.counter = 120;
// Precompute the message coordinates
myMsg.text = message;
myMsg.color = kBtnTextColor;
myMsg.showGauge = false;
myMsg.w = std::min(fontWidth * (MESSAGE_WIDTH) - HBORDER * 2,
font().getStringWidth(myMsg.text) + HBORDER * 2);
myMsg.h = fontHeight + VBORDER * 2;
myMsg.position = position;
myMsg.enabled = true;
myMsg.surface->setSrcSize(myMsg.w, myMsg.h);
myMsg.surface->setDstSize(myMsg.w * hidpiScaleFactor(), myMsg.h * hidpiScaleFactor());
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::showMessage(const string& message, const string& valueText,
float value, float minValue, float maxValue)
{
#ifdef GUI_SUPPORT
// Only show messages if they've been enabled
if(myMsg.surface == nullptr || !myOSystem.settings().getBool("uimessages"))
return;
const int fontWidth = font().getMaxCharWidth(),
fontHeight = font().getFontHeight();
const int VBORDER = fontHeight / 4;
const int HBORDER = fontWidth * 1.25 / 2.0;
myMsg.counter = uInt32(myOSystem.frameRate()) * 2; // Show message for 2 seconds
if(myMsg.counter == 0)
myMsg.counter = 120;
// Precompute the message coordinates
myMsg.text = message;
myMsg.color = kBtnTextColor;
myMsg.showGauge = true;
if(maxValue - minValue != 0)
myMsg.value = (value - minValue) / (maxValue - minValue) * 100.F;
else
myMsg.value = 100.F;
myMsg.valueText = valueText;
myMsg.w = std::min(fontWidth * MESSAGE_WIDTH,
font().getStringWidth(myMsg.text)
+ fontWidth * (GAUGEBAR_WIDTH + 2)
+ font().getStringWidth(myMsg.valueText))
+ HBORDER * 2;
myMsg.h = fontHeight + VBORDER * 2;
myMsg.position = MessagePosition::BottomCenter;
myMsg.enabled = true;
myMsg.surface->setSrcSize(myMsg.w, myMsg.h);
myMsg.surface->setDstSize(myMsg.w * hidpiScaleFactor(), myMsg.h * hidpiScaleFactor());
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBuffer::messageShown() const
{
#ifdef GUI_SUPPORT
return myMsg.enabled;
#else
return false;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::drawFrameStats(float framesPerSecond)
{
#ifdef GUI_SUPPORT
const ConsoleInfo& info = myOSystem.console().about();
int xPos = 2, yPos = 0;
const GUI::Font& f = hidpiEnabled() ? infoFont() : font();
const int dy = f.getFontHeight() + 2;
ostringstream ss;
myStatsMsg.surface->invalidate();
2018-02-05 12:45:34 +00:00
// draw scanlines
ColorId color = myOSystem.console().tia().frameBufferScanlinesLastFrame() !=
myLastScanlines ? kDbgColorRed : myStatsMsg.color;
ss
<< myOSystem.console().tia().frameBufferScanlinesLastFrame()
<< " / "
<< std::fixed << std::setprecision(1) << myOSystem.console().getFramerate()
<< "Hz => "
<< info.DisplayFormat;
myStatsMsg.surface->drawString(f, ss.str(), xPos, yPos,
myStatsMsg.w, color, TextAlign::Left, 0, true, kBGColor);
yPos += dy;
ss.str("");
ss
<< std::fixed << std::setprecision(1) << framesPerSecond
2018-07-31 15:07:51 +00:00
<< "fps @ "
2020-04-19 21:08:25 +00:00
<< std::fixed << std::setprecision(0) << 100 *
(myOSystem.settings().getBool("turbo")
? 20.0F
: myOSystem.settings().getFloat("speed"))
<< "% speed";
myStatsMsg.surface->drawString(f, ss.str(), xPos, yPos,
myStatsMsg.w, myStatsMsg.color, TextAlign::Left, 0, true, kBGColor);
yPos += dy;
ss.str("");
ss << info.BankSwitch;
if (myOSystem.settings().getBool("dev.settings")) ss << "| Developer";
myStatsMsg.surface->drawString(f, ss.str(), xPos, yPos,
myStatsMsg.w, myStatsMsg.color, TextAlign::Left, 0, true, kBGColor);
myStatsMsg.surface->setDstPos(myImageRect.x() + 10, myImageRect.y() + 8);
myStatsMsg.surface->setDstSize(myStatsMsg.w * hidpiScaleFactor(),
myStatsMsg.h * hidpiScaleFactor());
myStatsMsg.surface->render();
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::toggleFrameStats()
{
showFrameStats(!myStatsEnabled);
myOSystem.settings().setValue(
myOSystem.settings().getBool("dev.settings") ? "dev.stats" : "plr.stats", myStatsEnabled);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::showFrameStats(bool enable)
{
myStatsEnabled = myStatsMsg.enabled = enable;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::enableMessages(bool enable)
{
if(enable)
{
// Only re-enable frame stats if they were already enabled before
myStatsMsg.enabled = myStatsEnabled;
}
else
{
// Temporarily disable frame stats
myStatsMsg.enabled = false;
// Erase old messages on the screen
myMsg.enabled = false;
myMsg.counter = 0;
update(true); // Force update immediately
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline bool FrameBuffer::drawMessage()
{
#ifdef GUI_SUPPORT
// Either erase the entire message (when time is reached),
// or show again this frame
if(myMsg.counter == 0)
{
myMsg.enabled = false;
return true;
}
else if(myMsg.counter < 0)
{
myMsg.enabled = false;
return false;
}
// Draw the bounded box and text
const Common::Rect& dst = myMsg.surface->dstRect();
const int fontWidth = font().getMaxCharWidth(),
fontHeight = font().getFontHeight();
const int VBORDER = fontHeight / 4;
const int HBORDER = fontWidth * 1.25 / 2.0;
constexpr int BORDER = 1;
2017-12-22 02:34:17 +00:00
switch(myMsg.position)
{
case MessagePosition::TopLeft:
myMsg.x = 5;
myMsg.y = 5;
break;
case MessagePosition::TopCenter:
myMsg.x = (myImageRect.w() - dst.w()) >> 1;
myMsg.y = 5;
break;
case MessagePosition::TopRight:
myMsg.x = myImageRect.w() - dst.w() - 5;
myMsg.y = 5;
break;
case MessagePosition::MiddleLeft:
myMsg.x = 5;
myMsg.y = (myImageRect.h() - dst.h()) >> 1;
break;
case MessagePosition::MiddleCenter:
myMsg.x = (myImageRect.w() - dst.w()) >> 1;
myMsg.y = (myImageRect.h() - dst.h()) >> 1;
break;
case MessagePosition::MiddleRight:
myMsg.x = myImageRect.w() - dst.w() - 5;
myMsg.y = (myImageRect.h() - dst.h()) >> 1;
break;
case MessagePosition::BottomLeft:
myMsg.x = 5;
myMsg.y = myImageRect.h() - dst.h() - 5;
break;
case MessagePosition::BottomCenter:
myMsg.x = (myImageRect.w() - dst.w()) >> 1;
myMsg.y = myImageRect.h() - dst.h() - 5;
break;
case MessagePosition::BottomRight:
myMsg.x = myImageRect.w() - dst.w() - 5;
myMsg.y = myImageRect.h() - dst.h() - 5;
break;
}
myMsg.surface->setDstPos(myMsg.x + myImageRect.x(), myMsg.y + myImageRect.y());
myMsg.surface->fillRect(0, 0, myMsg.w, myMsg.h, kColor);
myMsg.surface->fillRect(BORDER, BORDER, myMsg.w - BORDER * 2, myMsg.h - BORDER * 2, kBtnColor);
myMsg.surface->drawString(font(), myMsg.text, HBORDER, VBORDER,
myMsg.w, myMsg.color);
if(myMsg.showGauge)
{
constexpr int NUM_TICKMARKS = 4;
// limit gauge bar width if texts are too long
const int swidth = std::min(fontWidth * GAUGEBAR_WIDTH,
fontWidth * (MESSAGE_WIDTH - 2)
- font().getStringWidth(myMsg.text)
- font().getStringWidth(myMsg.valueText));
const int bwidth = swidth * myMsg.value / 100.F;
const int bheight = fontHeight >> 1;
const int x = HBORDER + font().getStringWidth(myMsg.text) + fontWidth;
// align bar with bottom of text
const int y = VBORDER + font().desc().ascent - bheight;
// draw gauge bar
myMsg.surface->fillRect(x - BORDER, y, swidth + BORDER * 2, bheight, kSliderBGColor);
myMsg.surface->fillRect(x, y + BORDER, bwidth, bheight - BORDER * 2, kSliderColor);
// draw tickmark in the middle of the bar
for(int i = 1; i < NUM_TICKMARKS; ++i)
{
ColorId color;
int xt = x + swidth * i / NUM_TICKMARKS;
if(bwidth < xt - x)
color = kCheckColor; // kSliderColor;
else
color = kSliderBGColor;
myMsg.surface->vLine(xt, y + bheight / 2, y + bheight - 1, color);
}
// draw value text
myMsg.surface->drawString(font(), myMsg.valueText,
x + swidth + fontWidth, VBORDER,
myMsg.w, myMsg.color);
}
myMsg.surface->render();
myMsg.counter--;
#endif
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2017-12-15 17:10:40 +00:00
void FrameBuffer::setPauseDelay()
{
2017-12-15 17:10:40 +00:00
myPausedCount = uInt32(2 * myOSystem.frameRate());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
shared_ptr<FBSurface> FrameBuffer::allocateSurface(int w, int h, ScalingInterpolation interpolation, const uInt32* data)
{
// Add new surface to the list
mySurfaceList.push_back(createSurface(w, h, interpolation, data));
// And return a pointer to it (pointer should be treated read-only)
return mySurfaceList.at(mySurfaceList.size() - 1);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::freeSurfaces()
{
for(auto& s: mySurfaceList)
s->free();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::reloadSurfaces()
{
for(auto& s: mySurfaceList)
s->reload();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::resetSurfaces()
{
// Free all resources for each surface, then reload them
// Due to possible timing and/or synchronization issues, all free()'s
// are done first, then all reload()'s
// Any derived FrameBuffer classes that call this method should be
// aware of these restrictions, and act accordingly
freeSurfaces();
reloadSurfaces();
update(true); // force full update
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::setTIAPalette(const PaletteArray& rgb_palette)
{
// Create a TIA palette from the raw RGB data
PaletteArray tia_palette;
for(int i = 0; i < 256; ++i)
{
uInt8 r = (rgb_palette[i] >> 16) & 0xff;
uInt8 g = (rgb_palette[i] >> 8) & 0xff;
uInt8 b = rgb_palette[i] & 0xff;
tia_palette[i] = mapRGB(r, g, b);
}
// Remember the TIA palette; place it at the beginning of the full palette
std::copy_n(tia_palette.begin(), tia_palette.size(), myFullPalette.begin());
// Let the TIA surface know about the new palette
myTIASurface->setPalette(tia_palette, rgb_palette);
// Since the UI palette shares the TIA palette, we need to update it too
setUIPalette();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::setUIPalette()
{
// Set palette for UI (upper area of full palette)
const UIPaletteArray& ui_palette =
(myOSystem.settings().getString("uipalette") == "classic") ? ourClassicUIPalette :
(myOSystem.settings().getString("uipalette") == "light") ? ourLightUIPalette :
ourStandardUIPalette;
for(size_t i = 0, j = myFullPalette.size() - ui_palette.size();
i < ui_palette.size(); ++i, ++j)
{
const uInt8 r = (ui_palette[i] >> 16) & 0xff,
g = (ui_palette[i] >> 8) & 0xff,
b = ui_palette[i] & 0xff;
myFullPalette[j] = mapRGB(r, g, b);
}
FBSurface::setPalette(myFullPalette);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::stateChanged(EventHandlerState state)
{
// Make sure any onscreen messages are removed
myMsg.enabled = false;
myMsg.counter = 0;
update(true); // force full update
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string FrameBuffer::getDisplayKey()
{
// save current window's display and position
switch(myBufferType)
{
case BufferType::Launcher:
return "launcherdisplay";
case BufferType::Emulator:
return "display";
#ifdef DEBUGGER_SUPPORT
case BufferType::Debugger:
return "dbg.display";
#endif
default:
return "";
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string FrameBuffer::getPositionKey()
{
// save current window's display and position
switch(myBufferType)
{
case BufferType::Launcher:
return "launcherpos";
case BufferType::Emulator:
return "windowedpos";
#ifdef DEBUGGER_SUPPORT
case BufferType::Debugger:
return "dbg.pos";
#endif
default:
return "";
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::saveCurrentWindowPosition()
{
myOSystem.settings().setValue(getDisplayKey(), getCurrentDisplayIndex());
if(isCurrentWindowPositioned())
myOSystem.settings().setValue(getPositionKey(), getCurrentWindowPos());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::setFullscreen(bool enable)
{
#ifdef WINDOWED_SUPPORT
// Switching between fullscreen and windowed modes will invariably mean
// that the 'window' resolution changes. Currently, dialogs are not
// able to resize themselves when they are actively being shown
// (they would have to be closed and then re-opened, etc).
// For now, we simply disallow screen switches in such modes
switch(myOSystem.eventHandler().state())
{
case EventHandlerState::EMULATION:
case EventHandlerState::PAUSE:
break; // continue with processing (aka, allow a mode switch)
case EventHandlerState::DEBUGGER:
case EventHandlerState::LAUNCHER:
if(myOSystem.eventHandler().overlay().baseDialogIsActive())
break; // allow a mode switch when there is only one dialog
[[fallthrough]];
default:
return;
}
saveCurrentWindowPosition();
// Changing the video mode can take some time, during which the last
// sound played may get 'stuck'
// So we mute the sound until the operation completes
bool oldMuteState = myOSystem.sound().mute(true);
const VideoMode& mode = getSavedVidMode(enable);
if(setVideoMode(myScreenTitle, mode))
{
myImageRect = mode.image;
myScreenSize = mode.screen;
myScreenRect = Common::Rect(mode.screen);
// Inform TIA surface about new mode
if(myOSystem.eventHandler().state() != EventHandlerState::LAUNCHER &&
myOSystem.eventHandler().state() != EventHandlerState::DEBUGGER)
myTIASurface->initialize(myOSystem.console(), mode);
// Did we get the requested fullscreen state?
myOSystem.settings().setValue("fullscreen", fullScreen());
resetSurfaces();
setCursorState();
}
myOSystem.sound().mute(oldMuteState);
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::toggleFullscreen(bool toggle)
{
const bool isFullscreen = toggle ? !fullScreen() : fullScreen();
setFullscreen(isFullscreen);
if(myBufferType == BufferType::Emulator)
{
ostringstream msg;
2020-05-18 19:13:18 +00:00
msg << "Fullscreen ";
if(isFullscreen)
msg << "enabled (" << refreshRate() << " Hz)";
else
msg << "disabled";
2020-05-18 19:13:18 +00:00
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)
{
if (fullScreen())
{
int oldOverscan = myOSystem.settings().getInt("tia.fs_overscan");
int overscan = BSPF::clamp(oldOverscan + direction, 0, 10);
if (overscan != oldOverscan)
{
myOSystem.settings().setValue("tia.fs_overscan", overscan);
// issue a complete framebuffer re-initialization
myOSystem.createFrameBuffer();
}
ostringstream val;
if(overscan)
val << (overscan > 0 ? "+" : "" ) << overscan << "%";
else
val << "Off";
myOSystem.frameBuffer().showMessage("Overscan", val.str(), overscan, 0, 10);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::selectVidMode(int direction)
{
EventHandlerState state = myOSystem.eventHandler().state();
bool tiaMode = (state != EventHandlerState::DEBUGGER &&
state != EventHandlerState::LAUNCHER);
// Only applicable when in TIA/emulation mode
if(!tiaMode)
return;
if(direction == +1)
myCurrentModeList->next();
else if(direction == -1)
myCurrentModeList->previous();
saveCurrentWindowPosition();
// Changing the video mode can take some time, during which the last
// sound played may get 'stuck'
// So we mute the sound until the operation completes
bool oldMuteState = myOSystem.sound().mute(true);
const VideoMode& mode = myCurrentModeList->current();
if(setVideoMode(myScreenTitle, mode))
{
myImageRect = mode.image;
myScreenSize = mode.screen;
myScreenRect = Common::Rect(mode.screen);
// Inform TIA surface about new mode
myTIASurface->initialize(myOSystem.console(), mode);
resetSurfaces();
if(fullScreen())
showMessage(mode.description);
else
showMessage("Zoom", mode.description, mode.zoom, supportedTIAMinZoom(), myTIAMaxZoom);
myOSystem.sound().mute(oldMuteState);
if(fullScreen())
myOSystem.settings().setValue("tia.fs_stretch",
mode.stretch == VideoMode::Stretch::Fill);
else
myOSystem.settings().setValue("tia.zoom", mode.zoom);
return;
}
myOSystem.sound().mute(oldMuteState);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::setCursorState()
{
// Always grab mouse in emulation (if enabled) and emulating a controller
// that always uses the mouse
bool emulation =
myOSystem.eventHandler().state() == EventHandlerState::EMULATION;
bool analog = myOSystem.hasConsole() ?
(myOSystem.console().leftController().isAnalog() ||
myOSystem.console().rightController().isAnalog()) : false;
2019-12-30 16:27:28 +00:00
bool usesLightgun = emulation && myOSystem.hasConsole() ?
2019-12-30 08:11:46 +00:00
myOSystem.console().leftController().type() == Controller::Type::Lightgun ||
myOSystem.console().rightController().type() == Controller::Type::Lightgun : false;
bool alwaysUseMouse = BSPF::equalsIgnoreCase("always", myOSystem.settings().getString("usemouse"));
// Show/hide cursor in UI/emulation mode based on 'cursor' setting
int cursor = myOSystem.settings().getInt("cursor");
2019-12-30 16:27:28 +00:00
// always enable cursor in lightgun games
if (usesLightgun && !myGrabMouse)
cursor |= 1; // +Emulation
2019-12-30 16:27:28 +00:00
switch(cursor)
{
case 0: // -UI, -Emulation
showCursor(false);
break;
case 1:
showCursor(emulation); //-UI, +Emulation
myGrabMouse = false; // disable grab while cursor is shown in emulation
break;
case 2: // +UI, -Emulation
showCursor(!emulation);
break;
case 3:
showCursor(true); // +UI, +Emulation
myGrabMouse = false; // disable grab while cursor is shown in emulation
break;
}
grabMouse(emulation && (analog || alwaysUseMouse) && myGrabMouse);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::enableGrabMouse(bool enable)
{
myGrabMouse = enable;
setCursorState();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::toggleGrabMouse()
{
2020-05-14 09:12:56 +00:00
const bool oldState = myGrabMouse;
myGrabMouse = !myGrabMouse;
setCursorState();
myOSystem.settings().setValue("grabmouse", myGrabMouse);
2020-05-14 09:12:56 +00:00
myOSystem.frameBuffer().showMessage(oldState != myGrabMouse ? myGrabMouse
? "Grab mouse enabled" : "Grab mouse disabled"
: "Grab mouse not allowed while cursor shown");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
float FrameBuffer::maxZoomForScreen(uInt32 baseWidth, uInt32 baseHeight,
uInt32 screenWidth, uInt32 screenHeight) const
{
float multiplier = 1;
Added 'WINDOWED_SUPPORT' compile-time argument, which can be used for those systems which don't actually have a windowing environment. When this is set, toggling from fullscreen will not be possible, and certain window-related UI functions will not be accessible. Completely revamped video subsystem. Windowed and fullscreen modes are now dealt with separately. Windows can be zoomed using the 'zoom_ui' and 'zoom_tia' arguments. Fullscreen modes are now set by resolution, not zoom, so you can specify to always use a certain fullscreen resolution, and the images will be scaled appropriately. This also fixes the fullscreen issues on widescreen monitors; just select a widescreen video mode, and the aspect ratio will always be correct. Removed dirty-rect support for software rendering of the TIA image, as it ended up being slower than just updating the entire image. For those resolutions where it will start to slow down (1024x768 or higher), one should be using OpenGL. Fixed issue in Windows when returning from fullscreen mode made the window constantly 'shrink' in size. It was related to auto-detecting the desktop resolution, which is really the job of SDL. As such, all further releases of Stella will require SDL 1.2.10, which includes this auto-detection code internally. Made ROM launcher resizable, configurable in sizes from 320x240 to 800x600. Updated the UIDialog to change these quantities from the UI (Stella will need to be restarted for it to take effect). Removed aspect ratio support, since it was causing problems, and the new fullscreen mode work has made it obsolete. i *may* consider it again in the future, if there's sufficient demand. Added 'fullres' commandline argument, used to set the fullscreen resolution. Added 'launcherres' commandline argument, used to set the ROM launcher resolution. This replaces 'launchersize' argument, which has been removed. Changed 'scale_ui' and 'scale_tia' to 'zoom_ui' and 'zoom_tia', respectively. Their function remains the same. Changed meaning of 'gl_fsmax' argument to specify what modes to use fullscreen OpenGL scaling (previously, this was a boolean, and didn't consider different modes). git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1323 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2007-06-20 16:33:23 +00:00
for(;;)
{
Added 'WINDOWED_SUPPORT' compile-time argument, which can be used for those systems which don't actually have a windowing environment. When this is set, toggling from fullscreen will not be possible, and certain window-related UI functions will not be accessible. Completely revamped video subsystem. Windowed and fullscreen modes are now dealt with separately. Windows can be zoomed using the 'zoom_ui' and 'zoom_tia' arguments. Fullscreen modes are now set by resolution, not zoom, so you can specify to always use a certain fullscreen resolution, and the images will be scaled appropriately. This also fixes the fullscreen issues on widescreen monitors; just select a widescreen video mode, and the aspect ratio will always be correct. Removed dirty-rect support for software rendering of the TIA image, as it ended up being slower than just updating the entire image. For those resolutions where it will start to slow down (1024x768 or higher), one should be using OpenGL. Fixed issue in Windows when returning from fullscreen mode made the window constantly 'shrink' in size. It was related to auto-detecting the desktop resolution, which is really the job of SDL. As such, all further releases of Stella will require SDL 1.2.10, which includes this auto-detection code internally. Made ROM launcher resizable, configurable in sizes from 320x240 to 800x600. Updated the UIDialog to change these quantities from the UI (Stella will need to be restarted for it to take effect). Removed aspect ratio support, since it was causing problems, and the new fullscreen mode work has made it obsolete. i *may* consider it again in the future, if there's sufficient demand. Added 'fullres' commandline argument, used to set the fullscreen resolution. Added 'launcherres' commandline argument, used to set the ROM launcher resolution. This replaces 'launchersize' argument, which has been removed. Changed 'scale_ui' and 'scale_tia' to 'zoom_ui' and 'zoom_tia', respectively. Their function remains the same. Changed meaning of 'gl_fsmax' argument to specify what modes to use fullscreen OpenGL scaling (previously, this was a boolean, and didn't consider different modes). git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1323 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2007-06-20 16:33:23 +00:00
// Figure out the zoomed size of the window
uInt32 width = baseWidth * multiplier;
uInt32 height = baseHeight * multiplier;
Added 'WINDOWED_SUPPORT' compile-time argument, which can be used for those systems which don't actually have a windowing environment. When this is set, toggling from fullscreen will not be possible, and certain window-related UI functions will not be accessible. Completely revamped video subsystem. Windowed and fullscreen modes are now dealt with separately. Windows can be zoomed using the 'zoom_ui' and 'zoom_tia' arguments. Fullscreen modes are now set by resolution, not zoom, so you can specify to always use a certain fullscreen resolution, and the images will be scaled appropriately. This also fixes the fullscreen issues on widescreen monitors; just select a widescreen video mode, and the aspect ratio will always be correct. Removed dirty-rect support for software rendering of the TIA image, as it ended up being slower than just updating the entire image. For those resolutions where it will start to slow down (1024x768 or higher), one should be using OpenGL. Fixed issue in Windows when returning from fullscreen mode made the window constantly 'shrink' in size. It was related to auto-detecting the desktop resolution, which is really the job of SDL. As such, all further releases of Stella will require SDL 1.2.10, which includes this auto-detection code internally. Made ROM launcher resizable, configurable in sizes from 320x240 to 800x600. Updated the UIDialog to change these quantities from the UI (Stella will need to be restarted for it to take effect). Removed aspect ratio support, since it was causing problems, and the new fullscreen mode work has made it obsolete. i *may* consider it again in the future, if there's sufficient demand. Added 'fullres' commandline argument, used to set the fullscreen resolution. Added 'launcherres' commandline argument, used to set the ROM launcher resolution. This replaces 'launchersize' argument, which has been removed. Changed 'scale_ui' and 'scale_tia' to 'zoom_ui' and 'zoom_tia', respectively. Their function remains the same. Changed meaning of 'gl_fsmax' argument to specify what modes to use fullscreen OpenGL scaling (previously, this was a boolean, and didn't consider different modes). git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1323 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2007-06-20 16:33:23 +00:00
if((width > screenWidth) || (height > screenHeight))
break;
multiplier += ZOOM_STEPS;
Added 'WINDOWED_SUPPORT' compile-time argument, which can be used for those systems which don't actually have a windowing environment. When this is set, toggling from fullscreen will not be possible, and certain window-related UI functions will not be accessible. Completely revamped video subsystem. Windowed and fullscreen modes are now dealt with separately. Windows can be zoomed using the 'zoom_ui' and 'zoom_tia' arguments. Fullscreen modes are now set by resolution, not zoom, so you can specify to always use a certain fullscreen resolution, and the images will be scaled appropriately. This also fixes the fullscreen issues on widescreen monitors; just select a widescreen video mode, and the aspect ratio will always be correct. Removed dirty-rect support for software rendering of the TIA image, as it ended up being slower than just updating the entire image. For those resolutions where it will start to slow down (1024x768 or higher), one should be using OpenGL. Fixed issue in Windows when returning from fullscreen mode made the window constantly 'shrink' in size. It was related to auto-detecting the desktop resolution, which is really the job of SDL. As such, all further releases of Stella will require SDL 1.2.10, which includes this auto-detection code internally. Made ROM launcher resizable, configurable in sizes from 320x240 to 800x600. Updated the UIDialog to change these quantities from the UI (Stella will need to be restarted for it to take effect). Removed aspect ratio support, since it was causing problems, and the new fullscreen mode work has made it obsolete. i *may* consider it again in the future, if there's sufficient demand. Added 'fullres' commandline argument, used to set the fullscreen resolution. Added 'launcherres' commandline argument, used to set the ROM launcher resolution. This replaces 'launchersize' argument, which has been removed. Changed 'scale_ui' and 'scale_tia' to 'zoom_ui' and 'zoom_tia', respectively. Their function remains the same. Changed meaning of 'gl_fsmax' argument to specify what modes to use fullscreen OpenGL scaling (previously, this was a boolean, and didn't consider different modes). git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1323 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2007-06-20 16:33:23 +00:00
}
return multiplier > 1 ? multiplier - ZOOM_STEPS : 1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::setAvailableVidModes(uInt32 baseWidth, uInt32 baseHeight)
{
Added 'WINDOWED_SUPPORT' compile-time argument, which can be used for those systems which don't actually have a windowing environment. When this is set, toggling from fullscreen will not be possible, and certain window-related UI functions will not be accessible. Completely revamped video subsystem. Windowed and fullscreen modes are now dealt with separately. Windows can be zoomed using the 'zoom_ui' and 'zoom_tia' arguments. Fullscreen modes are now set by resolution, not zoom, so you can specify to always use a certain fullscreen resolution, and the images will be scaled appropriately. This also fixes the fullscreen issues on widescreen monitors; just select a widescreen video mode, and the aspect ratio will always be correct. Removed dirty-rect support for software rendering of the TIA image, as it ended up being slower than just updating the entire image. For those resolutions where it will start to slow down (1024x768 or higher), one should be using OpenGL. Fixed issue in Windows when returning from fullscreen mode made the window constantly 'shrink' in size. It was related to auto-detecting the desktop resolution, which is really the job of SDL. As such, all further releases of Stella will require SDL 1.2.10, which includes this auto-detection code internally. Made ROM launcher resizable, configurable in sizes from 320x240 to 800x600. Updated the UIDialog to change these quantities from the UI (Stella will need to be restarted for it to take effect). Removed aspect ratio support, since it was causing problems, and the new fullscreen mode work has made it obsolete. i *may* consider it again in the future, if there's sufficient demand. Added 'fullres' commandline argument, used to set the fullscreen resolution. Added 'launcherres' commandline argument, used to set the ROM launcher resolution. This replaces 'launchersize' argument, which has been removed. Changed 'scale_ui' and 'scale_tia' to 'zoom_ui' and 'zoom_tia', respectively. Their function remains the same. Changed meaning of 'gl_fsmax' argument to specify what modes to use fullscreen OpenGL scaling (previously, this was a boolean, and didn't consider different modes). git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1323 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2007-06-20 16:33:23 +00:00
myWindowedModeList.clear();
for(auto& mode: myFullscreenModeLists)
mode.clear();
for(size_t i = myFullscreenModeLists.size(); i < myFullscreenDisplays.size(); ++i)
myFullscreenModeLists.emplace_back(VideoModeList());
// Check if zooming is allowed for this state (currently only allowed
// for TIA screens)
EventHandlerState state = myOSystem.eventHandler().state();
bool tiaMode = (state != EventHandlerState::DEBUGGER &&
state != EventHandlerState::LAUNCHER);
float overscan = 1 - myOSystem.settings().getInt("tia.fs_overscan") / 100.0;
Added 'WINDOWED_SUPPORT' compile-time argument, which can be used for those systems which don't actually have a windowing environment. When this is set, toggling from fullscreen will not be possible, and certain window-related UI functions will not be accessible. Completely revamped video subsystem. Windowed and fullscreen modes are now dealt with separately. Windows can be zoomed using the 'zoom_ui' and 'zoom_tia' arguments. Fullscreen modes are now set by resolution, not zoom, so you can specify to always use a certain fullscreen resolution, and the images will be scaled appropriately. This also fixes the fullscreen issues on widescreen monitors; just select a widescreen video mode, and the aspect ratio will always be correct. Removed dirty-rect support for software rendering of the TIA image, as it ended up being slower than just updating the entire image. For those resolutions where it will start to slow down (1024x768 or higher), one should be using OpenGL. Fixed issue in Windows when returning from fullscreen mode made the window constantly 'shrink' in size. It was related to auto-detecting the desktop resolution, which is really the job of SDL. As such, all further releases of Stella will require SDL 1.2.10, which includes this auto-detection code internally. Made ROM launcher resizable, configurable in sizes from 320x240 to 800x600. Updated the UIDialog to change these quantities from the UI (Stella will need to be restarted for it to take effect). Removed aspect ratio support, since it was causing problems, and the new fullscreen mode work has made it obsolete. i *may* consider it again in the future, if there's sufficient demand. Added 'fullres' commandline argument, used to set the fullscreen resolution. Added 'launcherres' commandline argument, used to set the ROM launcher resolution. This replaces 'launchersize' argument, which has been removed. Changed 'scale_ui' and 'scale_tia' to 'zoom_ui' and 'zoom_tia', respectively. Their function remains the same. Changed meaning of 'gl_fsmax' argument to specify what modes to use fullscreen OpenGL scaling (previously, this was a boolean, and didn't consider different modes). git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1323 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2007-06-20 16:33:23 +00:00
// TIA mode allows zooming at integral factors in windowed modes,
// and also non-integral factors in fullscreen mode
if(tiaMode)
{
// TIA windowed modes
float minZoom = supportedTIAMinZoom();
myTIAMaxZoom = maxZoomForScreen(baseWidth, baseHeight,
2019-05-15 19:08:55 +00:00
myAbsDesktopSize.w, myAbsDesktopSize.h);
// Determine all zoom levels
for(float zoom = minZoom; zoom <= myTIAMaxZoom; zoom += ZOOM_STEPS)
{
ostringstream desc;
desc << (zoom * 100) << "%";
VideoMode mode(baseWidth*zoom, baseHeight*zoom, baseWidth*zoom, baseHeight*zoom,
2019-05-15 19:08:55 +00:00
VideoMode::Stretch::Fill, 1.0, desc.str(), zoom);
myWindowedModeList.add(mode);
}
// TIA fullscreen mode
for(uInt32 i = 0; i < myFullscreenDisplays.size(); ++i)
{
myTIAMaxZoom = maxZoomForScreen(baseWidth, baseHeight,
myFullscreenDisplays[i].w * overscan,
myFullscreenDisplays[i].h * overscan);
// Add both normal aspect and filled modes
// It's easier to define them both now, and simply switch between
// them when necessary
VideoMode mode1(baseWidth * myTIAMaxZoom, baseHeight * myTIAMaxZoom,
myFullscreenDisplays[i].w, myFullscreenDisplays[i].h,
VideoMode::Stretch::Preserve, overscan,
"Fullscreen: Preserve aspect, no stretch", myTIAMaxZoom, i);
myFullscreenModeLists[i].add(mode1);
VideoMode mode2(baseWidth * myTIAMaxZoom, baseHeight * myTIAMaxZoom,
myFullscreenDisplays[i].w, myFullscreenDisplays[i].h,
VideoMode::Stretch::Fill, overscan,
"Fullscreen: Ignore aspect, full stretch", myTIAMaxZoom, i);
myFullscreenModeLists[i].add(mode2);
}
}
else // UI mode
{
// Windowed and fullscreen mode differ only in screen size
myWindowedModeList.add(
VideoMode(baseWidth, baseHeight, baseWidth, baseHeight,
VideoMode::Stretch::None)
);
for(uInt32 i = 0; i < myFullscreenDisplays.size(); ++i)
{
myFullscreenModeLists[i].add(
VideoMode(baseWidth, baseHeight,
myFullscreenDisplays[i].w, myFullscreenDisplays[i].h,
VideoMode::Stretch::None, 1.0, "", 1, i)
);
}
}
Added 'WINDOWED_SUPPORT' compile-time argument, which can be used for those systems which don't actually have a windowing environment. When this is set, toggling from fullscreen will not be possible, and certain window-related UI functions will not be accessible. Completely revamped video subsystem. Windowed and fullscreen modes are now dealt with separately. Windows can be zoomed using the 'zoom_ui' and 'zoom_tia' arguments. Fullscreen modes are now set by resolution, not zoom, so you can specify to always use a certain fullscreen resolution, and the images will be scaled appropriately. This also fixes the fullscreen issues on widescreen monitors; just select a widescreen video mode, and the aspect ratio will always be correct. Removed dirty-rect support for software rendering of the TIA image, as it ended up being slower than just updating the entire image. For those resolutions where it will start to slow down (1024x768 or higher), one should be using OpenGL. Fixed issue in Windows when returning from fullscreen mode made the window constantly 'shrink' in size. It was related to auto-detecting the desktop resolution, which is really the job of SDL. As such, all further releases of Stella will require SDL 1.2.10, which includes this auto-detection code internally. Made ROM launcher resizable, configurable in sizes from 320x240 to 800x600. Updated the UIDialog to change these quantities from the UI (Stella will need to be restarted for it to take effect). Removed aspect ratio support, since it was causing problems, and the new fullscreen mode work has made it obsolete. i *may* consider it again in the future, if there's sufficient demand. Added 'fullres' commandline argument, used to set the fullscreen resolution. Added 'launcherres' commandline argument, used to set the ROM launcher resolution. This replaces 'launchersize' argument, which has been removed. Changed 'scale_ui' and 'scale_tia' to 'zoom_ui' and 'zoom_tia', respectively. Their function remains the same. Changed meaning of 'gl_fsmax' argument to specify what modes to use fullscreen OpenGL scaling (previously, this was a boolean, and didn't consider different modes). git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1323 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2007-06-20 16:33:23 +00:00
}
Added 'WINDOWED_SUPPORT' compile-time argument, which can be used for those systems which don't actually have a windowing environment. When this is set, toggling from fullscreen will not be possible, and certain window-related UI functions will not be accessible. Completely revamped video subsystem. Windowed and fullscreen modes are now dealt with separately. Windows can be zoomed using the 'zoom_ui' and 'zoom_tia' arguments. Fullscreen modes are now set by resolution, not zoom, so you can specify to always use a certain fullscreen resolution, and the images will be scaled appropriately. This also fixes the fullscreen issues on widescreen monitors; just select a widescreen video mode, and the aspect ratio will always be correct. Removed dirty-rect support for software rendering of the TIA image, as it ended up being slower than just updating the entire image. For those resolutions where it will start to slow down (1024x768 or higher), one should be using OpenGL. Fixed issue in Windows when returning from fullscreen mode made the window constantly 'shrink' in size. It was related to auto-detecting the desktop resolution, which is really the job of SDL. As such, all further releases of Stella will require SDL 1.2.10, which includes this auto-detection code internally. Made ROM launcher resizable, configurable in sizes from 320x240 to 800x600. Updated the UIDialog to change these quantities from the UI (Stella will need to be restarted for it to take effect). Removed aspect ratio support, since it was causing problems, and the new fullscreen mode work has made it obsolete. i *may* consider it again in the future, if there's sufficient demand. Added 'fullres' commandline argument, used to set the fullscreen resolution. Added 'launcherres' commandline argument, used to set the ROM launcher resolution. This replaces 'launchersize' argument, which has been removed. Changed 'scale_ui' and 'scale_tia' to 'zoom_ui' and 'zoom_tia', respectively. Their function remains the same. Changed meaning of 'gl_fsmax' argument to specify what modes to use fullscreen OpenGL scaling (previously, this was a boolean, and didn't consider different modes). git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1323 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2007-06-20 16:33:23 +00:00
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const FrameBuffer::VideoMode& FrameBuffer::getSavedVidMode(bool fullscreen)
Added 'WINDOWED_SUPPORT' compile-time argument, which can be used for those systems which don't actually have a windowing environment. When this is set, toggling from fullscreen will not be possible, and certain window-related UI functions will not be accessible. Completely revamped video subsystem. Windowed and fullscreen modes are now dealt with separately. Windows can be zoomed using the 'zoom_ui' and 'zoom_tia' arguments. Fullscreen modes are now set by resolution, not zoom, so you can specify to always use a certain fullscreen resolution, and the images will be scaled appropriately. This also fixes the fullscreen issues on widescreen monitors; just select a widescreen video mode, and the aspect ratio will always be correct. Removed dirty-rect support for software rendering of the TIA image, as it ended up being slower than just updating the entire image. For those resolutions where it will start to slow down (1024x768 or higher), one should be using OpenGL. Fixed issue in Windows when returning from fullscreen mode made the window constantly 'shrink' in size. It was related to auto-detecting the desktop resolution, which is really the job of SDL. As such, all further releases of Stella will require SDL 1.2.10, which includes this auto-detection code internally. Made ROM launcher resizable, configurable in sizes from 320x240 to 800x600. Updated the UIDialog to change these quantities from the UI (Stella will need to be restarted for it to take effect). Removed aspect ratio support, since it was causing problems, and the new fullscreen mode work has made it obsolete. i *may* consider it again in the future, if there's sufficient demand. Added 'fullres' commandline argument, used to set the fullscreen resolution. Added 'launcherres' commandline argument, used to set the ROM launcher resolution. This replaces 'launchersize' argument, which has been removed. Changed 'scale_ui' and 'scale_tia' to 'zoom_ui' and 'zoom_tia', respectively. Their function remains the same. Changed meaning of 'gl_fsmax' argument to specify what modes to use fullscreen OpenGL scaling (previously, this was a boolean, and didn't consider different modes). git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1323 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2007-06-20 16:33:23 +00:00
{
if(fullscreen)
{
Int32 i = getCurrentDisplayIndex();
if(i < 0)
{
// default to the first display
i = 0;
}
myCurrentModeList = &myFullscreenModeLists[i];
}
else
myCurrentModeList = &myWindowedModeList;
// Now select the best resolution depending on the state
// UI modes (launcher and debugger) have only one supported resolution
// so the 'current' one is the only valid one
EventHandlerState state = myOSystem.eventHandler().state();
if(state == EventHandlerState::DEBUGGER || state == EventHandlerState::LAUNCHER)
myCurrentModeList->setByZoom(1);
else // TIA mode
{
if(fullscreen)
myCurrentModeList->setByStretch(myOSystem.settings().getBool("tia.fs_stretch")
? VideoMode::Stretch::Fill : VideoMode::Stretch::Preserve);
else
myCurrentModeList->setByZoom(myOSystem.settings().getFloat("tia.zoom"));
}
return myCurrentModeList->current();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
// VideoMode implementation
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBuffer::VideoMode::VideoMode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh,
Stretch smode, float overscan, const string& desc,
float zoomLevel, Int32 fsindex)
: stretch(smode),
description(desc),
zoom(zoomLevel),
fsIndex(fsindex)
{
// First set default size and positioning
sw = std::max(sw, TIAConstants::viewableWidth);
sh = std::max(sh, TIAConstants::viewableHeight);
iw = std::min(iw, sw);
ih = std::min(ih, sh);
int ix = (sw - iw) >> 1;
int iy = (sh - ih) >> 1;
image = Common::Rect(ix, iy, ix+iw, iy+ih);
screen = Common::Size(sw, sh);
// Now resize based on windowed/fullscreen mode and stretch factor
iw = image.w();
ih = image.h();
if(fsIndex != -1)
{
switch(stretch)
{
case Stretch::Preserve:
{
float stretchFactor = 1.0;
float scaleX = float(iw) / screen.w;
float scaleY = float(ih) / screen.h;
// Scale to all available space, keep aspect correct
if(scaleX > scaleY)
stretchFactor = float(screen.w) / iw;
else
stretchFactor = float(screen.h) / ih;
iw = uInt32(stretchFactor * iw) * overscan;
ih = uInt32(stretchFactor * ih) * overscan;
break;
}
case Stretch::Fill:
// Scale to all available space
iw = screen.w * overscan;
ih = screen.h * overscan;
break;
case Stretch::None:
// Don't do any scaling at all, but obey overscan
iw = std::min(iw, screen.w) * overscan;
ih = std::min(ih, screen.h) * overscan;
break;
}
}
else
{
// In windowed mode, currently the size is scaled to the screen
// TODO - this may be updated if/when we allow variable-sized windows
switch(stretch)
{
case Stretch::Preserve:
case Stretch::Fill:
screen.w = iw;
screen.h = ih;
break;
case Stretch::None:
break; // Do not change image or screen rects whatsoever
}
}
// Now re-calculate the dimensions
iw = std::min(iw, screen.w);
ih = std::min(ih, screen.h);
image.moveTo((screen.w - iw) >> 1, (screen.h - ih) >> 1);
image.setWidth(iw);
image.setHeight(ih);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
// VideoModeList implementation
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::VideoModeList::add(const VideoMode& mode)
{
myModeList.emplace_back(mode);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::VideoModeList::clear()
{
myModeList.clear();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBuffer::VideoModeList::empty() const
{
return myModeList.empty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FrameBuffer::VideoModeList::size() const
{
return uInt32(myModeList.size());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::VideoModeList::previous()
{
--myIdx;
if(myIdx < 0) myIdx = int(myModeList.size()) - 1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const FrameBuffer::VideoMode& FrameBuffer::VideoModeList::current() const
{
return myModeList[myIdx];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::VideoModeList::next()
{
myIdx = (myIdx + 1) % myModeList.size();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::VideoModeList::setByZoom(float zoom)
{
for(uInt32 i = 0; i < myModeList.size(); ++i)
{
if(myModeList[i].zoom == zoom)
{
myIdx = i;
return;
}
}
myIdx = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::VideoModeList::setByStretch(FrameBuffer::VideoMode::Stretch stretch)
{
for(uInt32 i = 0; i < myModeList.size(); ++i)
{
if(myModeList[i].stretch == stretch)
{
myIdx = i;
return;
}
}
myIdx = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/*
Palette is defined as follows:
2017-12-17 09:09:59 +00:00
*** Base colors ***
kColor Normal foreground color (non-text)
kBGColor Normal background color (non-text)
kBGColorLo Disabled background color dark (non-text)
kBGColorHi Disabled background color light (non-text)
kShadowColor Item is disabled
2017-12-17 09:09:59 +00:00
*** Text colors ***
kTextColor Normal text color
kTextColorHi Highlighted text color
kTextColorEm Emphasized text color
kTextColorInv Color for selected text
2017-12-17 09:09:59 +00:00
*** UI elements (dialog and widgets) ***
kDlgColor Dialog background
kWidColor Widget background
kWidColorHi Widget highlight color
kWidFrameColor Border for currently selected widget
2017-12-17 09:09:59 +00:00
*** Button colors ***
kBtnColor Normal button background
kBtnColorHi Highlighted button background
kBtnBorderColor,
kBtnBorderColorHi,
kBtnTextColor Normal button font color
kBtnTextColorHi Highlighted button font color
2017-12-17 09:09:59 +00:00
*** Checkbox colors ***
kCheckColor Color of 'X' in checkbox
2017-12-17 09:09:59 +00:00
*** Scrollbar colors ***
kScrollColor Normal scrollbar color
kScrollColorHi Highlighted scrollbar color
2018-08-22 09:10:45 +00:00
*** Debugger colors ***
kDbgChangedColor Background color for changed cells
kDbgChangedTextColor Text color for changed cells
kDbgColorHi Highlighted color in debugger data cells
kDbgColorRed Red color in debugger
2017-12-17 09:09:59 +00:00
*** Slider colors ***
kSliderColor Enabled slider
kSliderColorHi Focussed slider
kSliderBGColor Enabled slider background
kSliderBGColorHi Focussed slider background
kSliderBGColorLo Disabled slider background
2018-01-24 13:37:07 +00:00
*** Other colors ***
kColorInfo TIA output position color
kColorTitleBar Title bar color
kColorTitleText Title text color
kColorTitleBarLo Disabled title bar color
kColorTitleTextLo Disabled title text color
*/
UIPaletteArray FrameBuffer::ourStandardUIPalette = {
{ 0x686868, 0x000000, 0xa38c61, 0xdccfa5, 0x404040, // base
0x000000, 0xac3410, 0x9f0000, 0xf0f0cf, // text
0xc9af7c, 0xf0f0cf, 0xd55941, 0xc80000, // UI elements
0xac3410, 0xd55941, 0x686868, 0xdccfa5, 0xf0f0cf, 0xf0f0cf, // buttons
0xac3410, // checkbox
0xac3410, 0xd55941, // scrollbar
2019-08-10 16:08:53 +00:00
0xc80000, 0xffff80, 0xc8c8ff, 0xc80000, // debugger
2018-08-22 09:10:45 +00:00
0xac3410, 0xd55941, 0xdccfa5, 0xf0f0cf, 0xa38c61, // slider
0xffffff, 0xac3410, 0xf0f0cf, 0x686868, 0xdccfa5 // other
}
};
UIPaletteArray FrameBuffer::ourClassicUIPalette = {
{ 0x686868, 0x000000, 0x404040, 0x404040, 0x404040, // base
0x20a020, 0x00ff00, 0xc80000, 0x000000, // text
0x000000, 0x000000, 0x00ff00, 0xc80000, // UI elements
0x000000, 0x000000, 0x686868, 0x00ff00, 0x20a020, 0x00ff00, // buttons
0x20a020, // checkbox
0x20a020, 0x00ff00, // scrollbar
0xc80000, 0x00ff00, 0xc8c8ff, 0xc80000, // debugger
2018-08-22 09:10:45 +00:00
0x20a020, 0x00ff00, 0x404040, 0x686868, 0x404040, // slider
0x00ff00, 0x20a020, 0x000000, 0x686868, 0x404040 // other
}
};
UIPaletteArray FrameBuffer::ourLightUIPalette = {
{ 0x808080, 0x000000, 0xc0c0c0, 0xe1e1e1, 0x333333, // base
0x000000, 0xBDDEF9, 0x0078d7, 0x000000, // text
0xf0f0f0, 0xffffff, 0x0078d7, 0x0f0f0f, // UI elements
0xe1e1e1, 0xe5f1fb, 0x808080, 0x0078d7, 0x000000, 0x000000, // buttons
0x333333, // checkbox
0xc0c0c0, 0x808080, // scrollbar
0xffc0c0, 0x000000, 0xe00000, 0xc00000, // debugger
2018-08-22 09:10:45 +00:00
0x333333, 0x0078d7, 0xc0c0c0, 0xffffff, 0xc0c0c0, // slider 0xBDDEF9| 0xe1e1e1 | 0xffffff
0xffffff, 0x333333, 0xf0f0f0, 0x808080, 0xc0c0c0 // other
}
};