Merge remote-tracking branch 'remotes/origin/feature/bezels'
|
@ -24,6 +24,8 @@
|
|||
|
||||
* Added 2nd UI theme and hotkey for toggling UI theme.
|
||||
|
||||
* Added bezel support.
|
||||
|
||||
* Added optional type format detection based on colors used.
|
||||
|
||||
* Added Joy2B+ controller support.
|
||||
|
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 4.0 KiB |
|
@ -320,6 +320,7 @@
|
|||
Blargg filtering</a>, including presets for several common TV outputs
|
||||
(Composite, S-Video, RGB, etc.), and ability to fully customize
|
||||
many attributes (contrast, brightness, saturation, gamma, etc.).</li>
|
||||
<li>Bezel support</li>
|
||||
<li>Built-in ROM database with information partially compiled by
|
||||
<a href="http://www.atarimania.com/rom_collection_archive_atari_2600_roms.html">
|
||||
RomHunter</a></li>
|
||||
|
@ -1178,8 +1179,8 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>Toggle aspect ratio correct scaling</td>
|
||||
<td>Control + C</td>
|
||||
<td>Control + C</td>
|
||||
<td>Shift-Control + C</td>
|
||||
<td>Shift-Control + C</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>Decrease</i> vertical display size</td>
|
||||
|
@ -1206,6 +1207,11 @@
|
|||
<td>Control + I</td>
|
||||
<td>Control + I</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Toggle bezel display</td>
|
||||
<td>Control + B</td>
|
||||
<td>Control + B</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3"><center><font size="-1">
|
||||
These settings can also be changed using <a href="#GlobalKeys"><b>Global Keys</a></font></center>
|
||||
|
@ -2688,6 +2694,39 @@
|
|||
<td>Enable or disable emulation dimming in pause mode.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><pre>-bezel.show <1|0></pre></td>
|
||||
<td>Enable or disable bezel display.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>-bezel.dir <path></pre></td>
|
||||
<td>Specifies from where bezel images are loaded.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>-bezel.windowed <1|0></pre></td>
|
||||
<td>Enable bezels in windowed modes as well.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>-bezel.win.auto<1|0></pre></td>
|
||||
<td>Enable manually set bezel's emulation window position.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>-bezel.win.left<1|0></pre></td>
|
||||
<td>Set left position of bezel's emulation window.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>-bezel.win.right<1|0></pre></td>
|
||||
<td>Set right position of bezel's emulation window.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>-bezel.win.top<1|0></pre></td>
|
||||
<td>Set top position of bezel's emulation window.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>-bezel.win.bottom<1|0></pre></td>
|
||||
<td>Set bottom position of bezel's emulation window.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><pre>-audio.enabled <1|0></pre></td>
|
||||
<td>Enable or disable sound generation.</td>
|
||||
|
@ -3750,6 +3789,29 @@
|
|||
</table>
|
||||
<br>
|
||||
|
||||
<p><b>Video & Audio Settings</b> dialog <a name="VideoAudioEffects">(Bezels)</a>:</p>
|
||||
<table style="border:hidden">
|
||||
<tr>
|
||||
<td valign="top"><img src="graphics/options_bezels.png"></td>
|
||||
<td style="border:hidden"> </td>
|
||||
<td valign="top">
|
||||
<table border="1" cellpadding="4">
|
||||
<tr><th>Item</th><th>Brief description</th><th>For more information,<br>see <a href="#CommandLine">Command Line</a></th></tr>
|
||||
<tr><td>Enable bezels</td><td>Enables the bezel display, if a matching bezel image or a
|
||||
default image (named 'default.png') can be found in the bezel path.</td><td>-bezel.show</td></tr>
|
||||
<tr><td>Bezel path</td><td>Specifies the path from where bezel images are loaded.</td><td>-bezel.dir</td></tr>
|
||||
<tr><td>Windowed modes</td><td>Enable bezels in windowed modes as well.</td><td>-bezel.windowed</td></tr>
|
||||
<tr><td>Manual emulation window</td><td>Enable manually set bezel's emulation window position.</td><td>-bezel.win.auto</td></tr>
|
||||
<tr><td>Left</td><td>Set left position of bezel's emulation window.</td><td>-bezel.win.left</td></tr>
|
||||
<tr><td>Right</td><td>Set right position of bezel's emulation window.</td><td>-bezel.win.right</td></tr>
|
||||
<tr><td>Top</td><td>Set top position of bezel's emulation window.</td><td>-bezel.win.top</td></tr>
|
||||
<tr><td>Bottom</td><td>Set bottom position of bezel's emulation window.</td><td>-bezel.win.bottom</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br>
|
||||
|
||||
<p><b>Video & Audio Settings</b> dialog <a name="VideoAudioAudio">(Audio)</a>:</p>
|
||||
<table style="border:hidden">
|
||||
<tr>
|
||||
|
@ -5048,7 +5110,7 @@ Ms Pac-Man (Stella extended codes):
|
|||
<tr><td>Paddles </td><td>Standard paddle controllers for use with games such as Breakout and Warlords. One pair of controller per connector (allows for 4-player Warlords).</td></tr>
|
||||
<tr><td>Paddles_IAxis ¹</td><td>Same as Paddles, except the axes are inverted.</td></tr>
|
||||
<tr><td>Paddles_IAxDr ¹</td><td>Same as Paddles, except both the axes and direction of movement are inverted.</td></tr>
|
||||
<tr><td>Driving ¹ </td><td>Looks like a paddle, but allows 360<EFBFBD> movement. Only one unit per connector, unlike paddles which were sold in pairs.</td></tr>
|
||||
<tr><td>Driving ¹ </td><td>Looks like a paddle, but allows 360° movement. Only one unit per connector, unlike paddles which were sold in pairs.</td></tr>
|
||||
<tr><td>Keyboard</td><td>Also known as the Star Raiders controller, functionally identical to the Kid's Controller and Keyboard Controller. Game included an overlay with commands, for use with Star Raiders.</td></tr>
|
||||
<tr><td>AmigaMouse</td><td>Commodore Amiga computer mouse.</td></tr>
|
||||
<tr><td>AtariMouse</td><td>Atari ST computer mouse.</td></tr>
|
||||
|
@ -5197,6 +5259,20 @@ Ms Pac-Man (Stella extended codes):
|
|||
<td>Contains any special notes about playing the game.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td VALIGN="TOP"><i>Cart.Url</i></td>
|
||||
<td>Defines a website link, e.g. for additional information or to the AtariAge store.
|
||||
Click the ">>" button to open the website.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td VALIGN="TOP"><i>Bezel.Name</i></td>
|
||||
<td>Defines the file name (without suffix) used for loading the bezel
|
||||
image. If the name fails or is not defined, the cart name is used. If
|
||||
that fails too, 'default.png' is tried. If no image can be found, no
|
||||
bezel is displayed.
|
||||
</br>Note: The bezel images should reside in their own directory, separate from the launcher images. </td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3><a name="HighScoreProps"><b>High Scores Properties</b></h3></a>
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
//============================================================================
|
||||
//
|
||||
// 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-2023 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 <cmath>
|
||||
|
||||
#include "OSystem.hxx"
|
||||
#include "Console.hxx"
|
||||
#include "EventHandler.hxx"
|
||||
#include "FBSurface.hxx"
|
||||
#include "PNGLibrary.hxx"
|
||||
|
||||
#include "Bezel.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Bezel::Bezel(OSystem& osystem)
|
||||
: myOSystem{osystem},
|
||||
myFB{osystem.frameBuffer()}
|
||||
{
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const string Bezel::getName(int& index) const
|
||||
{
|
||||
if(++index == 1)
|
||||
return myOSystem.console().properties().get(PropType::Bezel_Name);
|
||||
|
||||
// Try to generate bezel name from cart name
|
||||
const string& cartName = myOSystem.console().properties().get(PropType::Cart_Name);
|
||||
size_t pos = cartName.find_first_of("(");
|
||||
if(pos == std::string::npos)
|
||||
pos = cartName.length() + 1;
|
||||
if(index < 10 && pos != std::string::npos && pos > 0)
|
||||
{
|
||||
// The following suffixes are from "The Official No-Intro Convention",
|
||||
// covering all used combinations by "The Bezel Project" (except single ones)
|
||||
// (Unl) = unlicensed (Homebrews)
|
||||
const std::array<string, 8> suffixes = {
|
||||
" (USA)", " (USA) (Proto)", " (USA) (Unl)", " (USA) (Hack)",
|
||||
" (Europe)", " (Germany)", " (France) (Unl)", " (Australia)"
|
||||
};
|
||||
return cartName.substr(0, pos - 1) + suffixes[index - 2];
|
||||
}
|
||||
|
||||
if(index == 10)
|
||||
{
|
||||
index = -1;
|
||||
return "default";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 Bezel::borderSize(uInt32 x, uInt32 y, uInt32 size, Int32 step) const
|
||||
{
|
||||
uInt32 *pixels{nullptr}, pitch;
|
||||
uInt32 i;
|
||||
|
||||
mySurface->basePtr(pixels, pitch);
|
||||
pixels += x + y * pitch;
|
||||
|
||||
for(i = 0; i < size; ++i, pixels += step)
|
||||
{
|
||||
uInt8 r, g, b, a;
|
||||
|
||||
myFB.getRGBA(*pixels, &r, &g, &b, &a);
|
||||
if(a < 255)
|
||||
return i;
|
||||
}
|
||||
return size - 1;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Bezel::load()
|
||||
{
|
||||
bool isValid = false;
|
||||
|
||||
#ifdef IMAGE_SUPPORT
|
||||
const bool isShown = myOSystem.eventHandler().inTIAMode() &&
|
||||
myOSystem.settings().getBool("bezel.show") &&
|
||||
(myFB.fullScreen() || myOSystem.settings().getBool("bezel.windowed"));
|
||||
|
||||
if(mySurface)
|
||||
myFB.deallocateSurface(mySurface);
|
||||
mySurface = nullptr;
|
||||
|
||||
if(isShown)
|
||||
{
|
||||
mySurface = myFB.allocateSurface(1, 1); // dummy size
|
||||
try
|
||||
{
|
||||
const string& path = myOSystem.bezelDir().getPath();
|
||||
string imageName;
|
||||
VariantList metaData;
|
||||
int index = 0;
|
||||
|
||||
do
|
||||
{
|
||||
const string& name = getName(index);
|
||||
if(name != EmptyString)
|
||||
{
|
||||
// Note: JPG does not support transparency
|
||||
imageName = path + name + ".png";
|
||||
FSNode node(imageName);
|
||||
if(node.exists())
|
||||
{
|
||||
isValid = true;
|
||||
myOSystem.png().loadImage(imageName, *mySurface, metaData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(index != -1);
|
||||
}
|
||||
catch(const runtime_error&)
|
||||
{
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(isValid)
|
||||
{
|
||||
const Settings& settings = myOSystem.settings();
|
||||
const Int32 w = mySurface->width();
|
||||
const Int32 h = mySurface->height();
|
||||
uInt32 top, bottom, left, right;
|
||||
|
||||
if(settings.getBool("bezel.win.auto"))
|
||||
{
|
||||
// Determine transparent window inside bezel image
|
||||
uInt32 xCenter, yCenter;
|
||||
|
||||
xCenter = w >> 1;
|
||||
top = borderSize(xCenter, 0, h, w);
|
||||
bottom = h - 1 - borderSize(xCenter, h - 1, h, -w);
|
||||
yCenter = (bottom + top) >> 1;
|
||||
left = borderSize(0, yCenter, w, 1);
|
||||
right = w - 1 - borderSize(w - 1, yCenter, w, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// BP: 13, 13, 0, 0%
|
||||
// HY: 12, 12, 0, 0%
|
||||
// P1: 25, 25, 11, 22%
|
||||
// P2: 23, 23, 7, 20%
|
||||
left = std::min(w - 1, static_cast<Int32>(w * settings.getInt("bezel.win.left") / 100. + .5));
|
||||
right = w - 1 - std::min(w - 1, static_cast<Int32>(w * settings.getInt("bezel.win.right") / 100. + .5));
|
||||
top = std::min(h - 1, static_cast<Int32>(h * settings.getInt("bezel.win.top") / 100. + .5));
|
||||
bottom = h - 1 - std::min(h - 1, static_cast<Int32>(h * settings.getInt("bezel.win.bottom") / 100. + .5));
|
||||
}
|
||||
|
||||
//cerr << (int)(right - left + 1) << " x " << (int)(bottom - top + 1) << " = "
|
||||
// << double((int)(right - left + 1)) / double((int)(bottom - top + 1));
|
||||
|
||||
// Disable bezel is no transparent window was found
|
||||
if (left < right && top < bottom)
|
||||
myInfo = Info(Common::Size(w, h), Common::Rect(left, top, right, bottom));
|
||||
else
|
||||
myInfo = Info();
|
||||
}
|
||||
else
|
||||
myInfo = Info();
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Bezel::apply()
|
||||
{
|
||||
if(isShown())
|
||||
{
|
||||
const uInt32 bezelW =
|
||||
std::min(myFB.screenSize().w,
|
||||
static_cast<uInt32>(std::round(myFB.imageRect().w() * myInfo.ratioW())));
|
||||
const uInt32 bezelH =
|
||||
std::min(myFB.screenSize().h,
|
||||
static_cast<uInt32>(std::round(myFB.imageRect().h() * myInfo.ratioH())));
|
||||
|
||||
// Position and scale bezel
|
||||
mySurface->setDstSize(bezelW, bezelH);
|
||||
mySurface->setDstPos((myFB.screenSize().w - bezelW) / 2, // center
|
||||
(myFB.screenSize().h - bezelH) / 2);
|
||||
mySurface->setScalingInterpolation(ScalingInterpolation::sharp);
|
||||
// Note: Variable bezel window positions are handled in VideoModeHandler::Mode
|
||||
|
||||
// Enable blending to allow overlaying the bezel over the TIA output
|
||||
mySurface->attributes().blending = true;
|
||||
mySurface->attributes().blendalpha = 100;
|
||||
mySurface->applyAttributes();
|
||||
mySurface->setVisible(true);
|
||||
}
|
||||
else
|
||||
if(mySurface)
|
||||
mySurface->setVisible(false);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Bezel::render()
|
||||
{
|
||||
if(mySurface)
|
||||
mySurface->render();
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
//============================================================================
|
||||
//
|
||||
// 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-2023 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.
|
||||
//============================================================================
|
||||
|
||||
#ifndef BEZEL_HXX
|
||||
#define BEZEL_HXX
|
||||
|
||||
class OSystem;
|
||||
class FBSurface;
|
||||
class FrameBuffer;
|
||||
|
||||
#include "Rect.hxx"
|
||||
|
||||
/**
|
||||
This class handles the bezels.
|
||||
|
||||
Bezels are loaded using a file name which is either a bezel name property or
|
||||
is autogenerated from the cart name property. The bezels can be any size and
|
||||
their transparent emulation window can be at any position. The position of
|
||||
the window can be determined automatically.
|
||||
|
||||
+--------------------------------------+
|
||||
| | display.h
|
||||
+--------------------------------------+
|
||||
| |
|
||||
| +---------------+ |
|
||||
| | window | |
|
||||
| | | |
|
||||
| | tia.h * zoom | |
|
||||
| | | | bezel.h * zoom
|
||||
| | | |
|
||||
| +---------------+ |
|
||||
| |
|
||||
+--------------------------------------+ size
|
||||
| |
|
||||
+--------------------------------------+
|
||||
|
||||
The bezel and window sizes and their ratios are used for correct scaling.
|
||||
|
||||
@author Thomas Jentzsch
|
||||
*/
|
||||
|
||||
class Bezel
|
||||
{
|
||||
public:
|
||||
explicit Bezel(OSystem& osystem);
|
||||
~Bezel() = default;
|
||||
|
||||
struct Info
|
||||
{
|
||||
private:
|
||||
bool _isShown{false}; // Is bezel shown?
|
||||
Common::Size _size{1, 1}; // Bezel size
|
||||
Common::Rect _window{1, 1}; // Area of transparent TIA window inside bezel
|
||||
|
||||
public:
|
||||
explicit Info() = default;
|
||||
explicit Info(Common::Size size, Common::Rect window)
|
||||
: _isShown{true}, _size{size}, _window{window} { }
|
||||
|
||||
bool isShown() const { return _isShown; }
|
||||
Common::Size size() const { return _size; }
|
||||
Common::Rect window() const { return _window; }
|
||||
|
||||
// Ratios between bezel sizes and TIA window sizes
|
||||
double ratioW() const { return static_cast<double>(size().w) / window().w(); }
|
||||
double ratioH() const { return static_cast<double>(size().h) / window().h(); }
|
||||
};
|
||||
|
||||
// Structure access methods
|
||||
const Info& info() const { return myInfo; }
|
||||
bool isShown() const { return myInfo.isShown(); }
|
||||
Common::Size size() const { return myInfo.size(); }
|
||||
Common::Rect window() const { return myInfo.window(); }
|
||||
// Ratio between bezel size and TIA window size
|
||||
double ratioW() const { return myInfo.ratioW(); }
|
||||
double ratioH() const { return myInfo.ratioH(); }
|
||||
|
||||
/*
|
||||
Calculate size of a bezel border.
|
||||
*/
|
||||
uInt32 borderSize(uInt32 x, uInt32 y, uInt32 size, Int32 step) const;
|
||||
|
||||
/*
|
||||
Load the bezel.
|
||||
*/
|
||||
bool load();
|
||||
|
||||
/*
|
||||
Display scaled bezel.
|
||||
*/
|
||||
void apply();
|
||||
|
||||
/*
|
||||
Render bezel surface
|
||||
*/
|
||||
void render();
|
||||
|
||||
private:
|
||||
/*
|
||||
Generate bezel file name.
|
||||
*/
|
||||
const string getName(int& index) const;
|
||||
|
||||
private:
|
||||
// The parent system for the bezel
|
||||
OSystem& myOSystem;
|
||||
|
||||
// Pointer to the FrameBuffer object
|
||||
FrameBuffer& myFB;
|
||||
|
||||
// The bezel surface which blends over the TIA surface
|
||||
shared_ptr<FBSurface> mySurface;
|
||||
|
||||
// Bezel info structure
|
||||
Info myInfo;
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
Bezel() = delete;
|
||||
Bezel(const Bezel&) = delete;
|
||||
Bezel(Bezel&&) = delete;
|
||||
Bezel& operator=(const Bezel&) = delete;
|
||||
Bezel& operator=(Bezel&&) = delete;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -101,6 +101,18 @@ class FBBackendSDL2 : public FBBackend
|
|||
FORCE_INLINE void getRGB(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b) const override
|
||||
{ SDL_GetRGB(pixel, myPixelFormat, r, g, b); }
|
||||
|
||||
/**
|
||||
This method is called to retrieve the R/G/B/A data from the given pixel.
|
||||
|
||||
@param pixel The pixel containing R/G/B data
|
||||
@param r The red component of the color
|
||||
@param g The green component of the color
|
||||
@param b The blue component of the color
|
||||
@param a The alpha component of the color.
|
||||
*/
|
||||
FORCE_INLINE void getRGBA(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b, uInt8* a) const override
|
||||
{ SDL_GetRGBA(pixel, myPixelFormat, r, g, b, a); }
|
||||
|
||||
/**
|
||||
This method is called to map a given R/G/B triple to the screen palette.
|
||||
|
||||
|
@ -111,6 +123,17 @@ class FBBackendSDL2 : public FBBackend
|
|||
inline uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const override
|
||||
{ return SDL_MapRGB(myPixelFormat, r, g, b); }
|
||||
|
||||
/**
|
||||
This method is called to map a given R/G/B/A triple to the screen palette.
|
||||
|
||||
@param r The red component of the color.
|
||||
@param g The green component of the color.
|
||||
@param b The blue component of the color.
|
||||
@param a The alpha component of the color.
|
||||
*/
|
||||
inline uInt32 mapRGBA(uInt8 r, uInt8 g, uInt8 b, uInt8 a) const override
|
||||
{ return SDL_MapRGBA(myPixelFormat, r, g, b, a); }
|
||||
|
||||
/**
|
||||
This method is called to get a copy of the specified ARGB data from the
|
||||
viewable FrameBuffer area. Note that this isn't the same as any
|
||||
|
|
|
@ -48,6 +48,7 @@ FBSurfaceSDL2::FBSurfaceSDL2(FBBackendSDL2& backend,
|
|||
: myBackend{backend},
|
||||
myInterpolationMode{inter}
|
||||
{
|
||||
//cerr << width << " x " << height << endl;
|
||||
createSurface(width, height, staticData);
|
||||
}
|
||||
|
||||
|
@ -233,6 +234,7 @@ void FBSurfaceSDL2::createSurface(uInt32 width, uInt32 height,
|
|||
|
||||
mySurface = SDL_CreateRGBSurface(0, width, height,
|
||||
pf.BitsPerPixel, pf.Rmask, pf.Gmask, pf.Bmask, pf.Amask);
|
||||
//SDL_SetSurfaceBlendMode(mySurface, SDL_BLENDMODE_ADD); // default: SDL_BLENDMODE_BLEND
|
||||
|
||||
// We start out with the src and dst rectangles containing the same
|
||||
// dimensions, indicating no scaling or re-positioning
|
||||
|
|
|
@ -623,6 +623,7 @@ PhysicalKeyboardHandler::DefaultCommonMapping = {
|
|||
#endif
|
||||
{ Event::OptionsMenuMode, KBDK_TAB },
|
||||
{ Event::CmdMenuMode, KBDK_BACKSLASH },
|
||||
{ Event::ToggleBezel, KBDK_B, KBDM_CTRL },
|
||||
{ Event::TimeMachineMode, KBDK_T, KBDM_SHIFT },
|
||||
{ Event::DebuggerMode, KBDK_GRAVE },
|
||||
{ Event::PlusRomsSetupMode, KBDK_P, KBDM_SHIFT | KBDM_CTRL | MOD3 },
|
||||
|
@ -641,7 +642,7 @@ PhysicalKeyboardHandler::DefaultCommonMapping = {
|
|||
{ Event::VCenterIncrease, KBDK_PAGEDOWN, MOD3 },
|
||||
{ Event::VSizeAdjustDecrease, KBDK_PAGEDOWN, KBDM_SHIFT | MOD3 },
|
||||
{ Event::VSizeAdjustIncrease, KBDK_PAGEUP, KBDM_SHIFT | MOD3 },
|
||||
{ Event::ToggleCorrectAspectRatio, KBDK_C, KBDM_CTRL },
|
||||
{ Event::ToggleCorrectAspectRatio, KBDK_C, KBDM_SHIFT | KBDM_CTRL },
|
||||
{ Event::VolumeDecrease, KBDK_LEFTBRACKET, MOD3 },
|
||||
{ Event::VolumeIncrease, KBDK_RIGHTBRACKET, MOD3 },
|
||||
{ Event::SoundToggle, KBDK_RIGHTBRACKET, KBDM_CTRL },
|
||||
|
|
|
@ -33,12 +33,14 @@ PNGLibrary::PNGLibrary(OSystem& osystem)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void PNGLibrary::loadImage(const string& filename, FBSurface& surface, VariantList& metaData)
|
||||
void PNGLibrary::loadImage(const string& filename, FBSurface& surface,
|
||||
VariantList& metaData)
|
||||
{
|
||||
png_structp png_ptr{nullptr};
|
||||
png_infop info_ptr{nullptr};
|
||||
png_uint_32 iwidth{0}, iheight{0};
|
||||
int bit_depth{0}, color_type{0}, interlace_type{0};
|
||||
bool hasAlpha = false;
|
||||
|
||||
const auto loadImageERROR = [&](string_view s) {
|
||||
if(png_ptr)
|
||||
|
@ -76,18 +78,25 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface, VariantLi
|
|||
// byte into separate bytes (useful for paletted and grayscale images).
|
||||
png_set_packing(png_ptr);
|
||||
|
||||
// Only normal RBG(A) images are supported (without the alpha channel)
|
||||
// Alpha channel is supported
|
||||
if(color_type == PNG_COLOR_TYPE_RGBA)
|
||||
{
|
||||
png_set_strip_alpha(png_ptr);
|
||||
hasAlpha = true;
|
||||
}
|
||||
else if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||
{
|
||||
// TODO: preserve alpha
|
||||
png_set_gray_to_rgb(png_ptr);
|
||||
}
|
||||
else if(color_type == PNG_COLOR_TYPE_PALETTE)
|
||||
{
|
||||
png_set_palette_to_rgb(png_ptr);
|
||||
if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
|
||||
{
|
||||
png_set_tRNS_to_alpha(png_ptr);
|
||||
hasAlpha = true;
|
||||
}
|
||||
else
|
||||
png_set_palette_to_rgb(png_ptr);
|
||||
}
|
||||
else if(color_type != PNG_COLOR_TYPE_RGB)
|
||||
{
|
||||
|
@ -95,7 +104,7 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface, VariantLi
|
|||
}
|
||||
|
||||
// Create/initialize storage area for the current image
|
||||
if(!allocateStorage(iwidth, iheight))
|
||||
if(!allocateStorage(iwidth, iheight, hasAlpha))
|
||||
loadImageERROR("Not enough memory to read PNG image");
|
||||
|
||||
// The PNG read function expects an array of rows, not a single 1-D array
|
||||
|
@ -112,7 +121,7 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface, VariantLi
|
|||
readMetaData(png_ptr, info_ptr, metaData);
|
||||
|
||||
// Load image into the surface, setting the correct dimensions
|
||||
loadImagetoSurface(surface);
|
||||
loadImagetoSurface(surface, hasAlpha);
|
||||
|
||||
// Cleanup
|
||||
if(png_ptr)
|
||||
|
@ -380,10 +389,10 @@ void PNGLibrary::takeSnapshot(uInt32 number)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool PNGLibrary::allocateStorage(size_t width, size_t height)
|
||||
bool PNGLibrary::allocateStorage(size_t width, size_t height, bool hasAlpha)
|
||||
{
|
||||
// Create space for the entire image (3 bytes per pixel in RGB format)
|
||||
const size_t req_buffer_size = width * height * 3;
|
||||
// Create space for the entire image (3(4) bytes per pixel in RGB(A) format)
|
||||
const size_t req_buffer_size = width * height * (hasAlpha ? 4 : 3);
|
||||
if(req_buffer_size > ReadInfo.buffer.capacity())
|
||||
ReadInfo.buffer.reserve(req_buffer_size * 1.5);
|
||||
|
||||
|
@ -393,13 +402,13 @@ bool PNGLibrary::allocateStorage(size_t width, size_t height)
|
|||
|
||||
ReadInfo.width = static_cast<png_uint_32>(width);
|
||||
ReadInfo.height = static_cast<png_uint_32>(height);
|
||||
ReadInfo.pitch = static_cast<png_uint_32>(width * 3);
|
||||
ReadInfo.pitch = static_cast<png_uint_32>(width * (hasAlpha ? 4 : 3));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void PNGLibrary::loadImagetoSurface(FBSurface& surface)
|
||||
void PNGLibrary::loadImagetoSurface(FBSurface& surface, bool hasAlpha)
|
||||
{
|
||||
// First determine if we need to resize the surface
|
||||
const uInt32 iw = ReadInfo.width, ih = ReadInfo.height;
|
||||
|
@ -422,8 +431,12 @@ void PNGLibrary::loadImagetoSurface(FBSurface& surface)
|
|||
{
|
||||
const uInt8* i_ptr = i_buf;
|
||||
uInt32* s_ptr = s_buf;
|
||||
for(uInt32 icol = 0; icol < ReadInfo.width; ++icol, i_ptr += 3)
|
||||
*s_ptr++ = fb.mapRGB(*i_ptr, *(i_ptr+1), *(i_ptr+2));
|
||||
if(hasAlpha)
|
||||
for(uInt32 icol = 0; icol < ReadInfo.width; ++icol, i_ptr += 4)
|
||||
*s_ptr++ = fb.mapRGBA(*i_ptr, *(i_ptr+1), *(i_ptr+2), *(i_ptr+3));
|
||||
else
|
||||
for(uInt32 icol = 0; icol < ReadInfo.width; ++icol, i_ptr += 3)
|
||||
*s_ptr++ = fb.mapRGB(*i_ptr, *(i_ptr+1), *(i_ptr+2));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -153,7 +153,7 @@ class PNGLibrary
|
|||
@param width The width of the PNG image
|
||||
@param height The height of the PNG image
|
||||
*/
|
||||
static bool allocateStorage(size_t width, size_t height);
|
||||
static bool allocateStorage(size_t width, size_t height, bool hasAlpha);
|
||||
|
||||
/** The actual method which saves a PNG image.
|
||||
|
||||
|
@ -173,7 +173,7 @@ class PNGLibrary
|
|||
|
||||
@param surface The FBSurface into which to place the PNG data
|
||||
*/
|
||||
void loadImagetoSurface(FBSurface& surface);
|
||||
void loadImagetoSurface(FBSurface& surface, bool hasAlpha);
|
||||
|
||||
/**
|
||||
Write PNG tEXt chunks to the image.
|
||||
|
|
|
@ -15,7 +15,11 @@
|
|||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "Settings.hxx"
|
||||
#include "Bezel.hxx"
|
||||
|
||||
#include "VideoModeHandler.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -33,7 +37,7 @@ void VideoModeHandler::setDisplaySize(const Common::Size& display, Int32 fsIndex
|
|||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const VideoModeHandler::Mode&
|
||||
VideoModeHandler::buildMode(const Settings& settings, bool inTIAMode)
|
||||
VideoModeHandler::buildMode(const Settings& settings, bool inTIAMode, Bezel::Info bezelInfo)
|
||||
{
|
||||
const bool windowedRequested = myFSIndex == -1;
|
||||
|
||||
|
@ -42,23 +46,24 @@ VideoModeHandler::buildMode(const Settings& settings, bool inTIAMode)
|
|||
{
|
||||
if(windowedRequested)
|
||||
{
|
||||
const float zoom = settings.getFloat("tia.zoom");
|
||||
const double zoom = settings.getFloat("tia.zoom");
|
||||
ostringstream desc;
|
||||
desc << (zoom * 100) << "%";
|
||||
|
||||
// Image and screen (aka window) dimensions are the same
|
||||
// Overscan is not applicable in this mode
|
||||
myMode = Mode(myImage.w * zoom, myImage.h * zoom, Mode::Stretch::Fill,
|
||||
myFSIndex, desc.str(), zoom);
|
||||
myMode = Mode(myImage.w, myImage.h,
|
||||
Mode::Stretch::Fill, myFSIndex,
|
||||
desc.str(), zoom, bezelInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
const float overscan = 1 - settings.getInt("tia.fs_overscan") / 100.0;
|
||||
const double overscan = 1 - settings.getInt("tia.fs_overscan") / 100.0;
|
||||
|
||||
// First calculate maximum zoom that keeps aspect ratio
|
||||
const float scaleX = static_cast<float>(myImage.w) / myDisplay.w,
|
||||
scaleY = static_cast<float>(myImage.h) / myDisplay.h;
|
||||
float zoom = 1.F / std::max(scaleX, scaleY);
|
||||
const double scaleX = static_cast<double>(myImage.w) / (myDisplay.w / bezelInfo.ratioW()),
|
||||
scaleY = static_cast<double>(myImage.h) / (myDisplay.h / bezelInfo.ratioH());
|
||||
double zoom = 1. / std::max(scaleX, scaleY);
|
||||
|
||||
// When aspect ratio correction is off, we want pixel-exact images,
|
||||
// so we default to integer zooming
|
||||
|
@ -67,17 +72,19 @@ VideoModeHandler::buildMode(const Settings& settings, bool inTIAMode)
|
|||
|
||||
if(!settings.getBool("tia.fs_stretch")) // preserve aspect, use all space
|
||||
{
|
||||
myMode = Mode(myImage.w * zoom, myImage.h * zoom,
|
||||
myMode = Mode(myImage.w, myImage.h,
|
||||
myDisplay.w, myDisplay.h,
|
||||
Mode::Stretch::Preserve, myFSIndex,
|
||||
"Fullscreen: Preserve aspect, no stretch", zoom, overscan);
|
||||
"Fullscreen: Preserve aspect, no stretch",
|
||||
zoom, overscan, bezelInfo);
|
||||
}
|
||||
else // ignore aspect, use all space
|
||||
{
|
||||
myMode = Mode(myImage.w * zoom, myImage.h * zoom,
|
||||
myMode = Mode(myImage.w, myImage.h,
|
||||
myDisplay.w, myDisplay.h,
|
||||
Mode::Stretch::Fill, myFSIndex,
|
||||
"Fullscreen: Ignore aspect, full stretch", zoom, overscan);
|
||||
"Fullscreen: Ignore aspect, full stretch",
|
||||
zoom, overscan, bezelInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,26 +96,25 @@ VideoModeHandler::buildMode(const Settings& settings, bool inTIAMode)
|
|||
myMode = Mode(myImage.w, myImage.h, myDisplay.w, myDisplay.h,
|
||||
Mode::Stretch::None, myFSIndex);
|
||||
}
|
||||
|
||||
return myMode;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, Stretch smode,
|
||||
Int32 fsindex, string_view desc,
|
||||
float zoomLevel)
|
||||
: Mode(iw, ih, iw, ih, smode, fsindex, desc, zoomLevel)
|
||||
double zoomLevel, Bezel::Info bezelInfo)
|
||||
: Mode(iw, ih, iw, ih, smode, fsindex, desc, zoomLevel, 1., bezelInfo)
|
||||
{
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh,
|
||||
Stretch smode, Int32 fsindex, string_view desc,
|
||||
float zoomLevel, float overscan)
|
||||
double zoomLevel, double overscan, Bezel::Info bezelInfo)
|
||||
: screenS{sw, sh},
|
||||
stretch{smode},
|
||||
description{desc},
|
||||
zoom{zoomLevel},
|
||||
zoom{zoomLevel}, //hZoom{zoomLevel}, vZoom{zoomLevel},
|
||||
fsIndex{fsindex}
|
||||
{
|
||||
// Now resize based on windowed/fullscreen mode and stretch factor
|
||||
|
@ -117,20 +123,21 @@ VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh,
|
|||
switch(stretch)
|
||||
{
|
||||
case Stretch::Preserve:
|
||||
iw *= overscan;
|
||||
ih *= overscan;
|
||||
iw = std::round(iw * overscan * zoomLevel);
|
||||
ih = std::round(ih * overscan * zoomLevel);
|
||||
break;
|
||||
|
||||
case Stretch::Fill:
|
||||
{
|
||||
// Scale to all available space
|
||||
iw = screenS.w * overscan;
|
||||
ih = screenS.h * overscan;
|
||||
iw = std::round(screenS.w * overscan / bezelInfo.ratioW());
|
||||
ih = std::round(screenS.h * overscan / bezelInfo.ratioH());
|
||||
break;
|
||||
|
||||
case Stretch::None:
|
||||
}
|
||||
case Stretch::None: // UI Mode
|
||||
// Don't do any scaling at all
|
||||
iw = std::min(iw, screenS.w) * overscan;
|
||||
ih = std::min(ih, screenS.h) * overscan;
|
||||
iw = std::min(static_cast<uInt32>(iw * zoomLevel), screenS.w) * overscan;
|
||||
ih = std::min(static_cast<uInt32>(ih * zoomLevel), screenS.h) * overscan;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -142,11 +149,13 @@ VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh,
|
|||
{
|
||||
case Stretch::Preserve:
|
||||
case Stretch::Fill:
|
||||
screenS.w = iw;
|
||||
screenS.h = ih;
|
||||
iw *= zoomLevel;
|
||||
ih *= zoomLevel;
|
||||
screenS.w = std::round(iw * bezelInfo.ratioW());
|
||||
screenS.h = std::round(ih * bezelInfo.ratioH());
|
||||
break;
|
||||
|
||||
case Stretch::None:
|
||||
case Stretch::None: // UI Mode
|
||||
break; // Do not change image or screen rects whatsoever
|
||||
}
|
||||
}
|
||||
|
@ -155,7 +164,17 @@ VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh,
|
|||
iw = std::min(iw, screenS.w);
|
||||
ih = std::min(ih, screenS.h);
|
||||
|
||||
imageR.moveTo((screenS.w - iw) >> 1, (screenS.h - ih) >> 1);
|
||||
// Allow variable image positions in asymmetric bezels
|
||||
// (works in case of no bezel too)
|
||||
const uInt32 wx = bezelInfo.window().x() * iw / bezelInfo.window().w();
|
||||
const uInt32 wy = bezelInfo.window().y() * ih / bezelInfo.window().h();
|
||||
const uInt32 bezelW = std::min(screenS.w,
|
||||
static_cast<uInt32>(std::round(iw * bezelInfo.ratioW())));
|
||||
const uInt32 bezelH = std::min(screenS.h,
|
||||
static_cast<uInt32>(std::round(ih * bezelInfo.ratioH())));
|
||||
// Center image (no bezel) or move image relative to centered bezel
|
||||
imageR.moveTo(((screenS.w - bezelW) >> 1) + wx, ((screenS.h - bezelH) >> 1) + wy);
|
||||
|
||||
imageR.setWidth(iw);
|
||||
imageR.setHeight(ih);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ class Settings;
|
|||
|
||||
#include "Rect.hxx"
|
||||
#include "bspf.hxx"
|
||||
#include "Bezel.hxx"
|
||||
|
||||
class VideoModeHandler
|
||||
{
|
||||
|
@ -44,15 +45,17 @@ class VideoModeHandler
|
|||
Common::Size screenS;
|
||||
Stretch stretch{Mode::Stretch::None};
|
||||
string description;
|
||||
float zoom{1.F};
|
||||
double zoom{1.};
|
||||
Int32 fsIndex{-1}; // -1 indicates windowed mode
|
||||
|
||||
Mode() = default;
|
||||
Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh, Stretch smode,
|
||||
Int32 fsindex = -1, string_view desc = "",
|
||||
float zoomLevel = 1.F, float overscan = 1.F);
|
||||
double zoomLevel = 1., double overscan = 1.,
|
||||
Bezel::Info bezelInfo = Bezel::Info());
|
||||
Mode(uInt32 iw, uInt32 ih, Stretch smode, Int32 fsindex = -1,
|
||||
string_view desc = "", float zoomLevel = 1.F);
|
||||
string_view desc = "", double zoomLevel = 1.,
|
||||
Bezel::Info bezelInfo = Bezel::Info());
|
||||
|
||||
friend ostream& operator<<(ostream& os, const Mode& vm)
|
||||
{
|
||||
|
@ -94,8 +97,8 @@ class VideoModeHandler
|
|||
|
||||
@return A video mode based on the given criteria
|
||||
*/
|
||||
const VideoModeHandler::Mode& buildMode(const Settings& settings,
|
||||
bool inTIAMode);
|
||||
const VideoModeHandler::Mode& buildMode(const Settings& settings, bool inTIAMode,
|
||||
Bezel::Info bezelInfo = Bezel::Info());
|
||||
|
||||
private:
|
||||
Common::Size myImage, myDisplay;
|
||||
|
|
|
@ -386,6 +386,7 @@ NLOHMANN_JSON_SERIALIZE_ENUM(Event::Type, {
|
|||
{Event::ToggleBits, "ToggleBits"},
|
||||
{Event::ToggleFixedColors, "ToggleFixedColors"},
|
||||
{Event::ToggleFrameStats, "ToggleFrameStats"},
|
||||
{Event::ToggleBezel, "ToggleBezel"},
|
||||
{Event::ExitGame, "ExitGame"},
|
||||
{Event::SettingDecrease, "SettingDecrease"},
|
||||
{Event::SettingIncrease, "SettingIncrease"},
|
||||
|
|
|
@ -4,6 +4,7 @@ MODULE_OBJS := \
|
|||
src/common/AudioQueue.o \
|
||||
src/common/AudioSettings.o \
|
||||
src/common/Base.o \
|
||||
src/common/Bezel.o \
|
||||
src/common/DevSettingsHandler.o \
|
||||
src/common/EventHandlerSDL2.o \
|
||||
src/common/FBBackendSDL2.o \
|
||||
|
|
|
@ -126,13 +126,12 @@ void BilinearBlitter::recreateTexturesIfNecessary()
|
|||
}
|
||||
|
||||
if (myAttributes.blending) {
|
||||
const auto blendAlpha = static_cast<uInt8>(myAttributes.blendalpha * 2.55);
|
||||
|
||||
const std::array<SDL_Texture*, 2> textures = { myTexture, mySecondaryTexture };
|
||||
for (SDL_Texture* texture: textures) {
|
||||
if (!texture) continue;
|
||||
|
||||
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
|
||||
const auto blendAlpha = static_cast<uInt8>(myAttributes.blendalpha * 2.55);
|
||||
SDL_SetTextureAlphaMod(texture, blendAlpha);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,7 +126,7 @@ class Event
|
|||
ToggleBLCollision, ToggleBLBit, TogglePFCollision, TogglePFBit,
|
||||
ToggleCollisions, ToggleBits, ToggleFixedColors,
|
||||
|
||||
ToggleFrameStats, ToggleSAPortOrder, ExitGame,
|
||||
ToggleFrameStats, ToggleBezel, ToggleSAPortOrder, ExitGame,
|
||||
SettingDecrease, SettingIncrease, PreviousSetting, NextSetting,
|
||||
ToggleAdaptRefresh, PreviousMultiCartRom,
|
||||
// add new (after Version 4) events from here to avoid that user remapped events get overwritten
|
||||
|
|
|
@ -1517,6 +1517,14 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated)
|
|||
if(pressed && !repeated) myOSystem.toggleTimeMachine();
|
||||
return;
|
||||
|
||||
case Event::ToggleBezel:
|
||||
if(pressed && !repeated)
|
||||
{
|
||||
myOSystem.frameBuffer().toggleBezel();
|
||||
myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::BEZEL);
|
||||
}
|
||||
return;
|
||||
|
||||
#ifdef IMAGE_SUPPORT
|
||||
case Event::ToggleContSnapshots:
|
||||
if(pressed && !repeated) myOSystem.png().toggleContinuousSnapshots(false);
|
||||
|
@ -2827,6 +2835,7 @@ EventHandler::EmulActionList EventHandler::ourEmulActionList = { {
|
|||
{ Event::ExitMode, "Exit current Stella menu/mode" },
|
||||
{ Event::OptionsMenuMode, "Enter Options menu UI" },
|
||||
{ Event::CmdMenuMode, "Toggle Commands menu UI" },
|
||||
{ Event::ToggleBezel, "Toggle bezel display" },
|
||||
{ Event::HighScoresMenuMode, "Toggle High Scores UI" },
|
||||
{ Event::PlusRomsSetupMode, "Toggle PlusROMs setup UI" },
|
||||
{ Event::TogglePauseMode, "Toggle Pause mode" },
|
||||
|
@ -3148,7 +3157,7 @@ EventHandler::MenuActionList EventHandler::ourMenuActionList = { {
|
|||
const Event::EventSet EventHandler::MiscEvents = {
|
||||
Event::Quit, Event::ReloadConsole, Event::Fry, Event::StartPauseMode,
|
||||
Event::TogglePauseMode, Event::OptionsMenuMode, Event::CmdMenuMode,
|
||||
Event::PlusRomsSetupMode, Event::ExitMode,
|
||||
Event::ToggleBezel, Event::PlusRomsSetupMode, Event::ExitMode,
|
||||
Event::ToggleTurbo, Event::DecreaseSpeed, Event::IncreaseSpeed,
|
||||
Event::TakeSnapshot, Event::ToggleContSnapshots, Event::ToggleContSnapshotsFrame,
|
||||
// Event::MouseAxisXMove, Event::MouseAxisYMove,
|
||||
|
|
|
@ -537,7 +537,7 @@ class EventHandler
|
|||
#else
|
||||
REFRESH_SIZE = 0,
|
||||
#endif
|
||||
EMUL_ACTIONLIST_SIZE = 232 + PNG_SIZE + COMBO_SIZE + REFRESH_SIZE,
|
||||
EMUL_ACTIONLIST_SIZE = 233 + PNG_SIZE + COMBO_SIZE + REFRESH_SIZE,
|
||||
MENU_ACTIONLIST_SIZE = 20
|
||||
;
|
||||
|
||||
|
|
|
@ -128,6 +128,17 @@ class FBBackend
|
|||
*/
|
||||
virtual void getRGB(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b) const = 0;
|
||||
|
||||
/**
|
||||
This method is called to retrieve the R/G/B/A data from the given pixel.
|
||||
|
||||
@param pixel The pixel containing R/G/B data
|
||||
@param r The red component of the color
|
||||
@param g The green component of the color
|
||||
@param b The blue component of the color
|
||||
@param a The alpha component of the color.
|
||||
*/
|
||||
virtual void getRGBA(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b, uInt8* a) const = 0;
|
||||
|
||||
/**
|
||||
This method is called to map a given R/G/B triple to the screen palette.
|
||||
|
||||
|
@ -137,6 +148,16 @@ class FBBackend
|
|||
*/
|
||||
virtual uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const = 0;
|
||||
|
||||
/**
|
||||
This method is called to map a given R/G/B triple to the screen palette.
|
||||
|
||||
@param r The red component of the color.
|
||||
@param g The green component of the color.
|
||||
@param b The blue component of the color.
|
||||
@param a The alpha component of the color.
|
||||
*/
|
||||
virtual uInt32 mapRGBA(uInt8 r, uInt8 g, uInt8 b, uInt8 a) const = 0;
|
||||
|
||||
/**
|
||||
This method is called to get the specified ARGB data from the viewable
|
||||
FrameBuffer area. Note that this isn't the same as any internal
|
||||
|
|
|
@ -27,9 +27,11 @@
|
|||
#include "Sound.hxx"
|
||||
#include "AudioSettings.hxx"
|
||||
#include "MediaFactory.hxx"
|
||||
#include "PNGLibrary.hxx"
|
||||
|
||||
#include "FBSurface.hxx"
|
||||
#include "TIASurface.hxx"
|
||||
#include "Bezel.hxx"
|
||||
#include "FrameBuffer.hxx"
|
||||
#include "PaletteHandler.hxx"
|
||||
#include "StateManager.hxx"
|
||||
|
@ -125,6 +127,8 @@ void FrameBuffer::initialize()
|
|||
|
||||
// Create a TIA surface; we need it for rendering TIA images
|
||||
myTIASurface = make_unique<TIASurface>(myOSystem);
|
||||
// Create a bezel surface for TIA overlays
|
||||
myBezel = make_unique<Bezel>(myOSystem);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -277,8 +281,10 @@ FBInitStatus FrameBuffer::createDisplay(string_view title, BufferType type,
|
|||
|
||||
if(myBufferType == BufferType::Emulator)
|
||||
{
|
||||
myBezel->load();
|
||||
|
||||
// Determine possible TIA windowed zoom levels
|
||||
const float currentTIAZoom = myOSystem.settings().getFloat("tia.zoom");
|
||||
const double currentTIAZoom = myOSystem.settings().getFloat("tia.zoom");
|
||||
myOSystem.settings().setValue("tia.zoom",
|
||||
BSPF::clampw(currentTIAZoom, supportedTIAMinZoom(), supportedTIAMaxZoom()));
|
||||
}
|
||||
|
@ -393,10 +399,10 @@ void FrameBuffer::update(UpdateMode mode)
|
|||
{
|
||||
myPausedCount = static_cast<uInt32>(7 * myOSystem.frameRate());
|
||||
showTextMessage("Paused", MessagePosition::MiddleCenter);
|
||||
myTIASurface->render(shade);
|
||||
renderTIA(shade, false);
|
||||
}
|
||||
if(rerender)
|
||||
myTIASurface->render(shade);
|
||||
renderTIA(shade, false);
|
||||
break; // EventHandlerState::PAUSE
|
||||
}
|
||||
|
||||
|
@ -407,14 +413,12 @@ void FrameBuffer::update(UpdateMode mode)
|
|||
redraw |= myOSystem.optionsMenu().needsRedraw();
|
||||
if(redraw)
|
||||
{
|
||||
clear();
|
||||
myTIASurface->render(true);
|
||||
renderTIA(true);
|
||||
myOSystem.optionsMenu().draw(forceRedraw);
|
||||
}
|
||||
else if(rerender)
|
||||
{
|
||||
clear();
|
||||
myTIASurface->render(true);
|
||||
renderTIA(true);
|
||||
myOSystem.optionsMenu().render();
|
||||
}
|
||||
break; // EventHandlerState::OPTIONSMENU
|
||||
|
@ -426,14 +430,12 @@ void FrameBuffer::update(UpdateMode mode)
|
|||
redraw |= myOSystem.commandMenu().needsRedraw();
|
||||
if(redraw)
|
||||
{
|
||||
clear();
|
||||
myTIASurface->render(true);
|
||||
renderTIA(true);
|
||||
myOSystem.commandMenu().draw(forceRedraw);
|
||||
}
|
||||
else if(rerender)
|
||||
{
|
||||
clear();
|
||||
myTIASurface->render(true);
|
||||
renderTIA(true);
|
||||
myOSystem.commandMenu().render();
|
||||
}
|
||||
break; // EventHandlerState::CMDMENU
|
||||
|
@ -445,14 +447,12 @@ void FrameBuffer::update(UpdateMode mode)
|
|||
redraw |= myOSystem.highscoresMenu().needsRedraw();
|
||||
if(redraw)
|
||||
{
|
||||
clear();
|
||||
myTIASurface->render(true);
|
||||
renderTIA(true);
|
||||
myOSystem.highscoresMenu().draw(forceRedraw);
|
||||
}
|
||||
else if(rerender)
|
||||
{
|
||||
clear();
|
||||
myTIASurface->render(true);
|
||||
renderTIA(true);
|
||||
myOSystem.highscoresMenu().render();
|
||||
}
|
||||
break; // EventHandlerState::HIGHSCORESMENU
|
||||
|
@ -464,8 +464,7 @@ void FrameBuffer::update(UpdateMode mode)
|
|||
redraw |= myOSystem.messageMenu().needsRedraw();
|
||||
if(redraw)
|
||||
{
|
||||
clear();
|
||||
myTIASurface->render(true);
|
||||
renderTIA(true);
|
||||
myOSystem.messageMenu().draw(forceRedraw);
|
||||
}
|
||||
break; // EventHandlerState::MESSAGEMENU
|
||||
|
@ -477,8 +476,7 @@ void FrameBuffer::update(UpdateMode mode)
|
|||
redraw |= myOSystem.plusRomsMenu().needsRedraw();
|
||||
if(redraw)
|
||||
{
|
||||
clear();
|
||||
myTIASurface->render(true);
|
||||
renderTIA(true);
|
||||
myOSystem.plusRomsMenu().draw(forceRedraw);
|
||||
}
|
||||
break; // EventHandlerState::PLUSROMSMENU
|
||||
|
@ -490,14 +488,12 @@ void FrameBuffer::update(UpdateMode mode)
|
|||
redraw |= myOSystem.timeMachine().needsRedraw();
|
||||
if(redraw)
|
||||
{
|
||||
clear();
|
||||
myTIASurface->render();
|
||||
renderTIA();
|
||||
myOSystem.timeMachine().draw(forceRedraw);
|
||||
}
|
||||
else if(rerender)
|
||||
{
|
||||
clear();
|
||||
myTIASurface->render();
|
||||
renderTIA();
|
||||
myOSystem.timeMachine().render();
|
||||
}
|
||||
break; // EventHandlerState::TIMEMACHINE
|
||||
|
@ -529,7 +525,7 @@ void FrameBuffer::update(UpdateMode mode)
|
|||
}
|
||||
redraw |= success;
|
||||
if(redraw)
|
||||
myTIASurface->render();
|
||||
renderTIA(false, false);
|
||||
|
||||
// Stop playback mode at the end of the state buffer
|
||||
// and switch to Time Machine or Pause mode
|
||||
|
@ -591,8 +587,7 @@ void FrameBuffer::updateInEmulationMode(float framesPerSecond)
|
|||
// 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();
|
||||
renderTIA();
|
||||
|
||||
// Show frame statistics
|
||||
if(myStatsMsg.enabled)
|
||||
|
@ -758,7 +753,8 @@ void FrameBuffer::drawFrameStats(float framesPerSecond)
|
|||
myStatsMsg.w, color, TextAlign::Left, 0, true, kBGColor);
|
||||
}
|
||||
|
||||
myStatsMsg.surface->setDstPos(imageRect().x() + 10, imageRect().y() + 8);
|
||||
myStatsMsg.surface->setDstPos(imageRect().x() + imageRect().w() / 64,
|
||||
imageRect().y() + imageRect().h() / 64);
|
||||
myStatsMsg.surface->setDstSize(myStatsMsg.w * hidpiScaleFactor(),
|
||||
myStatsMsg.h * hidpiScaleFactor());
|
||||
myStatsMsg.surface->render();
|
||||
|
@ -832,7 +828,7 @@ inline bool FrameBuffer::drawMessage()
|
|||
// Draw the bounded box and text
|
||||
const Common::Rect& dst = myMsg.surface->dstRect();
|
||||
const int fontWidth = font().getMaxCharWidth(),
|
||||
fontHeight = font().getFontHeight();
|
||||
fontHeight = font().getFontHeight();
|
||||
const int VBORDER = fontHeight / 4;
|
||||
const int HBORDER = fontWidth * 1.25 / 2.0;
|
||||
constexpr int BORDER = 1;
|
||||
|
@ -962,6 +958,17 @@ void FrameBuffer::resetSurfaces()
|
|||
update(UpdateMode::REDRAW); // force full update
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void FrameBuffer::renderTIA(bool shade, bool doClear)
|
||||
{
|
||||
if(doClear)
|
||||
clear(); // TODO - test this: it may cause slowdowns on older systems
|
||||
|
||||
myTIASurface->render(shade);
|
||||
if(myBezel)
|
||||
myBezel->render();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void FrameBuffer::setTIAPalette(const PaletteArray& rgb_palette)
|
||||
{
|
||||
|
@ -1144,7 +1151,7 @@ void FrameBuffer::toggleFullscreen(bool toggle)
|
|||
msg << "enabled (" << myBackend->refreshRate() << " Hz, ";
|
||||
else
|
||||
msg << "disabled (";
|
||||
msg << "Zoom " << myActiveVidMode.zoom * 100 << "%)";
|
||||
msg << "Zoom " << round(myActiveVidMode.zoom * 100) << "%)";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1226,7 +1233,7 @@ void FrameBuffer::switchVideoMode(int direction)
|
|||
if(!fullScreen())
|
||||
{
|
||||
// Windowed TIA modes support variable zoom levels
|
||||
float zoom = myOSystem.settings().getFloat("tia.zoom");
|
||||
double zoom = myOSystem.settings().getFloat("tia.zoom");
|
||||
if(direction == +1) zoom += ZOOM_STEPS;
|
||||
else if(direction == -1) zoom -= ZOOM_STEPS;
|
||||
|
||||
|
@ -1256,6 +1263,25 @@ void FrameBuffer::switchVideoMode(int direction)
|
|||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void FrameBuffer::toggleBezel(bool toggle)
|
||||
{
|
||||
bool enabled = myOSystem.settings().getBool("bezel.show");
|
||||
|
||||
if(toggle)
|
||||
{
|
||||
if(myBufferType == BufferType::Emulator &&
|
||||
(fullScreen() || myOSystem.settings().getBool("bezel.windowed")))
|
||||
{
|
||||
enabled = !enabled;
|
||||
myOSystem.settings().setValue("bezel.show", enabled);
|
||||
myBezel->load();
|
||||
applyVideoMode();
|
||||
}
|
||||
}
|
||||
myOSystem.frameBuffer().showTextMessage(enabled ? "Bezel enabled" : "Bezel disabled");
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
FBInitStatus FrameBuffer::applyVideoMode()
|
||||
{
|
||||
|
@ -1271,7 +1297,8 @@ FBInitStatus FrameBuffer::applyVideoMode()
|
|||
const bool inTIAMode = myOSystem.eventHandler().inTIAMode();
|
||||
|
||||
// Build the new mode based on current settings
|
||||
const VideoModeHandler::Mode& mode = myVidModeHandler.buildMode(s, inTIAMode);
|
||||
const VideoModeHandler::Mode& mode
|
||||
= myVidModeHandler.buildMode(s, inTIAMode, myBezel->info());
|
||||
if(mode.imageR.size() > mode.screenS)
|
||||
return FBInitStatus::FailTooLarge;
|
||||
|
||||
|
@ -1295,7 +1322,11 @@ FBInitStatus FrameBuffer::applyVideoMode()
|
|||
// Inform TIA surface about new mode, and update TIA settings
|
||||
if(inTIAMode)
|
||||
{
|
||||
myTIASurface->initialize(myOSystem.console(), myActiveVidMode);
|
||||
#ifdef IMAGE_SUPPORT
|
||||
myBezel->apply();
|
||||
#endif
|
||||
|
||||
myTIASurface->initialize(myOSystem.console(), myActiveVidMode);
|
||||
if(fullScreen())
|
||||
myOSystem.settings().setValue("tia.fs_stretch",
|
||||
myActiveVidMode.stretch == VideoModeHandler::Mode::Stretch::Fill);
|
||||
|
@ -1317,18 +1348,19 @@ FBInitStatus FrameBuffer::applyVideoMode()
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
float FrameBuffer::maxWindowZoom() const
|
||||
double FrameBuffer::maxWindowZoom() const
|
||||
{
|
||||
const int display = displayId(BufferType::Emulator);
|
||||
float multiplier = 1;
|
||||
double multiplier = 1;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
// Figure out the zoomed size of the window
|
||||
const uInt32 width = TIAConstants::viewableWidth * multiplier;
|
||||
const uInt32 height = TIAConstants::viewableHeight * multiplier;
|
||||
// Figure out the zoomed size of the window (incl. the bezel)
|
||||
const uInt32 width = static_cast<double>(TIAConstants::viewableWidth) * myBezel->ratioW() * multiplier;
|
||||
const uInt32 height = static_cast<double>(TIAConstants::viewableHeight) * myBezel->ratioH() * multiplier;
|
||||
|
||||
if((width > myAbsDesktopSize[display].w) || (height > myAbsDesktopSize[display].h))
|
||||
if((width > myAbsDesktopSize[display].w) ||
|
||||
(height > myAbsDesktopSize[display].h))
|
||||
break;
|
||||
|
||||
multiplier += ZOOM_STEPS;
|
||||
|
|
|
@ -25,6 +25,7 @@ class Console;
|
|||
class Settings;
|
||||
class FBSurface;
|
||||
class TIASurface;
|
||||
class Bezel;
|
||||
|
||||
#ifdef GUI_SUPPORT
|
||||
#include "Font.hxx"
|
||||
|
@ -53,7 +54,7 @@ class FrameBuffer
|
|||
{
|
||||
public:
|
||||
// Zoom level step interval
|
||||
static constexpr float ZOOM_STEPS = 0.25;
|
||||
static constexpr double ZOOM_STEPS = 0.25;
|
||||
|
||||
enum UpdateMode {
|
||||
NONE = 0,
|
||||
|
@ -219,8 +220,8 @@ class FrameBuffer
|
|||
Get the minimum/maximum supported TIA zoom level (windowed mode)
|
||||
for the framebuffer.
|
||||
*/
|
||||
float supportedTIAMinZoom() const { return myTIAMinZoom * hidpiScaleFactor(); }
|
||||
float supportedTIAMaxZoom() const { return maxWindowZoom(); }
|
||||
double supportedTIAMinZoom() const { return myTIAMinZoom * hidpiScaleFactor(); }
|
||||
double supportedTIAMaxZoom() const { return maxWindowZoom(); }
|
||||
|
||||
/**
|
||||
Get the TIA surface associated with the framebuffer.
|
||||
|
@ -256,6 +257,11 @@ class FrameBuffer
|
|||
*/
|
||||
void switchVideoMode(int direction = +1);
|
||||
|
||||
/**
|
||||
Toggles the bezel display.
|
||||
*/
|
||||
void toggleBezel(bool toggle = true);
|
||||
|
||||
/**
|
||||
Sets the state of the cursor (hidden or grabbed) based on the
|
||||
current mode.
|
||||
|
@ -349,6 +355,19 @@ class FrameBuffer
|
|||
myBackend->getRGB(pixel, r, g, b);
|
||||
}
|
||||
|
||||
/**
|
||||
This method is called to retrieve the R/G/B/A data from the given pixel.
|
||||
|
||||
@param pixel The pixel containing R/G/B data
|
||||
@param r The red component of the color
|
||||
@param g The green component of the color
|
||||
@param b The blue component of the color
|
||||
@param a The alpha component of the color.
|
||||
*/
|
||||
void getRGBA(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b, uInt8* a) const {
|
||||
myBackend->getRGBA(pixel, r, g, b, a);
|
||||
}
|
||||
|
||||
/**
|
||||
This method is called to map a given R/G/B triple to the screen palette.
|
||||
|
||||
|
@ -360,6 +379,18 @@ class FrameBuffer
|
|||
return myBackend->mapRGB(r, g, b);
|
||||
}
|
||||
|
||||
/**
|
||||
This method is called to map a given R/G/B/A triple to the screen palette.
|
||||
|
||||
@param r The red component of the color.
|
||||
@param g The green component of the color.
|
||||
@param b The blue component of the color.
|
||||
@param a The alpha component of the color.
|
||||
*/
|
||||
uInt32 mapRGBA(uInt8 r, uInt8 g, uInt8 b, uInt8 a) const {
|
||||
return myBackend->mapRGBA(r, g, b, a);
|
||||
}
|
||||
|
||||
/**
|
||||
This method is called to get the specified ARGB data from the viewable
|
||||
FrameBuffer area. Note that this isn't the same as any internal
|
||||
|
@ -399,6 +430,14 @@ class FrameBuffer
|
|||
*/
|
||||
void resetSurfaces();
|
||||
|
||||
/**
|
||||
Renders TIA and overlaying, optional bezel surface
|
||||
|
||||
@param shade Shade the TIA surface after rendering
|
||||
@param doClear Clear the framebuffer before rendering
|
||||
*/
|
||||
void renderTIA(bool shade = false, bool doClear = true);
|
||||
|
||||
#ifdef GUI_SUPPORT
|
||||
/**
|
||||
Helps to create a basic message onscreen.
|
||||
|
@ -447,7 +486,7 @@ class FrameBuffer
|
|||
Calculate the maximum level by which the base window can be zoomed and
|
||||
still fit in the desktop screen.
|
||||
*/
|
||||
float maxWindowZoom() const;
|
||||
double maxWindowZoom() const;
|
||||
|
||||
/**
|
||||
Enables/disables fullscreen mode.
|
||||
|
@ -519,7 +558,10 @@ class FrameBuffer
|
|||
#endif
|
||||
|
||||
// The TIASurface class takes responsibility for TIA rendering
|
||||
unique_ptr<TIASurface> myTIASurface;
|
||||
shared_ptr<TIASurface> myTIASurface;
|
||||
|
||||
// The BezelSurface which blends over the TIA surface
|
||||
unique_ptr<Bezel> myBezel;
|
||||
|
||||
// Used for onscreen messages and frame statistics
|
||||
// (scanline count and framerate)
|
||||
|
@ -546,7 +588,7 @@ class FrameBuffer
|
|||
vector<bool> myHiDPIEnabled;
|
||||
|
||||
// Minimum TIA zoom level that can be used for this framebuffer
|
||||
float myTIAMinZoom{2.F};
|
||||
double myTIAMinZoom{2.F};
|
||||
|
||||
// Holds a reference to all the surfaces that have been created
|
||||
std::list<shared_ptr<FBSurface>> mySurfaceList;
|
||||
|
|
|
@ -208,6 +208,8 @@ bool GlobalKeyHandler::skipAVSetting() const
|
|||
myOSystem.settings().getInt("tv.scanlines") > 0;
|
||||
const bool isSoftwareRenderer =
|
||||
myOSystem.settings().getString("video") == "software";
|
||||
const bool allowBezel =
|
||||
myOSystem.settings().getBool("bezel.windowed") || isFullScreen;
|
||||
|
||||
return (mySetting == Setting::OVERSCAN && !isFullScreen)
|
||||
|| (mySetting == Setting::ZOOM && isFullScreen)
|
||||
|
@ -223,7 +225,8 @@ bool GlobalKeyHandler::skipAVSetting() const
|
|||
&& mySetting <= Setting::NTSC_BLEEDING
|
||||
&& !isCustomFilter)
|
||||
|| (mySetting == Setting::SCANLINE_MASK && !hasScanlines)
|
||||
|| (mySetting == Setting::INTERPOLATION && isSoftwareRenderer);
|
||||
|| (mySetting == Setting::INTERPOLATION && isSoftwareRenderer)
|
||||
|| (mySetting == Setting::BEZEL && !allowBezel);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -391,6 +394,7 @@ GlobalKeyHandler::SettingData GlobalKeyHandler::getSettingData(const Setting set
|
|||
{Setting::SCANLINES, {true, std::bind(&TIASurface::changeScanlineIntensity, &myOSystem.frameBuffer().tiaSurface(), _1)}},
|
||||
{Setting::SCANLINE_MASK, {false, std::bind(&TIASurface::cycleScanlineMask, &myOSystem.frameBuffer().tiaSurface(), _1)}},
|
||||
{Setting::INTERPOLATION, {false, std::bind(&Console::toggleInter, &myOSystem.console(), _1)}},
|
||||
{Setting::BEZEL, {false, std::bind(&FrameBuffer::toggleBezel, &myOSystem.frameBuffer(), _1)}},
|
||||
// *** Input group ***
|
||||
{Setting::DIGITAL_DEADZONE, {true, std::bind(&PhysicalJoystickHandler::changeDigitalDeadZone, &joyHandler(), _1)}},
|
||||
{Setting::ANALOG_DEADZONE, {true, std::bind(&PhysicalJoystickHandler::changeAnalogPaddleDeadZone, &joyHandler(), _1)}},
|
||||
|
|
|
@ -73,6 +73,7 @@ class GlobalKeyHandler
|
|||
SCANLINES,
|
||||
SCANLINE_MASK,
|
||||
INTERPOLATION,
|
||||
BEZEL,
|
||||
// *** Input group ***
|
||||
DIGITAL_DEADZONE,
|
||||
ANALOG_DEADZONE,
|
||||
|
@ -128,7 +129,7 @@ class GlobalKeyHandler
|
|||
// *** Ranges ***
|
||||
NUM_ADJ,
|
||||
START_AV_ADJ = VOLUME,
|
||||
END_AV_ADJ = INTERPOLATION,
|
||||
END_AV_ADJ = BEZEL,
|
||||
START_INPUT_ADJ = DIGITAL_DEADZONE,
|
||||
END_INPUT_ADJ = MOUSE_RANGE,
|
||||
START_DEBUG_ADJ = DEVELOPER,
|
||||
|
|
|
@ -292,24 +292,6 @@ void OSystem::setConfigPaths()
|
|||
buildDirIfRequired(myCfgDir, myBaseDir, "cfg");
|
||||
#endif
|
||||
|
||||
#ifdef IMAGE_SUPPORT
|
||||
const string_view ssSaveDir = mySettings->getString("snapsavedir");
|
||||
if(ssSaveDir == EmptyString)
|
||||
mySnapshotSaveDir = userDir();
|
||||
else
|
||||
mySnapshotSaveDir = FSNode(ssSaveDir);
|
||||
if(!mySnapshotSaveDir.isDirectory())
|
||||
mySnapshotSaveDir.makeDir();
|
||||
|
||||
const string_view ssLoadDir = mySettings->getString("snaploaddir");
|
||||
if(ssLoadDir == EmptyString)
|
||||
mySnapshotLoadDir = userDir();
|
||||
else
|
||||
mySnapshotLoadDir = FSNode(ssLoadDir);
|
||||
if(!mySnapshotLoadDir.isDirectory())
|
||||
mySnapshotLoadDir.makeDir();
|
||||
#endif
|
||||
|
||||
myCheatFile = myBaseDir; myCheatFile /= "stella.cht";
|
||||
myPaletteFile = myBaseDir; myPaletteFile /= "stella.pal";
|
||||
|
||||
|
@ -325,11 +307,56 @@ void OSystem::setConfigPaths()
|
|||
dbgPath("cfg dir ", myCfgDir);
|
||||
dbgPath("ssave dir ", mySnapshotSaveDir);
|
||||
dbgPath("sload dir ", mySnapshotLoadDir);
|
||||
dbgPath("bezel dir ", myBezelDir);
|
||||
dbgPath("cheat file", myCheatFile);
|
||||
dbgPath("pal file ", myPaletteFile);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef IMAGE_SUPPORT
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const FSNode& OSystem::snapshotSaveDir()
|
||||
{
|
||||
const string_view ssSaveDir = mySettings->getString("snapsavedir");
|
||||
if(ssSaveDir == EmptyString)
|
||||
mySnapshotSaveDir = userDir();
|
||||
else
|
||||
mySnapshotSaveDir = FSNode(ssSaveDir);
|
||||
if(!mySnapshotSaveDir.isDirectory())
|
||||
mySnapshotSaveDir.makeDir();
|
||||
|
||||
return mySnapshotSaveDir;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const FSNode& OSystem::snapshotLoadDir()
|
||||
{
|
||||
const string_view ssLoadDir = mySettings->getString("snaploaddir");
|
||||
if(ssLoadDir == EmptyString)
|
||||
mySnapshotLoadDir = userDir();
|
||||
else
|
||||
mySnapshotLoadDir = FSNode(ssLoadDir);
|
||||
if(!mySnapshotLoadDir.isDirectory())
|
||||
mySnapshotLoadDir.makeDir();
|
||||
|
||||
return mySnapshotLoadDir;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const FSNode& OSystem::bezelDir()
|
||||
{
|
||||
const string_view bezelDir = mySettings->getString("bezel.dir");
|
||||
if(bezelDir == EmptyString)
|
||||
myBezelDir = userDir();
|
||||
else
|
||||
myBezelDir = FSNode(bezelDir);
|
||||
if(!myBezelDir.isDirectory())
|
||||
myBezelDir.makeDir();
|
||||
|
||||
return myBezelDir;
|
||||
}
|
||||
#endif
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void OSystem::setUserDir(string_view path)
|
||||
{
|
||||
|
@ -726,6 +753,7 @@ unique_ptr<Console> OSystem::openConsole(const FSNode& romfile, string& md5)
|
|||
CMDLINE_PROPS_UPDATE("ppblend", PropType::Display_PPBlend);
|
||||
CMDLINE_PROPS_UPDATE("pxcenter", PropType::Controller_PaddlesXCenter);
|
||||
CMDLINE_PROPS_UPDATE("pycenter", PropType::Controller_PaddlesYCenter);
|
||||
CMDLINE_PROPS_UPDATE("bezelname", PropType::Bezel_Name);
|
||||
|
||||
// Finally, create the cart with the correct properties
|
||||
if(cart)
|
||||
|
|
|
@ -303,11 +303,12 @@ class OSystem
|
|||
|
||||
#ifdef IMAGE_SUPPORT
|
||||
/**
|
||||
Return the full/complete path name for saving and loading
|
||||
PNG snapshots.
|
||||
Return the full/complete path name for saving snapshots, loading
|
||||
launcher images and loading bezels.
|
||||
*/
|
||||
const FSNode& snapshotSaveDir() const { return mySnapshotSaveDir; }
|
||||
const FSNode& snapshotLoadDir() const { return mySnapshotLoadDir; }
|
||||
const FSNode& snapshotSaveDir();
|
||||
const FSNode& snapshotLoadDir();
|
||||
const FSNode& bezelDir();
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@ -599,7 +600,7 @@ class OSystem
|
|||
|
||||
private:
|
||||
FSNode myBaseDir, myStateDir, mySnapshotSaveDir, mySnapshotLoadDir,
|
||||
myNVRamDir, myCfgDir, myHomeDir, myUserDir;
|
||||
myNVRamDir, myCfgDir, myHomeDir, myUserDir, myBezelDir;
|
||||
FSNode myCheatFile, myPaletteFile;
|
||||
FSNode myRomFile; string myRomMD5;
|
||||
|
||||
|
|
|
@ -181,7 +181,8 @@ void Properties::print() const
|
|||
<< get(PropType::Display_Format) << "|"
|
||||
<< get(PropType::Display_VCenter) << "|"
|
||||
<< get(PropType::Display_Phosphor) << "|"
|
||||
<< get(PropType::Display_PPBlend)
|
||||
<< get(PropType::Display_PPBlend) << "|"
|
||||
<< get(PropType::Bezel_Name)
|
||||
<< endl;
|
||||
}
|
||||
|
||||
|
@ -241,7 +242,8 @@ void Properties::printHeader()
|
|||
<< "Display_Format|"
|
||||
<< "Display_VCenter|"
|
||||
<< "Display_Phosphor|"
|
||||
<< "Display_PPBlend"
|
||||
<< "Display_PPBlend|"
|
||||
<< "Bezel_Name"
|
||||
<< endl;
|
||||
}
|
||||
|
||||
|
@ -276,7 +278,8 @@ std::array<string, Properties::NUM_PROPS> Properties::ourDefaultProperties =
|
|||
"AUTO", // Display.Format
|
||||
"0", // Display.VCenter
|
||||
"NO", // Display.Phosphor
|
||||
"0" // Display.PPBlend
|
||||
"0", // Display.PPBlend
|
||||
"" // Bezel.Name
|
||||
};
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -310,5 +313,6 @@ std::array<string, Properties::NUM_PROPS> Properties::ourPropertyNames =
|
|||
"Display.Format",
|
||||
"Display.VCenter",
|
||||
"Display.Phosphor",
|
||||
"Display.PPBlend"
|
||||
"Display.PPBlend",
|
||||
"Bezel.Name"
|
||||
};
|
||||
|
|
|
@ -51,6 +51,7 @@ enum class PropType : uInt8 {
|
|||
Display_VCenter,
|
||||
Display_Phosphor,
|
||||
Display_PPBlend,
|
||||
Bezel_Name,
|
||||
NumTypes
|
||||
};
|
||||
|
||||
|
|
|
@ -62,6 +62,13 @@ Settings::Settings()
|
|||
setPermanent("display", 0);
|
||||
setPermanent("uimessages", "true");
|
||||
setPermanent("pausedim", "true");
|
||||
setPermanent("bezel.show", "true");
|
||||
setPermanent("bezel.windowed", "false");
|
||||
setPermanent("bezel.win.auto", "true");
|
||||
setPermanent("bezel.win.left", "12");
|
||||
setPermanent("bezel.win.right", "12");
|
||||
setPermanent("bezel.win.top", "0");
|
||||
setPermanent("bezel.win.bottom", "0");
|
||||
// TIA specific options
|
||||
setPermanent("tia.inter", "false");
|
||||
setPermanent("tia.zoom", "3");
|
||||
|
@ -160,6 +167,7 @@ Settings::Settings()
|
|||
setPermanent("romdir", "");
|
||||
setPermanent("userdir", "");
|
||||
setPermanent("saveuserdir", "false");
|
||||
setPermanent("bezel.dir", "");
|
||||
|
||||
// ROM browser options
|
||||
setPermanent("exitlauncher", "false");
|
||||
|
@ -534,10 +542,18 @@ void Settings::usage()
|
|||
<< " -detectpal60 <1|0> Enable PAL-60 autodetection\n"
|
||||
<< " -detectntsc50 <1|0> Enable NTSC-50 autodetection\n"
|
||||
<< endl
|
||||
<< " -speed <number> Run emulation at the given speed\n"
|
||||
<< " -turbo <1|0> Enable 'Turbo' mode for maximum emulation speed\n"
|
||||
<< " -uimessages <1|0> Show onscreen UI messages for different events\n"
|
||||
<< " -pausedim <1|0> Enable emulation dimming in pause mode\n"
|
||||
<< " -speed <number> Run emulation at the given speed\n"
|
||||
<< " -turbo <1|0> Enable 'Turbo' mode for maximum emulation speed\n"
|
||||
<< " -uimessages <1|0> Show onscreen UI messages for different events\n"
|
||||
<< " -pausedim <1|0> Enable emulation dimming in pause mode\n"
|
||||
<< endl
|
||||
<< " -bezel.show <1|0> Show bezel around emulation window\n"
|
||||
<< " -bezel.windowed <1|0> Show bezel in windowed modes\n"
|
||||
<< " -bezel.win.auto <1|0> Automatically set bezel window position\n"
|
||||
<< " -bezel.win.left <0-40> Set left bezel window position [%]\n"
|
||||
<< " -bezel.win.right <0-40> Set right bezel window position [%]\n"
|
||||
<< " -bezel.win.top <0-40> Set top bezel window position [%]\n"
|
||||
<< " -bezel.win.bottom <0-40> Set bottom bezel window position [%]\n"
|
||||
<< endl
|
||||
#ifdef SOUND_SUPPORT
|
||||
<< " -audio.enabled <1|0> Enable audio\n"
|
||||
|
@ -655,6 +671,7 @@ void Settings::usage()
|
|||
<< " -followlauncher <0|1> Default ROM path follows launcher navigation\n"
|
||||
<< " -userdir <dir> Set the path to save user files to\n"
|
||||
<< " -saveuserdir <0|1> Update user path when navigating in browser\n"
|
||||
<< " -bezel.dir <dir> Set the path to load bezels from\n"
|
||||
<< " -lastrom <name> Last played ROM, automatically selected in\n"
|
||||
<< " launcher\n"
|
||||
<< " -romloadcount <number> Number of ROM to load next from multicard\n"
|
||||
|
@ -746,6 +763,7 @@ void Settings::usage()
|
|||
<< " -vcenter <arg> Sets the 'Display.vcenter' property\n"
|
||||
<< " -pp <arg> Sets the 'Display.Phosphor' property\n"
|
||||
<< " -ppblend <arg> Sets the 'Display.PPBlend' property\n"
|
||||
<< " -bezelname <arg> Sets the 'Bezel.Name' property\n"
|
||||
<< endl
|
||||
#endif
|
||||
|
||||
|
|
|
@ -432,7 +432,7 @@ void TIASurface::createScanlineSurface()
|
|||
mySLineSurface = myFB.allocateSurface(width, height,
|
||||
interpolationModeFromSettings(myOSystem.settings()), data.data());
|
||||
|
||||
mySLineSurface->setSrcSize(mySLineSurface->width(), height);
|
||||
//mySLineSurface->setSrcSize(mySLineSurface->width(), height);
|
||||
mySLineSurface->setDstRect(myTiaSurface->dstRect());
|
||||
updateSurfaceSettings();
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ MODULE := src/emucore
|
|||
|
||||
MODULE_OBJS := \
|
||||
src/emucore/AtariVox.o \
|
||||
src/emucore/Bankswitch.o \
|
||||
src/emucore/Bankswitch.o \
|
||||
src/emucore/Booster.o \
|
||||
src/emucore/Cart.o \
|
||||
src/emucore/CartARM.o \
|
||||
|
|
|
@ -1410,7 +1410,7 @@ void DeveloperDialog::handleDebugColours(int idx, int color)
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void DeveloperDialog::handleDebugColours(string_view colors)
|
||||
{
|
||||
for(int i = 0; i < DEBUG_COLORS; ++i)
|
||||
for(int i = 0; i < DEBUG_COLORS && i < colors.length(); ++i)
|
||||
{
|
||||
switch(colors[i])
|
||||
{
|
||||
|
|
|
@ -269,7 +269,7 @@ void DialogContainer::handleMouseButtonEvent(MouseButton b, bool pressed,
|
|||
|
||||
if(b == MouseButton::MIDDLE)
|
||||
{
|
||||
// Middle mouse button emulates lef mouse button double click
|
||||
// Middle mouse button emulates left mouse button double click
|
||||
myLastClick.count = 2;
|
||||
b = MouseButton::LEFT;
|
||||
}
|
||||
|
|
|
@ -482,6 +482,13 @@ void GameInfoDialog::addCartridgeTab()
|
|||
buttonWidth(">>"), myUrl->getHeight(), ">>", kLinkPressed);
|
||||
wid.push_back(myUrl);
|
||||
|
||||
ypos += lineHeight + VGAP;
|
||||
new StaticTextWidget(myTab, _font, xpos, ypos + 1, lwidth, fontHeight, "Bezelname");
|
||||
myBezelName = new EditTextWidget(myTab, _font, xpos + lwidth, ypos - 1,
|
||||
fwidth, lineHeight, "");
|
||||
myBezelName->setToolTip("Define the name of the bezel file.");
|
||||
wid.push_back(myBezelName);
|
||||
|
||||
// Add items for tab 3
|
||||
addToFocusList(wid, myTab, tabID);
|
||||
|
||||
|
@ -870,6 +877,7 @@ void GameInfoDialog::loadCartridgeProperties(const Properties& props)
|
|||
myRarity->setText(props.get(PropType::Cart_Rarity));
|
||||
myNote->setText(props.get(PropType::Cart_Note));
|
||||
myUrl->setText(props.get(PropType::Cart_Url));
|
||||
myBezelName->setText(props.get(PropType::Bezel_Name));
|
||||
|
||||
updateLink();
|
||||
}
|
||||
|
@ -986,6 +994,7 @@ void GameInfoDialog::saveProperties()
|
|||
myGameProperties.set(PropType::Cart_Rarity, myRarity->getText());
|
||||
myGameProperties.set(PropType::Cart_Note, myNote->getText());
|
||||
myGameProperties.set(PropType::Cart_Url, myUrl->getText());
|
||||
myGameProperties.set(PropType::Bezel_Name, myBezelName->getText());
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -139,6 +139,7 @@ class GameInfoDialog : public Dialog, public CommandSender
|
|||
EditTextWidget* myNote{nullptr};
|
||||
ButtonWidget* myUrlButton{nullptr};
|
||||
EditTextWidget* myUrl{nullptr};
|
||||
EditTextWidget* myBezelName{nullptr};
|
||||
|
||||
// High Scores properties
|
||||
CheckboxWidget* myHighScores{nullptr};
|
||||
|
|
|
@ -299,7 +299,7 @@ UIDialog::UIDialog(OSystem& osystem, DialogContainer& parent,
|
|||
bwidth = font.getStringWidth("Image path" + ELLIPSIS) + fontWidth * 2 + 1;
|
||||
myOpenBrowserButton = new ButtonWidget(myTab, font, xpos, ypos, bwidth, buttonHeight,
|
||||
"Image path" + ELLIPSIS, kChooseSnapLoadDirCmd);
|
||||
myOpenBrowserButton->setToolTip("Select path for snapshot images used in Launcher.");
|
||||
myOpenBrowserButton->setToolTip("Select path for images used in Launcher.");
|
||||
wid.push_back(myOpenBrowserButton);
|
||||
|
||||
mySnapLoadPath = new EditTextWidget(myTab, font, HBORDER + lwidth,
|
||||
|
@ -316,10 +316,10 @@ UIDialog::UIDialog(OSystem& osystem, DialogContainer& parent,
|
|||
// Add message concerning usage
|
||||
xpos = HBORDER;
|
||||
ypos = myTab->getHeight() - fontHeight - ifont.getFontHeight() - VGAP - VBORDER;
|
||||
lwidth = ifont.getStringWidth("(*) Changes require an application restart");
|
||||
lwidth = ifont.getStringWidth("(*) Changes may require an application restart");
|
||||
new StaticTextWidget(myTab, ifont, xpos, ypos,
|
||||
std::min(lwidth, _w - HBORDER * 2), ifont.getFontHeight(),
|
||||
"(*) Changes require an application restart");
|
||||
"(*) Changes may require an application restart");
|
||||
|
||||
// Add items for tab 1
|
||||
addToFocusList(wid, myTab, tabID);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "Cart.hxx"
|
||||
#include "CartDPC.hxx"
|
||||
#include "Dialog.hxx"
|
||||
#include "BrowserDialog.hxx"
|
||||
#include "OSystem.hxx"
|
||||
#include "EditTextWidget.hxx"
|
||||
#include "PopUpWidget.hxx"
|
||||
|
@ -79,6 +80,7 @@ VideoAudioDialog::VideoAudioDialog(OSystem& osystem, DialogContainer& parent,
|
|||
addDisplayTab();
|
||||
addPaletteTab();
|
||||
addTVEffectsTab();
|
||||
addBezelTab();
|
||||
addAudioTab();
|
||||
|
||||
// Add Defaults, OK and Cancel buttons
|
||||
|
@ -351,7 +353,7 @@ void VideoAudioDialog::addTVEffectsTab()
|
|||
int pwidth = _font.getStringWidth("Bad adjust ");
|
||||
WidgetArray wid;
|
||||
VariantList items;
|
||||
const int tabID = myTab->addTab(" TV Effects ", TabWidget::AUTO_WIDTH);
|
||||
const int tabID = myTab->addTab("TV Effects", TabWidget::AUTO_WIDTH);
|
||||
|
||||
items.clear();
|
||||
VarList::push_back(items, "Disabled", static_cast<uInt32>(NTSCFilter::Preset::OFF));
|
||||
|
@ -438,6 +440,91 @@ void VideoAudioDialog::addTVEffectsTab()
|
|||
myTab->parentWidget(tabID)->setHelpAnchor("VideoAudioEffects");
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void VideoAudioDialog::addBezelTab()
|
||||
{
|
||||
const int lineHeight = Dialog::lineHeight(),
|
||||
buttonHeight = Dialog::buttonHeight(),
|
||||
fontWidth = Dialog::fontWidth(),
|
||||
VBORDER = Dialog::vBorder(),
|
||||
HBORDER = Dialog::hBorder(),
|
||||
VGAP = Dialog::vGap();
|
||||
const int INDENT = CheckboxWidget::prefixSize(_font);
|
||||
int xpos = HBORDER,
|
||||
ypos = VBORDER;
|
||||
WidgetArray wid;
|
||||
const int tabID = myTab->addTab(" Bezels ", TabWidget::AUTO_WIDTH);
|
||||
|
||||
// Enable bezels
|
||||
myBezelEnableCheckbox = new CheckboxWidget(myTab, _font, xpos, ypos,
|
||||
"Enable bezels", kBezelEnableChanged);
|
||||
//myBezelEnableCheckbox->setToolTip(Event::BezelToggle);
|
||||
wid.push_back(myBezelEnableCheckbox);
|
||||
xpos += INDENT;
|
||||
ypos += lineHeight + VGAP;
|
||||
|
||||
// Bezel path
|
||||
int bwidth = _font.getStringWidth("Bezel path" + ELLIPSIS) + fontWidth * 2 + 1;
|
||||
myOpenBrowserButton = new ButtonWidget(myTab, _font, xpos, ypos, bwidth, buttonHeight,
|
||||
"Bezel path" + ELLIPSIS, kChooseBezelDirCmd);
|
||||
myOpenBrowserButton->setToolTip("Select path for bezels.");
|
||||
wid.push_back(myOpenBrowserButton);
|
||||
|
||||
myBezelPath = new EditTextWidget(myTab, _font, xpos + bwidth + fontWidth,
|
||||
ypos + (buttonHeight - lineHeight) / 2 - 1,
|
||||
_w - xpos - bwidth - fontWidth - HBORDER - 2, lineHeight, "");
|
||||
wid.push_back(myBezelPath);
|
||||
|
||||
ypos += lineHeight + VGAP * 3;
|
||||
myBezelShowWindowed = new CheckboxWidget(myTab, _font, xpos, ypos,
|
||||
"Windowed modes");
|
||||
myBezelShowWindowed->setToolTip("Enable bezels in windowed modes as well.");
|
||||
wid.push_back(myBezelShowWindowed);
|
||||
|
||||
// Disable auto borders
|
||||
ypos += lineHeight + VGAP * 1;
|
||||
myManualWindow = new CheckboxWidget(myTab, _font, xpos, ypos,
|
||||
"Manual emulation window", kAutoWindowChanged);
|
||||
myManualWindow->setToolTip("Enable if automatic window detection fails.");
|
||||
wid.push_back(myManualWindow);
|
||||
xpos += INDENT;
|
||||
|
||||
const int lWidth = _font.getStringWidth("Bottom ");
|
||||
const int sWidth = myBezelPath->getRight() - xpos - lWidth - 4.5 * fontWidth; // _w - HBORDER - xpos - lwidth;
|
||||
ypos += lineHeight + VGAP * 1;
|
||||
myWinLeftSlider = new SliderWidget(myTab, _font, xpos, ypos, sWidth, lineHeight,
|
||||
"Left ", 0, 0, 4 * fontWidth, "%");
|
||||
myWinLeftSlider->setMinValue(0); myWinLeftSlider->setMaxValue(40);
|
||||
myWinLeftSlider->setTickmarkIntervals(4);
|
||||
wid.push_back(myWinLeftSlider);
|
||||
|
||||
ypos += lineHeight + VGAP * 1;
|
||||
myWinRightSlider = new SliderWidget(myTab, _font, xpos, ypos, sWidth, lineHeight,
|
||||
"Right ", 0, 0, 4 * fontWidth, "%");
|
||||
myWinRightSlider->setMinValue(0); myWinRightSlider->setMaxValue(40);
|
||||
myWinRightSlider->setTickmarkIntervals(4);
|
||||
wid.push_back(myWinRightSlider);
|
||||
|
||||
ypos += lineHeight + VGAP * 1;
|
||||
myWinTopSlider = new SliderWidget(myTab, _font, xpos, ypos, sWidth, lineHeight,
|
||||
"Top ", 0, 0, 4 * fontWidth, "%");
|
||||
myWinTopSlider->setMinValue(0); myWinTopSlider->setMaxValue(40);
|
||||
myWinTopSlider->setTickmarkIntervals(4);
|
||||
wid.push_back(myWinTopSlider);
|
||||
|
||||
ypos += lineHeight + VGAP;
|
||||
myWinBottomSlider = new SliderWidget(myTab, _font, xpos, ypos, sWidth, lineHeight,
|
||||
"Bottom ", 0, 0, 4 * fontWidth, "%");
|
||||
myWinBottomSlider->setMinValue(0); myWinBottomSlider->setMaxValue(40);
|
||||
myWinBottomSlider->setTickmarkIntervals(4);
|
||||
wid.push_back(myWinBottomSlider);
|
||||
|
||||
// Add items for tab 4
|
||||
addToFocusList(wid, myTab, tabID);
|
||||
|
||||
myTab->parentWidget(tabID)->setHelpAnchor("TODO???");
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void VideoAudioDialog::addAudioTab()
|
||||
{
|
||||
|
@ -450,7 +537,7 @@ void VideoAudioDialog::addAudioTab()
|
|||
int lwidth = _font.getStringWidth("Volume "), pwidth = 0;
|
||||
WidgetArray wid;
|
||||
VariantList items;
|
||||
const int tabID = myTab->addTab(" Audio ", TabWidget::AUTO_WIDTH);
|
||||
const int tabID = myTab->addTab(" Audio ", TabWidget::AUTO_WIDTH);
|
||||
|
||||
int xpos = HBORDER, ypos = VBORDER;
|
||||
|
||||
|
@ -460,7 +547,7 @@ void VideoAudioDialog::addAudioTab()
|
|||
mySoundEnableCheckbox->setToolTip(Event::SoundToggle);
|
||||
wid.push_back(mySoundEnableCheckbox);
|
||||
ypos += lineHeight + VGAP;
|
||||
xpos += CheckboxWidget::prefixSize(_font);
|
||||
xpos += INDENT;
|
||||
|
||||
// Volume
|
||||
myVolumeSlider = new SliderWidget(myTab, _font, xpos, ypos,
|
||||
|
@ -563,7 +650,7 @@ void VideoAudioDialog::addAudioTab()
|
|||
myDpcPitch->setTickmarkIntervals(2);
|
||||
wid.push_back(myDpcPitch);
|
||||
|
||||
// Add items for tab 4
|
||||
// Add items for tab 5
|
||||
addToFocusList(wid, myTab, tabID);
|
||||
|
||||
myTab->parentWidget(tabID)->setHelpAnchor("VideoAudioAudio");
|
||||
|
@ -630,7 +717,6 @@ void VideoAudioDialog::loadConfig()
|
|||
myPhaseShift->setTickmarkIntervals(4);
|
||||
myPhaseShift->setToolTip("Adjust PAL phase shift of 'Custom' palette.");
|
||||
myPhaseShift->setValue(myPaletteAdj.phasePal);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -679,6 +765,18 @@ void VideoAudioDialog::loadConfig()
|
|||
myTVScanIntense->setValue(settings.getInt("tv.scanlines"));
|
||||
myTVScanMask->setSelected(settings.getString("tv.scanmask"), TIASurface::SETTING_STANDARD);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Bezel tab
|
||||
myBezelEnableCheckbox->setState(settings.getBool("bezel.show"));
|
||||
myBezelPath->setText(settings.getString("bezel.dir"));
|
||||
myBezelShowWindowed->setState(settings.getBool("bezel.windowed"));
|
||||
myManualWindow->setState(!settings.getBool("bezel.win.auto"));
|
||||
myWinLeftSlider->setValue(settings.getInt("bezel.win.left"));
|
||||
myWinRightSlider->setValue(settings.getInt("bezel.win.right"));
|
||||
myWinTopSlider->setValue(settings.getInt("bezel.win.top"));
|
||||
myWinBottomSlider->setValue(settings.getInt("bezel.win.bottom"));
|
||||
handleBezelChange();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Audio tab
|
||||
AudioSettings& audioSettings = instance().audioSettings();
|
||||
|
@ -709,7 +807,7 @@ void VideoAudioDialog::loadConfig()
|
|||
|
||||
updateSettingsWithPreset(instance().audioSettings());
|
||||
|
||||
updateEnabledState();
|
||||
updateAudioEnabledState();
|
||||
|
||||
myTab->loadConfig();
|
||||
}
|
||||
|
@ -794,8 +892,7 @@ void VideoAudioDialog::saveConfig()
|
|||
NTSCFilter::saveConfig(settings);
|
||||
|
||||
// TV phosphor mode & blend
|
||||
settings.setValue("tv.phosphor",
|
||||
myTVPhosphor->getState() ? "always" : "byrom");
|
||||
settings.setValue("tv.phosphor", myTVPhosphor->getState() ? "always" : "byrom");
|
||||
settings.setValue("tv.phosblend", myTVPhosLevel->getValueLabel() == "Off"
|
||||
? "0" : myTVPhosLevel->getValueLabel());
|
||||
|
||||
|
@ -803,6 +900,18 @@ void VideoAudioDialog::saveConfig()
|
|||
settings.setValue("tv.scanlines", myTVScanIntense->getValueLabel());
|
||||
settings.setValue("tv.scanmask", myTVScanMask->getSelectedTag());
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Bezel tab
|
||||
settings.setValue("bezel.show", myBezelEnableCheckbox->getState());
|
||||
settings.setValue("bezel.dir", myBezelPath->getText());
|
||||
settings.setValue("bezel.windowed", myBezelShowWindowed->getState());
|
||||
settings.setValue("bezel.win.auto", !myManualWindow->getState());
|
||||
settings.setValue("bezel.win.left", myWinLeftSlider->getValueLabel());
|
||||
settings.setValue("bezel.win.right", myWinRightSlider->getValueLabel());
|
||||
settings.setValue("bezel.win.top", myWinTopSlider->getValueLabel());
|
||||
settings.setValue("bezel.win.bottom", myWinBottomSlider->getValueLabel());
|
||||
|
||||
// Note: The following has to happen after all video related setting have been saved
|
||||
if(instance().hasConsole())
|
||||
{
|
||||
instance().console().setTIAProperties();
|
||||
|
@ -936,7 +1045,15 @@ void VideoAudioDialog::setDefaults()
|
|||
loadTVAdjustables(NTSCFilter::Preset::CUSTOM);
|
||||
break;
|
||||
}
|
||||
case 3: // Audio
|
||||
case 3: // Bezels
|
||||
myBezelEnableCheckbox->setState(true);
|
||||
myBezelPath->setText(instance().userDir().getShortPath());
|
||||
myBezelShowWindowed->setState(false);
|
||||
myManualWindow->setState(false);
|
||||
handleBezelChange();
|
||||
break;
|
||||
|
||||
case 4: // Audio
|
||||
mySoundEnableCheckbox->setState(AudioSettings::DEFAULT_ENABLED);
|
||||
myVolumeSlider->setValue(AudioSettings::DEFAULT_VOLUME);
|
||||
myDevicePopup->setSelected(AudioSettings::DEFAULT_DEVICE);
|
||||
|
@ -953,7 +1070,7 @@ void VideoAudioDialog::setDefaults()
|
|||
}
|
||||
else updatePreset();
|
||||
|
||||
updateEnabledState();
|
||||
updateAudioEnabledState();
|
||||
break;
|
||||
|
||||
default: // satisfy compiler
|
||||
|
@ -1098,6 +1215,21 @@ void VideoAudioDialog::handlePhosphorChange()
|
|||
myTVPhosLevel->setEnabled(myTVPhosphor->getState());
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void VideoAudioDialog::handleBezelChange()
|
||||
{
|
||||
const bool enable = myBezelEnableCheckbox->getState();
|
||||
const bool nonAuto = myManualWindow->getState();
|
||||
|
||||
myOpenBrowserButton->setEnabled(enable);
|
||||
myBezelPath->setEnabled(enable);
|
||||
myBezelShowWindowed->setEnabled(enable);
|
||||
myWinLeftSlider->setEnabled(enable && nonAuto);
|
||||
myWinRightSlider->setEnabled(enable && nonAuto);
|
||||
myWinTopSlider->setEnabled(enable && nonAuto);
|
||||
myWinBottomSlider->setEnabled(enable && nonAuto);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void VideoAudioDialog::handleCommand(CommandSender* sender, int cmd,
|
||||
int data, int id)
|
||||
|
@ -1213,13 +1345,27 @@ void VideoAudioDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
myTVPhosLevel->setValueUnit("%");
|
||||
break;
|
||||
|
||||
case kBezelEnableChanged:
|
||||
case kAutoWindowChanged:
|
||||
handleBezelChange();
|
||||
break;
|
||||
|
||||
case kChooseBezelDirCmd:
|
||||
BrowserDialog::show(this, _font, "Select Bezel Directory",
|
||||
myBezelPath->getText(),
|
||||
BrowserDialog::Mode::Directories,
|
||||
[this](bool OK, const FSNode& node) {
|
||||
if(OK) myBezelPath->setText(node.getShortPath());
|
||||
});
|
||||
break;
|
||||
|
||||
case kSoundEnableChanged:
|
||||
updateEnabledState();
|
||||
updateAudioEnabledState();
|
||||
break;
|
||||
|
||||
case kModeChanged:
|
||||
updatePreset();
|
||||
updateEnabledState();
|
||||
updateAudioEnabledState();
|
||||
break;
|
||||
|
||||
case kHeadroomChanged:
|
||||
|
@ -1300,7 +1446,7 @@ void VideoAudioDialog::colorPalette()
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void VideoAudioDialog::updateEnabledState()
|
||||
void VideoAudioDialog::updateAudioEnabledState()
|
||||
{
|
||||
const bool active = mySoundEnableCheckbox->getState();
|
||||
const auto preset = static_cast<AudioSettings::Preset>
|
||||
|
|
|
@ -26,6 +26,7 @@ class PopUpWidget;
|
|||
class RadioButtonGroup;
|
||||
class SliderWidget;
|
||||
class StaticTextWidget;
|
||||
class EditTextWidget;
|
||||
class TabWidget;
|
||||
class OSystem;
|
||||
|
||||
|
@ -49,7 +50,9 @@ class VideoAudioDialog : public Dialog
|
|||
void addDisplayTab();
|
||||
void addPaletteTab();
|
||||
void addTVEffectsTab();
|
||||
void addBezelTab();
|
||||
void addAudioTab();
|
||||
|
||||
void handleTVModeChange(NTSCFilter::Preset);
|
||||
void loadTVAdjustables(NTSCFilter::Preset preset);
|
||||
void handleRendererChanged();
|
||||
|
@ -59,11 +62,15 @@ class VideoAudioDialog : public Dialog
|
|||
void handleFullScreenChange();
|
||||
void handleOverscanChange();
|
||||
void handlePhosphorChange();
|
||||
void handleBezelChange();
|
||||
|
||||
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
|
||||
|
||||
void addPalette(int x, int y, int w, int h);
|
||||
void colorPalette();
|
||||
|
||||
void updatePreset();
|
||||
void updateEnabledState();
|
||||
void updateAudioEnabledState();
|
||||
void updateSettingsWithPreset(AudioSettings&);
|
||||
|
||||
private:
|
||||
|
@ -123,6 +130,17 @@ class VideoAudioDialog : public Dialog
|
|||
std::array<StaticTextWidget*, 16> myColorLbl{nullptr};
|
||||
ColorWidget* myColor[16][8]{{nullptr}};
|
||||
|
||||
// Bezels
|
||||
CheckboxWidget* myBezelEnableCheckbox{nullptr};
|
||||
ButtonWidget* myOpenBrowserButton{nullptr};
|
||||
EditTextWidget* myBezelPath{nullptr};
|
||||
CheckboxWidget* myBezelShowWindowed{nullptr};
|
||||
CheckboxWidget* myManualWindow{nullptr};
|
||||
SliderWidget* myWinLeftSlider{nullptr};
|
||||
SliderWidget* myWinRightSlider{nullptr};
|
||||
SliderWidget* myWinTopSlider{nullptr};
|
||||
SliderWidget* myWinBottomSlider{nullptr};
|
||||
|
||||
// Audio
|
||||
CheckboxWidget* mySoundEnableCheckbox{nullptr};
|
||||
SliderWidget* myVolumeSlider{nullptr};
|
||||
|
@ -163,6 +181,10 @@ class VideoAudioDialog : public Dialog
|
|||
kPhosBlendChanged = 'VDbl',
|
||||
kScanlinesChanged = 'VDsc',
|
||||
|
||||
kBezelEnableChanged = 'BZen',
|
||||
kChooseBezelDirCmd = 'BZsl',
|
||||
kAutoWindowChanged = 'BZab',
|
||||
|
||||
kSoundEnableChanged = 'ADse',
|
||||
kDeviceChanged = 'ADdc',
|
||||
kModeChanged = 'ADmc',
|
||||
|
|
|
@ -47,6 +47,9 @@ class FBBackendLIBRETRO : public FBBackend
|
|||
uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const override {
|
||||
return (r << 16) | (g << 8) | b;
|
||||
}
|
||||
uInt32 mapRGBA(uInt8 r, uInt8 g, uInt8 b, uInt8 a) const override {
|
||||
return (a << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
/**
|
||||
This method is called to query and initialize the video hardware
|
||||
|
|
|
@ -734,6 +734,7 @@
|
|||
<ClCompile Include="..\..\common\audio\LanczosResampler.cxx" />
|
||||
<ClCompile Include="..\..\common\audio\SimpleResampler.cxx" />
|
||||
<ClCompile Include="..\..\common\Base.cxx" />
|
||||
<ClCompile Include="..\..\common\Bezel.cxx" />
|
||||
<ClCompile Include="..\..\common\DevSettingsHandler.cxx" />
|
||||
<ClCompile Include="..\..\common\EventHandlerSDL2.cxx" />
|
||||
<ClCompile Include="..\..\common\FBBackendSDL2.cxx" />
|
||||
|
@ -2050,6 +2051,7 @@
|
|||
<ClInclude Include="..\..\common\audio\Resampler.hxx" />
|
||||
<ClInclude Include="..\..\common\audio\SimpleResampler.hxx" />
|
||||
<ClInclude Include="..\..\common\Base.hxx" />
|
||||
<ClInclude Include="..\..\common\Bezel.hxx" />
|
||||
<ClInclude Include="..\..\common\bspf.hxx" />
|
||||
<ClInclude Include="..\..\common\DevSettingsHandler.hxx" />
|
||||
<ClInclude Include="..\..\common\EventHandlerSDL2.hxx" />
|
||||
|
|
|
@ -1209,6 +1209,9 @@
|
|||
<ClCompile Include="..\..\debugger\gui\Cart03E0Widget.cxx">
|
||||
<Filter>Source Files\debugger\gui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\common\Bezel.cxx">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\common\bspf.hxx">
|
||||
|
@ -2465,6 +2468,9 @@
|
|||
<ClInclude Include="..\..\debugger\gui\Cart03E0Widget.hxx">
|
||||
<Filter>Header Files\debugger\gui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\common\Bezel.hxx">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="stella.ico">
|
||||
|
|
|
@ -31,7 +31,8 @@ my %prop_type = (
|
|||
"Display.Format" => 25,
|
||||
"Display.VCenter" => 26,
|
||||
"Display.Phosphor" => 27,
|
||||
"Display.PPBlend" => 28
|
||||
"Display.PPBlend" => 28,
|
||||
"Bezel.Name" => 29
|
||||
);
|
||||
my @prop_type_as_string = (
|
||||
"Cart.MD5",
|
||||
|
@ -62,7 +63,8 @@ my @prop_type_as_string = (
|
|||
"Display.Format",
|
||||
"Display.VCenter",
|
||||
"Display.Phosphor",
|
||||
"Display.PPBlend"
|
||||
"Display.PPBlend",
|
||||
"Bezel.Name"
|
||||
);
|
||||
|
||||
my @prop_defaults = (
|
||||
|
@ -94,7 +96,8 @@ my @prop_defaults = (
|
|||
"AUTO", # Display.Format
|
||||
"0", # Display.VCenter
|
||||
"NO", # Display.Phosphor
|
||||
"0" # Display.PPBlend
|
||||
"0", # Display.PPBlend
|
||||
"" # Bezel.Name
|
||||
);
|
||||
|
||||
# Load and parse a properties file into an hash table of property
|
||||
|
|
After Width: | Height: | Size: 276 KiB |
After Width: | Height: | Size: 549 KiB |