Compare commits

..

2 Commits
master ... 7.0a

Author SHA1 Message Date
Stephen Anthony 01702f1230 Re-add ZIP build for Windows. 2024-10-07 16:27:13 -02:30
Stephen Anthony 3dbdcd0682 Automatically remove old DLL's for Windows install.
This fixes exe not starting with latest release.
2024-10-06 20:36:06 -02:30
854 changed files with 15957 additions and 24954 deletions

View File

@ -11,46 +11,35 @@ jobs:
linux: linux:
name: Linux name: Linux
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
SDL3_VERSION: 3.2.14
steps: steps:
- name: Check out the repository - name: Check out the repository
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8
- name: Install SDL3 - name: Install SDL2
run: | run: |
wget https://github.com/libsdl-org/SDL/releases/download/release-${SDL3_VERSION}/SDL3-${SDL3_VERSION}.tar.gz sudo apt-get update
tar -xzf SDL3-${SDL3_VERSION}.tar.gz sudo apt-get install libsdl2-dev
mv SDL3-${SDL3_VERSION} SDL3
cd SDL3
cmake -DCMAKE_INSTALL_PREFIX="`pwd`/prefix" -DSDL_UNIX_CONSOLE_BUILD=ON .
make -j2 install
- name: Build Stella - name: Build Stella
run: | run: |
CXXFLAGS="-I `pwd`/SDL3/prefix/include -O2 -g" PKG_CONFIG_PATH="`pwd`/SDL3/prefix/lib/pkgconfig/" ./configure ./configure && make -j2 all
make -j2 all
macos: macos:
name: macOS name: macOS
runs-on: macos-latest runs-on: macos-latest
env:
SDL3_VERSION: 3.2.14
steps: steps:
- uses: maxim-lobanov/setup-xcode@v1 - uses: maxim-lobanov/setup-xcode@v1
with: with:
xcode-version: '16.2' xcode-version: '15.4'
- name: Check out the repository - name: Check out the repository
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8
- name: Install SDL3 - name: Install SDL2
run: | run: |
wget https://github.com/libsdl-org/SDL/releases/download/release-${SDL3_VERSION}/SDL3-${SDL3_VERSION}.dmg curl https://www.libsdl.org/release/SDL2-2.26.0.tar.gz | tar xzf -
hdiutil attach SDL3-${SDL3_VERSION}.dmg mkdir SDL2-2.26.0/build
mkdir -p src/os/macos/Frameworks cd SDL2-2.26.0/build
cp -Rv /Volumes/SDL3/SDL3.xcframework src/os/macos/Frameworks ../configure && make -j3 && sudo make install
hdiutil detach /Volumes/SDL3
- name: Build Stella - name: Build Stella
run: | run: |
cd src/os/macos ./configure && make -j3 all
xcodebuild
windows: windows:
name: Windows name: Windows
@ -60,19 +49,19 @@ jobs:
platform: [x64] platform: [x64]
env: env:
Platform: ${{ matrix.platform }} Platform: ${{ matrix.platform }}
SDL3_version: 3.2.14 SDL2_version: 2.26.0
steps: steps:
- name: Check out the repository - name: Check out the repository
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8
- name: Set up MSBUILD - name: Set up MSBUILD
uses: microsoft/setup-msbuild@34cfbaee7f672c76950673338facd8a73f637506 uses: microsoft/setup-msbuild@34cfbaee7f672c76950673338facd8a73f637506
- name: Install SDL3 - name: Install SDL2
shell: cmd shell: cmd
run: | run: |
curl -o "C:\SDL3-devel.zip" "https://www.libsdl.org/release/SDL3-devel-%SDL3_version%-VC.zip" curl -o "C:\SDL2-devel.zip" "https://www.libsdl.org/release/SDL2-devel-%SDL2_version%-VC.zip"
7z x "C:\SDL3-devel.zip" -o"C:\" 7z x "C:\SDL2-devel.zip" -o"C:\"
xcopy /S "C:\SDL3-%SDL3_version%\include" src\common xcopy /S "C:\SDL2-%SDL2_version%\include" src\common
if %Platform%==x64 xcopy /S "C:\SDL3-%SDL3_version%\lib\x64" src\os\windows if %Platform%==x64 xcopy /S "C:\SDL2-%SDL2_version%\lib\x64" src\os\windows
- name: Build Stella - name: Build Stella
run: | run: |

3
.gitignore vendored
View File

@ -5,7 +5,6 @@ src/**/*.so
src/**/*.dylib src/**/*.dylib
src/**/*.dll src/**/*.dll
src/**/*.o src/**/*.o
src/**/*.d
src/**/*.obj src/**/*.obj
src/**/*.tlog src/**/*.tlog
out out
@ -26,7 +25,6 @@ src/os/macos/M6502.ins
.vscode/c_cpp_properties.json .vscode/c_cpp_properties.json
.vscode/settings.json .vscode/settings.json
src/os/windows/sdl/* src/os/windows/sdl/*
src/os/windows/sdl3/*
src/os/windows/x64/* src/os/windows/x64/*
src/os/windows/Win32/* src/os/windows/Win32/*
src/os/windows/Stella.vcxproj.user src/os/windows/Stella.vcxproj.user
@ -45,4 +43,3 @@ a.out
*.json *.json
*.sqlite3 *.sqlite3
*.bak *.bak
debian/files

View File

@ -28,7 +28,7 @@ distributions currently available are:
Stella-7.0-x64.exe (64-bit EXE installer) Stella-7.0-x64.exe (64-bit EXE installer)
Stella-7.0-windows.zip (64 bit ZIP version) Stella-7.0-windows.zip (64 bit ZIP version)
* Binary distribution for macOS 10.13 and above : * Binary distribution for macOS 10.12 and above :
Stella-7.0-macos.dmg (ARM M1 and 64-bit Intel) Stella-7.0-macos.dmg (ARM M1 and 64-bit Intel)
* Binary distribution for 64-bit Ubuntu : * Binary distribution for 64-bit Ubuntu :

View File

@ -12,20 +12,6 @@
Release History Release History
=========================================================================== ===========================================================================
7.x to 8.0 (xxx x, 202x)
* Ported Stella to SDL3
* Added option for switching Stella's theme in sync with OS
7.0 to 7.1 (xxx x, 202x)
* Enhanced PAL-60 detection, searches for signature (e.g. PAL60) in ROM
* Enhanced VSYNC emulation, considers VBLANK now too
* Added developer option for disabling PlusROM support
6.7.1 to 7.0 (October 5, 2024) 6.7.1 to 7.0 (October 5, 2024)
* Enhanced ROM launcher to allow multiple images per ROM. * Enhanced ROM launcher to allow multiple images per ROM.

View File

@ -12,7 +12,7 @@
License Information and Copyright Notice License Information and Copyright Notice
=========================================================================== ===========================================================================
Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony and the Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony and the
Stella Team Stella Team
This program is free software; you can redistribute it and/or modify it This program is free software; you can redistribute it and/or modify it

View File

@ -8,7 +8,7 @@
## SS SS tt ee ll ll aa aa ## SS SS tt ee ll ll aa aa
## SSSS ttt eeeee llll llll aaaaa ## SSSS ttt eeeee llll llll aaaaa
## ##
## Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony ## Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
## and the Stella Team ## and the Stella Team
## ##
## See the file "License.txt" for information on usage and redistribution of ## See the file "License.txt" for information on usage and redistribution of

65
configure vendored
View File

@ -6,7 +6,7 @@
# * command line options to... # * command line options to...
# - override the host settings (for cross compiles # - override the host settings (for cross compiles
# - whether to do a debug build (with -g) or an optimized build (-O3 etc.) # - whether to do a debug build (with -g) or an optimized build (-O3 etc.)
# * detect whether the chosen backend is available # * detect whether the chosen backend is available (e.g. call sdl2-config)
# * .... # * ....
@ -45,7 +45,6 @@ _ranlib=ranlib
_install=install _install=install
_ar="ar cru" _ar="ar cru"
_strip=strip _strip=strip
_pkg_config=pkg-config
_mkdir="mkdir -p" _mkdir="mkdir -p"
_echo=printf _echo=printf
_cat=cat _cat=cat
@ -54,6 +53,7 @@ _rm_rec="$_rm -r"
_zip="zip -q" _zip="zip -q"
_cp=cp _cp=cp
_windowspath="" _windowspath=""
_sdlconfig=sdl2-config
_sdlpath="$PATH" _sdlpath="$PATH"
_prefix=/usr/local _prefix=/usr/local
@ -335,22 +335,20 @@ case $_host in
# _host_os=amigaos # _host_os=amigaos
# _host_cpu=ppc # _host_cpu=ppc
# ;; # ;;
retron77)
_host_os=retron77
;;
mingw32-cross) mingw32-cross)
_host_os=mingw32msvc _host_os=mingw32msvc
_host_cpu=i386 _host_cpu=i386
_host_prefix=i386-mingw32msvc _host_prefix=i386-mingw32msvc
;; ;;
"") *)
guessed_host=`$_srcdir/config.guess` guessed_host=`$_srcdir/config.guess`
_host_cpu=`echo $guessed_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` _host_cpu=`echo $guessed_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
_host_os=`echo $guessed_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` _host_os=`echo $guessed_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
_host_vendor=`echo $guessed_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` _host_vendor=`echo $guessed_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
;; ;;
*)
_host_cpu=`echo "$_host" | sed 's/^\([^-]*\)-.*/\1/'`
_host_os=`echo "$_host" | sed 's/-\([^-]*\)-[^-]*$/\1/'`
_host_prefix="$_host"
;;
esac esac
# #
@ -555,6 +553,14 @@ fi
if test -n "$_host"; then if test -n "$_host"; then
# Cross-compiling mode - add your target here if needed # Cross-compiling mode - add your target here if needed
case "$_host" in case "$_host" in
retron77)
echo "Compiling for $_host, disabling windowed mode, debugger and cheats."
_build_gui=yes
_build_windowed=no
_build_debugger=no
_build_cheats=no
_build_httplib=no
;;
mingw32-cross) mingw32-cross)
echo "Cross-compiling for Windows using MinGW." echo "Cross-compiling for Windows using MinGW."
DEFINES="$DEFINES -DWIN32" DEFINES="$DEFINES -DWIN32"
@ -620,9 +626,6 @@ fi
# Cross-compilers use their own commands for the following functions # Cross-compilers use their own commands for the following functions
if test -n "$_host_prefix"; then if test -n "$_host_prefix"; then
_strip="$_host_prefix-$_strip" _strip="$_host_prefix-$_strip"
if command -v "$_host_prefix-$_pkg_config" >/dev/null 2>&1; then
_pkg_config="$_host_prefix-$_pkg_config"
fi
fi fi
# #
@ -636,7 +639,7 @@ if test "$_build_zip" = yes ; then
#include <zlib.h> #include <zlib.h>
int main(void) { return strcmp(ZLIB_VERSION, zlibVersion()); } int main(void) { return strcmp(ZLIB_VERSION, zlibVersion()); }
EOF EOF
cc_check $LDFLAGS $CXXFLAGS $ZLIB_CFLAGS $ZLIB_LIBS `$_pkg_config --libs zlib` && _zlib=yes cc_check $LDFLAGS $CXXFLAGS $ZLIB_CFLAGS $ZLIB_LIBS `pkg-config --libs zlib` && _zlib=yes
if test "$_zlib" = yes ; then if test "$_zlib" = yes ; then
echo "$_zlib" echo "$_zlib"
@ -662,7 +665,7 @@ if test "$_build_png" = yes ; then
#include <png.h> #include <png.h>
int main(void) { return printf("%s\n", PNG_HEADER_VERSION_STRING); } int main(void) { return printf("%s\n", PNG_HEADER_VERSION_STRING); }
EOF EOF
cc_check $LDFLAGS $CXXFLAGS $LIBPNG_CFLAGS $LIBPNG_LIBS `$_pkg_config --libs libpng` && _libpng=yes cc_check $LDFLAGS $CXXFLAGS $LIBPNG_CFLAGS $LIBPNG_LIBS `pkg-config --libs libpng` && _libpng=yes
if test "$_libpng" = yes ; then if test "$_libpng" = yes ; then
echo "$_libpng" echo "$_libpng"
@ -686,7 +689,7 @@ if test "$_build_sqlite3" = yes ; then
#include <sqlite3.h> #include <sqlite3.h>
int main(void) { return printf("%s\n", SQLITE_VERSION); } int main(void) { return printf("%s\n", SQLITE_VERSION); }
EOF EOF
cc_check $LDFLAGS $CXXFLAGS `$_pkg_config --libs sqlite3` && _libsqlite3=yes cc_check $LDFLAGS $CXXFLAGS `pkg-config --libs sqlite3` && _libsqlite3=yes
if test "$_libsqlite3" = yes ; then if test "$_libsqlite3" = yes ; then
echo "$_libsqlite3" echo "$_libsqlite3"
@ -805,8 +808,8 @@ fi
# #
# Now, add the appropriate defines/libraries/headers # Now, add the appropriate defines/libraries/headers
# #
#echo echo
#find_sdlconfig find_sdlconfig
SRC="src" SRC="src"
SRC_OS="$SRC/os" SRC_OS="$SRC/os"
@ -833,18 +836,15 @@ HTTP_LIB="$SRC_LIB/httplib"
INCLUDES="-I$CORE -I$COMMON -I$TV -I$TIA -I$TIA_FRAME_MANAGER -I$ELF -I$JSON -I$SQLITE_REPO" INCLUDES="-I$CORE -I$COMMON -I$TV -I$TIA -I$TIA_FRAME_MANAGER -I$ELF -I$JSON -I$SQLITE_REPO"
#INCLUDES="$INCLUDES `$_sdlconfig --cflags`" INCLUDES="$INCLUDES `$_sdlconfig --cflags`"
#if test "$_build_static" = yes ; then if test "$_build_static" = yes ; then
# _sdl_conf_libs="--static-libs" _sdl_conf_libs="--static-libs"
# LDFLAGS="-static $LDFLAGS" LDFLAGS="-static $LDFLAGS"
#else else
# _sdl_conf_libs="--libs" _sdl_conf_libs="--libs"
#fi fi
#if test "$_libpng" = yes ; then #FIXME:
LIBS="$LIBS `$_pkg_config --libs sdl3`"
#fi
LIBS="$LIBS `$_sdlconfig $_sdl_conf_libs`"
LD=$CXX LD=$CXX
case $_host_os in case $_host_os in
@ -865,6 +865,11 @@ case $_host_os in
fi fi
_libsqlite3=no _libsqlite3=no
;; ;;
retron77)
DEFINES="$DEFINES -DBSPF_UNIX -DRETRON77"
MODULES="$MODULES $SRC_OS/unix $SRC_OS/unix/r77"
INCLUDES="$INCLUDES -I$SRC_OS/unix -I$SRC_OS/unix/r77"
;;
win32) win32)
DEFINES="$DEFINES -DBSPF_WINDOWS" DEFINES="$DEFINES -DBSPF_WINDOWS"
MODULES="$MODULES $SRC_OS/windows" MODULES="$MODULES $SRC_OS/windows"
@ -917,7 +922,7 @@ if test "$_build_png" = yes ; then
INCLUDES="$INCLUDES -I$LIBJPG -I$LIBJPGEXIF" INCLUDES="$INCLUDES -I$LIBJPG -I$LIBJPGEXIF"
MODULES="$MODULES $LIBJPGEXIF" MODULES="$MODULES $LIBJPGEXIF"
if test "$_libpng" = yes ; then if test "$_libpng" = yes ; then
LIBS="$LIBS `$_pkg_config --libs libpng`" LIBS="$LIBS `pkg-config --libs libpng`"
else else
MODULES="$MODULES $LIBPNG" MODULES="$MODULES $LIBPNG"
INCLUDES="$INCLUDES -I$LIBPNG" INCLUDES="$INCLUDES -I$LIBPNG"
@ -925,7 +930,7 @@ if test "$_build_png" = yes ; then
fi fi
if test "$_libsqlite3" = yes ; then if test "$_libsqlite3" = yes ; then
LIBS="$LIBS `$_pkg_config --libs sqlite3`" LIBS="$LIBS `pkg-config --libs sqlite3`"
else else
MODULES="$MODULES $SQLITE_LIB" MODULES="$MODULES $SQLITE_LIB"
INCLUDES="$INCLUDES -I$SQLITE_LIB" INCLUDES="$INCLUDES -I$SQLITE_LIB"
@ -934,7 +939,7 @@ fi
if test "$_build_zip" = yes ; then if test "$_build_zip" = yes ; then
DEFINES="$DEFINES -DZIP_SUPPORT" DEFINES="$DEFINES -DZIP_SUPPORT"
if test "$_zlib" = yes ; then if test "$_zlib" = yes ; then
LIBS="$LIBS `$_pkg_config --libs zlib`" LIBS="$LIBS `pkg-config --libs zlib`"
else else
MODULES="$MODULES $ZLIB" MODULES="$MODULES $ZLIB"
INCLUDES="$INCLUDES -I$ZLIB" INCLUDES="$INCLUDES -I$ZLIB"

5
debian/control vendored
View File

@ -3,9 +3,8 @@ Maintainer: Stephen Anthony <sa666666@gmail.com>
Section: otherosfs Section: otherosfs
Priority: optional Priority: optional
Build-Depends: debhelper (>= 10~), Build-Depends: debhelper (>= 10~),
libgtest-dev,
libpng-dev, libpng-dev,
libsdl3-dev, libsdl2-dev,
zlib1g-dev zlib1g-dev
Standards-Version: 4.5.0 Standards-Version: 4.5.0
Vcs-Browser: https://github.com/stella-emu/stella/ Vcs-Browser: https://github.com/stella-emu/stella/
@ -19,7 +18,7 @@ Depends: ${misc:Depends},
${shlibs:Depends} ${shlibs:Depends}
Recommends: joystick (>= 1:1.5.1) Recommends: joystick (>= 1:1.5.1)
Pre-Depends: ${misc:Pre-Depends} Pre-Depends: ${misc:Pre-Depends}
Description: Atari 2600 Emulator for SDL Description: Atari 2600 Emulator for SDL2
Stella is a portable emulator of the old Atari 2600 video-game Stella is a portable emulator of the old Atari 2600 video-game
console. You can play most Atari 2600 games with it. console. You can play most Atari 2600 games with it.
. .

6
debian/copyright vendored
View File

@ -1,18 +1,18 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0 Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0
Upstream-Name: stella Upstream-Name: stella
Source: https://stella-emu.github.io Source: https://stella-emu.github.io
Copyright: 1995-2025 Bradford W. Mott, Stephen Anthony and the Stella Team Copyright: 1995-2024 Bradford W. Mott, Stephen Anthony and the Stella Team
License: GPL-2+ License: GPL-2+
Files: * Files: *
Copyright: 1995-2025 Bradford W. Mott, Stephen Anthony and the Stella Copyright: 1995-2024 Bradford W. Mott, Stephen Anthony and the Stella
Team Team
License: GPL-2+ License: GPL-2+
Files: debian/* Files: debian/*
Copyright: 1998-2004 Tom Lear <tom@trap.mtview.ca.us> Copyright: 1998-2004 Tom Lear <tom@trap.mtview.ca.us>
2006 Mario Iseli <admin@marioiseli.com> 2006 Mario Iseli <admin@marioiseli.com>
2010-2025 Stephen Kitt <skitt@debian.org> 2010-2024 Stephen Kitt <skitt@debian.org>
License: GPL-2+ License: GPL-2+
Files: Files:

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -21,7 +21,7 @@
<img src="graphics/stella_icon.png"> <img src="graphics/stella_icon.png">
<h2><b>A multi-platform Atari 2600 VCS emulator</b></h2> <h2><b>A multi-platform Atari 2600 VCS emulator</b></h2>
<h4><b>Release 8.0</b></h4> <h4><b>Release 7.0</b></h4>
<br> <br>
<h2><b>User's Guide</b></h2> <h2><b>User's Guide</b></h2>
@ -342,7 +342,7 @@
<p> <p>
<h3><b><u>General</u> (required for all versions of Stella)</b></h3> <h3><b><u>General</u> (required for all versions of Stella)</b></h3>
<ul> <ul>
<li>SDL version 3.2.14 or greater, latest version highly recommended</li> <li>SDL version 2.0.14 or greater, latest version highly recommended</li>
<li>32 bit color graphics card</li> <li>32 bit color graphics card</li>
<li>Enough RAM for the OS + 256MB RAM for the emulation; 512MB+ highly recommended</li> <li>Enough RAM for the OS + 256MB RAM for the emulation; 512MB+ highly recommended</li>
<li>Joysticks or gamepads are highly recommended</li> <li>Joysticks or gamepads are highly recommended</li>
@ -368,9 +368,9 @@
<p>The Mac version of Stella is designed to work on an Apple Macintosh with <p>The Mac version of Stella is designed to work on an Apple Macintosh with
the following:</p> the following:</p>
<ul> <ul>
<li>macOS 10.13 or above</li> <li>macOS 10.11 or above</li>
<li>64-bit ARM or Intel processor</li> <li>64-bit ARM or Intel processor</li>
<li>Xcode 16.0 is required to compile the Stella source code</li> <li>Xcode 13.0 is required to compile the Stella source code</li>
</ul> </ul>
<p> <p>
@ -387,7 +387,7 @@
<h3><b><u>Other</u></b></h3> <h3><b><u>Other</u></b></h3>
<p>Stella is extremely portable, and in its lifetime has been ported to almost every <p>Stella is extremely portable, and in its lifetime has been ported to almost every
platform where the SDL library exists. It is 64-bit and endian clean in Linux/Unix, macOS platform where the SDL library exists. It is 32/64-bit and endian clean in Linux/Unix, macOS
and Windows. The Stella Team is interested in hearing about any problems you may and Windows. The Stella Team is interested in hearing about any problems you may
encounter with diverse operating systems and CPU types.</p> encounter with diverse operating systems and CPU types.</p>
</blockquote></br> </blockquote></br>
@ -2526,7 +2526,7 @@
<br> <br>
<p><h2> <p><h2>
<a name="HighScores">High Scores Saving</a></h2> <a name="Highscores">High Scores Saving</a></h2>
<blockquote> <blockquote>
<p>Stella allows the user to save high scores when the required definitions <p>Stella allows the user to save high scores when the required definitions
@ -3249,17 +3249,12 @@
<tr> <tr>
<td><pre>-uipalette2 &lt;standard|classic|light|dark&gt;</pre></td> <td><pre>-uipalette2 &lt;standard|classic|light|dark&gt;</pre></td>
<td>Define alternative/light palette/theme for UI elements.</td> <td>Define alternative palette/theme for UI elements.</td>
</tr> </tr>
<tr> <tr>
<td><pre>-altpalette &lt;1|0&gt;</pre></td> <td><pre>-altpalette &lt;1|0&gt;</pre></td>
<td>Use alternative/dark palette/theme for UI elements.</td> <td>Use alternative palette/theme for UI elements.</td>
</tr>
<tr>
<td><pre>-autouipalette &lt;1|0&gt;</pre></td>
<td>Automatic switching between light and dark theme in sync with OS.</td>
</tr> </tr>
<tr> <tr>
@ -3640,9 +3635,6 @@
</tr><tr> </tr><tr>
<td><pre>-&lt;plr.|dev.&gt;console &lt;2600|7800&gt;</pre></td> <td><pre>-&lt;plr.|dev.&gt;console &lt;2600|7800&gt;</pre></td>
<td>Select console for B/W and Pause key handling and RAM initialization.</td> <td>Select console for B/W and Pause key handling and RAM initialization.</td>
</tr><tr>
<td><pre>-dev.plusroms.on &lt;1|0&gt;</pre></td>
<td>Enable/disable PlusROM support, e.g. for testing.</td>
</tr><tr> </tr><tr>
<td><pre>-&lt;plr.|dev.&gt;bankrandom &lt;1|0&gt;</pre></td> <td><pre>-&lt;plr.|dev.&gt;bankrandom &lt;1|0&gt;</pre></td>
<td>On reset, randomize the startup bank (only for selected bankswitch types).</td> <td>On reset, randomize the startup bank (only for selected bankswitch types).</td>
@ -4015,9 +4007,8 @@
<td valign="top"> <td valign="top">
<table border="1" cellpadding="4"> <table border="1" cellpadding="4">
<tr><th>Item</th><th>Brief description</th><th>For more information,<br>see <a href="#CommandLine">Command Line</a></th></tr> <tr><th>Item</th><th>Brief description</th><th>For more information,<br>see <a href="#CommandLine">Command Line</a></th></tr>
<tr><td>Light theme</td><td>Default theme to use for UI elements (see examples)</td><td>-uipalette</td></tr> <tr><td>Theme #1</td><td>Default theme to use for UI elements (see examples)</td><td>-uipalette</td></tr>
<tr><td>Dark theme</td><td>Alternative theme to use for UI elements (see examples)</td><td>-uipalette2</td></tr> <tr><td>Theme #2</td><td>Alternative theme to use for UI elements (see examples)</td><td>-uipalette2</td></tr>
<tr><td>Auto theme</td><td>Enable for automatic switching between light and dark themes in sync with OS.</td><td>-autouipalette</td></tr>
<tr><td>Dialogs font</td><td>The font used in the dialogs</td><td>-dialogfont</td></tr> <tr><td>Dialogs font</td><td>The font used in the dialogs</td><td>-dialogfont</td></tr>
<tr><td>HiDPI mode</td><td>Scale the UI by a factor of two when enabled</td><td>-hidpi</td></tr> <tr><td>HiDPI mode</td><td>Scale the UI by a factor of two when enabled</td><td>-hidpi</td></tr>
<tr><td>Dialogs position</td><td>Position of dialogs with Stella window</td><td>-dialogpos</td></tr> <tr><td>Dialogs position</td><td>Position of dialogs with Stella window</td><td>-dialogpos</td></tr>
@ -4585,11 +4576,6 @@
emulation and zero-page RAM initialization</td> emulation and zero-page RAM initialization</td>
<td>-plr.console <br/>-dev.console</td> <td>-plr.console <br/>-dev.console</td>
</tr> </tr>
<tr>
<td>PlusROM support</td>
<td>Enable/disable PlusROM support, e.g. for testing.</td>
<td>-dev.plusroms.on</td>
</tr>
<tr> <tr>
<td>Random startup bank</td> <td>Random startup bank</td>
<td>Randomize the startup bank (only for selected bankswitch types)</td> <td>Randomize the startup bank (only for selected bankswitch types)</td>
@ -5155,9 +5141,7 @@ Ms Pac-Man (Stella extended codes):
<tr><td>NTSC50 &#185;</td><td>NTSC50, NTSC 50, NTSC-50, PAL-N</td></tr> <tr><td>NTSC50 &#185;</td><td>NTSC50, NTSC 50, NTSC-50, PAL-N</td></tr>
<tr><td>PAL60 &#185;</td><td>PAL60, PAL 60, PAL-60</td></tr> <tr><td>PAL60 &#185;</td><td>PAL60, PAL 60, PAL-60</td></tr>
<tr><td>SECAM60 &#185;</td><td>SECAM60, SECAM 60, SECAM-60</td></tr> <tr><td>SECAM60 &#185;</td><td>SECAM60, SECAM 60, SECAM-60</td></tr>
</table> </table></td>
Additionally, to detect PAL-60 format games, the ROM is scanned for the following
signature strings: "PAL60", "PAL 60" and "PAL-60"</td>
</tr> </tr>
<tr> <tr>

View File

@ -264,7 +264,7 @@
<td width="41%">This dialog is similar to the PC version's <td width="41%">This dialog is similar to the PC version's
<a href="index.html#CommandMenu"><b>Command Menu</b></a>, but with some <a href="index.html#CommandMenu"><b>Command Menu</b></a>, but with some
commands especially selected for the RetroN&nbsp;77.</td> commands especially selected for the RetroN&nbsp;77.</td>
<td><p><img src="graphics/commandsmenu_r77.png"></p></td> <td><p><img src="commandsmenu_r77.png"></p></td>
</tr> </tr>
</table> </table>
</p> </p>
@ -309,7 +309,7 @@
</tr> </tr>
</table> </table>
</td> </td>
<td><p><img src="graphics/basic_settings.png"></p></td> <td><p><img src="basic_settings.png"></p></td>
</tr> </tr>
</table> </table>
</p> </p>

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -33,7 +33,7 @@ BankRomCheat::BankRomCheat(OSystem& os, string_view name, string_view code)
count = static_cast<uInt8>(BSPF::stoi<16>(myCode.substr(7, 1)) + 1); count = static_cast<uInt8>(BSPF::stoi<16>(myCode.substr(7, 1)) + 1);
// Back up original data; we need this if the cheat is ever disabled // Back up original data; we need this if the cheat is ever disabled
for(int i = 0; std::cmp_less(i, count); ++i) for(int i = 0; i < count; ++i)
savedRom[i] = myOSystem.console().cartridge().peek(address + i); savedRom[i] = myOSystem.console().cartridge().peek(address + i);
} }
@ -50,7 +50,7 @@ bool BankRomCheat::disable()
const int oldBank = myOSystem.console().cartridge().getBank(address); const int oldBank = myOSystem.console().cartridge().getBank(address);
myOSystem.console().cartridge().bank(bank); myOSystem.console().cartridge().bank(bank);
for(int i = 0; std::cmp_less(i, count); ++i) for(int i = 0; i < count; ++i)
myOSystem.console().cartridge().patch(address + i, savedRom[i]); myOSystem.console().cartridge().patch(address + i, savedRom[i]);
myOSystem.console().cartridge().bank(oldBank); myOSystem.console().cartridge().bank(oldBank);
@ -66,7 +66,7 @@ void BankRomCheat::evaluate()
const int oldBank = myOSystem.console().cartridge().getBank(address); const int oldBank = myOSystem.console().cartridge().getBank(address);
myOSystem.console().cartridge().bank(bank); myOSystem.console().cartridge().bank(bank);
for(int i = 0; std::cmp_less(i, count); ++i) for(int i = 0; i < count; ++i)
myOSystem.console().cartridge().patch(address + i, value); myOSystem.console().cartridge().patch(address + i, value);
myOSystem.console().cartridge().bank(oldBank); myOSystem.console().cartridge().bank(oldBank);

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -28,7 +28,7 @@ CheetahCheat::CheetahCheat(OSystem& os, string_view name, string_view code)
count{static_cast<uInt8>(BSPF::stoi<16>(code.substr(5, 1)) + 1)} count{static_cast<uInt8>(BSPF::stoi<16>(code.substr(5, 1)) + 1)}
{ {
// Back up original data; we need this if the cheat is ever disabled // Back up original data; we need this if the cheat is ever disabled
for(uInt8 i = 0; i < count; ++i) for(int i = 0; i < count; ++i)
savedRom[i] = myOSystem.console().cartridge().peek(address + i); savedRom[i] = myOSystem.console().cartridge().peek(address + i);
} }
@ -42,7 +42,7 @@ bool CheetahCheat::enable()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CheetahCheat::disable() bool CheetahCheat::disable()
{ {
for(uInt8 i = 0; i < count; ++i) for(int i = 0; i < count; ++i)
myOSystem.console().cartridge().patch(address + i, savedRom[i]); myOSystem.console().cartridge().patch(address + i, savedRom[i]);
return myEnabled = false; return myEnabled = false;
@ -53,7 +53,7 @@ void CheetahCheat::evaluate()
{ {
if(!myEnabled) if(!myEnabled)
{ {
for(uInt8 i = 0; i < count; ++i) for(int i = 0; i < count; ++i)
myOSystem.console().cartridge().patch(address + i, value); myOSystem.console().cartridge().patch(address + i, value);
myEnabled = true; myEnabled = true;

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -67,6 +67,20 @@ void AudioSettings::normalize(Settings& settings)
break; break;
} }
switch (settings.getInt(SETTING_FRAGMENT_SIZE)) {
case 128:
case 256:
case 512:
case 1024:
case 2048:
case 4096:
break;
default:
settings.setValue(SETTING_FRAGMENT_SIZE, DEFAULT_FRAGMENT_SIZE);
break;
}
const int settingBufferSize = settings.getInt(SETTING_BUFFER_SIZE); const int settingBufferSize = settings.getInt(SETTING_BUFFER_SIZE);
if (settingBufferSize < 0 || settingBufferSize > MAX_BUFFER_SIZE) settings.setValue(SETTING_BUFFER_SIZE, DEFAULT_BUFFER_SIZE); if (settingBufferSize < 0 || settingBufferSize > MAX_BUFFER_SIZE) settings.setValue(SETTING_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
@ -101,7 +115,7 @@ uInt32 AudioSettings::sampleRate()
uInt32 AudioSettings::fragmentSize() uInt32 AudioSettings::fragmentSize()
{ {
updatePresetFromSettings(); updatePresetFromSettings();
return myPresetFragmentSize; // No longer configurable in sound backend return customSettings() ? lboundInt(mySettings.getInt(SETTING_FRAGMENT_SIZE), DEFAULT_FRAGMENT_SIZE) : myPresetFragmentSize;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -141,6 +155,12 @@ uInt32 AudioSettings::volume() const
return lboundInt(mySettings.getInt(SETTING_VOLUME), 0); return lboundInt(mySettings.getInt(SETTING_VOLUME), 0);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 AudioSettings::device() const
{
return mySettings.getInt(SETTING_DEVICE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool AudioSettings::enabled() const bool AudioSettings::enabled() const
{ {
@ -165,6 +185,7 @@ void AudioSettings::setPreset(AudioSettings::Preset preset)
case Preset::lowQualityMediumLag: case Preset::lowQualityMediumLag:
myPresetSampleRate = 44100; myPresetSampleRate = 44100;
myPresetFragmentSize = 1024;
myPresetBufferSize = 6; myPresetBufferSize = 6;
myPresetHeadroom = 5; myPresetHeadroom = 5;
myPresetResamplingQuality = ResamplingQuality::nearestNeighbour; myPresetResamplingQuality = ResamplingQuality::nearestNeighbour;
@ -172,6 +193,7 @@ void AudioSettings::setPreset(AudioSettings::Preset preset)
case Preset::highQualityMediumLag: case Preset::highQualityMediumLag:
myPresetSampleRate = 44100; myPresetSampleRate = 44100;
myPresetFragmentSize = 1024;
myPresetBufferSize = 6; myPresetBufferSize = 6;
myPresetHeadroom = 5; myPresetHeadroom = 5;
myPresetResamplingQuality = ResamplingQuality::lanczos_2; myPresetResamplingQuality = ResamplingQuality::lanczos_2;
@ -179,6 +201,7 @@ void AudioSettings::setPreset(AudioSettings::Preset preset)
case Preset::highQualityLowLag: case Preset::highQualityLowLag:
myPresetSampleRate = 48000; myPresetSampleRate = 48000;
myPresetFragmentSize = 512;
myPresetBufferSize = 3; myPresetBufferSize = 3;
myPresetHeadroom = 2; myPresetHeadroom = 2;
myPresetResamplingQuality = ResamplingQuality::lanczos_2; myPresetResamplingQuality = ResamplingQuality::lanczos_2;
@ -186,6 +209,7 @@ void AudioSettings::setPreset(AudioSettings::Preset preset)
case Preset::ultraQualityMinimalLag: case Preset::ultraQualityMinimalLag:
myPresetSampleRate = 96000; myPresetSampleRate = 96000;
myPresetFragmentSize = 128;
myPresetBufferSize = 0; myPresetBufferSize = 0;
myPresetHeadroom = 0; myPresetHeadroom = 0;
myPresetResamplingQuality = ResamplingQuality::lanczos_3; myPresetResamplingQuality = ResamplingQuality::lanczos_3;
@ -207,6 +231,15 @@ void AudioSettings::setSampleRate(uInt32 sampleRate)
normalize(mySettings); normalize(mySettings);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AudioSettings::setFragmentSize(uInt32 fragmentSize)
{
if (!myIsPersistent) return;
mySettings.setValue(SETTING_FRAGMENT_SIZE, fragmentSize);
normalize(mySettings);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AudioSettings::setBufferSize(uInt32 bufferSize) void AudioSettings::setBufferSize(uInt32 bufferSize)
{ {
@ -259,6 +292,14 @@ void AudioSettings::setVolume(uInt32 volume)
normalize(mySettings); normalize(mySettings);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AudioSettings::setDevice(uInt32 device)
{
if(!myIsPersistent) return;
mySettings.setValue(SETTING_DEVICE, device);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AudioSettings::setEnabled(bool isEnabled) void AudioSettings::setEnabled(bool isEnabled)
{ {

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -42,11 +42,13 @@ class AudioSettings
static constexpr string_view SETTING_PRESET = "audio.preset"; static constexpr string_view SETTING_PRESET = "audio.preset";
static constexpr string_view SETTING_SAMPLE_RATE = "audio.sample_rate"; static constexpr string_view SETTING_SAMPLE_RATE = "audio.sample_rate";
static constexpr string_view SETTING_FRAGMENT_SIZE = "audio.fragment_size";
static constexpr string_view SETTING_BUFFER_SIZE = "audio.buffer_size"; static constexpr string_view SETTING_BUFFER_SIZE = "audio.buffer_size";
static constexpr string_view SETTING_HEADROOM = "audio.headroom"; static constexpr string_view SETTING_HEADROOM = "audio.headroom";
static constexpr string_view SETTING_RESAMPLING_QUALITY = "audio.resampling_quality"; static constexpr string_view SETTING_RESAMPLING_QUALITY = "audio.resampling_quality";
static constexpr string_view SETTING_STEREO = "audio.stereo"; static constexpr string_view SETTING_STEREO = "audio.stereo";
static constexpr string_view SETTING_VOLUME = "audio.volume"; static constexpr string_view SETTING_VOLUME = "audio.volume";
static constexpr string_view SETTING_DEVICE = "audio.device";
static constexpr string_view SETTING_ENABLED = "audio.enabled"; static constexpr string_view SETTING_ENABLED = "audio.enabled";
static constexpr string_view SETTING_DPC_PITCH = "audio.dpc_pitch"; static constexpr string_view SETTING_DPC_PITCH = "audio.dpc_pitch";
@ -130,8 +132,7 @@ class AudioSettings
Preset myPreset{Preset::custom}; Preset myPreset{Preset::custom};
uInt32 myPresetSampleRate{0}; uInt32 myPresetSampleRate{0};
uInt32 myPresetFragmentSize{1024}; // no longer configurable in sound backend uInt32 myPresetFragmentSize{0};
uInt32 myPresetBufferSize{0}; uInt32 myPresetBufferSize{0};
uInt32 myPresetHeadroom{0}; uInt32 myPresetHeadroom{0};
ResamplingQuality myPresetResamplingQuality{ResamplingQuality::nearestNeighbour}; ResamplingQuality myPresetResamplingQuality{ResamplingQuality::nearestNeighbour};

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -234,8 +234,9 @@ void Bezel::apply()
// Note: Variable bezel window positions are handled in VideoModeHandler::Mode // Note: Variable bezel window positions are handled in VideoModeHandler::Mode
// Enable blending to allow overlaying the bezel over the TIA output // Enable blending to allow overlaying the bezel over the TIA output
mySurface->enableBlend(true); mySurface->attributes().blending = true;
mySurface->setBlendLevel(100); mySurface->attributes().blendalpha = 100;
mySurface->applyAttributes();
mySurface->setVisible(true); mySurface->setVisible(true);
} }
else else

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -46,7 +46,6 @@ void DevSettingsHandler::loadSettings(SettingsSet set)
// AtariVox/SaveKey/PlusROM access // AtariVox/SaveKey/PlusROM access
myExternAccess[set] = settings.getBool(prefix + "extaccess"); myExternAccess[set] = settings.getBool(prefix + "extaccess");
myConsole[set] = settings.getString(prefix + "console") == "7800" ? 1 : 0; myConsole[set] = settings.getString(prefix + "console") == "7800" ? 1 : 0;
myPlusROM[set] = devSettings ? settings.getBool("dev.plusroms.on") : true;
// Randomization // Randomization
myRandomBank[set] = settings.getBool(prefix + "bankrandom"); myRandomBank[set] = settings.getBool(prefix + "bankrandom");
myRandomizeTIA[set] = settings.getBool(prefix + "tiarandom"); myRandomizeTIA[set] = settings.getBool(prefix + "tiarandom");
@ -119,7 +118,6 @@ void DevSettingsHandler::saveSettings(SettingsSet set)
if(devSettings) if(devSettings)
{ {
settings.setValue("dev.plusroms.on", myPlusROM[set]);
settings.setValue("dev.hsrandom", myRandomHotspots[set]); settings.setValue("dev.hsrandom", myRandomHotspots[set]);
// Undriven TIA pins // Undriven TIA pins
settings.setValue("dev.tiadriven", myUndrivenPins[set]); settings.setValue("dev.tiadriven", myUndrivenPins[set]);
@ -185,7 +183,6 @@ void DevSettingsHandler::applySettings(SettingsSet set)
{ {
myOSystem.console().cartridge().enableRandomHotspots(myRandomHotspots[set]); myOSystem.console().cartridge().enableRandomHotspots(myRandomHotspots[set]);
myOSystem.console().tia().driveUnusedPinsRandom(myUndrivenPins[set]); myOSystem.console().tia().driveUnusedPinsRandom(myUndrivenPins[set]);
myOSystem.console().cartridge().enablePlusROM(myPlusROM[set]);
// Notes: // Notes:
// - thumb exceptions not updated, because set in cart constructor // - thumb exceptions not updated, because set in cart constructor
// - other missing settings are used on-the-fly // - other missing settings are used on-the-fly

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -51,7 +51,6 @@ class DevSettingsHandler
std::array<bool, numSets> myDetectedInfo{}; std::array<bool, numSets> myDetectedInfo{};
std::array<bool, numSets> myExternAccess{}; std::array<bool, numSets> myExternAccess{};
std::array<int, numSets> myConsole{}; std::array<int, numSets> myConsole{};
std::array<int, numSets> myPlusROM{};
std::array<bool, numSets> myRandomBank{}; std::array<bool, numSets> myRandomBank{};
std::array<bool, numSets> myRandomizeTIA{}; std::array<bool, numSets> myRandomizeTIA{};
std::array<bool, numSets> myRandomizeRAM{}; std::array<bool, numSets> myRandomizeRAM{};

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -17,12 +17,12 @@
#include "Logger.hxx" #include "Logger.hxx"
#include "OSystem.hxx" #include "OSystem.hxx"
#include "EventHandlerSDL.hxx" #include "EventHandlerSDL2.hxx"
#include "ThreadDebugging.hxx" #include "ThreadDebugging.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandlerSDL::EventHandlerSDL(OSystem& osystem) EventHandlerSDL2::EventHandlerSDL2(OSystem& osystem)
: EventHandler{osystem} : EventHandler{osystem}
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -31,30 +31,28 @@ EventHandlerSDL::EventHandlerSDL(OSystem& osystem)
{ {
ostringstream buf; ostringstream buf;
myQwertz = int{'y'} == static_cast<int> myQwertz = int{'y'} == static_cast<int>
(SDL_GetKeyFromScancode(static_cast<SDL_Scancode>(KBDK_Z), static_cast<SDL_Keymod>(StellaMod::KBDM_NONE), false)); (SDL_GetKeyFromScancode(static_cast<SDL_Scancode>(KBDK_Z)));
buf << "Keyboard: " << (myQwertz ? "QWERTZ" : "QWERTY"); buf << "Keyboard: " << (myQwertz ? "QWERTZ" : "QWERTY");
Logger::debug(buf.view()); Logger::debug(buf.view());
} }
#endif #endif
#ifdef JOYSTICK_SUPPORT #ifdef JOYSTICK_SUPPORT
if(!SDL_InitSubSystem(SDL_INIT_JOYSTICK)) if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
{ {
ostringstream buf; ostringstream buf;
buf << "ERROR: Couldn't initialize SDL joystick support: " buf << "ERROR: Couldn't initialize SDL joystick support: "
<< SDL_GetError() << '\n'; << SDL_GetError() << '\n';
Logger::error(buf.view()); Logger::error(buf.view());
} }
Logger::debug("EventHandlerSDL::EventHandlerSDL SDL_INIT_JOYSTICK"); Logger::debug("EventHandlerSDL2::EventHandlerSDL2 SDL_INIT_JOYSTICK");
#endif #endif
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
//SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE, "1");
//SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, "0");
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandlerSDL::~EventHandlerSDL() EventHandlerSDL2::~EventHandlerSDL2()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -63,13 +61,24 @@ EventHandlerSDL::~EventHandlerSDL()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandlerSDL::copyText(const string& text) const void EventHandlerSDL2::enableTextEvents(bool enable)
{
ASSERT_MAIN_THREAD;
if(enable)
SDL_StartTextInput();
else
SDL_StopTextInput();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandlerSDL2::copyText(const string& text) const
{ {
SDL_SetClipboardText(text.c_str()); SDL_SetClipboardText(text.c_str());
}; };
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string EventHandlerSDL::pasteText(string& text) const string EventHandlerSDL2::pasteText(string& text) const
{ {
if(SDL_HasClipboardText()) if(SDL_HasClipboardText())
text = SDL_GetClipboardText(); text = SDL_GetClipboardText();
@ -80,7 +89,7 @@ string EventHandlerSDL::pasteText(string& text) const
}; };
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandlerSDL::pollEvent() void EventHandlerSDL2::pollEvent()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -89,31 +98,31 @@ void EventHandlerSDL::pollEvent()
switch(myEvent.type) switch(myEvent.type)
{ {
// keyboard events // keyboard events
case SDL_EVENT_KEY_UP: case SDL_KEYUP:
case SDL_EVENT_KEY_DOWN: case SDL_KEYDOWN:
{ {
handleKeyEvent(static_cast<StellaKey>(myEvent.key.scancode), handleKeyEvent(static_cast<StellaKey>(myEvent.key.keysym.scancode),
static_cast<StellaMod>(myEvent.key.mod), static_cast<StellaMod>(myEvent.key.keysym.mod),
myEvent.type == SDL_EVENT_KEY_DOWN, myEvent.key.type == SDL_KEYDOWN,
myEvent.key.repeat); myEvent.key.repeat);
break; break;
} }
case SDL_EVENT_TEXT_INPUT: case SDL_TEXTINPUT:
{ {
handleTextEvent(*(myEvent.text.text)); handleTextEvent(*(myEvent.text.text));
break; break;
} }
case SDL_EVENT_MOUSE_MOTION: case SDL_MOUSEMOTION:
{ {
handleMouseMotionEvent(myEvent.motion.x, myEvent.motion.y, handleMouseMotionEvent(myEvent.motion.x, myEvent.motion.y,
myEvent.motion.xrel, myEvent.motion.yrel); myEvent.motion.xrel, myEvent.motion.yrel);
break; break;
} }
case SDL_EVENT_MOUSE_BUTTON_DOWN: case SDL_MOUSEBUTTONDOWN:
case SDL_EVENT_MOUSE_BUTTON_UP: case SDL_MOUSEBUTTONUP:
{ {
// ToDo: check support of more buttons and double-click // ToDo: check support of more buttons and double-click
MouseButton b{MouseButton::NONE}; MouseButton b{MouseButton::NONE};
@ -131,43 +140,39 @@ void EventHandlerSDL::pollEvent()
default: default:
break; break;
} }
handleMouseButtonEvent(b, myEvent.button.type == SDL_EVENT_MOUSE_BUTTON_DOWN, handleMouseButtonEvent(b, myEvent.button.type == SDL_MOUSEBUTTONDOWN,
myEvent.button.x, myEvent.button.y); myEvent.button.x, myEvent.button.y);
break; break;
} }
case SDL_EVENT_MOUSE_WHEEL: case SDL_MOUSEWHEEL:
{ {
// TODO: SDL now uses float for mouse coords, but the core still int x{0}, y{0};
// uses int throughout; maybe this is sufficient?
float x{0.F}, y{0.F};
SDL_GetMouseState(&x, &y); // we need mouse position too SDL_GetMouseState(&x, &y); // we need mouse position too
if(myEvent.wheel.y < 0) if(myEvent.wheel.y < 0)
handleMouseButtonEvent(MouseButton::WHEELDOWN, true, handleMouseButtonEvent(MouseButton::WHEELDOWN, true, x, y);
static_cast<int>(x), static_cast<int>(y));
else if(myEvent.wheel.y > 0) else if(myEvent.wheel.y > 0)
handleMouseButtonEvent(MouseButton::WHEELUP, true, handleMouseButtonEvent(MouseButton::WHEELUP, true, x, y);
static_cast<int>(x), static_cast<int>(y));
break; break;
} }
#ifdef JOYSTICK_SUPPORT #ifdef JOYSTICK_SUPPORT
case SDL_EVENT_JOYSTICK_BUTTON_UP: case SDL_JOYBUTTONUP:
case SDL_EVENT_JOYSTICK_BUTTON_DOWN: case SDL_JOYBUTTONDOWN:
{ {
handleJoyBtnEvent(myEvent.jbutton.which, myEvent.jbutton.button, handleJoyBtnEvent(myEvent.jbutton.which, myEvent.jbutton.button,
myEvent.jbutton.down); myEvent.jbutton.state == SDL_PRESSED);
break; break;
} }
case SDL_EVENT_JOYSTICK_AXIS_MOTION: case SDL_JOYAXISMOTION:
{ {
handleJoyAxisEvent(myEvent.jaxis.which, myEvent.jaxis.axis, handleJoyAxisEvent(myEvent.jaxis.which, myEvent.jaxis.axis,
myEvent.jaxis.value); myEvent.jaxis.value);
break; break;
} }
case SDL_EVENT_JOYSTICK_HAT_MOTION: case SDL_JOYHATMOTION:
{ {
int value = 0; int value = 0;
const int v = myEvent.jhat.value; const int v = myEvent.jhat.value;
@ -182,82 +187,86 @@ void EventHandlerSDL::pollEvent()
} }
handleJoyHatEvent(myEvent.jhat.which, myEvent.jhat.hat, value); handleJoyHatEvent(myEvent.jhat.which, myEvent.jhat.hat, value);
break; break; // SDL_JOYHATMOTION
} }
case SDL_EVENT_JOYSTICK_ADDED: case SDL_JOYDEVICEADDED:
{ {
addPhysicalJoystick(make_shared<JoystickSDL>(myEvent.jdevice.which)); addPhysicalJoystick(make_shared<JoystickSDL2>(myEvent.jdevice.which));
break; break; // SDL_JOYDEVICEADDED
} }
case SDL_EVENT_JOYSTICK_REMOVED: case SDL_JOYDEVICEREMOVED:
{ {
removePhysicalJoystick(myEvent.jdevice.which); removePhysicalJoystick(myEvent.jdevice.which);
break; break; // SDL_JOYDEVICEREMOVED
} }
#endif #endif
case SDL_EVENT_QUIT: case SDL_QUIT:
{ {
handleEvent(Event::Quit); handleEvent(Event::Quit);
break; break; // SDL_QUIT
} }
case SDL_EVENT_WINDOW_SHOWN: case SDL_WINDOWEVENT:
handleSystemEvent(SystemEvent::WINDOW_SHOWN); switch(myEvent.window.event)
break; {
case SDL_EVENT_WINDOW_HIDDEN: case SDL_WINDOWEVENT_SHOWN:
handleSystemEvent(SystemEvent::WINDOW_HIDDEN); handleSystemEvent(SystemEvent::WINDOW_SHOWN);
break; break;
case SDL_EVENT_WINDOW_EXPOSED: case SDL_WINDOWEVENT_HIDDEN:
handleSystemEvent(SystemEvent::WINDOW_EXPOSED); handleSystemEvent(SystemEvent::WINDOW_HIDDEN);
break; break;
case SDL_EVENT_WINDOW_MOVED: case SDL_WINDOWEVENT_EXPOSED:
handleSystemEvent(SystemEvent::WINDOW_MOVED, handleSystemEvent(SystemEvent::WINDOW_EXPOSED);
myEvent.window.data1, myEvent.window.data1); break;
break; case SDL_WINDOWEVENT_MOVED:
case SDL_EVENT_WINDOW_RESIZED: handleSystemEvent(SystemEvent::WINDOW_MOVED,
handleSystemEvent(SystemEvent::WINDOW_RESIZED, myEvent.window.data1, myEvent.window.data1);
myEvent.window.data1, myEvent.window.data1); break;
break; case SDL_WINDOWEVENT_RESIZED:
case SDL_EVENT_WINDOW_MINIMIZED: handleSystemEvent(SystemEvent::WINDOW_RESIZED,
handleSystemEvent(SystemEvent::WINDOW_MINIMIZED); myEvent.window.data1, myEvent.window.data1);
break; break;
case SDL_EVENT_WINDOW_MAXIMIZED: case SDL_WINDOWEVENT_MINIMIZED:
handleSystemEvent(SystemEvent::WINDOW_MAXIMIZED); handleSystemEvent(SystemEvent::WINDOW_MINIMIZED);
break; break;
case SDL_EVENT_WINDOW_RESTORED: case SDL_WINDOWEVENT_MAXIMIZED:
handleSystemEvent(SystemEvent::WINDOW_RESTORED); handleSystemEvent(SystemEvent::WINDOW_MAXIMIZED);
break; break;
case SDL_EVENT_WINDOW_MOUSE_ENTER: case SDL_WINDOWEVENT_RESTORED:
handleSystemEvent(SystemEvent::WINDOW_ENTER); handleSystemEvent(SystemEvent::WINDOW_RESTORED);
break; break;
case SDL_EVENT_WINDOW_MOUSE_LEAVE: case SDL_WINDOWEVENT_ENTER:
handleSystemEvent(SystemEvent::WINDOW_LEAVE); handleSystemEvent(SystemEvent::WINDOW_ENTER);
break; break;
case SDL_EVENT_WINDOW_FOCUS_GAINED: case SDL_WINDOWEVENT_LEAVE:
handleSystemEvent(SystemEvent::WINDOW_FOCUS_GAINED); handleSystemEvent(SystemEvent::WINDOW_LEAVE);
break; break;
case SDL_EVENT_WINDOW_FOCUS_LOST: case SDL_WINDOWEVENT_FOCUS_GAINED:
handleSystemEvent(SystemEvent::WINDOW_FOCUS_LOST); handleSystemEvent(SystemEvent::WINDOW_FOCUS_GAINED);
break; break;
case SDL_EVENT_SYSTEM_THEME_CHANGED: case SDL_WINDOWEVENT_FOCUS_LOST:
handleSystemEvent(SystemEvent::THEME_CHANGED); handleSystemEvent(SystemEvent::WINDOW_FOCUS_LOST);
break; break;
default:
break;
}
break; // SDL_WINDOWEVENT
default: default:
break; break;
} }
} }
} }
#ifdef JOYSTICK_SUPPORT
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandlerSDL::JoystickSDL::JoystickSDL(int idx) EventHandlerSDL2::JoystickSDL2::JoystickSDL2(int idx)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
// NOLINTNEXTLINE: we want to initialize here, not in the member list // NOLINTNEXTLINE: we want to initialize here, not in the member list
myStick = SDL_OpenJoystick(idx); myStick = SDL_JoystickOpen(idx);
if(myStick) if(myStick)
{ {
// In Windows, all XBox controllers using the XInput API seem to name // In Windows, all XBox controllers using the XInput API seem to name
@ -265,24 +274,21 @@ EventHandlerSDL::JoystickSDL::JoystickSDL(int idx)
// it also appends " #x", where x seems to vary. Obviously this wreaks // it also appends " #x", where x seems to vary. Obviously this wreaks
// havoc with the idea that a joystick will always have the same name. // havoc with the idea that a joystick will always have the same name.
// So we truncate the number. // So we truncate the number.
const char* const sdlname = SDL_GetJoystickName(myStick); const char* const sdlname = SDL_JoystickName(myStick);
const string& desc = BSPF::startsWithIgnoreCase(sdlname, "XInput Controller") const string& desc = BSPF::startsWithIgnoreCase(sdlname, "XInput Controller")
? "XInput Controller" : sdlname; ? "XInput Controller" : sdlname;
initialize(SDL_GetJoystickID(myStick), desc, initialize(SDL_JoystickInstanceID(myStick), desc,
SDL_GetNumJoystickAxes(myStick), SDL_JoystickNumAxes(myStick), SDL_JoystickNumButtons(myStick),
SDL_GetNumJoystickButtons(myStick), SDL_JoystickNumHats(myStick), SDL_JoystickNumBalls(myStick));
SDL_GetNumJoystickHats(myStick),
SDL_GetNumJoystickBalls(myStick));
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandlerSDL::JoystickSDL::~JoystickSDL() EventHandlerSDL2::JoystickSDL2::~JoystickSDL2()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
if(SDL_WasInit(SDL_INIT_JOYSTICK) && myStick) if(SDL_WasInit(SDL_INIT_JOYSTICK) && myStick)
SDL_CloseJoystick(myStick); SDL_JoystickClose(myStick);
} }
#endif

View File

@ -8,15 +8,15 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================ //============================================================================
#ifndef EVENTHANDLER_SDL_HXX #ifndef EVENTHANDLER_SDL2_HXX
#define EVENTHANDLER_SDL_HXX #define EVENTHANDLER_SDL2_HXX
#include "SDL_lib.hxx" #include "SDL_lib.hxx"
#include "EventHandler.hxx" #include "EventHandler.hxx"
@ -24,21 +24,26 @@
/** /**
This class handles event collection from the point of view of the specific This class handles event collection from the point of view of the specific
backend toolkit (SDL). It converts from SDL-specific events into events backend toolkit (SDL2). It converts from SDL2-specific events into events
that the Stella core can understand. that the Stella core can understand.
@author Stephen Anthony @author Stephen Anthony
*/ */
class EventHandlerSDL : public EventHandler class EventHandlerSDL2 : public EventHandler
{ {
public: public:
/** /**
Create a new SDL event handler object Create a new SDL2 event handler object
*/ */
explicit EventHandlerSDL(OSystem& osystem); explicit EventHandlerSDL2(OSystem& osystem);
~EventHandlerSDL() override; ~EventHandlerSDL2() override;
private: private:
/**
Enable/disable text events (distinct from single-key events).
*/
void enableTextEvents(bool enable) override;
/** /**
Clipboard methods. Clipboard methods.
*/ */
@ -46,42 +51,40 @@ class EventHandlerSDL : public EventHandler
string pasteText(string& text) const override; string pasteText(string& text) const override;
/** /**
Collects and dispatches any pending SDL events. Collects and dispatches any pending SDL2 events.
*/ */
void pollEvent() override; void pollEvent() override;
private: private:
SDL_Event myEvent{0}; SDL_Event myEvent{0};
#ifdef JOYSTICK_SUPPORT
// A thin wrapper around a basic PhysicalJoystick, holding the pointer to // A thin wrapper around a basic PhysicalJoystick, holding the pointer to
// the underlying SDL joystick device. // the underlying SDL joystick device.
class JoystickSDL : public PhysicalJoystick class JoystickSDL2 : public PhysicalJoystick
{ {
public: public:
explicit JoystickSDL(int idx); explicit JoystickSDL2(int idx);
virtual ~JoystickSDL(); virtual ~JoystickSDL2();
private: private:
SDL_Joystick* myStick{nullptr}; SDL_Joystick* myStick{nullptr};
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
JoystickSDL() = delete; JoystickSDL2() = delete;
JoystickSDL(const JoystickSDL&) = delete; JoystickSDL2(const JoystickSDL2&) = delete;
JoystickSDL(JoystickSDL&&) = delete; JoystickSDL2(JoystickSDL2&&) = delete;
JoystickSDL& operator=(const JoystickSDL&) = delete; JoystickSDL2& operator=(const JoystickSDL2&) = delete;
JoystickSDL& operator=(JoystickSDL&&) = delete; JoystickSDL2& operator=(JoystickSDL2&&) = delete;
}; };
#endif
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
EventHandlerSDL() = delete; EventHandlerSDL2() = delete;
EventHandlerSDL(const EventHandlerSDL&) = delete; EventHandlerSDL2(const EventHandlerSDL2&) = delete;
EventHandlerSDL(EventHandlerSDL&&) = delete; EventHandlerSDL2(EventHandlerSDL2&&) = delete;
EventHandlerSDL& operator=(const EventHandlerSDL&) = delete; EventHandlerSDL2& operator=(const EventHandlerSDL2&) = delete;
EventHandlerSDL& operator=(EventHandlerSDL&&) = delete; EventHandlerSDL2& operator=(EventHandlerSDL2&&) = delete;
}; };
#endif #endif

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -25,36 +25,38 @@
#include "Settings.hxx" #include "Settings.hxx"
#include "ThreadDebugging.hxx" #include "ThreadDebugging.hxx"
#include "FBSurfaceSDL.hxx" #include "FBSurfaceSDL2.hxx"
#include "FBBackendSDL.hxx" #include "FBBackendSDL2.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBBackendSDL::FBBackendSDL(OSystem& osystem) FBBackendSDL2::FBBackendSDL2(OSystem& osystem)
: myOSystem{osystem} : myOSystem{osystem}
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
// Initialize SDL context // Initialize SDL2 context
if(!SDL_InitSubSystem(SDL_INIT_VIDEO)) if(SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
{ {
ostringstream buf; ostringstream buf;
buf << "ERROR: Couldn't initialize SDL: " << SDL_GetError(); buf << "ERROR: Couldn't initialize SDL: " << SDL_GetError();
throw runtime_error(buf.str()); throw runtime_error(buf.str());
} }
Logger::debug("FBBackendSDL::FBBackendSDL SDL_Init()"); Logger::debug("FBBackendSDL2::FBBackendSDL2 SDL_Init()");
// We need a pixel format for palette value calculations // We need a pixel format for palette value calculations
// It's done this way (vs directly accessing a FBSurfaceSDL object) // It's done this way (vs directly accessing a FBSurfaceSDL2 object)
// since the structure may be needed before any FBSurface's have // since the structure may be needed before any FBSurface's have
// been created // been created
myPixelFormat = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_ARGB8888); myPixelFormat = SDL_AllocFormat(SDL_PIXELFORMAT_ARGB8888);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBBackendSDL::~FBBackendSDL() FBBackendSDL2::~FBBackendSDL2()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
SDL_FreeFormat(myPixelFormat);
if(myRenderer) if(myRenderer)
{ {
SDL_DestroyRenderer(myRenderer); SDL_DestroyRenderer(myRenderer);
@ -62,55 +64,46 @@ FBBackendSDL::~FBBackendSDL()
} }
if(myWindow) if(myWindow)
{ {
SDL_SetWindowFullscreen(myWindow, false); // on some systems, a crash occurs SDL_SetWindowFullscreen(myWindow, 0); // on some systems, a crash occurs
// when destroying fullscreen window // when destroying fullscreen window
SDL_DestroyWindow(myWindow); SDL_DestroyWindow(myWindow);
myWindow = nullptr; myWindow = nullptr;
} }
SDL_QuitSubSystem(SDL_INIT_VIDEO); SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL::queryHardware(vector<Common::Size>& fullscreenRes, void FBBackendSDL2::queryHardware(vector<Common::Size>& fullscreenRes,
vector<Common::Size>& windowedRes, vector<Common::Size>& windowedRes,
VariantList& renderers) VariantList& renderers)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
// Get number of displays (for most systems, this will be '1') // Get number of displays (for most systems, this will be '1')
int count = 0; myNumDisplays = SDL_GetNumVideoDisplays();
SDL_DisplayID* displays = SDL_GetDisplays(&count);
if(displays && count > 0)
myNumDisplays = static_cast<uInt32>(count);
else
return;
// Get the maximum fullscreen and windowed desktop resolutions // First get the maximum fullscreen desktop resolution
for(uInt32 i = 0; i < myNumDisplays; ++i) SDL_DisplayMode display;
for(int i = 0; i < myNumDisplays; ++i)
{ {
// Fullscreen mode SDL_GetDesktopDisplayMode(i, &display);
const SDL_DisplayMode* display = SDL_GetDesktopDisplayMode(displays[i]); fullscreenRes.emplace_back(display.w, display.h);
fullscreenRes.emplace_back(display->w, display->h);
// Windowed mode // evaluate fullscreen display modes (debug only for now)
SDL_Rect r{}; const int numModes = SDL_GetNumDisplayModes(i);
if(SDL_GetDisplayUsableBounds(displays[i], &r))
windowedRes.emplace_back(r.w, r.h);
int numModes = 0;
ostringstream s; ostringstream s;
string lastRes;
SDL_DisplayMode** modes = SDL_GetFullscreenDisplayModes(displays[i], &numModes);
s << "Supported video modes (" << numModes << ") for display " << i s << "Supported video modes (" << numModes << ") for display " << i
<< " (" << SDL_GetDisplayName(displays[i]) << "):"; << " (" << SDL_GetDisplayName(i) << "):";
for(int m = 0; modes != nullptr && modes[m] != nullptr; m++) string lastRes;
for(int m = 0; m < numModes; ++m)
{ {
const SDL_DisplayMode* mode = modes[m]; SDL_DisplayMode mode;
ostringstream res, ref; ostringstream res;
res << std::setw(4) << mode->w << "x" << std::setw(4) << mode->h; SDL_GetDisplayMode(i, m, &mode);
res << std::setw(4) << mode.w << "x" << std::setw(4) << mode.h;
if(lastRes != res.view()) if(lastRes != res.view())
{ {
@ -119,23 +112,47 @@ void FBBackendSDL::queryHardware(vector<Common::Size>& fullscreenRes,
lastRes = res.view(); lastRes = res.view();
s << " " << lastRes << ": "; s << " " << lastRes << ": ";
} }
s << mode.refresh_rate << "Hz";
ref << mode->refresh_rate << "Hz"; if(mode.w == display.w && mode.h == display.h && mode.refresh_rate == display.refresh_rate)
s << std::setw(7) << std::left << ref.str();
if(mode->w == display->w && mode->h == display->h &&
mode->refresh_rate == display->refresh_rate)
s << "* "; s << "* ";
else else
s << " "; s << " ";
} }
Logger::debug(s.view()); Logger::debug(s.view());
} }
SDL_free(displays);
// Now get the maximum windowed desktop resolution
// Try to take into account taskbars, etc, if available
#if SDL_VERSION_ATLEAST(2,0,5)
// Take window title-bar into account; SDL_GetDisplayUsableBounds doesn't do that
int wTop = 0, wLeft = 0, wBottom = 0, wRight = 0;
SDL_Window* tmpWindow = SDL_CreateWindow("", 0, 0, 0, 0, SDL_WINDOW_HIDDEN);
if(tmpWindow != nullptr)
{
SDL_GetWindowBordersSize(tmpWindow, &wTop, &wLeft, &wBottom, &wRight);
SDL_DestroyWindow(tmpWindow);
}
SDL_Rect r;
for(int i = 0; i < myNumDisplays; ++i)
{
// Display bounds minus dock
SDL_GetDisplayUsableBounds(i, &r); // Requires SDL-2.0.5 or higher
r.h -= (wTop + wBottom);
windowedRes.emplace_back(r.w, r.h);
}
#else
for(int i = 0; i < myNumDisplays; ++i)
{
SDL_GetDesktopDisplayMode(i, &display);
windowedRes.emplace_back(display.w, display.h);
}
#endif
struct RenderName struct RenderName
{ {
string_view sdlName; string sdlName;
string_view stellaName; string stellaName;
}; };
// Create name map for all currently known SDL renderers // Create name map for all currently known SDL renderers
static const std::array<RenderName, 8> RENDERER_NAMES = {{ static const std::array<RenderName, 8> RENDERER_NAMES = {{
@ -152,66 +169,68 @@ void FBBackendSDL::queryHardware(vector<Common::Size>& fullscreenRes,
const int numDrivers = SDL_GetNumRenderDrivers(); const int numDrivers = SDL_GetNumRenderDrivers();
for(int i = 0; i < numDrivers; ++i) for(int i = 0; i < numDrivers; ++i)
{ {
const char* const rendername = SDL_GetRenderDriver(i); SDL_RendererInfo info;
if(rendername) if(SDL_GetRenderDriverInfo(i, &info) == 0)
{ {
// Map SDL names into nicer Stella names (if available) // Map SDL names into nicer Stella names (if available)
bool found = false; bool found = false;
for(const auto& render: RENDERER_NAMES) for(const auto& render: RENDERER_NAMES)
{ {
if(render.sdlName == rendername) if(render.sdlName == info.name)
{ {
VarList::push_back(renderers, render.stellaName, rendername); VarList::push_back(renderers, render.stellaName, info.name);
found = true; found = true;
break; break;
} }
} }
if(!found) if(!found)
VarList::push_back(renderers, rendername, rendername); VarList::push_back(renderers, info.name, info.name);
} }
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FBBackendSDL::isCurrentWindowPositioned() const bool FBBackendSDL2::isCurrentWindowPositioned() const
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
return myWindow && !fullScreen() && !myCenter; return !myCenter
&& myWindow && !(SDL_GetWindowFlags(myWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Common::Point FBBackendSDL::getCurrentWindowPos() const Common::Point FBBackendSDL2::getCurrentWindowPos() const
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
Common::Point pos; Common::Point pos;
SDL_GetWindowPosition(myWindow, &pos.x, &pos.y); SDL_GetWindowPosition(myWindow, &pos.x, &pos.y);
return pos; return pos;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FBBackendSDL::getCurrentDisplayID() const Int32 FBBackendSDL2::getCurrentDisplayIndex() const
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
return SDL_GetDisplayForWindow(myWindow); return SDL_GetWindowDisplayIndex(myWindow);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FBBackendSDL::setVideoMode(const VideoModeHandler::Mode& mode, bool FBBackendSDL2::setVideoMode(const VideoModeHandler::Mode& mode,
int winIdx, const Common::Point& winPos) int winIdx, const Common::Point& winPos)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
// cerr << mode << '\n';
// If not initialized by this point, then immediately fail // If not initialized by this point, then immediately fail
if(SDL_WasInit(SDL_INIT_VIDEO) == 0) if(SDL_WasInit(SDL_INIT_VIDEO) == 0)
return false; return false;
const uInt32 displayIndex = std::min<uInt32>(myNumDisplays, winIdx); const bool fullScreen = mode.fsIndex != -1;
const Int32 displayIndex = std::min(myNumDisplays - 1, winIdx);
int posX = 0, posY = 0; int posX = 0, posY = 0;
myCenter = myOSystem.settings().getBool("center"); myCenter = myOSystem.settings().getBool("center");
@ -242,10 +261,10 @@ bool FBBackendSDL::setVideoMode(const VideoModeHandler::Mode& mode,
} }
#ifdef ADAPTABLE_REFRESH_SUPPORT #ifdef ADAPTABLE_REFRESH_SUPPORT
SDL_DisplayMode adaptedSdlMode{}; SDL_DisplayMode adaptedSdlMode;
const int gameRefreshRate = const int gameRefreshRate =
myOSystem.hasConsole() ? myOSystem.console().gameRefreshRate() : 0; myOSystem.hasConsole() ? myOSystem.console().gameRefreshRate() : 0;
const bool shouldAdapt = mode.fullscreen const bool shouldAdapt = fullScreen
&& myOSystem.settings().getBool("tia.fs_refresh") && myOSystem.settings().getBool("tia.fs_refresh")
&& gameRefreshRate && gameRefreshRate
// take care of 59.94 Hz // take care of 59.94 Hz
@ -256,18 +275,20 @@ bool FBBackendSDL::setVideoMode(const VideoModeHandler::Mode& mode,
#else #else
const bool adaptRefresh = false; const bool adaptRefresh = false;
#endif #endif
const uInt32 flags = SDL_WINDOW_ALLOW_HIGHDPI
| (fullScreen ? adaptRefresh ? SDL_WINDOW_FULLSCREEN :
SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
// Don't re-create the window if its display and size hasn't changed, // Don't re-create the window if its display and size hasn't changed,
// as it's not necessary, and causes flashing in fullscreen mode // as it's not necessary, and causes flashing in fullscreen mode
if(myWindow) if(myWindow)
{ {
const uInt32 d = getCurrentDisplayID(); const int d = SDL_GetWindowDisplayIndex(myWindow);
int w{0}, h{0}; int w{0}, h{0};
SDL_GetWindowSize(myWindow, &w, &h); SDL_GetWindowSize(myWindow, &w, &h);
if(d != displayIndex || if(d != displayIndex || static_cast<uInt32>(w) != mode.screenS.w ||
std::cmp_not_equal(w, mode.screenS.w) || static_cast<uInt32>(h) != mode.screenS.h || adaptRefresh)
std::cmp_not_equal(h, mode.screenS.h) || adaptRefresh)
{ {
// Renderer has to be destroyed *before* the window gets destroyed to avoid memory leaks // Renderer has to be destroyed *before* the window gets destroyed to avoid memory leaks
SDL_DestroyRenderer(myRenderer); SDL_DestroyRenderer(myRenderer);
@ -285,25 +306,8 @@ bool FBBackendSDL::setVideoMode(const VideoModeHandler::Mode& mode,
} }
else else
{ {
// Re-create with new properties myWindow = SDL_CreateWindow(myScreenTitle.c_str(), posX, posY,
const SDL_PropertiesID props = SDL_CreateProperties(); mode.screenS.w, mode.screenS.h, flags);
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING,
myScreenTitle.c_str());
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, posX);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, posY);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER,
mode.screenS.w);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER,
mode.screenS.h);
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIDDEN_BOOLEAN,
true);
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN,
mode.fullscreen);
SDL_SetBooleanProperty(props,
SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN,
true);
myWindow = SDL_CreateWindowWithProperties(props);
SDL_DestroyProperties(props);
if(myWindow == nullptr) if(myWindow == nullptr)
{ {
const string msg = "ERROR: Unable to open SDL window: " + string(SDL_GetError()); const string msg = "ERROR: Unable to open SDL window: " + string(SDL_GetError());
@ -311,7 +315,6 @@ bool FBBackendSDL::setVideoMode(const VideoModeHandler::Mode& mode,
return false; return false;
} }
enableTextEvents(myTextEventsEnabled);
setWindowIcon(); setWindowIcon();
} }
@ -319,7 +322,7 @@ bool FBBackendSDL::setVideoMode(const VideoModeHandler::Mode& mode,
if(adaptRefresh) if(adaptRefresh)
{ {
// Switch to mode for adapted refresh rate // Switch to mode for adapted refresh rate
if(!SDL_SetWindowFullscreenMode(myWindow, &adaptedSdlMode)) if(SDL_SetWindowDisplayMode(myWindow, &adaptedSdlMode) != 0)
{ {
Logger::error("ERROR: Display refresh rate change failed"); Logger::error("ERROR: Display refresh rate change failed");
} }
@ -331,39 +334,31 @@ bool FBBackendSDL::setVideoMode(const VideoModeHandler::Mode& mode,
<< adaptedSdlMode.refresh_rate << " Hz " << "(" << adaptedSdlMode.w << "x" << adaptedSdlMode.h << ")"; << adaptedSdlMode.refresh_rate << " Hz " << "(" << adaptedSdlMode.w << "x" << adaptedSdlMode.h << ")";
Logger::info(msg.view()); Logger::info(msg.view());
const SDL_DisplayMode* setSdlMode = SDL_GetWindowFullscreenMode(myWindow); SDL_DisplayMode setSdlMode;
cerr << setSdlMode->refresh_rate << "Hz\n"; SDL_GetWindowDisplayMode(myWindow, &setSdlMode);
cerr << setSdlMode.refresh_rate << "Hz\n";
} }
} }
#endif #endif
const bool result = createRenderer(); return createRenderer();
if(result)
{
// TODO: Checking for fullscreen status later returns invalid results,
// so we check and cache it here
myIsFullscreen = SDL_GetWindowFlags(myWindow) & SDL_WINDOW_FULLSCREEN;
SDL_ShowWindow(myWindow);
}
return result;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FBBackendSDL::adaptRefreshRate(Int32 displayIndex, bool FBBackendSDL2::adaptRefreshRate(Int32 displayIndex,
SDL_DisplayMode& adaptedSdlMode) SDL_DisplayMode& adaptedSdlMode)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
const SDL_DisplayMode* sdlMode = SDL_GetCurrentDisplayMode(displayIndex); SDL_DisplayMode sdlMode;
if(sdlMode == nullptr) if(SDL_GetCurrentDisplayMode(displayIndex, &sdlMode) != 0)
{ {
Logger::error("ERROR: Display mode could not be retrieved"); Logger::error("ERROR: Display mode could not be retrieved");
return false; return false;
} }
const int currentRefreshRate = sdlMode->refresh_rate; const int currentRefreshRate = sdlMode.refresh_rate;
const int wantedRefreshRate = const int wantedRefreshRate =
myOSystem.hasConsole() ? myOSystem.console().gameRefreshRate() : 0; myOSystem.hasConsole() ? myOSystem.console().gameRefreshRate() : 0;
// Take care of rounded refresh rates (e.g. 59.94 Hz) // Take care of rounded refresh rates (e.g. 59.94 Hz)
@ -379,19 +374,18 @@ bool FBBackendSDL::adaptRefreshRate(Int32 displayIndex,
// Check for integer factors 1 (60/50 Hz) and 2 (120/100 Hz) // Check for integer factors 1 (60/50 Hz) and 2 (120/100 Hz)
for(int m = 1; m <= 2; ++m) for(int m = 1; m <= 2; ++m)
{ {
SDL_DisplayMode closestSdlMode{}; SDL_DisplayMode closestSdlMode;
const float refresh_rate = wantedRefreshRate * m;
if(!SDL_GetClosestFullscreenDisplayMode(displayIndex, sdlMode->w, sdlMode->h, refresh_rate, true, &closestSdlMode)) sdlMode.refresh_rate = wantedRefreshRate * m;
if(SDL_GetClosestDisplayMode(displayIndex, &sdlMode, &closestSdlMode) == nullptr)
{ {
Logger::error("ERROR: Closest display mode could not be retrieved"); Logger::error("ERROR: Closest display mode could not be retrieved");
return adapt; return adapt;
} }
factor = std::min( factor = std::min(
static_cast<float>(refresh_rate) / refresh_rate, static_cast<float>(sdlMode.refresh_rate) / sdlMode.refresh_rate,
static_cast<float>(refresh_rate) / (refresh_rate - 1)); static_cast<float>(sdlMode.refresh_rate) / (sdlMode.refresh_rate - 1));
const float diff = std::abs(factor - std::round(factor)) / factor; const float diff = std::abs(factor - std::round(factor)) / factor;
if(diff < bestDiff) if(diff < bestDiff)
{ {
bestDiff = diff; bestDiff = diff;
@ -411,46 +405,38 @@ bool FBBackendSDL::adaptRefreshRate(Int32 displayIndex,
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FBBackendSDL::createRenderer() bool FBBackendSDL2::createRenderer()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
// A new renderer is only created when necessary: // A new renderer is only created when necessary:
// - no renderer existing // - no renderer existing
// - different renderer flags
// - different renderer name // - different renderer name
// - different renderer vsync
const bool enableVSync = myOSystem.settings().getBool("vsync") &&
!myOSystem.settings().getBool("turbo");
const string& video = myOSystem.settings().getString("video");
bool recreate = myRenderer == nullptr; bool recreate = myRenderer == nullptr;
if(myRenderer) uInt32 renderFlags = SDL_RENDERER_ACCELERATED;
{ const string& video = myOSystem.settings().getString("video"); // Render hint
recreate = recreate || video != SDL_GetRendererName(myRenderer); SDL_RendererInfo renderInfo;
const SDL_PropertiesID props = SDL_GetRendererProperties(myRenderer); if(myOSystem.settings().getBool("vsync")
const bool currentVSync = SDL_GetNumberProperty(props, && !myOSystem.settings().getBool("turbo")) // V'synced blits option
SDL_PROP_RENDERER_VSYNC_NUMBER, 0) != 0; renderFlags |= SDL_RENDERER_PRESENTVSYNC;
recreate = recreate || currentVSync != enableVSync;
} // check renderer flags and name
recreate |= (SDL_GetRendererInfo(myRenderer, &renderInfo) != 0)
|| ((renderInfo.flags & (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC)) != renderFlags
|| (video != renderInfo.name));
if(recreate) if(recreate)
{ {
//cerr << "Create new renderer for buffer type #" << int(myBufferType) << '\n';
if(myRenderer) if(myRenderer)
SDL_DestroyRenderer(myRenderer); SDL_DestroyRenderer(myRenderer);
// Re-create with new properties
const SDL_PropertiesID props = SDL_CreateProperties();
if(!video.empty()) if(!video.empty())
SDL_SetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING, SDL_SetHint(SDL_HINT_RENDER_DRIVER, video.c_str());
video.c_str());
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER,
enableVSync ? 1 : 0);
SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER,
myWindow);
myRenderer = SDL_CreateRendererWithProperties(props); myRenderer = SDL_CreateRenderer(myWindow, -1, renderFlags);
SDL_DestroyProperties(props);
detectFeatures(); detectFeatures();
determineDimensions(); determineDimensions();
@ -464,106 +450,85 @@ bool FBBackendSDL::createRenderer()
} }
clear(); clear();
const char* const detectedvideo = SDL_GetRendererName(myRenderer); SDL_RendererInfo renderinfo;
if(detectedvideo)
myOSystem.settings().setValue("video", detectedvideo); if(SDL_GetRendererInfo(myRenderer, &renderinfo) >= 0)
myOSystem.settings().setValue("video", renderinfo.name);
return true; return true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL::setTitle(string_view title) void FBBackendSDL2::setTitle(string_view title)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
myScreenTitle = title; myScreenTitle = title;
if(myWindow) if(myWindow)
SDL_SetWindowTitle(myWindow, myScreenTitle.c_str()); SDL_SetWindowTitle(myWindow, string{title}.c_str());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string FBBackendSDL::about() const string FBBackendSDL2::about() const
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
ostringstream out; ostringstream out;
out << "Video system: " << SDL_GetCurrentVideoDriver() << '\n'; out << "Video system: " << SDL_GetCurrentVideoDriver() << '\n';
SDL_RendererInfo info;
const SDL_PropertiesID props = SDL_GetRendererProperties(myRenderer); if(SDL_GetRendererInfo(myRenderer, &info) >= 0)
if(props != 0)
{ {
out << " Renderer: " out << " Renderer: " << info.name << '\n';
<< SDL_GetStringProperty(props, SDL_PROP_RENDERER_NAME_STRING, "") if(info.max_texture_width > 0 && info.max_texture_height > 0)
<< '\n'; out << " Max texture: " << info.max_texture_width << "x"
const uInt64 maxTexSize = << info.max_texture_height << '\n';
SDL_GetNumberProperty(props, SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 0);
if(maxTexSize > 0)
out << " Max texture: " << maxTexSize << "x" << maxTexSize << '\n';
const bool usingVSync = SDL_GetNumberProperty(props,
SDL_PROP_RENDERER_VSYNC_NUMBER, 0) != 0;
out << " Flags: " out << " Flags: "
<< (usingVSync ? "+" : "-") << "vsync" << ((info.flags & SDL_RENDERER_PRESENTVSYNC) ? "+" : "-") << "vsync, "
<< ((info.flags & SDL_RENDERER_ACCELERATED) ? "+" : "-") << "accel"
<< '\n'; << '\n';
} }
return out.str(); return out.str();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL::showCursor(bool show) void FBBackendSDL2::showCursor(bool show)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
if(show) SDL_ShowCursor(); SDL_ShowCursor(show ? SDL_ENABLE : SDL_DISABLE);
else SDL_HideCursor();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL::grabMouse(bool grab) void FBBackendSDL2::grabMouse(bool grab)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
SDL_SetWindowMouseGrab(myWindow, grab); SDL_SetRelativeMouseMode(grab ? SDL_TRUE : SDL_FALSE);
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE, grab ? "1" : "0");
SDL_SetWindowRelativeMouseMode(myWindow, grab);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL::enableTextEvents(bool enable) bool FBBackendSDL2::fullScreen() const
{
ASSERT_MAIN_THREAD;
if(enable)
SDL_StartTextInput(myWindow);
else
SDL_StopTextInput(myWindow);
// myWindows can still be null, so we remember the state and set again when
// the window is created
myTextEventsEnabled = enable;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FBBackendSDL::fullScreen() const
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
#ifdef WINDOWED_SUPPORT #ifdef WINDOWED_SUPPORT
return myIsFullscreen; // TODO: should query SDL directly return SDL_GetWindowFlags(myWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP;
#else #else
return true; return true;
#endif #endif
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int FBBackendSDL::refreshRate() const int FBBackendSDL2::refreshRate() const
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
const SDL_DisplayID displayID = getCurrentDisplayID(); const uInt32 displayIndex = SDL_GetWindowDisplayIndex(myWindow);
const SDL_DisplayMode* mode = SDL_GetCurrentDisplayMode(displayID); SDL_DisplayMode sdlMode;
if(mode) if(SDL_GetCurrentDisplayMode(displayIndex, &sdlMode) == 0)
return mode->refresh_rate; return sdlMode.refresh_rate;
if(myWindow != nullptr) if(myWindow != nullptr)
Logger::error("Could not retrieve current display mode"); Logger::error("Could not retrieve current display mode");
@ -572,7 +537,7 @@ int FBBackendSDL::refreshRate() const
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL::renderToScreen() void FBBackendSDL2::renderToScreen()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -581,42 +546,35 @@ void FBBackendSDL::renderToScreen()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL::setWindowIcon() void FBBackendSDL2::setWindowIcon()
{ {
#if !defined(BSPF_MACOS) #if !defined(BSPF_MACOS) && !defined(RETRON77)
#include "stella_icon.hxx" #include "stella_icon.hxx"
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
SDL_Surface* surface = SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(stella_icon, 32, 32, 32,
SDL_CreateSurfaceFrom(32, 32, pixelFormat().format, stella_icon, 32 * 4); 32 * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000);
SDL_SetWindowIcon(myWindow, surface); SDL_SetWindowIcon(myWindow, surface);
SDL_DestroySurface(surface); SDL_FreeSurface(surface);
#endif #endif
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
unique_ptr<FBSurface> FBBackendSDL::createSurface( unique_ptr<FBSurface> FBBackendSDL2::createSurface(
uInt32 w, uInt32 w,
uInt32 h, uInt32 h,
ScalingInterpolation inter, ScalingInterpolation inter,
const uInt32* data const uInt32* data
) const ) const
{ {
unique_ptr<FBSurface> s = make_unique<FBSurfaceSDL> return make_unique<FBSurfaceSDL2>
(const_cast<FBBackendSDL&>(*this), w, h, inter, data); (const_cast<FBBackendSDL2&>(*this), w, h, inter, data);
s->setBlendLevel(100); // by default, disable shading (use full alpha)
return s;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL::readPixels(uInt8* buffer, size_t pitch, void FBBackendSDL2::readPixels(uInt8* buffer, size_t pitch,
const Common::Rect& rect) const const Common::Rect& rect) const
{ {
// FIXME: this method needs to be refactored to return an FBSurface,
// since SDL_RenderReadPixels now returns an SDL_Surface
// PNGLibrary is the only user of this; that will need to be refactored too
#if 0
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
SDL_Rect r; SDL_Rect r;
@ -624,11 +582,10 @@ void FBBackendSDL::readPixels(uInt8* buffer, size_t pitch,
r.w = rect.w(); r.h = rect.h(); r.w = rect.w(); r.h = rect.h();
SDL_RenderReadPixels(myRenderer, &r, 0, buffer, static_cast<int>(pitch)); SDL_RenderReadPixels(myRenderer, &r, 0, buffer, static_cast<int>(pitch));
#endif
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL::clear() void FBBackendSDL2::clear()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -636,7 +593,7 @@ void FBBackendSDL::clear()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL::detectFeatures() void FBBackendSDL2::detectFeatures()
{ {
myRenderTargetSupport = detectRenderTargetSupport(); myRenderTargetSupport = detectRenderTargetSupport();
@ -645,20 +602,36 @@ void FBBackendSDL::detectFeatures()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FBBackendSDL::detectRenderTargetSupport() bool FBBackendSDL2::detectRenderTargetSupport()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
if(myRenderer == nullptr) if(myRenderer == nullptr)
return false; return false;
// All texture modes except software support render targets SDL_RendererInfo info;
const char* const detectedvideo = SDL_GetRendererName(myRenderer); SDL_GetRendererInfo(myRenderer, &info);
return detectedvideo && !BSPF::equalsIgnoreCase(detectedvideo, "software");
if(!(info.flags & SDL_RENDERER_TARGETTEXTURE))
return false;
SDL_Texture* tex =
SDL_CreateTexture(myRenderer, myPixelFormat->format,
SDL_TEXTUREACCESS_TARGET, 16, 16);
if(!tex)
return false;
const int sdlError = SDL_SetRenderTarget(myRenderer, tex);
SDL_SetRenderTarget(myRenderer, nullptr);
SDL_DestroyTexture(tex);
return sdlError == 0;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL::determineDimensions() void FBBackendSDL2::determineDimensions()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -670,5 +643,5 @@ void FBBackendSDL::determineDimensions()
myRenderH = myWindowH; myRenderH = myWindowH;
} }
else else
SDL_GetCurrentRenderOutputSize(myRenderer, &myRenderW, &myRenderH); SDL_GetRendererOutputSize(myRenderer, &myRenderW, &myRenderH);
} }

View File

@ -8,38 +8,38 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================ //============================================================================
#ifndef FB_BACKEND_SDL_HXX #ifndef FB_BACKEND_SDL2_HXX
#define FB_BACKEND_SDL_HXX #define FB_BACKEND_SDL2_HXX
#include "SDL_lib.hxx" #include "SDL_lib.hxx"
class OSystem; class OSystem;
class FBSurfaceSDL; class FBSurfaceSDL2;
#include "bspf.hxx" #include "bspf.hxx"
#include "FBBackend.hxx" #include "FBBackend.hxx"
/** /**
This class implements a standard SDL 2D, hardware accelerated framebuffer This class implements a standard SDL2 2D, hardware accelerated framebuffer
backend. Behind the scenes, it may be using Direct3D, OpenGL(ES), etc. backend. Behind the scenes, it may be using Direct3D, OpenGL(ES), etc.
@author Stephen Anthony @author Stephen Anthony
*/ */
class FBBackendSDL : public FBBackend class FBBackendSDL2 : public FBBackend
{ {
public: public:
/** /**
Creates a new SDL framebuffer Creates a new SDL2 framebuffer
*/ */
explicit FBBackendSDL(OSystem& osystem); explicit FBBackendSDL2(OSystem& osystem);
~FBBackendSDL() override; ~FBBackendSDL2() override;
public: public:
/** /**
@ -55,7 +55,7 @@ class FBBackendSDL : public FBBackend
/** /**
Get the SDL pixel format. Get the SDL pixel format.
*/ */
const SDL_PixelFormatDetails& pixelFormat() const { return *myPixelFormat; } const SDL_PixelFormat& pixelFormat() const { return *myPixelFormat; }
/** /**
Does the renderer support render targets? Does the renderer support render targets?
@ -99,7 +99,7 @@ class FBBackendSDL : public FBBackend
@param b The blue component of the color @param b The blue component of the color
*/ */
FORCE_INLINE void getRGB(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b) const override FORCE_INLINE void getRGB(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b) const override
{ SDL_GetRGB(pixel, myPixelFormat, nullptr, r, g, b); } { SDL_GetRGB(pixel, myPixelFormat, r, g, b); }
/** /**
This method is called to retrieve the R/G/B/A data from the given pixel. This method is called to retrieve the R/G/B/A data from the given pixel.
@ -111,7 +111,7 @@ class FBBackendSDL : public FBBackend
@param a The alpha 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 FORCE_INLINE void getRGBA(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b, uInt8* a) const override
{ SDL_GetRGBA(pixel, myPixelFormat, nullptr, r, g, b, a); } { SDL_GetRGBA(pixel, myPixelFormat, r, g, b, a); }
/** /**
This method is called to map a given R/G/B triple to the screen palette. This method is called to map a given R/G/B triple to the screen palette.
@ -121,7 +121,7 @@ class FBBackendSDL : public FBBackend
@param b The blue component of the color. @param b The blue component of the color.
*/ */
uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const override uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const override
{ return SDL_MapRGB(myPixelFormat, nullptr, r, g, b); } { return SDL_MapRGB(myPixelFormat, r, g, b); }
/** /**
This method is called to map a given R/G/B/A triple to the screen palette. This method is called to map a given R/G/B/A triple to the screen palette.
@ -132,7 +132,7 @@ class FBBackendSDL : public FBBackend
@param a The alpha component of the color. @param a The alpha component of the color.
*/ */
uInt32 mapRGBA(uInt8 r, uInt8 g, uInt8 b, uInt8 a) const override uInt32 mapRGBA(uInt8 r, uInt8 g, uInt8 b, uInt8 a) const override
{ return SDL_MapRGBA(myPixelFormat, nullptr, r, g, b, a); } { return SDL_MapRGBA(myPixelFormat, r, g, b, a); }
/** /**
This method is called to get a copy of the specified ARGB data from the This method is called to get a copy of the specified ARGB data from the
@ -167,9 +167,10 @@ class FBBackendSDL : public FBBackend
This method is called to query the video hardware for the index This method is called to query the video hardware for the index
of the display the current window is displayed on of the display the current window is displayed on
@return the current display index or a 0 if no window is displayed @return the current display index or a negative value if no
window is displayed
*/ */
uInt32 getCurrentDisplayID() const override; Int32 getCurrentDisplayIndex() const override;
/** /**
Clear the frame buffer. Clear the frame buffer.
@ -222,11 +223,6 @@ class FBBackendSDL : public FBBackend
*/ */
void grabMouse(bool grab) override; void grabMouse(bool grab) override;
/**
Enable/disable text events (distinct from single-key events).
*/
void enableTextEvents(bool enable) override;
/** /**
This method is called to provide information about the backend. This method is called to provide information about the backend.
*/ */
@ -250,20 +246,6 @@ class FBBackendSDL : public FBBackend
*/ */
int refreshRate() const override; int refreshRate() const override;
/**
Checks if the OS theme is set to light.
*/
bool isLightTheme() const override {
return SDL_GetSystemTheme() == SDL_SYSTEM_THEME_LIGHT;
}
/**
Checks if the OS theme is set to dark.
*/
bool isDarkTheme() const override {
return SDL_GetSystemTheme() == SDL_SYSTEM_THEME_DARK;
}
/** /**
Checks if the display refresh rate should be adapted to game refresh Checks if the display refresh rate should be adapted to game refresh
rate in (real) fullscreen mode. rate in (real) fullscreen mode.
@ -304,18 +286,7 @@ class FBBackendSDL : public FBBackend
SDL_Renderer* myRenderer{nullptr}; SDL_Renderer* myRenderer{nullptr};
// Used by mapRGB (when palettes are created) // Used by mapRGB (when palettes are created)
const SDL_PixelFormatDetails* myPixelFormat{nullptr}; SDL_PixelFormat* myPixelFormat{nullptr};
// Are we in fullscreen mode?
// There seem to be issues with creating the window and renderer separately,
// and doing so means we can't query the window for fullscreen status
// So we do it at window creation and cache the result
// TODO: Is this a bug in SDL?
bool myIsFullscreen{false};
// Text events are sometimes enabled before a window exists
// So we cache the request here, and honour it after the window has been created
bool myTextEventsEnabled{false};
// Center setting of current window // Center setting of current window
bool myCenter{false}; bool myCenter{false};
@ -327,18 +298,18 @@ class FBBackendSDL : public FBBackend
string myScreenTitle; string myScreenTitle;
// Number of displays // Number of displays
uInt32 myNumDisplays{0}; int myNumDisplays{1};
// Window and renderer dimensions // Window and renderer dimensions
int myWindowW{0}, myWindowH{0}, myRenderW{0}, myRenderH{0}; int myWindowW{0}, myWindowH{0}, myRenderW{0}, myRenderH{0};
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
FBBackendSDL() = delete; FBBackendSDL2() = delete;
FBBackendSDL(const FBBackendSDL&) = delete; FBBackendSDL2(const FBBackendSDL2&) = delete;
FBBackendSDL(FBBackendSDL&&) = delete; FBBackendSDL2(FBBackendSDL2&&) = delete;
FBBackendSDL& operator=(const FBBackendSDL&) = delete; FBBackendSDL2& operator=(const FBBackendSDL2&) = delete;
FBBackendSDL& operator=(FBBackendSDL&&) = delete; FBBackendSDL2& operator=(FBBackendSDL2&&) = delete;
}; };
#endif #endif

View File

@ -8,14 +8,14 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================ //============================================================================
#include "FBSurfaceSDL.hxx" #include "FBSurfaceSDL2.hxx"
#include "Logger.hxx" #include "Logger.hxx"
#include "ThreadDebugging.hxx" #include "ThreadDebugging.hxx"
@ -41,10 +41,10 @@ namespace {
} // namespace } // namespace
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceSDL::FBSurfaceSDL(FBBackendSDL& backend, FBSurfaceSDL2::FBSurfaceSDL2(FBBackendSDL2& backend,
uInt32 width, uInt32 height, uInt32 width, uInt32 height,
ScalingInterpolation inter, ScalingInterpolation inter,
const uInt32* staticData) const uInt32* staticData)
: myBackend{backend}, : myBackend{backend},
myInterpolationMode{inter} myInterpolationMode{inter}
{ {
@ -53,19 +53,19 @@ FBSurfaceSDL::FBSurfaceSDL(FBBackendSDL& backend,
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceSDL::~FBSurfaceSDL() FBSurfaceSDL2::~FBSurfaceSDL2()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
if(mySurface) if(mySurface)
{ {
SDL_DestroySurface(mySurface); SDL_FreeSurface(mySurface);
mySurface = nullptr; mySurface = nullptr;
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, ColorId color) void FBSurfaceSDL2::fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, ColorId color)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -75,49 +75,49 @@ void FBSurfaceSDL::fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, ColorId colo
tmp.y = y; tmp.y = y;
tmp.w = w; tmp.w = w;
tmp.h = h; tmp.h = h;
SDL_FillSurfaceRect(mySurface, &tmp, myPalette[color]); SDL_FillRect(mySurface, &tmp, myPalette[color]);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FBSurfaceSDL::width() const uInt32 FBSurfaceSDL2::width() const
{ {
return mySurface->w; return mySurface->w;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FBSurfaceSDL::height() const uInt32 FBSurfaceSDL2::height() const
{ {
return mySurface->h; return mySurface->h;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const Common::Rect& FBSurfaceSDL::srcRect() const const Common::Rect& FBSurfaceSDL2::srcRect() const
{ {
return mySrcGUIR; return mySrcGUIR;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const Common::Rect& FBSurfaceSDL::dstRect() const const Common::Rect& FBSurfaceSDL2::dstRect() const
{ {
return myDstGUIR; return myDstGUIR;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::setSrcPos(uInt32 x, uInt32 y) void FBSurfaceSDL2::setSrcPos(uInt32 x, uInt32 y)
{ {
if(setSrcPosInternal(x, y)) if(setSrcPosInternal(x, y))
reinitializeBlitter(); reinitializeBlitter();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::setSrcSize(uInt32 w, uInt32 h) void FBSurfaceSDL2::setSrcSize(uInt32 w, uInt32 h)
{ {
if(setSrcSizeInternal(w, h)) if(setSrcSizeInternal(w, h))
reinitializeBlitter(); reinitializeBlitter();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::setSrcRect(const Common::Rect& r) void FBSurfaceSDL2::setSrcRect(const Common::Rect& r)
{ {
const bool posChanged = setSrcPosInternal(r.x(), r.y()), const bool posChanged = setSrcPosInternal(r.x(), r.y()),
sizeChanged = setSrcSizeInternal(r.w(), r.h()); sizeChanged = setSrcSizeInternal(r.w(), r.h());
@ -127,21 +127,21 @@ void FBSurfaceSDL::setSrcRect(const Common::Rect& r)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::setDstPos(uInt32 x, uInt32 y) void FBSurfaceSDL2::setDstPos(uInt32 x, uInt32 y)
{ {
if(setDstPosInternal(x, y)) if(setDstPosInternal(x, y))
reinitializeBlitter(); reinitializeBlitter();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::setDstSize(uInt32 w, uInt32 h) void FBSurfaceSDL2::setDstSize(uInt32 w, uInt32 h)
{ {
if(setDstSizeInternal(w, h)) if(setDstSizeInternal(w, h))
reinitializeBlitter(); reinitializeBlitter();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::setDstRect(const Common::Rect& r) void FBSurfaceSDL2::setDstRect(const Common::Rect& r)
{ {
const bool posChanged = setDstPosInternal(r.x(), r.y()), const bool posChanged = setDstPosInternal(r.x(), r.y()),
sizeChanged = setDstSizeInternal(r.w(), r.h()); sizeChanged = setDstSizeInternal(r.w(), r.h());
@ -151,23 +151,22 @@ void FBSurfaceSDL::setDstRect(const Common::Rect& r)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::setVisible(bool visible) void FBSurfaceSDL2::setVisible(bool visible)
{ {
myIsVisible = visible; myIsVisible = visible;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::translateCoords(Int32& x, Int32& y) const void FBSurfaceSDL2::translateCoords(Int32& x, Int32& y) const
{ {
x -= myDstR.x; x /= myDstR.w / mySrcR.w; x -= myDstR.x; x /= myDstR.w / mySrcR.w;
y -= myDstR.y; y /= myDstR.h / mySrcR.h; y -= myDstR.y; y /= myDstR.h / mySrcR.h;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FBSurfaceSDL::render() bool FBSurfaceSDL2::render()
{ {
if(!myBlitter) if (!myBlitter) reinitializeBlitter();
reinitializeBlitter();
if(myIsVisible && myBlitter) if(myIsVisible && myBlitter)
{ {
@ -179,15 +178,15 @@ bool FBSurfaceSDL::render()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::invalidate() void FBSurfaceSDL2::invalidate()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
SDL_FillSurfaceRect(mySurface, nullptr, 0); SDL_FillRect(mySurface, nullptr, 0);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::invalidateRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) void FBSurfaceSDL2::invalidateRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -199,22 +198,22 @@ void FBSurfaceSDL::invalidateRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h)
tmp.h = h; tmp.h = h;
// Note: Transparency has to be 0 to clear the rectangle foreground // Note: Transparency has to be 0 to clear the rectangle foreground
// without affecting the background display. // without affecting the background display.
SDL_FillSurfaceRect(mySurface, &tmp, 0); SDL_FillRect(mySurface, &tmp, 0);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::reload() void FBSurfaceSDL2::reload()
{ {
reinitializeBlitter(true); reinitializeBlitter(true);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::resize(uInt32 width, uInt32 height) void FBSurfaceSDL2::resize(uInt32 width, uInt32 height)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
if(mySurface) if(mySurface)
SDL_DestroySurface(mySurface); SDL_FreeSurface(mySurface);
// NOTE: Currently, a resize changes a 'static' surface to 'streaming' // NOTE: Currently, a resize changes a 'static' surface to 'streaming'
// No code currently does this, but we should at least check for it // No code currently does this, but we should at least check for it
@ -225,13 +224,17 @@ void FBSurfaceSDL::resize(uInt32 width, uInt32 height)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::createSurface(uInt32 width, uInt32 height, const uInt32* data) void FBSurfaceSDL2::createSurface(uInt32 width, uInt32 height,
const uInt32* data)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
// Create a surface in the same format as the parent GL class // Create a surface in the same format as the parent GL class
const SDL_PixelFormatDetails& pf = myBackend.pixelFormat(); const SDL_PixelFormat& pf = myBackend.pixelFormat();
mySurface = SDL_CreateSurface(width, height, pf.format);
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 // We start out with the src and dst rectangles containing the same
// dimensions, indicating no scaling or re-positioning // dimensions, indicating no scaling or re-positioning
@ -243,7 +246,7 @@ void FBSurfaceSDL::createSurface(uInt32 width, uInt32 height, const uInt32* data
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
// These *must* be set for the parent class // These *must* be set for the parent class
myPixels = static_cast<uInt32*>(mySurface->pixels); myPixels = static_cast<uInt32*>(mySurface->pixels);
myPitch = mySurface->pitch / pf.bytes_per_pixel; myPitch = mySurface->pitch / pf.BytesPerPixel;
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
myIsStatic = data != nullptr; myIsStatic = data != nullptr;
@ -255,40 +258,30 @@ void FBSurfaceSDL::createSurface(uInt32 width, uInt32 height, const uInt32* data
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::reinitializeBlitter(bool force) void FBSurfaceSDL2::reinitializeBlitter(bool force)
{ {
if(force) if (force)
myBlitter.reset(); myBlitter.reset();
if(!myBlitter && myBackend.isInitialized()) if (!myBlitter && myBackend.isInitialized())
myBlitter = BlitterFactory::createBlitter( myBlitter = BlitterFactory::createBlitter(
myBackend, scalingAlgorithm(myInterpolationMode)); myBackend, scalingAlgorithm(myInterpolationMode));
if(myBlitter) if (myBlitter)
myBlitter->reinitialize(mySrcR, myDstR, myEnableBlend, myBlendLevel, myBlitter->reinitialize(mySrcR, myDstR, myAttributes,
myIsStatic ? mySurface : nullptr); myIsStatic ? mySurface : nullptr);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::setBlendLevel(uInt32 percent) void FBSurfaceSDL2::applyAttributes()
{ {
myBlendLevel = BSPF::clamp(percent, 0U, 100U);
reinitializeBlitter(); reinitializeBlitter();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::enableBlend(bool enableBlend) void FBSurfaceSDL2::setScalingInterpolation(ScalingInterpolation interpolation)
{ {
myEnableBlend = enableBlend; if (interpolation == ScalingInterpolation::sharp &&
reinitializeBlitter();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL::setScalingInterpolation(ScalingInterpolation interpolation)
{
if(interpolation == ScalingInterpolation::sharp &&
( (
static_cast<int>(mySrcGUIR.h()) >= myBackend.scaleY(myDstGUIR.h()) || static_cast<int>(mySrcGUIR.h()) >= myBackend.scaleY(myDstGUIR.h()) ||
static_cast<int>(mySrcGUIR.w()) >= myBackend.scaleX(myDstGUIR.w()) static_cast<int>(mySrcGUIR.w()) >= myBackend.scaleX(myDstGUIR.w())
@ -296,8 +289,7 @@ void FBSurfaceSDL::setScalingInterpolation(ScalingInterpolation interpolation)
) )
interpolation = ScalingInterpolation::blur; interpolation = ScalingInterpolation::blur;
if(interpolation == myInterpolationMode) if (interpolation == myInterpolationMode) return;
return;
myInterpolationMode = interpolation; myInterpolationMode = interpolation;
reload(); reload();

View File

@ -8,33 +8,33 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================ //============================================================================
#ifndef FBSURFACE_SDL_HXX #ifndef FBSURFACE_SDL2_HXX
#define FBSURFACE_SDL_HXX #define FBSURFACE_SDL2_HXX
#include "bspf.hxx" #include "bspf.hxx"
#include "FBSurface.hxx" #include "FBSurface.hxx"
#include "FBBackendSDL.hxx" #include "FBBackendSDL2.hxx"
#include "sdl_blitter/Blitter.hxx" #include "sdl_blitter/Blitter.hxx"
/** /**
An FBSurface suitable for the SDL Render2D API, making use of hardware An FBSurface suitable for the SDL2 Render2D API, making use of hardware
acceleration behind the scenes. acceleration behind the scenes.
@author Stephen Anthony @author Stephen Anthony
*/ */
class FBSurfaceSDL : public FBSurface class FBSurfaceSDL2 : public FBSurface
{ {
public: public:
FBSurfaceSDL(FBBackendSDL& backend, uInt32 width, uInt32 height, FBSurfaceSDL2(FBBackendSDL2& backend, uInt32 width, uInt32 height,
ScalingInterpolation inter, const uInt32* staticData); ScalingInterpolation inter, const uInt32* staticData);
~FBSurfaceSDL() override; ~FBSurfaceSDL2() override;
// Most of the surface drawing primitives are implemented in FBSurface; // Most of the surface drawing primitives are implemented in FBSurface;
// the ones implemented here use SDL-specific code for extra performance // the ones implemented here use SDL-specific code for extra performance
@ -63,13 +63,14 @@ class FBSurfaceSDL : public FBSurface
void reload() override; void reload() override;
void resize(uInt32 width, uInt32 height) override; void resize(uInt32 width, uInt32 height) override;
void enableBlend(bool enable) override;
void setBlendLevel(uInt32 percent) override;
void setScalingInterpolation(ScalingInterpolation) override; void setScalingInterpolation(ScalingInterpolation) override;
protected:
void applyAttributes() override;
private: private:
bool setSrcPosInternal(uInt32 x, uInt32 y) { bool setSrcPosInternal(uInt32 x, uInt32 y) {
if(std::cmp_not_equal(x, mySrcR.x) || std::cmp_not_equal(y, mySrcR.y)) if(x != static_cast<uInt32>(mySrcR.x) || y != static_cast<uInt32>(mySrcR.y))
{ {
mySrcR.x = x; mySrcR.y = y; mySrcR.x = x; mySrcR.y = y;
mySrcGUIR.moveTo(x, y); mySrcGUIR.moveTo(x, y);
@ -78,7 +79,7 @@ class FBSurfaceSDL : public FBSurface
return false; return false;
} }
bool setSrcSizeInternal(uInt32 w, uInt32 h) { bool setSrcSizeInternal(uInt32 w, uInt32 h) {
if(std::cmp_not_equal(w, mySrcR.w) || std::cmp_not_equal(h, mySrcR.h)) if(w != static_cast<uInt32>(mySrcR.w) || h != static_cast<uInt32>(mySrcR.h))
{ {
mySrcR.w = w; mySrcR.h = h; mySrcR.w = w; mySrcR.h = h;
mySrcGUIR.setWidth(w); mySrcGUIR.setHeight(h); mySrcGUIR.setWidth(w); mySrcGUIR.setHeight(h);
@ -87,7 +88,7 @@ class FBSurfaceSDL : public FBSurface
return false; return false;
} }
bool setDstPosInternal(uInt32 x, uInt32 y) { bool setDstPosInternal(uInt32 x, uInt32 y) {
if(std::cmp_not_equal(x, myDstR.x) || std::cmp_not_equal(y, myDstR.y)) if(x != static_cast<uInt32>(myDstR.x) || y != static_cast<uInt32>(myDstR.y))
{ {
myDstR.x = x; myDstR.y = y; myDstR.x = x; myDstR.y = y;
myDstGUIR.moveTo(x, y); myDstGUIR.moveTo(x, y);
@ -96,7 +97,7 @@ class FBSurfaceSDL : public FBSurface
return false; return false;
} }
bool setDstSizeInternal(uInt32 w, uInt32 h) { bool setDstSizeInternal(uInt32 w, uInt32 h) {
if(std::cmp_not_equal(w, myDstR.w) || std::cmp_not_equal(h, myDstR.h)) if(w != static_cast<uInt32>(myDstR.w) || h != static_cast<uInt32>(myDstR.h))
{ {
myDstR.w = w; myDstR.h = h; myDstR.w = w; myDstR.h = h;
myDstGUIR.setWidth(w); myDstGUIR.setHeight(h); myDstGUIR.setWidth(w); myDstGUIR.setHeight(h);
@ -110,17 +111,18 @@ class FBSurfaceSDL : public FBSurface
void reinitializeBlitter(bool force = false); void reinitializeBlitter(bool force = false);
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
FBSurfaceSDL() = delete; FBSurfaceSDL2() = delete;
FBSurfaceSDL(const FBSurfaceSDL&) = delete; FBSurfaceSDL2(const FBSurfaceSDL2&) = delete;
FBSurfaceSDL(FBSurfaceSDL&&) = delete; FBSurfaceSDL2(FBSurfaceSDL2&&) = delete;
FBSurfaceSDL& operator=(const FBSurfaceSDL&) = delete; FBSurfaceSDL2& operator=(const FBSurfaceSDL2&) = delete;
FBSurfaceSDL& operator=(FBSurfaceSDL&&) = delete; FBSurfaceSDL2& operator=(FBSurfaceSDL2&&) = delete;
private: private:
FBBackendSDL& myBackend; FBBackendSDL2& myBackend;
unique_ptr<Blitter> myBlitter; unique_ptr<Blitter> myBlitter;
ScalingInterpolation myInterpolationMode{ScalingInterpolation::none}; ScalingInterpolation myInterpolationMode
{ScalingInterpolation::none};
SDL_Surface* mySurface{nullptr}; SDL_Surface* mySurface{nullptr};
SDL_Rect mySrcR{-1, -1, -1, -1}, myDstR{-1, -1, -1, -1}; SDL_Rect mySrcR{-1, -1, -1, -1}, myDstR{-1, -1, -1, -1};

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -381,7 +381,8 @@ string HighScoresManager::formattedScore(Int32 score, Int32 width) const
if(scoreBCD(jprops)) if(scoreBCD(jprops))
{ {
digits = std::max(width, digits); if(width > digits)
digits = width;
buf << std::setw(digits) << std::setfill(' ') << score; buf << std::setw(digits) << std::setfill(' ') << score;
} }
else { else {

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -132,7 +132,7 @@ class HighScoresManager
@return The number of score address bytes @return The number of score address bytes
*/ */
static constexpr uInt32 numAddrBytes(Int32 digits, Int32 trailing) { static uInt32 numAddrBytes(Int32 digits, Int32 trailing) {
return (digits - trailing + 1) / 2; return (digits - trailing + 1) / 2;
} }

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -188,26 +188,27 @@ json JoyMap::saveMapping(EventMode mode) const
using MapType = std::pair<JoyMapping, Event::Type>; using MapType = std::pair<JoyMapping, Event::Type>;
std::vector<MapType> sortedMap(myMap.begin(), myMap.end()); std::vector<MapType> sortedMap(myMap.begin(), myMap.end());
std::ranges::sort(sortedMap, [](const MapType& a, const MapType& b) std::sort(sortedMap.begin(), sortedMap.end(),
{ [](const MapType& a, const MapType& b)
// Event::Type first {
if(a.first.button != b.first.button) // Event::Type first
return a.first.button < b.first.button; if(a.first.button != b.first.button)
return a.first.button < b.first.button;
if(a.first.axis != b.first.axis) if(a.first.axis != b.first.axis)
return a.first.axis < b.first.axis; return a.first.axis < b.first.axis;
if(a.first.adir != b.first.adir) if(a.first.adir != b.first.adir)
return a.first.adir < b.first.adir; return a.first.adir < b.first.adir;
if(a.first.hat != b.first.hat) if(a.first.hat != b.first.hat)
return a.first.hat < b.first.hat; return a.first.hat < b.first.hat;
if(a.first.hdir != b.first.hdir) if(a.first.hdir != b.first.hdir)
return a.first.hdir < b.first.hdir; return a.first.hdir < b.first.hdir;
return a.second < b.second; return a.second < b.second;
} }
); );
json eventMappings = json::array(); json eventMappings = json::array();
@ -284,17 +285,17 @@ int JoyMap::loadMapping(const json& eventMappings, EventMode mode)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
json JoyMap::convertLegacyMapping(string lst) json JoyMap::convertLegacyMapping(string list)
{ {
json eventMappings = json::array(); json eventMappings = json::array();
// Since istringstream swallows whitespace, we have to make the // Since istringstream swallows whitespace, we have to make the
// delimiters be spaces // delimiters be spaces
std::ranges::replace(lst, '|', ' '); std::replace(list.begin(), list.end(), '|', ' ');
std::ranges::replace(lst, ':', ' '); std::replace(list.begin(), list.end(), ':', ' ');
std::ranges::replace(lst, ',', ' '); std::replace(list.begin(), list.end(), ',', ' ');
istringstream buf(lst); istringstream buf(list);
int event = 0, button = 0, axis = 0, adir = 0, hat = 0, hdir = 0; int event = 0, button = 0, axis = 0, adir = 0, hat = 0, hdir = 0;
while(buf >> event && buf >> button while(buf >> event && buf >> button

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -112,7 +112,7 @@ class JoyMap
nlohmann::json saveMapping(EventMode mode) const; nlohmann::json saveMapping(EventMode mode) const;
int loadMapping(const nlohmann::json& eventMappings, EventMode mode); int loadMapping(const nlohmann::json& eventMappings, EventMode mode);
static nlohmann::json convertLegacyMapping(string lst); static nlohmann::json convertLegacyMapping(string list);
/** Erase all mappings for given mode */ /** Erase all mappings for given mode */
void eraseMode(EventMode mode); void eraseMode(EventMode mode);
@ -129,10 +129,10 @@ class JoyMap
size_t operator()(const JoyMapping& m)const { size_t operator()(const JoyMapping& m)const {
return std::hash<uInt64>()((static_cast<uInt64>(m.mode)) // 3 bits return std::hash<uInt64>()((static_cast<uInt64>(m.mode)) // 3 bits
+ ((static_cast<uInt64>(m.button)) * 7) // 3 bits + ((static_cast<uInt64>(m.button)) * 7) // 3 bits
+ (((static_cast<uInt64>(m.axis)) << 0) // 3 bits + (((static_cast<uInt64>(m.axis)) << 0) // 2 bits
| ((static_cast<uInt64>(m.adir)) << 3) // 2 bits | ((static_cast<uInt64>(m.adir)) << 2) // 2 bits
| ((static_cast<uInt64>(m.hat )) << 5) // 1 bit | ((static_cast<uInt64>(m.hat )) << 4) // 1 bit
| ((static_cast<uInt64>(m.hdir)) << 6) // 2 bits | ((static_cast<uInt64>(m.hdir)) << 5) // 2 bits
) * 61 ) * 61
); );
} }

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -43,7 +43,8 @@ namespace {
StellaMod::KBDM_RGUI, StellaMod::KBDM_RGUI,
StellaMod::KBDM_NUM, StellaMod::KBDM_NUM,
StellaMod::KBDM_CAPS, StellaMod::KBDM_CAPS,
StellaMod::KBDM_MODE StellaMod::KBDM_MODE,
StellaMod::KBDM_RESERVED
}) { }) {
if((mask & mod) != mod) continue; if((mask & mod) != mod) continue;
@ -221,7 +222,8 @@ json KeyMap::saveMapping(EventMode mode) const
using MapType = std::pair<Mapping, Event::Type>; using MapType = std::pair<Mapping, Event::Type>;
std::vector<MapType> sortedMap(myMap.begin(), myMap.end()); std::vector<MapType> sortedMap(myMap.begin(), myMap.end());
std::ranges::sort(sortedMap, [](const MapType& a, const MapType& b) std::sort(sortedMap.begin(), sortedMap.end(),
[](const MapType& a, const MapType& b)
{ {
// Event::Type first // Event::Type first
if(a.first.key != b.first.key) if(a.first.key != b.first.key)
@ -288,11 +290,11 @@ json KeyMap::convertLegacyMapping(string_view lm)
// Since istringstream swallows whitespace, we have to make the // Since istringstream swallows whitespace, we have to make the
// delimiters be spaces // delimiters be spaces
string lst{lm}; string list{lm};
std::ranges::replace(lst, '|', ' '); std::replace(list.begin(), list.end(), '|', ' ');
std::ranges::replace(lst, ':', ' '); std::replace(list.begin(), list.end(), ':', ' ');
std::ranges::replace(lst, ',', ' '); std::replace(list.begin(), list.end(), ',', ' ');
istringstream buf(lst); istringstream buf(list);
int event = 0, key = 0, mod = 0; int event = 0, key = 0, mod = 0;
while(buf >> event && buf >> key && buf >> mod) while(buf >> event && buf >> key && buf >> mod)

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -17,10 +17,6 @@
#include "Logger.hxx" #include "Logger.hxx"
#ifdef __LIB_RETRO__
extern void libretro_logger(int log_level, const char *string);
#endif
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Logger& Logger::instance() Logger& Logger::instance()
{ {
@ -56,10 +52,6 @@ void Logger::debug(string_view message)
void Logger::logMessage(string_view message, Level level) void Logger::logMessage(string_view message, Level level)
{ {
const std::lock_guard<std::mutex> lock(mutex); const std::lock_guard<std::mutex> lock(mutex);
#ifdef __LIB_RETRO__
libretro_logger(static_cast<int>(level), string{message}.c_str());
#endif
if(level == Logger::Level::ERR) if(level == Logger::Level::ERR)
{ {

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -28,13 +28,21 @@
#include "SerialPort.hxx" #include "SerialPort.hxx"
#if defined(BSPF_UNIX) #if defined(BSPF_UNIX)
#include "SerialPortUNIX.hxx" #include "SerialPortUNIX.hxx"
#include "OSystemUNIX.hxx" #if defined(RETRON77)
#include "SettingsR77.hxx"
#include "OSystemR77.hxx"
#else
#include "OSystemUNIX.hxx"
#endif
#elif defined(BSPF_WINDOWS) #elif defined(BSPF_WINDOWS)
#include "SerialPortWINDOWS.hxx" #include "SerialPortWINDOWS.hxx"
#include "OSystemWINDOWS.hxx" #include "OSystemWINDOWS.hxx"
#elif defined(BSPF_MACOS) #elif defined(BSPF_MACOS)
#include "SerialPortMACOS.hxx" #include "SerialPortMACOS.hxx"
#include "OSystemMACOS.hxx" #include "OSystemMACOS.hxx"
extern "C" {
int stellaMain(int argc, char* argv[]);
}
#elif defined(__LIB_RETRO__) #elif defined(__LIB_RETRO__)
#include "OSystemLIBRETRO.hxx" #include "OSystemLIBRETRO.hxx"
#else #else
@ -45,8 +53,8 @@
#include "EventHandlerLIBRETRO.hxx" #include "EventHandlerLIBRETRO.hxx"
#include "FBBackendLIBRETRO.hxx" #include "FBBackendLIBRETRO.hxx"
#elif defined(SDL_SUPPORT) #elif defined(SDL_SUPPORT)
#include "EventHandlerSDL.hxx" #include "EventHandlerSDL2.hxx"
#include "FBBackendSDL.hxx" #include "FBBackendSDL2.hxx"
#else #else
#error Unsupported backend! #error Unsupported backend!
#endif #endif
@ -55,7 +63,7 @@
#if defined(__LIB_RETRO__) #if defined(__LIB_RETRO__)
#include "SoundLIBRETRO.hxx" #include "SoundLIBRETRO.hxx"
#elif defined(SDL_SUPPORT) #elif defined(SDL_SUPPORT)
#include "SoundSDL.hxx" #include "SoundSDL2.hxx"
#else #else
#include "SoundNull.hxx" #include "SoundNull.hxx"
#endif #endif
@ -70,6 +78,10 @@ class AudioSettings;
implementations for the various ports of Stella, and always returns a implementations for the various ports of Stella, and always returns a
valid object based on the specific port and restrictions on that port. valid object based on the specific port and restrictions on that port.
As of SDL2, this code is greatly simplified. However, it remains here
in case we ever have multiple backend implementations again (should
not be necessary since SDL2 covers this nicely).
@author Stephen Anthony @author Stephen Anthony
*/ */
class MediaFactory class MediaFactory
@ -78,7 +90,11 @@ class MediaFactory
static unique_ptr<OSystem> createOSystem() static unique_ptr<OSystem> createOSystem()
{ {
#if defined(BSPF_UNIX) #if defined(BSPF_UNIX)
return make_unique<OSystemUNIX>(); #if defined(RETRON77)
return make_unique<OSystemR77>();
#else
return make_unique<OSystemUNIX>();
#endif
#elif defined(BSPF_WINDOWS) #elif defined(BSPF_WINDOWS)
return make_unique<OSystemWINDOWS>(); return make_unique<OSystemWINDOWS>();
#elif defined(BSPF_MACOS) #elif defined(BSPF_MACOS)
@ -92,7 +108,11 @@ class MediaFactory
static unique_ptr<Settings> createSettings() static unique_ptr<Settings> createSettings()
{ {
#ifdef RETRON77
return make_unique<SettingsR77>();
#else
return make_unique<Settings>(); return make_unique<Settings>();
#endif
} }
static unique_ptr<SerialPort> createSerialPort() static unique_ptr<SerialPort> createSerialPort()
@ -113,7 +133,7 @@ class MediaFactory
#if defined(__LIB_RETRO__) #if defined(__LIB_RETRO__)
return make_unique<FBBackendLIBRETRO>(osystem); return make_unique<FBBackendLIBRETRO>(osystem);
#elif defined(SDL_SUPPORT) #elif defined(SDL_SUPPORT)
return make_unique<FBBackendSDL>(osystem); return make_unique<FBBackendSDL2>(osystem);
#else #else
#error Unsupported platform for FrameBuffer! #error Unsupported platform for FrameBuffer!
#endif #endif
@ -125,7 +145,7 @@ class MediaFactory
#if defined(__LIB_RETRO__) #if defined(__LIB_RETRO__)
return make_unique<SoundLIBRETRO>(osystem, audioSettings); return make_unique<SoundLIBRETRO>(osystem, audioSettings);
#elif defined(SOUND_SUPPORT) && defined(SDL_SUPPORT) #elif defined(SOUND_SUPPORT) && defined(SDL_SUPPORT)
return make_unique<SoundSDL>(osystem, audioSettings); return make_unique<SoundSDL2>(osystem, audioSettings);
#else #else
return make_unique<SoundNull>(osystem); return make_unique<SoundNull>(osystem);
#endif #endif
@ -139,7 +159,7 @@ class MediaFactory
#if defined(__LIB_RETRO__) #if defined(__LIB_RETRO__)
return make_unique<EventHandlerLIBRETRO>(osystem); return make_unique<EventHandlerLIBRETRO>(osystem);
#elif defined(SDL_SUPPORT) #elif defined(SDL_SUPPORT)
return make_unique<EventHandlerSDL>(osystem); return make_unique<EventHandlerSDL2>(osystem);
#else #else
#error Unsupported platform for EventHandler! #error Unsupported platform for EventHandler!
#endif #endif
@ -161,6 +181,15 @@ class MediaFactory
#endif #endif
} }
static bool supportsURL()
{
#if defined(SDL_SUPPORT)
return SDLSupportsURL();
#else
return false;
#endif
}
static bool openURL(const string& url) static bool openURL(const string& url)
{ {
#if defined(SDL_SUPPORT) #if defined(SDL_SUPPORT)

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -405,11 +405,18 @@ void PhysicalJoystickHandler::setStickDefaultMapping(
setDefaultAction(stick, item, event, EventMode::kDrivingMode, updateDefaults); setDefaultAction(stick, item, event, EventMode::kDrivingMode, updateDefaults);
} }
#if defined(RETRON77)
constexpr bool retron77 = true;
#else
constexpr bool retron77 = false;
#endif
// Regular joysticks can only be used by one player at a time, // Regular joysticks can only be used by one player at a time,
// so we need to separate the paddles onto different // so we need to separate the paddles onto different
// devices. The Stelladaptor and 2600-daptor controllers support // devices. The R77 controllers support two players each when
// two players each when used as paddles, so are different. // used as paddles, so are different. Similarly, stelladaptors
const int paddlesPerJoystick = (j->type == PhysicalJoystick::Type::REGULAR) ? 1 : 2; // and 2600-daptors support two players natively.
const int paddlesPerJoystick = (j->type == PhysicalJoystick::Type::REGULAR && !retron77) ? 1 : 2;
if(paddlesPerJoystick == 2) if(paddlesPerJoystick == 2)
{ {
@ -1348,11 +1355,17 @@ PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultRight
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultLeftPaddlesMapping = { PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultLeftPaddlesMapping = {
{Event::LeftPaddleAAnalog, JOY_CTRL_NONE, JoyAxis::X, JoyDir::ANALOG}, {Event::LeftPaddleAAnalog, JOY_CTRL_NONE, JoyAxis::X, JoyDir::ANALOG},
#if defined(RETRON77)
{Event::LeftPaddleAAnalog, JOY_CTRL_NONE, JoyAxis::Z, JoyDir::ANALOG},
#endif
// Current code does NOT allow digital and anlog events on the same axis at the same time // Current code does NOT allow digital and anlog events on the same axis at the same time
//{Event::LeftPaddleADecrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS}, //{Event::LeftPaddleADecrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
//{Event::LeftPaddleAIncrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG}, //{Event::LeftPaddleAIncrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG},
{Event::LeftPaddleAFire, 0}, {Event::LeftPaddleAFire, 0},
{Event::LeftPaddleBAnalog, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::ANALOG}, {Event::LeftPaddleBAnalog, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::ANALOG},
#if defined(RETRON77)
{Event::LeftPaddleBAnalog, JOY_CTRL_NONE, JoyAxis::A3, JoyDir::ANALOG},
#endif
// Current code does NOT allow digital and anlog events on the same axis at the same // Current code does NOT allow digital and anlog events on the same axis at the same
//{Event::LeftPaddleBDecrease, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::POS}, //{Event::LeftPaddleBDecrease, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::POS},
//{Event::LeftPaddleBIncrease, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::NEG}, //{Event::LeftPaddleBIncrease, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::NEG},
@ -1362,11 +1375,17 @@ PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultLeftP
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultRightPaddlesMapping = { PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultRightPaddlesMapping = {
{Event::RightPaddleAAnalog, JOY_CTRL_NONE, JoyAxis::X, JoyDir::ANALOG}, {Event::RightPaddleAAnalog, JOY_CTRL_NONE, JoyAxis::X, JoyDir::ANALOG},
#if defined(RETRON77)
{Event::RightPaddleAAnalog, JOY_CTRL_NONE, JoyAxis::Z, JoyDir::ANALOG},
#endif
// Current code does NOT allow digital and anlog events on the same axis at the same // Current code does NOT allow digital and anlog events on the same axis at the same
//{Event::RightPaddleADecrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS}, //{Event::RightPaddleADecrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
//{Event::RightPaddleAIncrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG}, //{Event::RightPaddleAIncrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG},
{Event::RightPaddleAFire, 0}, {Event::RightPaddleAFire, 0},
{Event::RightPaddleBAnalog, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::ANALOG}, {Event::RightPaddleBAnalog, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::ANALOG},
#if defined(RETRON77)
{Event::RightPaddleBAnalog, JOY_CTRL_NONE, JoyAxis::A3, JoyDir::ANALOG},
#endif
// Current code does NOT allow digital and anlog events on the same axis at the same // Current code does NOT allow digital and anlog events on the same axis at the same
//{Event::RightPaddleBDecrease,JOY_CTRL_NONE, JoyAxis::Y, JoyDir::POS}, //{Event::RightPaddleBDecrease,JOY_CTRL_NONE, JoyAxis::Y, JoyDir::POS},
//{Event::RightPaddleBIncrease,JOY_CTRL_NONE, JoyAxis::Y, JoyDir::NEG}, //{Event::RightPaddleBIncrease,JOY_CTRL_NONE, JoyAxis::Y, JoyDir::NEG},
@ -1466,6 +1485,14 @@ PhysicalJoystickHandler::EventMappingArray
PhysicalJoystickHandler::DefaultCommonMapping = { PhysicalJoystickHandler::DefaultCommonMapping = {
// valid for all joysticks // valid for all joysticks
// Note: buttons 0..2 are used by controllers! // Note: buttons 0..2 are used by controllers!
#if defined(RETRON77)
{Event::CmdMenuMode, 3}, // Button "Y" / "4"
{Event::ExitMode, 4}, // Left Shoulder Button
{Event::OptionsMenuMode, 5}, // Right Shoulder Button
{Event::RewindPause, 7}, // Right Trigger Button
{Event::ConsoleSelect, 8}, // Button "Select"
{Event::ConsoleReset, 9}, // Button "Start"
#else
{Event::ConsoleSelect, 8}, // Button "Select" {Event::ConsoleSelect, 8}, // Button "Select"
{Event::ConsoleReset, 9}, // Button "Start" {Event::ConsoleReset, 9}, // Button "Start"
{Event::ConsoleColorToggle, 3}, // Button "Y" / "4" {Event::ConsoleColorToggle, 3}, // Button "Y" / "4"
@ -1473,6 +1500,7 @@ PhysicalJoystickHandler::DefaultCommonMapping = {
{Event::ConsoleRightDiffToggle, 5}, // Right Shoulder Button {Event::ConsoleRightDiffToggle, 5}, // Right Shoulder Button
{Event::CmdMenuMode, 6}, // Left Trigger Button {Event::CmdMenuMode, 6}, // Left Trigger Button
{Event::OptionsMenuMode, 7}, // Right Trigger Button {Event::OptionsMenuMode, 7}, // Right Trigger Button
#endif
}; };
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -806,10 +806,18 @@ PhysicalKeyboardHandler::DefaultCommonMapping = {
{ Event::HighScoresMenuMode, KBDK_INSERT }, { Event::HighScoresMenuMode, KBDK_INSERT },
{ Event::TogglePlayBackMode, KBDK_SPACE, KBDM_SHIFT }, { Event::TogglePlayBackMode, KBDK_SPACE, KBDM_SHIFT },
#if defined(RETRON77)
{ Event::ConsoleColorToggle, KBDK_F4 }, // back ("COLOR","B/W")
{ Event::ConsoleLeftDiffToggle, KBDK_F6 }, // front ("SKILL P1")
{ Event::ConsoleRightDiffToggle, KBDK_F8 }, // front ("SKILL P2")
{ Event::CmdMenuMode, KBDK_F13 }, // back ("4:3","16:9")
{ Event::ExitMode, KBDK_BACKSPACE }, // back ("FRY")
#else // defining duplicate keys must be avoided!
{ Event::ConsoleBlackWhite, KBDK_F4 }, { Event::ConsoleBlackWhite, KBDK_F4 },
{ Event::ConsoleLeftDiffB, KBDK_F6 }, { Event::ConsoleLeftDiffB, KBDK_F6 },
{ Event::ConsoleRightDiffB, KBDK_F8 }, { Event::ConsoleRightDiffB, KBDK_F8 },
{ Event::Fry, KBDK_BACKSPACE, KBDM_SHIFT } { Event::Fry, KBDK_BACKSPACE, KBDM_SHIFT },
#endif
}; };
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -854,12 +862,24 @@ PhysicalKeyboardHandler::DefaultMenuMapping = {
{Event::Quit, KBDK_Q, KBDM_CTRL}, {Event::Quit, KBDK_Q, KBDM_CTRL},
#endif #endif
#if defined(RETRON77)
{Event::UIUp, KBDK_F9}, // front ("SAVE")
{Event::UIDown, KBDK_F2}, // front ("RESET")
{Event::UINavPrev, KBDK_F11}, // front ("LOAD")
{Event::UINavNext, KBDK_F1}, // front ("MODE")
{Event::UISelect, KBDK_F6}, // front ("SKILL P1")
{Event::UICancel, KBDK_F8}, // front ("SKILL P2")
//{Event::NoType, KBDK_F4}, // back ("COLOR","B/W")
{Event::UITabPrev, KBDK_F13}, // back ("4:3","16:9")
{Event::UITabNext, KBDK_BACKSPACE}, // back (FRY)
#else // defining duplicate keys must be avoided!
{Event::UIPrevDir, KBDK_BACKSPACE}, {Event::UIPrevDir, KBDK_BACKSPACE},
#ifdef BSPF_MACOS #ifdef BSPF_MACOS
{Event::UIHelp, KBDK_SLASH, KBDM_SHIFT | CMD}, {Event::UIHelp, KBDK_SLASH, KBDM_SHIFT | CMD},
#else #else
{Event::UIHelp, KBDK_F1}, {Event::UIHelp, KBDK_F1},
#endif #endif
#endif
}; };
#ifdef GUI_SUPPORT #ifdef GUI_SUPPORT
@ -1017,8 +1037,6 @@ PhysicalKeyboardHandler::DefaultPaddleMapping = {
{Event::LeftPaddleAFire, KBDK_SPACE}, {Event::LeftPaddleAFire, KBDK_SPACE},
{Event::LeftPaddleAFire, KBDK_LCTRL}, {Event::LeftPaddleAFire, KBDK_LCTRL},
{Event::LeftPaddleAFire, KBDK_KP_5}, {Event::LeftPaddleAFire, KBDK_KP_5},
{Event::LeftPaddleAButton1, KBDK_UP, KBDM_SHIFT},
{Event::LeftPaddleAButton2, KBDK_DOWN, KBDM_SHIFT},
{Event::LeftPaddleBDecrease, KBDK_DOWN}, {Event::LeftPaddleBDecrease, KBDK_DOWN},
{Event::LeftPaddleBIncrease, KBDK_UP}, {Event::LeftPaddleBIncrease, KBDK_UP},
@ -1028,8 +1046,6 @@ PhysicalKeyboardHandler::DefaultPaddleMapping = {
{Event::RightPaddleADecrease, KBDK_J}, {Event::RightPaddleADecrease, KBDK_J},
{Event::RightPaddleAIncrease, KBDK_G}, {Event::RightPaddleAIncrease, KBDK_G},
{Event::RightPaddleAFire, KBDK_F}, {Event::RightPaddleAFire, KBDK_F},
{Event::RightPaddleAButton1, KBDK_Y, KBDM_SHIFT},
{Event::RightPaddleAButton2, KBDK_H, KBDM_SHIFT},
{Event::RightPaddleBDecrease, KBDK_H}, {Event::RightPaddleBDecrease, KBDK_H},
{Event::RightPaddleBIncrease, KBDK_Y}, {Event::RightPaddleBIncrease, KBDK_Y},
@ -1081,16 +1097,10 @@ PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultDrivi
{Event::LeftDrivingFire, KBDK_SPACE}, {Event::LeftDrivingFire, KBDK_SPACE},
{Event::LeftDrivingFire, KBDK_LCTRL}, {Event::LeftDrivingFire, KBDK_LCTRL},
{Event::LeftDrivingFire, KBDK_KP_5}, {Event::LeftDrivingFire, KBDK_KP_5},
{Event::LeftDrivingButton1, KBDK_UP},
{Event::LeftDrivingButton2, KBDK_DOWN},
{Event::LeftDrivingButton1, KBDK_KP_8},
{Event::LeftDrivingButton2, KBDK_KP_2},
{Event::RightDrivingCCW, KBDK_G}, {Event::RightDrivingCCW, KBDK_G},
{Event::RightDrivingCW, KBDK_J}, {Event::RightDrivingCW, KBDK_J},
{Event::RightDrivingFire, KBDK_F}, {Event::RightDrivingFire, KBDK_F},
{Event::RightDrivingButton1, KBDK_Y},
{Event::RightDrivingButton2, KBDK_H},
}; };
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1147,7 +1157,9 @@ PhysicalKeyboardHandler::CompuMateMapping = {
{Event::CompuMateRightBracket, KBDK_RIGHTBRACKET}, {Event::CompuMateRightBracket, KBDK_RIGHTBRACKET},
{Event::CompuMateMinus, KBDK_MINUS}, {Event::CompuMateMinus, KBDK_MINUS},
{Event::CompuMateQuote, KBDK_APOSTROPHE, KBDM_SHIFT}, {Event::CompuMateQuote, KBDK_APOSTROPHE, KBDM_SHIFT},
#ifndef RETRON77
{Event::CompuMateBackspace, KBDK_BACKSPACE}, {Event::CompuMateBackspace, KBDK_BACKSPACE},
#endif
{Event::CompuMateEquals, KBDK_EQUALS}, {Event::CompuMateEquals, KBDK_EQUALS},
{Event::CompuMatePlus, KBDK_EQUALS, KBDM_SHIFT}, {Event::CompuMatePlus, KBDK_EQUALS, KBDM_SHIFT},
{Event::CompuMateSlash, KBDK_SLASH} {Event::CompuMateSlash, KBDK_SLASH}

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -449,15 +449,13 @@ void PaletteHandler::generateCustomPalette(ConsoleTiming timing) const
{ {
constexpr int NUM_CHROMA = 16; constexpr int NUM_CHROMA = 16;
constexpr int NUM_LUMA = 8; constexpr int NUM_LUMA = 8;
constexpr float SATURATION = 0.25F; // default saturation
if(timing == ConsoleTiming::ntsc) if(timing == ConsoleTiming::ntsc)
{ {
constexpr float SATURATION = 0.30F; // default NTSC saturation
vector2d IQ[NUM_CHROMA]; vector2d IQ[NUM_CHROMA];
// YIQ is YUV shifted by 33° // YIQ is YUV shifted by 33 degrees
// -90° + 33° = -57° would create a greenish yellow constexpr float offset = 33 * BSPF::PI_f / 180;
// -90° + 53° = -37° creates gold (which is correct according to the documentation)
constexpr float offset = (33 + 20) * BSPF::PI_f / 180;
const float shift = myPhaseNTSC * BSPF::PI_f / 180; const float shift = myPhaseNTSC * BSPF::PI_f / 180;
// color 0 is grayscale // color 0 is grayscale
@ -480,9 +478,9 @@ void PaletteHandler::generateCustomPalette(ConsoleTiming timing) const
float G = Y + dotProduct(IQ[chroma], IQG); float G = Y + dotProduct(IQ[chroma], IQG);
float B = Y + dotProduct(IQ[chroma], IQB); float B = Y + dotProduct(IQ[chroma], IQB);
R = std::max(R, 0.F); if(R < 0) R = 0;
G = std::max(G, 0.F); if(G < 0) G = 0;
B = std::max(B, 0.F); if(B < 0) B = 0;
R = powf(R, 0.9F); R = powf(R, 0.9F);
G = powf(G, 0.9F); G = powf(G, 0.9F);
@ -498,7 +496,6 @@ void PaletteHandler::generateCustomPalette(ConsoleTiming timing) const
} }
else if(timing == ConsoleTiming::pal) else if(timing == ConsoleTiming::pal)
{ {
constexpr float SATURATION = 0.25F; // default PAL saturation
constexpr float offset = BSPF::PI_f; constexpr float offset = BSPF::PI_f;
const float shift = myPhasePAL * BSPF::PI_f / 180; const float shift = myPhasePAL * BSPF::PI_f / 180;
constexpr float fixedShift = 22.5F * BSPF::PI_f / 180; constexpr float fixedShift = 22.5F * BSPF::PI_f / 180;
@ -672,9 +669,9 @@ const PaletteArray PaletteHandler::ourPALPalette = {
0x6c6c6c, 0, 0x909090, 0, 0xb4b4b4, 0, 0xd8d8d8, 0, 0x6c6c6c, 0, 0x909090, 0, 0xb4b4b4, 0, 0xd8d8d8, 0,
#else #else
0x0b0b0b, 0, 0x333333, 0, 0x595959, 0, 0x7b7b7b, 0, // 0 0x0b0b0b, 0, 0x333333, 0, 0x595959, 0, 0x7b7b7b, 0, // 0
0x999999, 0, 0xb6b6b6, 0, 0xcfcfcf, 0, 0xe6e6e6, 0, 0x8b8b8b, 0, 0xaaaaaa, 0, 0xc7c7c7, 0, 0xe3e3e3, 0,
0x0b0b0b, 0, 0x333333, 0, 0x595959, 0, 0x7b7b7b, 0, // 1 0x000000, 0, 0x272727, 0, 0x404040, 0, 0x696969, 0, // 1
0x999999, 0, 0xb6b6b6, 0, 0xcfcfcf, 0, 0xe6e6e6, 0, 0x8b8b8b, 0, 0xaaaaaa, 0, 0xc7c7c7, 0, 0xe3e3e3, 0,
0x3b2400, 0, 0x664700, 0, 0x8b7000, 0, 0xac9200, 0, // 2 0x3b2400, 0, 0x664700, 0, 0x8b7000, 0, 0xac9200, 0, // 2
0xc5ae36, 0, 0xdec85e, 0, 0xf7e27f, 0, 0xfff19e, 0, 0xc5ae36, 0, 0xdec85e, 0, 0xf7e27f, 0, 0xfff19e, 0,
0x004500, 0, 0x006f00, 0, 0x3b9200, 0, 0x65b009, 0, // 3 0x004500, 0, 0x006f00, 0, 0x3b9200, 0, 0x65b009, 0, // 3

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -33,7 +33,7 @@ class PaletteHandler
static constexpr string_view SETTING_CUSTOM = "custom"; static constexpr string_view SETTING_CUSTOM = "custom";
// Phase shift default and limits // Phase shift default and limits
static constexpr float DEF_NTSC_SHIFT = 26.7F; // makes color $fx fall between $1x and $2x static constexpr float DEF_NTSC_SHIFT = 26.2F;
static constexpr float DEF_PAL_SHIFT = 31.3F; // ~= 360 / 11.5 static constexpr float DEF_PAL_SHIFT = 31.3F; // ~= 360 / 11.5
static constexpr float MAX_PHASE_SHIFT = 4.5F; static constexpr float MAX_PHASE_SHIFT = 4.5F;
static constexpr float DEF_RGB_SHIFT = 0.0F; static constexpr float DEF_RGB_SHIFT = 0.0F;

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -64,15 +64,15 @@ class PhosphorHandler
static constexpr uInt32 getPixel(const uInt32 c, const uInt32 p) static constexpr uInt32 getPixel(const uInt32 c, const uInt32 p)
{ {
// Mix current calculated frame with previous displayed frame // Mix current calculated frame with previous displayed frame
const auto rc = static_cast<uInt8>(c), const auto rc = static_cast<uInt8>(c >> 16),
gc = static_cast<uInt8>(c >> 8), gc = static_cast<uInt8>(c >> 8),
bc = static_cast<uInt8>(c >> 16), bc = static_cast<uInt8>(c),
rp = static_cast<uInt8>(p), rp = static_cast<uInt8>(p >> 16),
gp = static_cast<uInt8>(p >> 8), gp = static_cast<uInt8>(p >> 8),
bp = static_cast<uInt8>(p >> 16); bp = static_cast<uInt8>(p);
return ourPhosphorLUT[rc][rp] | (ourPhosphorLUT[gc][gp] << 8) | return (ourPhosphorLUT[rc][rp] << 16) | (ourPhosphorLUT[gc][gp] << 8) |
(ourPhosphorLUT[bc][bp] << 16); ourPhosphorLUT[bc][bp];
} }
private: private:

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -112,26 +112,26 @@ json PhysicalJoystick::convertLegacyMapping(string_view mapping, string_view nam
{ {
istringstream buf(string{mapping}); // TODO: fixed in C++23 istringstream buf(string{mapping}); // TODO: fixed in C++23
json convertedMapping = json::object(); json convertedMapping = json::object();
string lmap; string map;
// Skip joystick name // Skip joystick name
getline(buf, lmap, MODE_DELIM); getline(buf, map, MODE_DELIM);
while (getline(buf, lmap, MODE_DELIM)) while (getline(buf, map, MODE_DELIM))
{ {
int mode{0}; int mode{0};
// Get event mode // Get event mode
std::ranges::replace(lmap, '|', ' '); std::replace(map.begin(), map.end(), '|', ' ');
istringstream modeBuf(lmap); istringstream modeBuf(map);
modeBuf >> mode; modeBuf >> mode;
// Remove leading "<mode>|" string // Remove leading "<mode>|" string
lmap.erase(0, 2); map.erase(0, 2);
const json lmappingForMode = JoyMap::convertLegacyMapping(lmap); const json mappingForMode = JoyMap::convertLegacyMapping(map);
convertedMapping[jsonName(static_cast<EventMode>(mode))] = lmappingForMode; convertedMapping[jsonName(static_cast<EventMode>(mode))] = mappingForMode;
} }
convertedMapping["name"] = name; convertedMapping["name"] = name;

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -114,10 +114,10 @@ private:
public: public:
constexpr Rect() = default; constexpr Rect() = default;
constexpr explicit Rect(const Size& s) : bottom{s.h}, right{s.w} { assert(valid()); } constexpr explicit Rect(const Size& s) : bottom{ s.h }, right{ s.w } { assert(valid()); }
constexpr Rect(uInt32 w, uInt32 h) : bottom{h}, right{w} { assert(valid()); } constexpr Rect(uInt32 w, uInt32 h) : bottom{ h }, right{ w } { assert(valid()); }
constexpr Rect(const Point& p, uInt32 w, uInt32 h) constexpr Rect(const Point& p, uInt32 w, uInt32 h)
: top(p.y), left(p.x), bottom(p.y + h), right(p.x + w) { assert(valid()); } : top(p.y), left(p.x), bottom(p.y + h), right( p.x + w) { assert(valid()); }
constexpr Rect(uInt32 x1, uInt32 y1, uInt32 x2, uInt32 y2) : top{y1}, left{x1}, bottom{y2}, right{x2} { assert(valid()); } constexpr Rect(uInt32 x1, uInt32 y1, uInt32 x2, uInt32 y2) : top{y1}, left{x1}, bottom{y2}, right{x2} { assert(valid()); }
constexpr uInt32 x() const { return left; } constexpr uInt32 x() const { return left; }

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -114,7 +114,7 @@ bool RewindManager::addState(string_view message, bool timeMachine)
interval = interval * scanlines / 262; interval = interval * scanlines / 262;
} }
if(myOSystem.console().system().cycles() - lastState.cycles < interval) if(myOSystem.console().tia().cycles() - lastState.cycles < interval)
return false; return false;
} }
@ -135,7 +135,7 @@ bool RewindManager::addState(string_view message, bool timeMachine)
if(myStateManager.saveState(s) && myOSystem.console().tia().saveDisplay(s)) if(myStateManager.saveState(s) && myOSystem.console().tia().saveDisplay(s))
{ {
state.message = message; state.message = message;
state.cycles = myOSystem.console().system().cycles(); state.cycles = myOSystem.console().tia().cycles();
myLastTimeMachineAdd = timeMachine; myLastTimeMachineAdd = timeMachine;
return true; return true;
} }
@ -145,7 +145,7 @@ bool RewindManager::addState(string_view message, bool timeMachine)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 RewindManager::rewindStates(uInt32 numStates) uInt32 RewindManager::rewindStates(uInt32 numStates)
{ {
const uInt64 startCycles = myOSystem.console().system().cycles(); const uInt64 startCycles = myOSystem.console().tia().cycles();
uInt32 i{0}; uInt32 i{0};
string message; string message;
@ -185,7 +185,7 @@ uInt32 RewindManager::rewindStates(uInt32 numStates)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 RewindManager::unwindStates(uInt32 numStates) uInt32 RewindManager::unwindStates(uInt32 numStates)
{ {
const uInt64 startCycles = myOSystem.console().system().cycles(); const uInt64 startCycles = myOSystem.console().tia().cycles();
uInt32 i{0}; uInt32 i{0};
string message; string message;

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -33,30 +33,48 @@
#pragma clang diagnostic ignored "-Wold-style-cast" #pragma clang diagnostic ignored "-Wold-style-cast"
#pragma clang diagnostic ignored "-Wreserved-identifier" #pragma clang diagnostic ignored "-Wreserved-identifier"
#pragma clang diagnostic ignored "-Wswitch-default" #pragma clang diagnostic ignored "-Wswitch-default"
#include <SDL3/SDL.h> #include <SDL.h>
#pragma clang diagnostic pop #pragma clang diagnostic pop
#elif defined(BSPF_WINDOWS) #elif defined(BSPF_WINDOWS)
#pragma warning(push, 0) #pragma warning(push, 0)
#include <SDL3/SDL.h> #include <SDL.h>
#pragma warning(pop) #pragma warning(pop)
#else #else
#include <SDL3/SDL.h> #include <SDL.h>
#endif #endif
/*
* Seems to be needed for ppc64le, doesn't hurt other archs
* Note that this is a problem in SDL2, which includes <altivec.h>
* https://bugzilla.redhat.com/show_bug.cgi?id=1419452
*/
#undef vector
#undef pixel
#undef bool
static inline string SDLVersion() static inline string SDLVersion()
{ {
ostringstream buf; ostringstream buf;
const int ver = SDL_GetVersion(); SDL_version ver;
buf << "SDL " SDL_GetVersion(&ver);
<< SDL_VERSIONNUM_MAJOR(ver) << "." buf << "SDL " << static_cast<int>(ver.major) << "." << static_cast<int>(ver.minor)
<< SDL_VERSIONNUM_MINOR(ver) << "." << "." << static_cast<int>(ver.patch);
<< SDL_VERSIONNUM_MICRO(ver);
return buf.str(); return buf.str();
} }
static inline bool SDLSupportsURL()
{
return SDL_VERSION_ATLEAST(2,0,14);
}
static inline bool SDLOpenURL(const string& url) static inline bool SDLOpenURL(const string& url)
{ {
return SDL_OpenURL(url.c_str()); #if SDL_VERSION_ATLEAST(2,0,14)
return SDL_OpenURL(url.c_str()) == 0;
#else
cerr << "OpenURL requires at least SDL 2.0.14\n";
return false;
#endif
} }
#endif // SDL_LIB_HXX #endif // SDL_LIB_HXX

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -90,10 +90,9 @@ class SoundNull : public Sound
volume is given as a range from 0 to 100 (0 indicates mute). Values volume is given as a range from 0 to 100 (0 indicates mute). Values
outside this range indicate that the volume shouldn't be changed at all. outside this range indicate that the volume shouldn't be changed at all.
@param volume The new volume level for the sound device @param volume The new volume level for the sound device
@param persist Whether to save the volume change to settings
*/ */
void setVolume(uInt32 volume, bool persist = true) override { } void setVolume(uInt32 volume) override { }
/** /**
Adjusts the volume of the sound device based on the given direction. Adjusts the volume of the sound device based on the given direction.

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -31,18 +31,18 @@
#include "audio/LanczosResampler.hxx" #include "audio/LanczosResampler.hxx"
#include "ThreadDebugging.hxx" #include "ThreadDebugging.hxx"
#include "SoundSDL.hxx" #include "SoundSDL2.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::SoundSDL(OSystem& osystem, AudioSettings& audioSettings) SoundSDL2::SoundSDL2(OSystem& osystem, AudioSettings& audioSettings)
: Sound{osystem}, : Sound{osystem},
myAudioSettings{audioSettings} myAudioSettings{audioSettings}
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
Logger::debug("SoundSDL::SoundSDL started ..."); Logger::debug("SoundSDL2::SoundSDL2 started ...");
if(!SDL_InitSubSystem(SDL_INIT_AUDIO)) if(SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
{ {
ostringstream buf; ostringstream buf;
@ -52,35 +52,77 @@ SoundSDL::SoundSDL(OSystem& osystem, AudioSettings& audioSettings)
return; return;
} }
SDL_zero(mySpec); queryHardware(myDevices); // NOLINT
if(!myAudioSettings.enabled())
Logger::info("Sound disabled\n");
Logger::debug("SoundSDL::SoundSDL initialized"); SDL_zero(myHardwareSpec);
if(!openDevice())
return;
// Reserve 8K for the audio buffer; seems to be enough on most systems Logger::debug("SoundSDL2::SoundSDL2 initialized");
myBuffer.reserve(8_KB);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::~SoundSDL() SoundSDL2::~SoundSDL2()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
if(!myIsInitializedFlag) if(!myIsInitializedFlag)
return; return;
SDL_DestroyAudioStream(myStream);
SDL_CloseAudioDevice(myDevice); SDL_CloseAudioDevice(myDevice);
SDL_QuitSubSystem(SDL_INIT_AUDIO); SDL_QuitSubSystem(SDL_INIT_AUDIO);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::openDevice() void SoundSDL2::queryHardware(VariantList& devices)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
auto SOUND_ERROR = [this]() -> bool const int numDevices = SDL_GetNumAudioDevices(0);
// log the available audio devices
ostringstream s;
s << "Supported audio devices (" << numDevices << "):";
Logger::debug(s.view());
VarList::push_back(devices, "Default", 0);
for(int i = 0; i < numDevices; ++i)
{
ostringstream ss;
ss << " " << i + 1 << ": " << SDL_GetAudioDeviceName(i, 0);
Logger::debug(ss.view());
VarList::push_back(devices, SDL_GetAudioDeviceName(i, 0), i + 1);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL2::openDevice()
{
ASSERT_MAIN_THREAD;
SDL_AudioSpec desired;
desired.freq = myAudioSettings.sampleRate();
desired.format = AUDIO_F32SYS;
desired.channels = 2;
desired.samples = static_cast<Uint16>(myAudioSettings.fragmentSize());
desired.callback = callback;
desired.userdata = this;
if(myIsInitializedFlag)
SDL_CloseAudioDevice(myDevice);
myDeviceId = BSPF::clamp(myAudioSettings.device(), 0U,
static_cast<uInt32>(myDevices.size() - 1));
const char* const device = myDeviceId
? myDevices.at(myDeviceId).first.c_str()
: nullptr;
myDevice = SDL_OpenAudioDevice(device, 0, &desired, &myHardwareSpec,
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
if(myDevice == 0)
{ {
ostringstream buf; ostringstream buf;
@ -89,72 +131,49 @@ bool SoundSDL::openDevice()
Logger::error(buf.view()); Logger::error(buf.view());
return myIsInitializedFlag = false; return myIsInitializedFlag = false;
};
if(myIsInitializedFlag)
{
SDL_DestroyAudioStream(myStream);
myStream = nullptr;
} }
mySpec = { SDL_AUDIO_F32, 2, static_cast<int>(myAudioSettings.sampleRate()) };
myDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &mySpec);
if(myDevice == 0)
return SOUND_ERROR();
myStream = SDL_CreateAudioStream(&mySpec, nullptr);
if(!myStream)
return SOUND_ERROR();
if(!SDL_BindAudioStream(myDevice, myStream))
return SOUND_ERROR();
if(!SDL_SetAudioStreamGetCallback(myStream, audioCallback, this))
return SOUND_ERROR();
return myIsInitializedFlag = true; return myIsInitializedFlag = true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::setEnabled(bool enable) void SoundSDL2::setEnabled(bool enable)
{ {
if(myIsInitializedFlag) mute(!enable);
{ pause(!enable);
mute(!enable);
pause(!enable);
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::open(shared_ptr<AudioQueue> audioQueue, void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue,
shared_ptr<const EmulationTiming> emulationTiming) shared_ptr<const EmulationTiming> emulationTiming)
{ {
const string pre_about = myAboutString;
// Do we need to re-open the sound device?
// Only do this when absolutely necessary
if(myAudioSettings.sampleRate() != static_cast<uInt32>(myHardwareSpec.freq) ||
myAudioSettings.fragmentSize() != static_cast<uInt32>(myHardwareSpec.samples) ||
myAudioSettings.device() != myDeviceId)
openDevice();
myEmulationTiming = emulationTiming;
myWavHandler.setSpeed(262 * 60 * 2. / myEmulationTiming->audioSampleRate());
Logger::debug("SoundSDL2::open started ...");
audioQueue->ignoreOverflows(!myAudioSettings.enabled());
if(!myAudioSettings.enabled()) if(!myAudioSettings.enabled())
{ {
Logger::info("Sound disabled\n"); Logger::info("Sound disabled\n");
return; return;
} }
pause(true);
const string pre_about = myAboutString;
myAudioQueue = audioQueue; myAudioQueue = audioQueue;
myEmulationTiming = emulationTiming;
myUnderrun = true; myUnderrun = true;
myCurrentFragment = nullptr; myCurrentFragment = nullptr;
myAudioQueue->ignoreOverflows(!myAudioSettings.enabled());
myWavHandler.setSpeed(262 * 60 * 2. / myEmulationTiming->audioSampleRate());
// Do we need to re-open the sound device?
// Only do this when absolutely necessary
if(myAudioSettings.sampleRate() != static_cast<uInt32>(mySpec.freq))
openDevice();
Logger::debug("SoundSDL::open started ...");
// Adjust volume to that defined in settings // Adjust volume to that defined in settings
setVolume(myAudioSettings.volume()); setVolume(myAudioSettings.volume());
// Initialize resampler; must be done after the sound device has opened
initResampler(); initResampler();
// Show some info // Show some info
@ -165,20 +184,20 @@ void SoundSDL::open(shared_ptr<AudioQueue> audioQueue,
// And start the SDL sound subsystem ... // And start the SDL sound subsystem ...
pause(false); pause(false);
Logger::debug("SoundSDL::open finished"); Logger::debug("SoundSDL2::open finished");
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::mute(bool enable) void SoundSDL2::mute(bool enable)
{ {
if(enable) if(enable)
setVolume(0, false); myVolumeFactor = 0;
else else
setVolume(myAudioSettings.volume()); setVolume(myAudioSettings.volume());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::toggleMute() void SoundSDL2::toggleMute()
{ {
const bool wasMuted = myVolumeFactor == 0; const bool wasMuted = myVolumeFactor == 0;
mute(!wasMuted); mute(!wasMuted);
@ -192,40 +211,31 @@ void SoundSDL::toggleMute()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::pause(bool enable) bool SoundSDL2::pause(bool enable)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
const bool wasPaused = SDL_AudioStreamDevicePaused(myStream); const bool wasPaused = SDL_GetAudioDeviceStatus(myDevice) == SDL_AUDIO_PAUSED;
if(myIsInitializedFlag) if(myIsInitializedFlag)
{ {
if(enable) SDL_PauseAudioStreamDevice(myStream); SDL_PauseAudioDevice(myDevice, enable ? 1 : 0);
else SDL_ResumeAudioStreamDevice(myStream);
myWavHandler.pause(enable); myWavHandler.pause(enable);
} }
return wasPaused; return wasPaused;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::setVolume(uInt32 volume, bool persist) void SoundSDL2::setVolume(uInt32 volume)
{ {
if(myIsInitializedFlag && (volume <= 100)) if(myIsInitializedFlag && (volume <= 100))
{ {
myVolumeFactor = myAudioSettings.enabled() myAudioSettings.setVolume(volume);
? static_cast<float>(volume) / 100.F myVolumeFactor = myAudioSettings.enabled() ? static_cast<float>(volume) / 100.F : 0;
: 0.F;
SDL_SetAudioStreamGain(myStream, myVolumeFactor);
myWavHandler.setVolumeFactor(myVolumeFactor);
if(persist)
myAudioSettings.setVolume(volume);
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::adjustVolume(int direction) void SoundSDL2::adjustVolume(int direction)
{ {
Int32 percent = myAudioSettings.volume(); Int32 percent = myAudioSettings.volume();
percent = BSPF::clamp(percent + direction * 2, 0, 100); percent = BSPF::clamp(percent + direction * 2, 0, 100);
@ -247,12 +257,13 @@ void SoundSDL::adjustVolume(int direction)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string SoundSDL::about() const string SoundSDL2::about() const
{ {
ostringstream buf; ostringstream buf;
buf << "Sound enabled:\n" buf << "Sound enabled:\n"
<< " Volume: " << myAudioSettings.volume() << "%\n" << " Volume: " << myAudioSettings.volume() << "%\n"
<< " Channels: " << static_cast<uInt32>(mySpec.channels) << " Device: " << myDevices.at(myDeviceId).first << '\n'
<< " Channels: " << static_cast<uInt32>(myHardwareSpec.channels)
<< (myAudioQueue->isStereo() ? " (Stereo)" : " (Mono)") << '\n' << (myAudioQueue->isStereo() ? " (Stereo)" : " (Mono)") << '\n'
<< " Preset: "; << " Preset: ";
switch(myAudioSettings.preset()) switch(myAudioSettings.preset())
@ -276,7 +287,10 @@ string SoundSDL::about() const
default: default:
break; // Not supposed to get here break; // Not supposed to get here
} }
buf << " Sample rate: " << static_cast<uInt32>(mySpec.freq) << " Hz\n"; buf << " Fragment size: " << static_cast<uInt32>(myHardwareSpec.samples)
<< " bytes\n"
<< " Sample rate: " << static_cast<uInt32>(myHardwareSpec.freq)
<< " Hz\n";
buf << " Resampling: "; buf << " Resampling: ";
switch(myAudioSettings.resamplingQuality()) switch(myAudioSettings.resamplingQuality())
{ {
@ -301,7 +315,7 @@ string SoundSDL::about() const
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::initResampler() void SoundSDL2::initResampler()
{ {
const Resampler::NextFragmentCallback nextFragmentCallback = [this] () -> Int16* { const Resampler::NextFragmentCallback nextFragmentCallback = [this] () -> Int16* {
Int16* nextFragment = nullptr; Int16* nextFragment = nullptr;
@ -319,11 +333,13 @@ void SoundSDL::initResampler()
return nextFragment; return nextFragment;
}; };
const Resampler::Format formatFrom = const Resampler::Format formatFrom =
Resampler::Format(myEmulationTiming->audioSampleRate(), Resampler::Format(myEmulationTiming->audioSampleRate(),
myAudioQueue->fragmentSize(), myAudioQueue->isStereo()); myAudioQueue->fragmentSize(), myAudioQueue->isStereo());
const Resampler::Format formatTo = const Resampler::Format formatTo =
Resampler::Format(mySpec.freq, 1024, mySpec.channels > 1); Resampler::Format(myHardwareSpec.freq, myHardwareSpec.samples,
myHardwareSpec.channels > 1);
switch(myAudioSettings.resamplingQuality()) switch(myAudioSettings.resamplingQuality())
{ {
@ -349,147 +365,183 @@ void SoundSDL::initResampler()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::audioCallback(void* object, SDL_AudioStream* stream, void SoundSDL2::callback(void* object, uInt8* stream, int len)
int additional_amt, int)
{ {
auto* self = static_cast<SoundSDL*>(object); auto* self = static_cast<SoundSDL2*>(object);
std::vector<uInt8>& buf = self->myBuffer;
// Make sure we always have enough room in the buffer if(self->myAudioQueue && self->myResampler)
if(std::cmp_greater_equal(additional_amt, buf.capacity())) {
buf.resize(additional_amt); // The stream is 32-bit float (even though this callback is 8-bits), since
// the resampler and TIA audio subsystem always generate float samples
auto* s = reinterpret_cast<float*>(stream);
const uInt32 length = len >> 2;
self->myResampler->fillFragment(s, length);
// The stream is 32-bit float (even though this callback is 8-bits), since for(uInt32 i = 0; i < length; ++i)
// the resampler and TIA audio subsystem always generate float samples s[i] *= SoundSDL2::myVolumeFactor;
auto* s = reinterpret_cast<float*>(buf.data()); }
self->myResampler->fillFragment(s, additional_amt >> 2);
SDL_PutAudioStreamData(stream, buf.data(), additional_amt);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::playWav(const string& fileName, uInt32 position, uInt32 length)
{
if(myStream)
return myWavHandler.play(SDL_GetAudioStreamDevice(myStream),
fileName, position, length);
else else
return false; SDL_memset(stream, 0, len);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::stopWav() bool SoundSDL2::playWav(const string& fileName, uInt32 position, uInt32 length)
{
const char* const device = myDeviceId
? myDevices.at(myDeviceId).first.c_str()
: nullptr;
return myWavHandler.play(fileName, device, position, length);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::stopWav()
{ {
myWavHandler.stop(); myWavHandler.stop();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundSDL::wavSize() const uInt32 SoundSDL2::wavSize() const
{ {
return myWavHandler.size(); return myWavHandler.size();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::WavHandler::play(SDL_AudioDeviceID device, bool SoundSDL2::WavHandlerSDL2::play(
const string& fileName, uInt32 position, uInt32 length) const string& fileName, const char* device, uInt32 position, uInt32 length)
{ {
// Load WAV file // Load WAV file
if(fileName != myFilename || myBuffer == nullptr) if(fileName != myFilename || myBuffer == nullptr)
{ {
if(myBuffer) if(myBuffer)
{ {
SDL_free(myBuffer); SDL_FreeWAV(myBuffer);
myBuffer = nullptr; myBuffer = nullptr;
} }
SDL_zero(mySpec); SDL_zero(mySpec);
if(!SDL_LoadWAV(fileName.c_str(), &mySpec, &myBuffer, &myLength)) if(SDL_LoadWAV(fileName.c_str(), &mySpec, &myBuffer, &myLength) == nullptr)
return false; return false;
}
// Set the callback function
mySpec.callback = callback;
mySpec.userdata = this;
}
if(position > myLength) if(position > myLength)
return false; return false;
if(myStream)
SDL_UnbindAudioStream(myStream);
myStream = SDL_CreateAudioStream(&mySpec, nullptr);
if(myStream == nullptr)
return false;
if(!SDL_BindAudioStream(device, myStream))
return false;
if(!SDL_SetAudioStreamGetCallback(myStream, WavHandler::wavCallback, this))
return false;
SDL_SetAudioStreamGain(myStream, myVolumeFactor);
myFilename = fileName; myFilename = fileName;
myRemaining = length myRemaining = length
? std::min(length, myLength - position) ? std::min(length, myLength - position)
: myLength; : myLength;
myPos = myBuffer + position; myPos = myBuffer + position;
// Open audio device
if(!myDevice)
{
myDevice = SDL_OpenAudioDevice(device, 0, &mySpec, nullptr, 0);
if(!myDevice)
return false;
// Play audio
pause(false);
}
return true; return true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::WavHandler::stop() void SoundSDL2::WavHandlerSDL2::stop()
{ {
if(myBuffer) if(myBuffer)
{ {
// Clean up // Clean up
myRemaining = 0; myRemaining = 0;
SDL_UnbindAudioStream(myStream); myStream = nullptr; SDL_CloseAudioDevice(myDevice); myDevice = 0;
SDL_free(myBuffer); myBuffer = nullptr; SDL_FreeWAV(myBuffer); myBuffer = nullptr;
} }
} if(myCvtBuffer)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::WavHandler::setVolumeFactor(float volumeFactor)
{
myVolumeFactor = volumeFactor;
if(myStream)
SDL_SetAudioStreamGain(myStream, myVolumeFactor);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::WavHandler::pause(bool state) const
{
if(myStream)
{ {
if(state) SDL_PauseAudioStreamDevice(myStream); myCvtBuffer.reset();
else SDL_ResumeAudioStreamDevice(myStream); myCvtBufferSize = 0;
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::WavHandler::wavCallback(void* object, SDL_AudioStream* stream, void SoundSDL2::WavHandlerSDL2::processWav(uInt8* stream, uInt32 len)
int additional_amt, int)
{ {
auto* self = static_cast<WavHandler*>(object); SDL_memset(stream, mySpec.silence, len);
auto len = static_cast<uInt32>(additional_amt); if(myRemaining)
auto& remaining = self->myRemaining;
if(remaining)
{ {
if(self->mySpeed != 1.0) if(mySpeed != 1.0)
len = std::round(len / self->mySpeed); {
const int origLen = len;
len = std::round(len / mySpeed);
const int newFreq =
std::round(static_cast<double>(mySpec.freq) * origLen / len);
if(len > remaining) // NOLINT(readability-use-std-min-max) if(len > myRemaining)
len = remaining; len = myRemaining;
SDL_PutAudioStreamData(stream, self->myPos, len); SDL_AudioCVT cvt;
SDL_BuildAudioCVT(&cvt, mySpec.format, mySpec.channels, mySpec.freq,
mySpec.format, mySpec.channels, newFreq);
SDL_assert(cvt.needed); // Obviously, this one is always needed.
cvt.len = len * mySpec.channels; // Mono 8 bit sample frames
self->myPos += len; if(!myCvtBuffer ||
remaining -= len; myCvtBufferSize < static_cast<uInt32>(cvt.len * cvt.len_mult))
{
myCvtBufferSize = cvt.len * cvt.len_mult;
myCvtBuffer = make_unique<uInt8[]>(myCvtBufferSize);
}
cvt.buf = myCvtBuffer.get();
// Read original data into conversion buffer
SDL_memcpy(cvt.buf, myPos, cvt.len);
SDL_ConvertAudio(&cvt);
// Mix volume adjusted WAV data into silent buffer
SDL_MixAudioFormat(stream, cvt.buf, mySpec.format, cvt.len_cvt,
SDL_MIX_MAXVOLUME * SoundSDL2::myVolumeFactor);
}
else
{
if(len > myRemaining)
len = myRemaining;
// Mix volume adjusted WAV data into silent buffer
SDL_MixAudioFormat(stream, myPos, mySpec.format, len,
SDL_MIX_MAXVOLUME * myVolumeFactor);
}
myPos += len;
myRemaining -= len;
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::WavHandler::~WavHandler() void SoundSDL2::WavHandlerSDL2::callback(void* object, uInt8* stream, int len)
{ {
ASSERT_MAIN_THREAD; static_cast<WavHandlerSDL2*>(object)->processWav(
stream, static_cast<uInt32>(len));
if(myBuffer)
SDL_free(myBuffer);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL2::WavHandlerSDL2::~WavHandlerSDL2()
{
if(myDevice)
{
SDL_CloseAudioDevice(myDevice);
SDL_FreeWAV(myBuffer);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::WavHandlerSDL2::pause(bool state) const
{
if(myDevice)
SDL_PauseAudioDevice(myDevice, state ? 1 : 0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
float SoundSDL2::myVolumeFactor = 0.F;
#endif // SOUND_SUPPORT #endif // SOUND_SUPPORT

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -17,8 +17,8 @@
#ifdef SOUND_SUPPORT #ifdef SOUND_SUPPORT
#ifndef SOUND_SDL_HXX #ifndef SOUND_SDL2_HXX
#define SOUND_SDL_HXX #define SOUND_SDL2_HXX
class OSystem; class OSystem;
class AudioQueue; class AudioQueue;
@ -36,15 +36,15 @@ class Resampler;
@author Stephen Anthony and Christian Speckner (DirtyHairy) @author Stephen Anthony and Christian Speckner (DirtyHairy)
*/ */
class SoundSDL : public Sound class SoundSDL2 : public Sound
{ {
public: public:
/** /**
Create a new sound object. The init method must be invoked before Create a new sound object. The init method must be invoked before
using the object. using the object.
*/ */
SoundSDL(OSystem& osystem, AudioSettings& audioSettings); SoundSDL2(OSystem& osystem, AudioSettings& audioSettings);
~SoundSDL() override; ~SoundSDL2() override;
public: public:
/** /**
@ -92,10 +92,9 @@ class SoundSDL : public Sound
volume is given as a range from 0 to 100 (0 indicates mute). Values volume is given as a range from 0 to 100 (0 indicates mute). Values
outside this range indicate that the volume shouldn't be changed at all. outside this range indicate that the volume shouldn't be changed at all.
@param volume The new volume level for the sound device @param volume The new volume level for the sound device
@param persist Whether to save the volume change to settings
*/ */
void setVolume(uInt32 volume, bool persist = true) override; void setVolume(uInt32 volume) override;
/** /**
Adjusts the volume of the sound device based on the given direction. Adjusts the volume of the sound device based on the given direction.
@ -134,6 +133,13 @@ class SoundSDL : public Sound
uInt32 wavSize() const override; uInt32 wavSize() const override;
private: private:
/**
This method is called to query the audio devices.
@param devices List of device names
*/
void queryHardware(VariantList& devices) override;
/** /**
The actual sound device is opened only when absolutely necessary. The actual sound device is opened only when absolutely necessary.
Typically this will only happen once per program run, but it can also Typically this will only happen once per program run, but it can also
@ -150,14 +156,10 @@ class SoundSDL : public Sound
bool myIsInitializedFlag{false}; bool myIsInitializedFlag{false};
// Audio specification structure // Audio specification structure
SDL_AudioSpec mySpec{}; SDL_AudioSpec myHardwareSpec{};
// Audio device and stream, which handles all interaction with SDL sound backend SDL_AudioDeviceID myDevice{0};
SDL_AudioDeviceID myDevice{}; uInt32 myDeviceId{0};
SDL_AudioStream* myStream{nullptr};
// Audio buffer, passed to the audio callback and filled from the resampler
std::vector<uInt8> myBuffer;
shared_ptr<AudioQueue> myAudioQueue; shared_ptr<AudioQueue> myAudioQueue;
unique_ptr<Resampler> myResampler; unique_ptr<Resampler> myResampler;
@ -167,66 +169,65 @@ class SoundSDL : public Sound
Int16* myCurrentFragment{nullptr}; Int16* myCurrentFragment{nullptr};
bool myUnderrun{false}; bool myUnderrun{false};
float myVolumeFactor{1.F}; // Current volume level (0.F - 1.F)
string myAboutString; string myAboutString;
/** /**
This class implements WAV file playback using the SDL sound API. This class implements WAV file playback using the SDL2 sound API.
*/ */
class WavHandler class WavHandlerSDL2
{ {
public: public:
explicit WavHandler() = default; explicit WavHandlerSDL2() = default;
~WavHandler(); ~WavHandlerSDL2();
bool play(SDL_AudioDeviceID device, const string& fileName, bool play(const string& fileName, const char* device,
uInt32 position, uInt32 length); uInt32 position, uInt32 length);
void stop(); void stop();
uInt32 size() const { return myBuffer ? myRemaining : 0; } uInt32 size() const { return myBuffer ? myRemaining : 0; }
void setSpeed(double speed) { mySpeed = speed; }
void setVolumeFactor(float volumeFactor); void setSpeed(const double speed) { mySpeed = speed; }
void pause(bool state) const; void pause(bool state) const;
private: private:
string myFilename; string myFilename;
SDL_AudioStream* myStream{nullptr};
SDL_AudioSpec mySpec{};
uInt8* myBuffer{nullptr};
uInt32 myLength{0}; uInt32 myLength{0};
SDL_AudioDeviceID myDevice{0};
uInt8* myBuffer{nullptr};
double mySpeed{1.0}; double mySpeed{1.0};
float myVolumeFactor{1.F}; // Current volume level (0.F - 1.F) unique_ptr<uInt8[]> myCvtBuffer;
uInt32 myCvtBufferSize{0};
SDL_AudioSpec mySpec{}; // audio output format
uInt8* myPos{nullptr}; // pointer to the audio buffer to be played uInt8* myPos{nullptr}; // pointer to the audio buffer to be played
uInt32 myRemaining{0}; // remaining length of the sample we have to play uInt32 myRemaining{0}; // remaining length of the sample we have to play
private: private:
// Callback function invoked by the SDL Audio library when it needs data // Callback function invoked by the SDL Audio library when it needs data
static void wavCallback(void* object, SDL_AudioStream* stream, void processWav(uInt8* stream, uInt32 len);
int additional_amt, int); static void callback(void* object, uInt8* stream, int len);
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
WavHandler(const WavHandler&) = delete; WavHandlerSDL2(const WavHandlerSDL2&) = delete;
WavHandler(WavHandler&&) = delete; WavHandlerSDL2(WavHandlerSDL2&&) = delete;
WavHandler& operator=(const WavHandler&) = delete; WavHandlerSDL2& operator=(const WavHandlerSDL2&) = delete;
WavHandler& operator=(WavHandler&&) = delete; WavHandlerSDL2& operator=(WavHandlerSDL2&&) = delete;
}; };
WavHandler myWavHandler; WavHandlerSDL2 myWavHandler;
static float myVolumeFactor; // Current volume level (0 - 100)
private: private:
// Callback functions invoked by the SDL Audio library when it needs data // Callback functions invoked by the SDL Audio library when it needs data
static void audioCallback(void* object, SDL_AudioStream* stream, static void callback(void* object, uInt8* stream, int len);
int additional_amt, int);
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
SoundSDL() = delete; SoundSDL2() = delete;
SoundSDL(const SoundSDL&) = delete; SoundSDL2(const SoundSDL2&) = delete;
SoundSDL(SoundSDL&&) = delete; SoundSDL2(SoundSDL2&&) = delete;
SoundSDL& operator=(const SoundSDL&) = delete; SoundSDL2& operator=(const SoundSDL2&) = delete;
SoundSDL& operator=(SoundSDL&&) = delete; SoundSDL2& operator=(SoundSDL2&&) = delete;
}; };
#endif // SOUND_SDL_HXX #endif
#endif // SOUND_SUPPORT #endif // SOUND_SUPPORT

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -116,9 +116,7 @@ void StaggeredLogger::startInterval()
Int64 msecSinceLastIntervalEnd = Int64 msecSinceLastIntervalEnd =
duration_cast<duration<Int64, std::milli>>(now - myLastIntervalEndTimestamp).count(); duration_cast<duration<Int64, std::milli>>(now - myLastIntervalEndTimestamp).count();
while (std::cmp_greater(msecSinceLastIntervalEnd, myCooldownTime) && while (msecSinceLastIntervalEnd > myCooldownTime && myCurrentIntervalFactor > 1) {
myCurrentIntervalFactor > 1)
{
msecSinceLastIntervalEnd -= myCooldownTime; msecSinceLastIntervalEnd -= myCooldownTime;
decreaseInterval(); decreaseInterval();
} }

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -18,7 +18,7 @@
#ifndef STATE_MANAGER_HXX #ifndef STATE_MANAGER_HXX
#define STATE_MANAGER_HXX #define STATE_MANAGER_HXX
#define STATE_HEADER "07000001state" #define STATE_HEADER "07000000state"
class OSystem; class OSystem;
class RewindManager; class RewindManager;

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -221,16 +221,16 @@ enum StellaKey // NOLINT: use 32-bit, even though 16-bit is sufficient
KBDK_F23 = 114, KBDK_F23 = 114,
KBDK_F24 = 115, KBDK_F24 = 115,
KBDK_EXECUTE = 116, KBDK_EXECUTE = 116,
KBDK_HELP = 117, /**< AL Integrated Help Center */ KBDK_HELP = 117,
KBDK_MENU = 118, /**< Menu (show menu) */ KBDK_MENU = 118,
KBDK_SELECT = 119, KBDK_SELECT = 119,
KBDK_STOP = 120, /**< AC Stop */ KBDK_STOP = 120,
KBDK_AGAIN = 121, /**< AC Redo/Repeat */ KBDK_AGAIN = 121, /**< redo */
KBDK_UNDO = 122, /**< AC Undo */ KBDK_UNDO = 122,
KBDK_CUT = 123, /**< AC Cut */ KBDK_CUT = 123,
KBDK_COPY = 124, /**< AC Copy */ KBDK_COPY = 124,
KBDK_PASTE = 125, /**< AC Paste */ KBDK_PASTE = 125,
KBDK_FIND = 126, /**< AC Find */ KBDK_FIND = 126,
KBDK_MUTE = 127, KBDK_MUTE = 127,
KBDK_VOLUMEUP = 128, KBDK_VOLUMEUP = 128,
KBDK_VOLUMEDOWN = 129, KBDK_VOLUMEDOWN = 129,
@ -261,9 +261,9 @@ enum StellaKey // NOLINT: use 32-bit, even though 16-bit is sufficient
KBDK_LANG8 = 151, /**< reserved */ KBDK_LANG8 = 151, /**< reserved */
KBDK_LANG9 = 152, /**< reserved */ KBDK_LANG9 = 152, /**< reserved */
KBDK_ALTERASE = 153, /**< Erase-Eaze */ KBDK_ALTERASE = 153, /**< Erase-Eaze */
KBDK_SYSREQ = 154, KBDK_SYSREQ = 154,
KBDK_CANCEL = 155, /**< AC Cancel */ KBDK_CANCEL = 155,
KBDK_CLEAR = 156, KBDK_CLEAR = 156,
KBDK_PRIOR = 157, KBDK_PRIOR = 157,
KBDK_RETURN2 = 158, KBDK_RETURN2 = 158,
@ -330,10 +330,10 @@ enum StellaKey // NOLINT: use 32-bit, even though 16-bit is sufficient
KBDK_RALT = 230, /**< alt gr, option */ KBDK_RALT = 230, /**< alt gr, option */
KBDK_RGUI = 231, /**< windows, command (apple), meta */ KBDK_RGUI = 231, /**< windows, command (apple), meta */
KBDK_MODE = 257, /**< I'm not sure if this is really not covered KBDK_MODE = 257, /**< ALT-GR(aph) key on non-American keyboards
* by any of the above, but since there's a * This is like pressing KBDK_RALT + KBDK_RCTRL
* special SDL_KMOD_MODE for it I'm adding it here * on some keyboards
*/ */
/* @} *//* Usage page 0x07 */ /* @} *//* Usage page 0x07 */
@ -341,98 +341,77 @@ enum StellaKey // NOLINT: use 32-bit, even though 16-bit is sufficient
* \name Usage page 0x0C * \name Usage page 0x0C
* *
* These values are mapped from usage page 0x0C (USB consumer page). * These values are mapped from usage page 0x0C (USB consumer page).
*
* There are way more keys in the spec than we can represent in the
* current scancode range, so pick the ones that commonly come up in
* real world usage.
*/ */
/* @{ */ /* @{ */
KBDK_SLEEP = 258, /**< Sleep */ KBDK_AUDIONEXT = 258,
KBDK_WAKE = 259, /**< Wake */ KBDK_AUDIOPREV = 259,
KBDK_AUDIOSTOP = 260,
KBDK_CHANNEL_INCREMENT = 260, /**< Channel Increment */ KBDK_AUDIOPLAY = 261,
KBDK_CHANNEL_DECREMENT = 261, /**< Channel Decrement */ KBDK_AUDIOMUTE = 262,
KBDK_MEDIASELECT = 263,
KBDK_MEDIA_PLAY = 262, /**< Play */ KBDK_WWW = 264,
KBDK_MEDIA_PAUSE = 263, /**< Pause */ KBDK_MAIL = 265,
KBDK_MEDIA_RECORD = 264, /**< Record */ KBDK_CALCULATOR = 266,
KBDK_MEDIA_FAST_FORWARD = 265, /**< Fast Forward */ KBDK_COMPUTER = 267,
KBDK_MEDIA_REWIND = 266, /**< Rewind */ KBDK_AC_SEARCH = 268,
KBDK_MEDIA_NEXT_TRACK = 267, /**< Next Track */ KBDK_AC_HOME = 269,
KBDK_MEDIA_PREVIOUS_TRACK = 268, /**< Previous Track */ KBDK_AC_BACK = 270,
KBDK_MEDIA_STOP = 269, /**< Stop */ KBDK_AC_FORWARD = 271,
KBDK_MEDIA_EJECT = 270, /**< Eject */ KBDK_AC_STOP = 272,
KBDK_MEDIA_PLAY_PAUSE = 271, /**< Play / Pause */ KBDK_AC_REFRESH = 273,
KBDK_MEDIA_SELECT = 272, /* Media Select */ KBDK_AC_BOOKMARKS = 274,
KBDK_AC_NEW = 273, /**< AC New */
KBDK_AC_OPEN = 274, /**< AC Open */
KBDK_AC_CLOSE = 275, /**< AC Close */
KBDK_AC_EXIT = 276, /**< AC Exit */
KBDK_AC_SAVE = 277, /**< AC Save */
KBDK_AC_PRINT = 278, /**< AC Print */
KBDK_AC_PROPERTIES = 279, /**< AC Properties */
KBDK_AC_SEARCH = 280, /**< AC Search */
KBDK_AC_HOME = 281, /**< AC Home */
KBDK_AC_BACK = 282, /**< AC Back */
KBDK_AC_FORWARD = 283, /**< AC Forward */
KBDK_AC_STOP = 284, /**< AC Stop */
KBDK_AC_REFRESH = 285, /**< AC Refresh */
KBDK_AC_BOOKMARKS = 286, /**< AC Bookmarks */
/* @} *//* Usage page 0x0C */ /* @} *//* Usage page 0x0C */
/** /**
* \name Mobile keys * \name Walther keys
* *
* These are values that are often used on mobile phones. * These are values that Christian Walther added (for mac keyboard?).
*/ */
/* @{ */ /* @{ */
KBDK_SOFTLEFT = 287, /**< Usually situated below the display on phones and KBDK_BRIGHTNESSDOWN = 275,
used as a multi-function feature key for selecting KBDK_BRIGHTNESSUP = 276,
a software defined function shown on the bottom left KBDK_DISPLAYSWITCH = 277, /**< display mirroring/dual display
of the display. */ switch, video mode switch */
KBDK_SOFTRIGHT = 288, /**< Usually situated below the display on phones and KBDK_KBDILLUMTOGGLE = 278,
used as a multi-function feature key for selecting KBDK_KBDILLUMDOWN = 279,
a software defined function shown on the bottom right KBDK_KBDILLUMUP = 280,
of the display. */ KBDK_EJECT = 281,
KBDK_CALL = 289, /**< Used for accepting phone calls. */ KBDK_SLEEP = 282,
KBDK_ENDCALL = 290, /**< Used for rejecting phone calls. */
/* @} *//* Mobile keys */ KBDK_APP1 = 283,
KBDK_APP2 = 284,
/* @} *//* Walther keys */
/* Add any other keys here. */ /* Add any other keys here. */
KBDK_RESERVED = 400, /**< 400-500 reserved for dynamic keycodes */ KBDK_LAST = 512 /**< not a key, just marks the number of scancodes
for array bounds */
KBDK_COUNT = 512 /**< not a key, just marks the number of scancodes for array bounds */
}; };
// This comes directly from SDL_keycode.h // This comes directly from SDL_keycode.h
enum StellaMod: uInt16 enum StellaMod: uInt16
{ {
KBDM_NONE = 0x0000U, /**< no modifier is applicable. */ KBDM_NONE = 0x0000,
KBDM_LSHIFT = 0x0001U, /**< the left Shift key is down. */ KBDM_LSHIFT = 0x0001,
KBDM_RSHIFT = 0x0002U, /**< the right Shift key is down. */ KBDM_RSHIFT = 0x0002,
KBDM_LEVEL5 = 0x0004U, /**< the Level 5 Shift key is down. */ KBDM_LCTRL = 0x0040,
KBDM_LCTRL = 0x0040U, /**< the left Ctrl (Control) key is down. */ KBDM_RCTRL = 0x0080,
KBDM_RCTRL = 0x0080U, /**< the right Ctrl (Control) key is down. */ KBDM_LALT = 0x0100,
KBDM_LALT = 0x0100U, /**< the left Alt key is down. */ KBDM_RALT = 0x0200,
KBDM_RALT = 0x0200U, /**< the right Alt key is down. */ KBDM_LGUI = 0x0400,
KBDM_LGUI = 0x0400U, /**< the left GUI key (often the Windows key) is down. */ KBDM_RGUI = 0x0800,
KBDM_RGUI = 0x0800U, /**< the right GUI key (often the Windows key) is down. */ KBDM_NUM = 0x1000,
KBDM_NUM = 0x1000U, /**< the Num Lock key (may be located on an extended keypad) is down. */ KBDM_CAPS = 0x2000,
KBDM_CAPS = 0x2000U, /**< the Caps Lock key is down. */ KBDM_MODE = 0x4000,
KBDM_MODE = 0x4000U, /**< the !AltGr key is down. */ KBDM_RESERVED = 0x8000,
KBDM_SCROLL = 0x8000U, /**< the Scroll Lock key is down. */ KBDM_CTRL = (KBDM_LCTRL|KBDM_RCTRL),
KBDM_CTRL = (KBDM_LCTRL | KBDM_RCTRL), /**< Any Ctrl key is down. */ KBDM_SHIFT = (KBDM_LSHIFT|KBDM_RSHIFT),
KBDM_SHIFT = (KBDM_LSHIFT | KBDM_RSHIFT), /**< Any Shift key is down. */ KBDM_ALT = (KBDM_LALT|KBDM_RALT),
KBDM_ALT = (KBDM_LALT | KBDM_RALT), /**< Any Alt key is down. */ KBDM_GUI = (KBDM_LGUI|KBDM_RGUI)
KBDM_GUI = (KBDM_LGUI | KBDM_RGUI) /**< Any GUI key is down. */
}; };
// Test if specified modifier is pressed // Test if specified modifier is pressed
@ -463,7 +442,7 @@ namespace StellaKeyName
inline string_view forKey(StellaKey key) inline string_view forKey(StellaKey key)
{ {
#ifdef SDL_SUPPORT #ifdef SDL_SUPPORT
return SDL_GetScancodeName(static_cast<SDL_Scancode>(key)); return SDL_GetScancodeName(SDL_Scancode(key));
#else #else
return string_view{}; return string_view{};
#endif #endif

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -18,7 +18,7 @@
#ifndef VERSION_HXX #ifndef VERSION_HXX
#define VERSION_HXX #define VERSION_HXX
#define STELLA_VERSION "8.0_pre" #define STELLA_VERSION "7.0"
#define STELLA_BUILD "8005" #define STELLA_BUILD "8005"
#endif #endif

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -29,17 +29,17 @@ void VideoModeHandler::setImageSize(const Common::Size& image)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void VideoModeHandler::setDisplaySize(const Common::Size& display, bool fullscreen) void VideoModeHandler::setDisplaySize(const Common::Size& display, Int32 fsIndex)
{ {
myDisplay = display; myDisplay = display;
myFullscreen = fullscreen; myFSIndex = fsIndex;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const VideoModeHandler::Mode& const VideoModeHandler::Mode&
VideoModeHandler::buildMode(const Settings& settings, bool inTIAMode, Bezel::Info bezelInfo) VideoModeHandler::buildMode(const Settings& settings, bool inTIAMode, Bezel::Info bezelInfo)
{ {
const bool windowedRequested = !myFullscreen; const bool windowedRequested = myFSIndex == -1;
// TIA mode allows zooming at non-integral factors in most cases // TIA mode allows zooming at non-integral factors in most cases
if(inTIAMode) if(inTIAMode)
@ -53,7 +53,7 @@ const VideoModeHandler::Mode&
// Image and screen (aka window) dimensions are the same // Image and screen (aka window) dimensions are the same
// Overscan is not applicable in this mode // Overscan is not applicable in this mode
myMode = Mode(myImage.w, myImage.h, myMode = Mode(myImage.w, myImage.h,
Mode::Stretch::Fill, myFullscreen, Mode::Stretch::Fill, myFSIndex,
desc.view(), zoom, bezelInfo); desc.view(), zoom, bezelInfo);
} }
else else
@ -74,7 +74,7 @@ const VideoModeHandler::Mode&
{ {
myMode = Mode(myImage.w, myImage.h, myMode = Mode(myImage.w, myImage.h,
myDisplay.w, myDisplay.h, myDisplay.w, myDisplay.h,
Mode::Stretch::Preserve, myFullscreen, Mode::Stretch::Preserve, myFSIndex,
"Fullscreen: Preserve aspect, no stretch", "Fullscreen: Preserve aspect, no stretch",
zoom, overscan, bezelInfo); zoom, overscan, bezelInfo);
} }
@ -82,7 +82,7 @@ const VideoModeHandler::Mode&
{ {
myMode = Mode(myImage.w, myImage.h, myMode = Mode(myImage.w, myImage.h,
myDisplay.w, myDisplay.h, myDisplay.w, myDisplay.h,
Mode::Stretch::Fill, myFullscreen, Mode::Stretch::Fill, myFSIndex,
"Fullscreen: Ignore aspect, full stretch", "Fullscreen: Ignore aspect, full stretch",
zoom, overscan, bezelInfo); zoom, overscan, bezelInfo);
} }
@ -94,31 +94,31 @@ const VideoModeHandler::Mode&
myMode = Mode(myImage.w, myImage.h, Mode::Stretch::None); myMode = Mode(myImage.w, myImage.h, Mode::Stretch::None);
else else
myMode = Mode(myImage.w, myImage.h, myDisplay.w, myDisplay.h, myMode = Mode(myImage.w, myImage.h, myDisplay.w, myDisplay.h,
Mode::Stretch::None, myFullscreen); Mode::Stretch::None, myFSIndex);
} }
return myMode; return myMode;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, Stretch smode, VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, Stretch smode,
bool fullscreen, string_view desc, Int32 fsindex, string_view desc,
double zoomLevel, Bezel::Info bezelInfo) double zoomLevel, Bezel::Info bezelInfo)
: Mode(iw, ih, iw, ih, smode, fullscreen, desc, zoomLevel, 1., bezelInfo) : Mode(iw, ih, iw, ih, smode, fsindex, desc, zoomLevel, 1., bezelInfo)
{ {
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh, VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh,
Stretch smode, bool fullscreen, string_view desc, Stretch smode, Int32 fsindex, string_view desc,
double zoomLevel, double overscan, Bezel::Info bezelInfo) double zoomLevel, double overscan, Bezel::Info bezelInfo)
: screenS{sw, sh}, : screenS{sw, sh},
stretch{smode}, stretch{smode},
description{desc}, description{desc},
zoom{zoomLevel}, //hZoom{zoomLevel}, vZoom{zoomLevel}, zoom{zoomLevel}, //hZoom{zoomLevel}, vZoom{zoomLevel},
fullscreen{fullscreen} fsIndex{fsindex}
{ {
// Now resize based on windowed/fullscreen mode and stretch factor // Now resize based on windowed/fullscreen mode and stretch factor
if(fullscreen) if(fsIndex != -1) // fullscreen mode
{ {
switch(stretch) switch(stretch)
{ {
@ -174,9 +174,9 @@ VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh,
const uInt32 wx = bezelInfo.window().x() * iw / bezelInfo.window().w(); const uInt32 wx = bezelInfo.window().x() * iw / bezelInfo.window().w();
const uInt32 wy = bezelInfo.window().y() * ih / bezelInfo.window().h(); const uInt32 wy = bezelInfo.window().y() * ih / bezelInfo.window().h();
const uInt32 bezelW = std::min(screenS.w, const uInt32 bezelW = std::min(screenS.w,
static_cast<uInt32>(std::round(iw * bezelInfo.ratioW()))); static_cast<uInt32>(std::round(iw * bezelInfo.ratioW())));
const uInt32 bezelH = std::min(screenS.h, const uInt32 bezelH = std::min(screenS.h,
static_cast<uInt32>(std::round(ih * bezelInfo.ratioH()))); static_cast<uInt32>(std::round(ih * bezelInfo.ratioH())));
// Center image (no bezel) or move image relative to centered bezel // Center image (no bezel) or move image relative to centered bezel
imageR.moveTo(((screenS.w - bezelW) >> 1) + wx, ((screenS.h - bezelH) >> 1) + wy); imageR.moveTo(((screenS.w - bezelW) >> 1) + wx, ((screenS.h - bezelH) >> 1) + wy);

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -46,14 +46,14 @@ class VideoModeHandler
Stretch stretch{Mode::Stretch::None}; Stretch stretch{Mode::Stretch::None};
string description; string description;
double zoom{1.}; double zoom{1.};
bool fullscreen{false}; // false indicates windowed mode Int32 fsIndex{-1}; // -1 indicates windowed mode
Mode() = default; Mode() = default;
Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh, Stretch smode, Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh, Stretch smode,
bool fullscreen = false, string_view desc = "", Int32 fsindex = -1, string_view desc = "",
double zoomLevel = 1., double overscan = 1., double zoomLevel = 1., double overscan = 1.,
Bezel::Info bezelInfo = Bezel::Info()); Bezel::Info bezelInfo = Bezel::Info());
Mode(uInt32 iw, uInt32 ih, Stretch smode, bool fullscreen = false, Mode(uInt32 iw, uInt32 ih, Stretch smode, Int32 fsindex = -1,
string_view desc = "", double zoomLevel = 1., string_view desc = "", double zoomLevel = 1.,
Bezel::Info bezelInfo = Bezel::Info()); Bezel::Info bezelInfo = Bezel::Info());
@ -63,7 +63,7 @@ class VideoModeHandler
<< " stretch=" << (vm.stretch == Stretch::Preserve ? "preserve" : << " stretch=" << (vm.stretch == Stretch::Preserve ? "preserve" :
vm.stretch == Stretch::Fill ? "fill" : "none") vm.stretch == Stretch::Fill ? "fill" : "none")
<< " desc=" << vm.description << " zoom=" << vm.zoom << " desc=" << vm.description << " zoom=" << vm.zoom
<< " fullscreen= " << vm.fullscreen; << " fsIndex= " << vm.fsIndex;
return os; return os;
} }
}; };
@ -84,10 +84,10 @@ class VideoModeHandler
Set the size of the display. This could be either the desktop size, Set the size of the display. This could be either the desktop size,
or the size of the monitor currently active. or the size of the monitor currently active.
@param display The dimensions of the enclosing display @param display The dimensions of the enclosing display
@param fullscreen Whether to use fullscreen or windowed mode @param fsIndex Fullscreen mode in use (-1 indicates windowed mode)
*/ */
void setDisplaySize(const Common::Size& display, bool fullscreen); void setDisplaySize(const Common::Size& display, Int32 fsIndex = -1);
/** /**
Build a video mode based on the given parameters, assuming that Build a video mode based on the given parameters, assuming that
@ -103,7 +103,7 @@ class VideoModeHandler
private: private:
Common::Size myImage, myDisplay; Common::Size myImage, myDisplay;
bool myFullscreen{false}; Int32 myFSIndex{-1};
Mode myMode; Mode myMode;

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
@ -266,7 +266,8 @@ void ZipHandler::ZipFile::readEcd()
uInt64 read_length = 0; uInt64 read_length = 0;
// Max out the buf length at the size of the file // Max out the buf length at the size of the file
buflen = std::min(buflen, myLength); if(buflen > myLength)
buflen = myLength;
// Allocate buffer // Allocate buffer
const ByteBuffer buffer = make_unique<uInt8[]>(buflen + 1); const ByteBuffer buffer = make_unique<uInt8[]>(buflen + 1);

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

View File

@ -8,7 +8,7 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // SSSS ttt eeeee llll llll aaaaa
// //
// Copyright (c) 1995-2025 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
// and the Stella Team // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of

Some files were not shown because too many files have changed in this diff Show More