Compare commits

..

123 Commits
7.0b ... master

Author SHA1 Message Date
Stephen Anthony 211b4902d5 Fix log message being output incorrectly based on log level. 2025-07-28 19:46:03 -02:30
Stephen Anthony d2cb9d3011 Revert palette to use ARGB8888, which allows the snapshot and libretro
code to work without any further changes.
2025-07-27 23:21:32 -02:30
Stephen Anthony c73637bc3d Merge branch 'feature/sdl3-conversion' 2025-07-27 18:26:39 -02:30
Stephen Anthony b1cfe7a486 Minor fix from clang-tidy. 2025-07-25 17:16:56 -02:30
Stephen Anthony c6ce6e80e8 Updated included sqlite library to latest release. 2025-06-26 19:19:12 -02:30
hunterk c4afcbae32
small libretro follow-on fixes (#1085)
* add missing gitignore

* split paddles and rearrange core option
2025-06-26 12:55:06 -02:30
Stephen Anthony f0eec9b53a Merge branch 'master' into feature/sdl3-conversion 2025-06-26 10:24:05 -02:30
hunterk 6f8def5b3d
libretro additions and fixes (#1083) 2025-06-26 10:21:19 -02:30
Stephen Anthony 878851ab61 Merge branch 'master' into feature/sdl3-conversion 2025-06-23 15:08:40 -02:30
thrust26 6ce835b3e6 added extra buttons to right Paddle A too 2025-06-23 09:06:18 +02:00
thrust26 ac57149ebd added extra buttons to Paddles and Driving Controller (resolves #1082) 2025-06-22 22:30:56 +02:00
Stephen Anthony 6f4c2f7bf6 Merge branch 'master' into feature/sdl3-conversion 2025-06-20 13:54:16 -02:30
Christian Speckner 54c3c197ab Properly initialise the shadow registers for HMxy. 2025-06-19 19:48:49 +02:00
Stephen Anthony a61325f195 Merge branch 'master' into feature/sdl3-conversion 2025-06-13 18:17:43 -02:30
Stephen Anthony 6a251308e8 Fix minor warnings from clang-tidy. 2025-06-13 18:17:05 -02:30
Greg Kennedy ee0b96cd87
De-duplicate some cart names (#1080) 2025-06-12 13:01:43 +00:00
Stephen Anthony 69857756e3 Fix error with never reaching code. 2025-06-02 10:14:45 -02:30
Stephen Anthony f198be5d2f Remove UNSAFE_OPTIMIZATIONS from Thumbulator. 2025-06-01 17:34:09 -02:30
Stephen Anthony 3efa52d2c4 Fix typo in method name. 2025-06-01 12:38:00 -02:30
Stephen Anthony 4c7fdf59eb Remove dead code. 2025-06-01 12:33:57 -02:30
Stephen Anthony 6868abec68 Fixed WAV playback; KidVid games work again. 2025-05-31 18:46:54 -02:30
thrust26 9eac43823d update doc 2025-05-31 11:49:08 +02:00
thrust26 8581c7c54b updated doc (fixes #959) 2025-05-31 11:15:49 +02:00
thrust26 df3c9838b8 trying to fix Trackball mouse barriers (#1079), but mouse becomes sluggish now. 2025-05-31 10:47:43 +02:00
Stephen Anthony 644dc07604 Removed R77 support.
Did it all in one commit, so it can be reverted if necessary.
But I don't think we will, since SDL3 will not support that device.
2025-05-30 15:27:10 -02:30
thrust26 eea8c368f9 fixed mangled logging of available display modes
changed version to 8.0pre
2025-05-30 19:41:45 +02:00
Stephen Anthony 2b1d57b48e Fix some compile warnings, and also errors for libretro. 2025-05-30 13:12:38 -02:30
thrust26 b2b5db7034 fixed text events and QWERTY detection (see #978) 2025-05-30 15:27:14 +02:00
thrust26 59b236e493 fixed fullscreen refresh rate adaption (see #978)
fixed logging of available video modes
2025-05-30 14:00:22 +02:00
thrust26 857f133614 Merge branch 'feature/sdl3-conversion' of https://github.com/stella-emu/stella into feature/sdl3-conversion 2025-05-30 09:18:00 +02:00
thrust26 99880d50b6 added option for switching Stella's theme in sync with OS (TODO: Doc)
updated .gitignore
2025-05-30 09:17:51 +02:00
Christian Speckner bf3995a566 Fix pkgconfig. SIgh. 2025-05-29 22:55:36 +02:00
Christian Speckner 11c2776508 Add the actual lib for the linker. 2025-05-29 22:35:14 +02:00
Christian Speckner b209f56df2 Fix paths. 2025-05-29 22:25:53 +02:00
Christian Speckner 351c1c9dd1 Ups. 2025-05-29 22:22:37 +02:00
Christian Speckner b9250e2c9c A stab at fixing linux. 2025-05-29 22:19:31 +02:00
Christian Speckner 18ce2ebe42 Hrmpf. 2025-05-29 21:53:03 +02:00
Christian Speckner 4786e8e277 Update CI workflow to build via XCode. 2025-05-29 21:49:58 +02:00
Christian Speckner 236b6abea1 Fix SDL3 in XCode. 2025-05-29 21:21:43 +02:00
Christian Speckner 96d986b7b4 Consistency. 2025-05-29 20:23:06 +02:00
Christian Speckner 4a6c799d1c Adjust to changed color order. 2025-05-29 20:19:18 +02:00
Christian Speckner 6c851ca672 Fix blitting surfaces without alpha blending. 2025-05-29 20:12:17 +02:00
Christian Speckner d466022bcd Fix undefined behavior for rsync during hblank. 2025-05-29 10:53:42 +02:00
Stephen Anthony 6c815186a6 Some video refactoring, but render-to-texture still not working. 2025-05-27 18:01:13 -02:30
Stephen Anthony d2436a2637 Don't reserve audio buffer if sound is disabled. 2025-05-26 18:14:08 -02:30
Stephen Anthony a4bd8a562b Fixed sound crash in Windows. 2025-05-26 17:10:30 -02:30
Stephen Anthony bd607a37dc Added sound support.
- Still needs more testing, particularly in Windows
- WAV playback not working yet
2025-05-26 13:11:13 -02:30
Stephen Anthony 75d4cff7f9 Attempt to fix auto-builds for SDL3 (not yet working for macOS). 2025-05-23 17:15:08 -02:30
Stephen Anthony d18bde79a3 Merge branch 'feature/sdl3-conversion' of github.com:stella-emu/stella into feature/sdl3-conversion 2025-05-23 16:29:35 -02:30
Stephen Anthony b35d71e10d Remove more SDL2 stuff from various locations. 2025-05-23 14:35:23 -02:30
Stephen Anthony 96d2d44aae Fixes for Xcode project to build with SDL3.
Still doesn't compile since I haven't committed the WIP sound code yet.
2025-05-23 14:26:16 -02:30
Stephen Anthony 6bd7dedeef Merge branch 'master' into feature/sdl3-conversion 2025-05-22 12:56:54 -02:30
Stephen Anthony c9c077aa07 Visual Studio can't find main without a new SDL header file. 2025-05-22 12:20:13 -02:30
thrust26 3d3f223a17 abort dialog rendering if surface is null (fixes #1078) 2025-05-21 22:14:00 +02:00
thrust26 8421af8931 forgot some formatting 2025-05-17 23:31:31 +02:00
thrust26 6a8714fb9b Merge branch 'master' of https://github.com/stella-emu/stella 2025-05-17 23:30:15 +02:00
thrust26 a1e7323abb fixed timer saving (fixes #1077) 2025-05-17 23:29:40 +02:00
Stephen Anthony 916fb88dd8 Fixed joystick support. 2025-05-15 17:17:05 -02:30
Stephen Anthony 0db0774147 Updated Visual Studio project file for building with SDL3.
It doesn't actually compile yet, since I haven't fixed the sound code.
But at least it's not complaining about not finding SDL.
2025-05-15 16:35:56 -02:30
Stephen Anthony c02aa1ea0a First pass at converting to SDL3. Mostly still broken in several ways:
- only works in Linux
- sound, debugger and joystick not working (compiled out)
- QisBlitter not working (issues with render targets)
- adaptable refresh not working (not converted, and unable to test)
- text events not working (so no input in UI textboxes, etc)
- not entirely optimized
- probably some other stuff.

It does create a launcher and selecting a ROM works.  Toggling fullscreen
also works.
2025-05-15 15:52:37 -02:30
Wolfgang Stubig c217990d6d
Fix CM description (#1076) 2025-05-15 14:25:48 +02:00
Stephen Anthony 807b3c8ea1 Fix size conversion warning. 2025-05-12 18:20:00 -02:30
Stephen Anthony b91f0d9299 Fix clang warning. 2025-05-12 17:46:10 -02:30
thrust26 0a2d94ec1d Merge branch 'master' of https://github.com/stella-emu/stella 2025-05-12 21:10:23 +02:00
thrust26 2ee4bbd3ab Fixed Starpath debugging (fixes #1075) 2025-05-12 21:10:00 +02:00
Stephen Anthony 0d660c2be6 Updated included libpng to version 1.6.48. 2025-05-08 16:55:38 -02:30
Stephen Anthony e13b683c52 Updated Xcode project with name changes for SDL2 classes. 2025-04-29 10:46:39 -02:30
Stephen Anthony c34127a536 First pass at SDL3 conversion. For now, just rename classes to 'SDL'. 2025-04-26 22:15:08 -02:30
Stephen Anthony 7b5c19ff5e Fix warning from clang-tidy. 2025-04-19 16:11:51 -02:30
Stephen Anthony 709237c79c Fix minor warning from clang-20. 2025-04-19 12:59:04 -02:30
thrust26 b753f6eefa added joystick detection pattern for RubyQ 2025-03-29 21:14:17 +01:00
Stephen Anthony aaa6c15475 Updated included libpng to v1.6.47. 2025-03-17 15:21:17 -02:30
Stephen Anthony dccefede9b Fix minor warning from clang-tidy.
Test if commit is working on repo after reorg changes.
2025-02-16 13:34:58 -03:30
Christian Speckner 17652d9348 Attempt to fix MacOS CI. 2025-02-16 10:30:44 +01:00
Christian Speckner d9d4ea0ad4 Clarify licensing for PGO ROMs. 2025-02-16 10:24:53 +01:00
thrust26 d67ac42bbc enhanced duplicated local label handling (fixes #1067) 2025-02-13 19:54:43 +01:00
thrust26 60e07663d1 aligned gray PAL colors in standard palette (fixes #1066) 2025-02-09 00:18:09 +01:00
thrust26 4c8740f309 Merge branch 'master' of https://github.com/stella-emu/stella 2025-02-08 20:28:33 +01:00
thrust26 acaeed8322 added mirrors to 3E hotspots
adapted derived 3E+ so that it does not use the mirrors
2025-02-08 20:28:22 +01:00
Stephen Kitt 37e02b5a69 Detect _host_prefix and allow cross pkg-config (#1065)
stella fails to cross build from source on Unix-style platforms,
because ./configure hard codes the build architecture pkg-config and
thus fails finding libraries. It already has a bit of knowledge about
cross compilation and actually considers a _host_prefix for some
tools. Unfortunately, _host_prefix is not yet generally initialized
and it also does not prepend _host_prefix to pkg-config.

This fixes these issues.

Signed-off-by: Stephen Kitt <steve@sk2.org>
Co-authored-by: Helmut Grohne <helmut@subdivi.de>
2025-01-31 21:27:22 +00:00
thrust26 d4f3187ee0 added four more axis to joystick handlers (fixes #1063) 2025-01-17 20:10:42 +01:00
Stephen Anthony 122409e6f8 Include latest required vcruntime libs, so Stella can start again. 2025-01-02 17:49:47 -03:30
thrust26 34e4439729 fixed #1053 2025-01-01 09:50:33 +01:00
Stephen Anthony 9b0c8c18de Updated copyright to 2025. To anyone reading these logs, Happy New Year! 2024-12-31 19:25:08 -03:30
thrust26 108478aaab fixed problems with VS project file 2024-12-31 19:49:48 +01:00
thrust26 175d578c07 changed VS setting for PGO 2024-12-30 15:48:27 +01:00
thrust26 0852d72dc9 Merge branch 'master' of https://github.com/stella-emu/stella 2024-12-30 12:41:18 +01:00
thrust26 e4b4b08c46 fixed a bug where the combination of RAM banks and PlusROM would break a game (this should fix #1058) 2024-12-30 12:41:01 +01:00
Stephen Anthony 0197b406b8 Fixes for minor warnings from clang-tidy. 2024-12-29 17:25:05 -03:30
thrust26 727917773e code cleanup 2024-12-29 12:15:12 +01:00
thrust26 d4b5cf6659 eliminated cycles() method in TIA.hxx 2024-12-29 12:14:52 +01:00
thrust26 56a82682d5 started working on emulating VBLANK during VSYNC 2024-12-29 10:59:14 +01:00
Stephen Anthony 0933f1ae66 More const fixes, this time from suggestions from VS code analyzer. 2024-12-20 19:24:20 -03:30
Stephen Anthony 6dfef64461 Disable warnings from external libs (SDL) in VS code analysis mode. 2024-12-20 16:29:02 -03:30
Stephen Anthony 27b109b483 Fixed non UTF-8 chars. 2024-12-20 14:47:37 -03:30
jfroco abb92327c0 libretro: add reload/next game (#1059)
- Enable reload/next game option in Core options
- Map reload/next game action to X button
2024-12-18 11:41:12 +01:00
thrust26 682cdb0e28 added PAL60 signature check to auto detect display type (resolves #1061) 2024-12-17 23:03:57 +01:00
thrust26 146bec13ee moved loading of config and DASM list & symbols files before executing auto scripts 2024-12-15 22:29:25 +01:00
thrust26 13cf6aaacd slightly enhanced PlusROM display in debugger (keeps last send/receive now) 2024-12-13 09:33:15 +01:00
Stephen Anthony f744e34687 Fix potential uninitialized array access. 2024-12-10 21:23:05 -03:30
thrust26 ac64828880 fixed #1057 2024-12-09 16:09:54 +01:00
thrust26 f6b1d674db improved default NTSC custom palette 2024-12-09 15:24:38 +01:00
Stephen Anthony 6fe0e61332 Use constexpr. 2024-12-05 17:57:24 -03:30
thrust26 991a6b2447 Merge branch 'master' of https://github.com/stella-emu/stella 2024-12-01 09:33:20 +01:00
thrust26 ce37689720 improved 'help' command in debugger prompt (case insensitive now) 2024-12-01 09:33:09 +01:00
Stephen Anthony 18c1b44419 Fix whitespace. 2024-11-17 19:14:20 -03:30
SvOlli 09679fb228 Debian build requires libgtest-dev, but is not specified (#1049)
Co-authored-by: Sven Oliver Moll <svolli@svolli.de>
2024-11-10 20:13:54 +00:00
Stephen Anthony 41fa2922f9 Updated libpng to latest release. 2024-10-31 16:13:50 -02:30
Stephen Anthony e787941351 Updated httplib to latest release. 2024-10-31 16:07:37 -02:30
Stephen Anthony f2164a9637 Remove executable bit on data files. 2024-10-21 14:27:31 -02:30
Eric Warmenhoven 173a87f41b ios/tvos: properly set min supported version (#1048) 2024-10-21 15:30:52 +00:00
thrust26 5e6ef2eaf7 Merge branch 'master' of https://github.com/stella-emu/stella 2024-10-20 09:59:25 +02:00
thrust26 51b8b06e91 added missing "PlusROM support enabled" initialization 2024-10-20 09:59:15 +02:00
Stephen Anthony f77ddc6fee Match style in surrounding code for FileListWidget. 2024-10-19 16:54:47 -02:30
thrust26 6b982c8325 added PlusROM support developer option (fixes #1019) 2024-10-19 10:01:16 +02:00
thrust26 c9311529cb fixed #1047 2024-10-17 15:48:16 +02:00
Stephen Anthony 594380b2f9 Fix doc to match commit to webpage docs. 2024-10-17 10:21:29 -02:30
Stephen Anthony c003a21362 Update docs; macOS 10.13 is the minimum supported version. 2024-10-10 17:03:18 -02:30
Stephen Anthony ec8a8df269 For Windows build, remove *all* DLL's before install, and only install
SDL2.dll.
2024-10-07 17:05:27 -02:30
Stephen Anthony 3faee00fa7 Re-add ZIP build for Windows. 2024-10-07 16:28:50 -02:30
Stephen Anthony 02e0ff625b Fix misplaced PNG files. 2024-10-07 16:23:31 -02:30
Stephen Anthony dded9debdb Fixes from clang-tidy-19 that didn't make it for the last release. 2024-10-06 20:27:25 -02:30
Stephen Anthony 5d034cec3c Automatically remove old DLL's for Windows install.
This fixes exe not starting with latest release.
2024-10-06 20:25:59 -02:30
854 changed files with 24975 additions and 15977 deletions

View File

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

3
.gitignore vendored
View File

@ -5,6 +5,7 @@ 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
@ -25,6 +26,7 @@ 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
@ -43,3 +45,4 @@ 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.12 and above : * Binary distribution for macOS 10.13 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,6 +12,20 @@
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-2024 by Bradford W. Mott, Stephen Anthony and the Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony ## Copyright (c) 1995-2025 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 (e.g. call sdl2-config) # * detect whether the chosen backend is available
# * .... # * ....
@ -45,6 +45,7 @@ _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
@ -53,7 +54,6 @@ _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,20 +335,22 @@ 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
# #
@ -553,14 +555,6 @@ 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"
@ -626,6 +620,9 @@ 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
# #
@ -639,7 +636,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"
@ -665,7 +662,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"
@ -689,7 +686,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"
@ -808,8 +805,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"
@ -836,15 +833,18 @@ 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,11 +865,6 @@ 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"
@ -922,7 +917,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"
@ -930,7 +925,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"
@ -939,7 +934,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,8 +3,9 @@ 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,
libsdl2-dev, libsdl3-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/
@ -18,7 +19,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 SDL2 Description: Atari 2600 Emulator for SDL
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-2024 Bradford W. Mott, Stephen Anthony and the Stella Team Copyright: 1995-2025 Bradford W. Mott, Stephen Anthony and the Stella Team
License: GPL-2+ License: GPL-2+
Files: * Files: *
Copyright: 1995-2024 Bradford W. Mott, Stephen Anthony and the Stella Copyright: 1995-2025 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-2024 Stephen Kitt <skitt@debian.org> 2010-2025 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.2 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.4 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 7.0</b></h4> <h4><b>Release 8.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 2.0.14 or greater, latest version highly recommended</li> <li>SDL version 3.2.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.11 or above</li> <li>macOS 10.13 or above</li>
<li>64-bit ARM or Intel processor</li> <li>64-bit ARM or Intel processor</li>
<li>Xcode 13.0 is required to compile the Stella source code</li> <li>Xcode 16.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 32/64-bit and endian clean in Linux/Unix, macOS platform where the SDL library exists. It is 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,12 +3249,17 @@
<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 palette/theme for UI elements.</td> <td>Define alternative/light 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 palette/theme for UI elements.</td> <td>Use alternative/dark 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>
@ -3635,6 +3640,9 @@
</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>
@ -4007,8 +4015,9 @@
<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>Theme #1</td><td>Default theme to use for UI elements (see examples)</td><td>-uipalette</td></tr> <tr><td>Light theme</td><td>Default theme to use for UI elements (see examples)</td><td>-uipalette</td></tr>
<tr><td>Theme #2</td><td>Alternative theme to use for UI elements (see examples)</td><td>-uipalette2</td></tr> <tr><td>Dark theme</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>
@ -4576,6 +4585,11 @@
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>
@ -5141,7 +5155,9 @@ 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></td> </table>
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="commandsmenu_r77.png"></p></td> <td><p><img src="graphics/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="basic_settings.png"></p></td> <td><p><img src="graphics/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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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; i < count; ++i) for(int i = 0; std::cmp_less(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; i < count; ++i) for(int i = 0; std::cmp_less(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; i < count; ++i) for(int i = 0; std::cmp_less(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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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(int i = 0; i < count; ++i) for(uInt8 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(int i = 0; i < count; ++i) for(uInt8 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(int i = 0; i < count; ++i) for(uInt8 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,20 +67,6 @@ 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);
@ -115,7 +101,7 @@ uInt32 AudioSettings::sampleRate()
uInt32 AudioSettings::fragmentSize() uInt32 AudioSettings::fragmentSize()
{ {
updatePresetFromSettings(); updatePresetFromSettings();
return customSettings() ? lboundInt(mySettings.getInt(SETTING_FRAGMENT_SIZE), DEFAULT_FRAGMENT_SIZE) : myPresetFragmentSize; return myPresetFragmentSize; // No longer configurable in sound backend
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -155,12 +141,6 @@ 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
{ {
@ -185,7 +165,6 @@ 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;
@ -193,7 +172,6 @@ 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;
@ -201,7 +179,6 @@ 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;
@ -209,7 +186,6 @@ 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;
@ -231,15 +207,6 @@ 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)
{ {
@ -292,14 +259,6 @@ 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,13 +42,11 @@ 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";
@ -132,7 +130,8 @@ class AudioSettings
Preset myPreset{Preset::custom}; Preset myPreset{Preset::custom};
uInt32 myPresetSampleRate{0}; uInt32 myPresetSampleRate{0};
uInt32 myPresetFragmentSize{0}; uInt32 myPresetFragmentSize{1024}; // no longer configurable in sound backend
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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,9 +234,8 @@ 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->attributes().blending = true; mySurface->enableBlend(true);
mySurface->attributes().blendalpha = 100; mySurface->setBlendLevel(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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,6 +46,7 @@ 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");
@ -118,6 +119,7 @@ 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]);
@ -183,6 +185,7 @@ 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,6 +51,7 @@ 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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 "EventHandlerSDL2.hxx" #include "EventHandlerSDL.hxx"
#include "ThreadDebugging.hxx" #include "ThreadDebugging.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandlerSDL2::EventHandlerSDL2(OSystem& osystem) EventHandlerSDL::EventHandlerSDL(OSystem& osystem)
: EventHandler{osystem} : EventHandler{osystem}
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -31,28 +31,30 @@ EventHandlerSDL2::EventHandlerSDL2(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))); (SDL_GetKeyFromScancode(static_cast<SDL_Scancode>(KBDK_Z), static_cast<SDL_Keymod>(StellaMod::KBDM_NONE), false));
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) < 0) if(!SDL_InitSubSystem(SDL_INIT_JOYSTICK))
{ {
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("EventHandlerSDL2::EventHandlerSDL2 SDL_INIT_JOYSTICK"); Logger::debug("EventHandlerSDL::EventHandlerSDL 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");
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandlerSDL2::~EventHandlerSDL2() EventHandlerSDL::~EventHandlerSDL()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -61,24 +63,13 @@ EventHandlerSDL2::~EventHandlerSDL2()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandlerSDL2::enableTextEvents(bool enable) void EventHandlerSDL::copyText(const string& text) const
{
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 EventHandlerSDL2::pasteText(string& text) const string EventHandlerSDL::pasteText(string& text) const
{ {
if(SDL_HasClipboardText()) if(SDL_HasClipboardText())
text = SDL_GetClipboardText(); text = SDL_GetClipboardText();
@ -89,7 +80,7 @@ string EventHandlerSDL2::pasteText(string& text) const
}; };
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandlerSDL2::pollEvent() void EventHandlerSDL::pollEvent()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -98,31 +89,31 @@ void EventHandlerSDL2::pollEvent()
switch(myEvent.type) switch(myEvent.type)
{ {
// keyboard events // keyboard events
case SDL_KEYUP: case SDL_EVENT_KEY_UP:
case SDL_KEYDOWN: case SDL_EVENT_KEY_DOWN:
{ {
handleKeyEvent(static_cast<StellaKey>(myEvent.key.keysym.scancode), handleKeyEvent(static_cast<StellaKey>(myEvent.key.scancode),
static_cast<StellaMod>(myEvent.key.keysym.mod), static_cast<StellaMod>(myEvent.key.mod),
myEvent.key.type == SDL_KEYDOWN, myEvent.type == SDL_EVENT_KEY_DOWN,
myEvent.key.repeat); myEvent.key.repeat);
break; break;
} }
case SDL_TEXTINPUT: case SDL_EVENT_TEXT_INPUT:
{ {
handleTextEvent(*(myEvent.text.text)); handleTextEvent(*(myEvent.text.text));
break; break;
} }
case SDL_MOUSEMOTION: case SDL_EVENT_MOUSE_MOTION:
{ {
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_MOUSEBUTTONDOWN: case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_MOUSEBUTTONUP: case SDL_EVENT_MOUSE_BUTTON_UP:
{ {
// 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};
@ -140,39 +131,43 @@ void EventHandlerSDL2::pollEvent()
default: default:
break; break;
} }
handleMouseButtonEvent(b, myEvent.button.type == SDL_MOUSEBUTTONDOWN, handleMouseButtonEvent(b, myEvent.button.type == SDL_EVENT_MOUSE_BUTTON_DOWN,
myEvent.button.x, myEvent.button.y); myEvent.button.x, myEvent.button.y);
break; break;
} }
case SDL_MOUSEWHEEL: case SDL_EVENT_MOUSE_WHEEL:
{ {
int x{0}, y{0}; // TODO: SDL now uses float for mouse coords, but the core still
// 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, x, y); handleMouseButtonEvent(MouseButton::WHEELDOWN, true,
static_cast<int>(x), static_cast<int>(y));
else if(myEvent.wheel.y > 0) else if(myEvent.wheel.y > 0)
handleMouseButtonEvent(MouseButton::WHEELUP, true, x, y); handleMouseButtonEvent(MouseButton::WHEELUP, true,
static_cast<int>(x), static_cast<int>(y));
break; break;
} }
#ifdef JOYSTICK_SUPPORT #ifdef JOYSTICK_SUPPORT
case SDL_JOYBUTTONUP: case SDL_EVENT_JOYSTICK_BUTTON_UP:
case SDL_JOYBUTTONDOWN: case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
{ {
handleJoyBtnEvent(myEvent.jbutton.which, myEvent.jbutton.button, handleJoyBtnEvent(myEvent.jbutton.which, myEvent.jbutton.button,
myEvent.jbutton.state == SDL_PRESSED); myEvent.jbutton.down);
break; break;
} }
case SDL_JOYAXISMOTION: case SDL_EVENT_JOYSTICK_AXIS_MOTION:
{ {
handleJoyAxisEvent(myEvent.jaxis.which, myEvent.jaxis.axis, handleJoyAxisEvent(myEvent.jaxis.which, myEvent.jaxis.axis,
myEvent.jaxis.value); myEvent.jaxis.value);
break; break;
} }
case SDL_JOYHATMOTION: case SDL_EVENT_JOYSTICK_HAT_MOTION:
{ {
int value = 0; int value = 0;
const int v = myEvent.jhat.value; const int v = myEvent.jhat.value;
@ -187,86 +182,82 @@ void EventHandlerSDL2::pollEvent()
} }
handleJoyHatEvent(myEvent.jhat.which, myEvent.jhat.hat, value); handleJoyHatEvent(myEvent.jhat.which, myEvent.jhat.hat, value);
break; // SDL_JOYHATMOTION break;
} }
case SDL_JOYDEVICEADDED: case SDL_EVENT_JOYSTICK_ADDED:
{ {
addPhysicalJoystick(make_shared<JoystickSDL2>(myEvent.jdevice.which)); addPhysicalJoystick(make_shared<JoystickSDL>(myEvent.jdevice.which));
break; // SDL_JOYDEVICEADDED break;
} }
case SDL_JOYDEVICEREMOVED: case SDL_EVENT_JOYSTICK_REMOVED:
{ {
removePhysicalJoystick(myEvent.jdevice.which); removePhysicalJoystick(myEvent.jdevice.which);
break; // SDL_JOYDEVICEREMOVED break;
} }
#endif #endif
case SDL_QUIT: case SDL_EVENT_QUIT:
{ {
handleEvent(Event::Quit); handleEvent(Event::Quit);
break; // SDL_QUIT break;
} }
case SDL_WINDOWEVENT: case SDL_EVENT_WINDOW_SHOWN:
switch(myEvent.window.event) handleSystemEvent(SystemEvent::WINDOW_SHOWN);
{ break;
case SDL_WINDOWEVENT_SHOWN: case SDL_EVENT_WINDOW_HIDDEN:
handleSystemEvent(SystemEvent::WINDOW_SHOWN); handleSystemEvent(SystemEvent::WINDOW_HIDDEN);
break; break;
case SDL_WINDOWEVENT_HIDDEN: case SDL_EVENT_WINDOW_EXPOSED:
handleSystemEvent(SystemEvent::WINDOW_HIDDEN); handleSystemEvent(SystemEvent::WINDOW_EXPOSED);
break; break;
case SDL_WINDOWEVENT_EXPOSED: case SDL_EVENT_WINDOW_MOVED:
handleSystemEvent(SystemEvent::WINDOW_EXPOSED); handleSystemEvent(SystemEvent::WINDOW_MOVED,
break; myEvent.window.data1, myEvent.window.data1);
case SDL_WINDOWEVENT_MOVED: break;
handleSystemEvent(SystemEvent::WINDOW_MOVED, case SDL_EVENT_WINDOW_RESIZED:
myEvent.window.data1, myEvent.window.data1); handleSystemEvent(SystemEvent::WINDOW_RESIZED,
break; myEvent.window.data1, myEvent.window.data1);
case SDL_WINDOWEVENT_RESIZED: break;
handleSystemEvent(SystemEvent::WINDOW_RESIZED, case SDL_EVENT_WINDOW_MINIMIZED:
myEvent.window.data1, myEvent.window.data1); handleSystemEvent(SystemEvent::WINDOW_MINIMIZED);
break; break;
case SDL_WINDOWEVENT_MINIMIZED: case SDL_EVENT_WINDOW_MAXIMIZED:
handleSystemEvent(SystemEvent::WINDOW_MINIMIZED); handleSystemEvent(SystemEvent::WINDOW_MAXIMIZED);
break; break;
case SDL_WINDOWEVENT_MAXIMIZED: case SDL_EVENT_WINDOW_RESTORED:
handleSystemEvent(SystemEvent::WINDOW_MAXIMIZED); handleSystemEvent(SystemEvent::WINDOW_RESTORED);
break; break;
case SDL_WINDOWEVENT_RESTORED: case SDL_EVENT_WINDOW_MOUSE_ENTER:
handleSystemEvent(SystemEvent::WINDOW_RESTORED); handleSystemEvent(SystemEvent::WINDOW_ENTER);
break; break;
case SDL_WINDOWEVENT_ENTER: case SDL_EVENT_WINDOW_MOUSE_LEAVE:
handleSystemEvent(SystemEvent::WINDOW_ENTER); handleSystemEvent(SystemEvent::WINDOW_LEAVE);
break; break;
case SDL_WINDOWEVENT_LEAVE: case SDL_EVENT_WINDOW_FOCUS_GAINED:
handleSystemEvent(SystemEvent::WINDOW_LEAVE); handleSystemEvent(SystemEvent::WINDOW_FOCUS_GAINED);
break; break;
case SDL_WINDOWEVENT_FOCUS_GAINED: case SDL_EVENT_WINDOW_FOCUS_LOST:
handleSystemEvent(SystemEvent::WINDOW_FOCUS_GAINED); handleSystemEvent(SystemEvent::WINDOW_FOCUS_LOST);
break; break;
case SDL_WINDOWEVENT_FOCUS_LOST: case SDL_EVENT_SYSTEM_THEME_CHANGED:
handleSystemEvent(SystemEvent::WINDOW_FOCUS_LOST); handleSystemEvent(SystemEvent::THEME_CHANGED);
break; break;
default:
break;
}
break; // SDL_WINDOWEVENT
default: default:
break; break;
} }
} }
} }
#ifdef JOYSTICK_SUPPORT
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandlerSDL2::JoystickSDL2::JoystickSDL2(int idx) EventHandlerSDL::JoystickSDL::JoystickSDL(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_JoystickOpen(idx); myStick = SDL_OpenJoystick(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
@ -274,21 +265,24 @@ EventHandlerSDL2::JoystickSDL2::JoystickSDL2(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_JoystickName(myStick); const char* const sdlname = SDL_GetJoystickName(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_JoystickInstanceID(myStick), desc, initialize(SDL_GetJoystickID(myStick), desc,
SDL_JoystickNumAxes(myStick), SDL_JoystickNumButtons(myStick), SDL_GetNumJoystickAxes(myStick),
SDL_JoystickNumHats(myStick), SDL_JoystickNumBalls(myStick)); SDL_GetNumJoystickButtons(myStick),
SDL_GetNumJoystickHats(myStick),
SDL_GetNumJoystickBalls(myStick));
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandlerSDL2::JoystickSDL2::~JoystickSDL2() EventHandlerSDL::JoystickSDL::~JoystickSDL()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
if(SDL_WasInit(SDL_INIT_JOYSTICK) && myStick) if(SDL_WasInit(SDL_INIT_JOYSTICK) && myStick)
SDL_JoystickClose(myStick); SDL_CloseJoystick(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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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_SDL2_HXX #ifndef EVENTHANDLER_SDL_HXX
#define EVENTHANDLER_SDL2_HXX #define EVENTHANDLER_SDL_HXX
#include "SDL_lib.hxx" #include "SDL_lib.hxx"
#include "EventHandler.hxx" #include "EventHandler.hxx"
@ -24,26 +24,21 @@
/** /**
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 (SDL2). It converts from SDL2-specific events into events backend toolkit (SDL). It converts from SDL-specific events into events
that the Stella core can understand. that the Stella core can understand.
@author Stephen Anthony @author Stephen Anthony
*/ */
class EventHandlerSDL2 : public EventHandler class EventHandlerSDL : public EventHandler
{ {
public: public:
/** /**
Create a new SDL2 event handler object Create a new SDL event handler object
*/ */
explicit EventHandlerSDL2(OSystem& osystem); explicit EventHandlerSDL(OSystem& osystem);
~EventHandlerSDL2() override; ~EventHandlerSDL() override;
private: private:
/**
Enable/disable text events (distinct from single-key events).
*/
void enableTextEvents(bool enable) override;
/** /**
Clipboard methods. Clipboard methods.
*/ */
@ -51,40 +46,42 @@ class EventHandlerSDL2 : public EventHandler
string pasteText(string& text) const override; string pasteText(string& text) const override;
/** /**
Collects and dispatches any pending SDL2 events. Collects and dispatches any pending SDL 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 JoystickSDL2 : public PhysicalJoystick class JoystickSDL : public PhysicalJoystick
{ {
public: public:
explicit JoystickSDL2(int idx); explicit JoystickSDL(int idx);
virtual ~JoystickSDL2(); virtual ~JoystickSDL();
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
JoystickSDL2() = delete; JoystickSDL() = delete;
JoystickSDL2(const JoystickSDL2&) = delete; JoystickSDL(const JoystickSDL&) = delete;
JoystickSDL2(JoystickSDL2&&) = delete; JoystickSDL(JoystickSDL&&) = delete;
JoystickSDL2& operator=(const JoystickSDL2&) = delete; JoystickSDL& operator=(const JoystickSDL&) = delete;
JoystickSDL2& operator=(JoystickSDL2&&) = delete; JoystickSDL& operator=(JoystickSDL&&) = delete;
}; };
#endif
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
EventHandlerSDL2() = delete; EventHandlerSDL() = delete;
EventHandlerSDL2(const EventHandlerSDL2&) = delete; EventHandlerSDL(const EventHandlerSDL&) = delete;
EventHandlerSDL2(EventHandlerSDL2&&) = delete; EventHandlerSDL(EventHandlerSDL&&) = delete;
EventHandlerSDL2& operator=(const EventHandlerSDL2&) = delete; EventHandlerSDL& operator=(const EventHandlerSDL&) = delete;
EventHandlerSDL2& operator=(EventHandlerSDL2&&) = delete; EventHandlerSDL& operator=(EventHandlerSDL&&) = 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,38 +25,36 @@
#include "Settings.hxx" #include "Settings.hxx"
#include "ThreadDebugging.hxx" #include "ThreadDebugging.hxx"
#include "FBSurfaceSDL2.hxx" #include "FBSurfaceSDL.hxx"
#include "FBBackendSDL2.hxx" #include "FBBackendSDL.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBBackendSDL2::FBBackendSDL2(OSystem& osystem) FBBackendSDL::FBBackendSDL(OSystem& osystem)
: myOSystem{osystem} : myOSystem{osystem}
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
// Initialize SDL2 context // Initialize SDL context
if(SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) if(!SDL_InitSubSystem(SDL_INIT_VIDEO))
{ {
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("FBBackendSDL2::FBBackendSDL2 SDL_Init()"); Logger::debug("FBBackendSDL::FBBackendSDL 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 FBSurfaceSDL2 object) // It's done this way (vs directly accessing a FBSurfaceSDL 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_AllocFormat(SDL_PIXELFORMAT_ARGB8888); myPixelFormat = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_ARGB8888);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBBackendSDL2::~FBBackendSDL2() FBBackendSDL::~FBBackendSDL()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
SDL_FreeFormat(myPixelFormat);
if(myRenderer) if(myRenderer)
{ {
SDL_DestroyRenderer(myRenderer); SDL_DestroyRenderer(myRenderer);
@ -64,46 +62,55 @@ FBBackendSDL2::~FBBackendSDL2()
} }
if(myWindow) if(myWindow)
{ {
SDL_SetWindowFullscreen(myWindow, 0); // on some systems, a crash occurs SDL_SetWindowFullscreen(myWindow, false); // 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_INIT_TIMER); SDL_QuitSubSystem(SDL_INIT_VIDEO);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL2::queryHardware(vector<Common::Size>& fullscreenRes, void FBBackendSDL::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')
myNumDisplays = SDL_GetNumVideoDisplays(); int count = 0;
SDL_DisplayID* displays = SDL_GetDisplays(&count);
if(displays && count > 0)
myNumDisplays = static_cast<uInt32>(count);
else
return;
// First get the maximum fullscreen desktop resolution // Get the maximum fullscreen and windowed desktop resolutions
SDL_DisplayMode display; for(uInt32 i = 0; i < myNumDisplays; ++i)
for(int i = 0; i < myNumDisplays; ++i)
{ {
SDL_GetDesktopDisplayMode(i, &display); // Fullscreen mode
fullscreenRes.emplace_back(display.w, display.h); const SDL_DisplayMode* display = SDL_GetDesktopDisplayMode(displays[i]);
fullscreenRes.emplace_back(display->w, display->h);
// evaluate fullscreen display modes (debug only for now) // Windowed mode
const int numModes = SDL_GetNumDisplayModes(i); SDL_Rect r{};
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(i) << "):"; << " (" << SDL_GetDisplayName(displays[i]) << "):";
string lastRes; for(int m = 0; modes != nullptr && modes[m] != nullptr; m++)
for(int m = 0; m < numModes; ++m)
{ {
SDL_DisplayMode mode; const SDL_DisplayMode* mode = modes[m];
ostringstream res; ostringstream res, ref;
SDL_GetDisplayMode(i, m, &mode); res << std::setw(4) << mode->w << "x" << std::setw(4) << mode->h;
res << std::setw(4) << mode.w << "x" << std::setw(4) << mode.h;
if(lastRes != res.view()) if(lastRes != res.view())
{ {
@ -112,47 +119,23 @@ void FBBackendSDL2::queryHardware(vector<Common::Size>& fullscreenRes,
lastRes = res.view(); lastRes = res.view();
s << " " << lastRes << ": "; s << " " << lastRes << ": ";
} }
s << mode.refresh_rate << "Hz";
if(mode.w == display.w && mode.h == display.h && mode.refresh_rate == display.refresh_rate) ref << mode->refresh_rate << "Hz";
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 sdlName; string_view sdlName;
string stellaName; string_view 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 = {{
@ -169,68 +152,66 @@ void FBBackendSDL2::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)
{ {
SDL_RendererInfo info; const char* const rendername = SDL_GetRenderDriver(i);
if(SDL_GetRenderDriverInfo(i, &info) == 0) if(rendername)
{ {
// 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 == info.name) if(render.sdlName == rendername)
{ {
VarList::push_back(renderers, render.stellaName, info.name); VarList::push_back(renderers, render.stellaName, rendername);
found = true; found = true;
break; break;
} }
} }
if(!found) if(!found)
VarList::push_back(renderers, info.name, info.name); VarList::push_back(renderers, rendername, rendername);
} }
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FBBackendSDL2::isCurrentWindowPositioned() const bool FBBackendSDL::isCurrentWindowPositioned() const
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
return !myCenter return myWindow && !fullScreen() && !myCenter;
&& myWindow && !(SDL_GetWindowFlags(myWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Common::Point FBBackendSDL2::getCurrentWindowPos() const Common::Point FBBackendSDL::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;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int32 FBBackendSDL2::getCurrentDisplayIndex() const uInt32 FBBackendSDL::getCurrentDisplayID() const
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
return SDL_GetWindowDisplayIndex(myWindow); return SDL_GetDisplayForWindow(myWindow);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FBBackendSDL2::setVideoMode(const VideoModeHandler::Mode& mode, bool FBBackendSDL::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 bool fullScreen = mode.fsIndex != -1; const uInt32 displayIndex = std::min<uInt32>(myNumDisplays, winIdx);
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");
@ -261,10 +242,10 @@ bool FBBackendSDL2::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 = fullScreen const bool shouldAdapt = mode.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
@ -275,20 +256,18 @@ bool FBBackendSDL2::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 int d = SDL_GetWindowDisplayIndex(myWindow); const uInt32 d = getCurrentDisplayID();
int w{0}, h{0}; int w{0}, h{0};
SDL_GetWindowSize(myWindow, &w, &h); SDL_GetWindowSize(myWindow, &w, &h);
if(d != displayIndex || static_cast<uInt32>(w) != mode.screenS.w || if(d != displayIndex ||
static_cast<uInt32>(h) != mode.screenS.h || adaptRefresh) std::cmp_not_equal(w, mode.screenS.w) ||
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);
@ -306,8 +285,25 @@ bool FBBackendSDL2::setVideoMode(const VideoModeHandler::Mode& mode,
} }
else else
{ {
myWindow = SDL_CreateWindow(myScreenTitle.c_str(), posX, posY, // Re-create with new properties
mode.screenS.w, mode.screenS.h, flags); const SDL_PropertiesID props = SDL_CreateProperties();
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());
@ -315,6 +311,7 @@ bool FBBackendSDL2::setVideoMode(const VideoModeHandler::Mode& mode,
return false; return false;
} }
enableTextEvents(myTextEventsEnabled);
setWindowIcon(); setWindowIcon();
} }
@ -322,7 +319,7 @@ bool FBBackendSDL2::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_SetWindowDisplayMode(myWindow, &adaptedSdlMode) != 0) if(!SDL_SetWindowFullscreenMode(myWindow, &adaptedSdlMode))
{ {
Logger::error("ERROR: Display refresh rate change failed"); Logger::error("ERROR: Display refresh rate change failed");
} }
@ -334,31 +331,39 @@ bool FBBackendSDL2::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());
SDL_DisplayMode setSdlMode; const SDL_DisplayMode* setSdlMode = SDL_GetWindowFullscreenMode(myWindow);
SDL_GetWindowDisplayMode(myWindow, &setSdlMode); cerr << setSdlMode->refresh_rate << "Hz\n";
cerr << setSdlMode.refresh_rate << "Hz\n";
} }
} }
#endif #endif
return createRenderer(); const bool result = 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 FBBackendSDL2::adaptRefreshRate(Int32 displayIndex, bool FBBackendSDL::adaptRefreshRate(Int32 displayIndex,
SDL_DisplayMode& adaptedSdlMode) SDL_DisplayMode& adaptedSdlMode)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
SDL_DisplayMode sdlMode; const SDL_DisplayMode* sdlMode = SDL_GetCurrentDisplayMode(displayIndex);
if(SDL_GetCurrentDisplayMode(displayIndex, &sdlMode) != 0) if(sdlMode == nullptr)
{ {
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)
@ -374,18 +379,19 @@ bool FBBackendSDL2::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;
sdlMode.refresh_rate = wantedRefreshRate * m; if(!SDL_GetClosestFullscreenDisplayMode(displayIndex, sdlMode->w, sdlMode->h, refresh_rate, true, &closestSdlMode))
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>(sdlMode.refresh_rate) / sdlMode.refresh_rate, static_cast<float>(refresh_rate) / refresh_rate,
static_cast<float>(sdlMode.refresh_rate) / (sdlMode.refresh_rate - 1)); static_cast<float>(refresh_rate) / (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;
@ -405,38 +411,46 @@ bool FBBackendSDL2::adaptRefreshRate(Int32 displayIndex,
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FBBackendSDL2::createRenderer() bool FBBackendSDL::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;
uInt32 renderFlags = SDL_RENDERER_ACCELERATED; if(myRenderer)
const string& video = myOSystem.settings().getString("video"); // Render hint {
SDL_RendererInfo renderInfo; recreate = recreate || video != SDL_GetRendererName(myRenderer);
if(myOSystem.settings().getBool("vsync") const SDL_PropertiesID props = SDL_GetRendererProperties(myRenderer);
&& !myOSystem.settings().getBool("turbo")) // V'synced blits option const bool currentVSync = SDL_GetNumberProperty(props,
renderFlags |= SDL_RENDERER_PRESENTVSYNC; SDL_PROP_RENDERER_VSYNC_NUMBER, 0) != 0;
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_SetHint(SDL_HINT_RENDER_DRIVER, video.c_str()); SDL_SetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING,
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_CreateRenderer(myWindow, -1, renderFlags); myRenderer = SDL_CreateRendererWithProperties(props);
SDL_DestroyProperties(props);
detectFeatures(); detectFeatures();
determineDimensions(); determineDimensions();
@ -450,85 +464,106 @@ bool FBBackendSDL2::createRenderer()
} }
clear(); clear();
SDL_RendererInfo renderinfo; const char* const detectedvideo = SDL_GetRendererName(myRenderer);
if(detectedvideo)
if(SDL_GetRendererInfo(myRenderer, &renderinfo) >= 0) myOSystem.settings().setValue("video", detectedvideo);
myOSystem.settings().setValue("video", renderinfo.name);
return true; return true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL2::setTitle(string_view title) void FBBackendSDL::setTitle(string_view title)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
myScreenTitle = title; myScreenTitle = title;
if(myWindow) if(myWindow)
SDL_SetWindowTitle(myWindow, string{title}.c_str()); SDL_SetWindowTitle(myWindow, myScreenTitle.c_str());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string FBBackendSDL2::about() const string FBBackendSDL::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;
if(SDL_GetRendererInfo(myRenderer, &info) >= 0) const SDL_PropertiesID props = SDL_GetRendererProperties(myRenderer);
if(props != 0)
{ {
out << " Renderer: " << info.name << '\n'; out << " Renderer: "
if(info.max_texture_width > 0 && info.max_texture_height > 0) << SDL_GetStringProperty(props, SDL_PROP_RENDERER_NAME_STRING, "")
out << " Max texture: " << info.max_texture_width << "x" << '\n';
<< info.max_texture_height << '\n'; const uInt64 maxTexSize =
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: "
<< ((info.flags & SDL_RENDERER_PRESENTVSYNC) ? "+" : "-") << "vsync, " << (usingVSync ? "+" : "-") << "vsync"
<< ((info.flags & SDL_RENDERER_ACCELERATED) ? "+" : "-") << "accel"
<< '\n'; << '\n';
} }
return out.str(); return out.str();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL2::showCursor(bool show) void FBBackendSDL::showCursor(bool show)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
SDL_ShowCursor(show ? SDL_ENABLE : SDL_DISABLE); if(show) SDL_ShowCursor();
else SDL_HideCursor();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL2::grabMouse(bool grab) void FBBackendSDL::grabMouse(bool grab)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
SDL_SetRelativeMouseMode(grab ? SDL_TRUE : SDL_FALSE); SDL_SetWindowMouseGrab(myWindow, grab);
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE, grab ? "1" : "0");
SDL_SetWindowRelativeMouseMode(myWindow, grab);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FBBackendSDL2::fullScreen() const void FBBackendSDL::enableTextEvents(bool enable)
{
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 SDL_GetWindowFlags(myWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP; return myIsFullscreen; // TODO: should query SDL directly
#else #else
return true; return true;
#endif #endif
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int FBBackendSDL2::refreshRate() const int FBBackendSDL::refreshRate() const
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
const uInt32 displayIndex = SDL_GetWindowDisplayIndex(myWindow); const SDL_DisplayID displayID = getCurrentDisplayID();
SDL_DisplayMode sdlMode; const SDL_DisplayMode* mode = SDL_GetCurrentDisplayMode(displayID);
if(SDL_GetCurrentDisplayMode(displayIndex, &sdlMode) == 0) if(mode)
return sdlMode.refresh_rate; return mode->refresh_rate;
if(myWindow != nullptr) if(myWindow != nullptr)
Logger::error("Could not retrieve current display mode"); Logger::error("Could not retrieve current display mode");
@ -537,7 +572,7 @@ int FBBackendSDL2::refreshRate() const
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL2::renderToScreen() void FBBackendSDL::renderToScreen()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -546,35 +581,42 @@ void FBBackendSDL2::renderToScreen()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL2::setWindowIcon() void FBBackendSDL::setWindowIcon()
{ {
#if !defined(BSPF_MACOS) && !defined(RETRON77) #if !defined(BSPF_MACOS)
#include "stella_icon.hxx" #include "stella_icon.hxx"
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(stella_icon, 32, 32, 32, SDL_Surface* surface =
32 * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000); SDL_CreateSurfaceFrom(32, 32, pixelFormat().format, stella_icon, 32 * 4);
SDL_SetWindowIcon(myWindow, surface); SDL_SetWindowIcon(myWindow, surface);
SDL_FreeSurface(surface); SDL_DestroySurface(surface);
#endif #endif
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
unique_ptr<FBSurface> FBBackendSDL2::createSurface( unique_ptr<FBSurface> FBBackendSDL::createSurface(
uInt32 w, uInt32 w,
uInt32 h, uInt32 h,
ScalingInterpolation inter, ScalingInterpolation inter,
const uInt32* data const uInt32* data
) const ) const
{ {
return make_unique<FBSurfaceSDL2> unique_ptr<FBSurface> s = make_unique<FBSurfaceSDL>
(const_cast<FBBackendSDL2&>(*this), w, h, inter, data); (const_cast<FBBackendSDL&>(*this), w, h, inter, data);
s->setBlendLevel(100); // by default, disable shading (use full alpha)
return s;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL2::readPixels(uInt8* buffer, size_t pitch, void FBBackendSDL::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;
@ -582,10 +624,11 @@ void FBBackendSDL2::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 FBBackendSDL2::clear() void FBBackendSDL::clear()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -593,7 +636,7 @@ void FBBackendSDL2::clear()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBBackendSDL2::detectFeatures() void FBBackendSDL::detectFeatures()
{ {
myRenderTargetSupport = detectRenderTargetSupport(); myRenderTargetSupport = detectRenderTargetSupport();
@ -602,36 +645,20 @@ void FBBackendSDL2::detectFeatures()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FBBackendSDL2::detectRenderTargetSupport() bool FBBackendSDL::detectRenderTargetSupport()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
if(myRenderer == nullptr) if(myRenderer == nullptr)
return false; return false;
SDL_RendererInfo info; // All texture modes except software support render targets
SDL_GetRendererInfo(myRenderer, &info); const char* const detectedvideo = SDL_GetRendererName(myRenderer);
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 FBBackendSDL2::determineDimensions() void FBBackendSDL::determineDimensions()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -643,5 +670,5 @@ void FBBackendSDL2::determineDimensions()
myRenderH = myWindowH; myRenderH = myWindowH;
} }
else else
SDL_GetRendererOutputSize(myRenderer, &myRenderW, &myRenderH); SDL_GetCurrentRenderOutputSize(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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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_SDL2_HXX #ifndef FB_BACKEND_SDL_HXX
#define FB_BACKEND_SDL2_HXX #define FB_BACKEND_SDL_HXX
#include "SDL_lib.hxx" #include "SDL_lib.hxx"
class OSystem; class OSystem;
class FBSurfaceSDL2; class FBSurfaceSDL;
#include "bspf.hxx" #include "bspf.hxx"
#include "FBBackend.hxx" #include "FBBackend.hxx"
/** /**
This class implements a standard SDL2 2D, hardware accelerated framebuffer This class implements a standard SDL 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 FBBackendSDL2 : public FBBackend class FBBackendSDL : public FBBackend
{ {
public: public:
/** /**
Creates a new SDL2 framebuffer Creates a new SDL framebuffer
*/ */
explicit FBBackendSDL2(OSystem& osystem); explicit FBBackendSDL(OSystem& osystem);
~FBBackendSDL2() override; ~FBBackendSDL() override;
public: public:
/** /**
@ -55,7 +55,7 @@ class FBBackendSDL2 : public FBBackend
/** /**
Get the SDL pixel format. Get the SDL pixel format.
*/ */
const SDL_PixelFormat& pixelFormat() const { return *myPixelFormat; } const SDL_PixelFormatDetails& pixelFormat() const { return *myPixelFormat; }
/** /**
Does the renderer support render targets? Does the renderer support render targets?
@ -99,7 +99,7 @@ class FBBackendSDL2 : 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, r, g, b); } { SDL_GetRGB(pixel, myPixelFormat, nullptr, 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 FBBackendSDL2 : 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, r, g, b, a); } { SDL_GetRGBA(pixel, myPixelFormat, nullptr, 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 FBBackendSDL2 : 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, r, g, b); } { return SDL_MapRGB(myPixelFormat, nullptr, 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 FBBackendSDL2 : 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, r, g, b, a); } { return SDL_MapRGBA(myPixelFormat, nullptr, 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,10 +167,9 @@ class FBBackendSDL2 : 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 negative value if no @return the current display index or a 0 if no window is displayed
window is displayed
*/ */
Int32 getCurrentDisplayIndex() const override; uInt32 getCurrentDisplayID() const override;
/** /**
Clear the frame buffer. Clear the frame buffer.
@ -223,6 +222,11 @@ class FBBackendSDL2 : 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.
*/ */
@ -246,6 +250,20 @@ class FBBackendSDL2 : 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.
@ -286,7 +304,18 @@ class FBBackendSDL2 : public FBBackend
SDL_Renderer* myRenderer{nullptr}; SDL_Renderer* myRenderer{nullptr};
// Used by mapRGB (when palettes are created) // Used by mapRGB (when palettes are created)
SDL_PixelFormat* myPixelFormat{nullptr}; const SDL_PixelFormatDetails* 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};
@ -298,18 +327,18 @@ class FBBackendSDL2 : public FBBackend
string myScreenTitle; string myScreenTitle;
// Number of displays // Number of displays
int myNumDisplays{1}; uInt32 myNumDisplays{0};
// 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
FBBackendSDL2() = delete; FBBackendSDL() = delete;
FBBackendSDL2(const FBBackendSDL2&) = delete; FBBackendSDL(const FBBackendSDL&) = delete;
FBBackendSDL2(FBBackendSDL2&&) = delete; FBBackendSDL(FBBackendSDL&&) = delete;
FBBackendSDL2& operator=(const FBBackendSDL2&) = delete; FBBackendSDL& operator=(const FBBackendSDL&) = delete;
FBBackendSDL2& operator=(FBBackendSDL2&&) = delete; FBBackendSDL& operator=(FBBackendSDL&&) = 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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 "FBSurfaceSDL2.hxx" #include "FBSurfaceSDL.hxx"
#include "Logger.hxx" #include "Logger.hxx"
#include "ThreadDebugging.hxx" #include "ThreadDebugging.hxx"
@ -41,10 +41,10 @@ namespace {
} // namespace } // namespace
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceSDL2::FBSurfaceSDL2(FBBackendSDL2& backend, FBSurfaceSDL::FBSurfaceSDL(FBBackendSDL& 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 @@ FBSurfaceSDL2::FBSurfaceSDL2(FBBackendSDL2& backend,
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceSDL2::~FBSurfaceSDL2() FBSurfaceSDL::~FBSurfaceSDL()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
if(mySurface) if(mySurface)
{ {
SDL_FreeSurface(mySurface); SDL_DestroySurface(mySurface);
mySurface = nullptr; mySurface = nullptr;
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, ColorId color) void FBSurfaceSDL::fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, ColorId color)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -75,49 +75,49 @@ void FBSurfaceSDL2::fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, ColorId col
tmp.y = y; tmp.y = y;
tmp.w = w; tmp.w = w;
tmp.h = h; tmp.h = h;
SDL_FillRect(mySurface, &tmp, myPalette[color]); SDL_FillSurfaceRect(mySurface, &tmp, myPalette[color]);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FBSurfaceSDL2::width() const uInt32 FBSurfaceSDL::width() const
{ {
return mySurface->w; return mySurface->w;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FBSurfaceSDL2::height() const uInt32 FBSurfaceSDL::height() const
{ {
return mySurface->h; return mySurface->h;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const Common::Rect& FBSurfaceSDL2::srcRect() const const Common::Rect& FBSurfaceSDL::srcRect() const
{ {
return mySrcGUIR; return mySrcGUIR;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const Common::Rect& FBSurfaceSDL2::dstRect() const const Common::Rect& FBSurfaceSDL::dstRect() const
{ {
return myDstGUIR; return myDstGUIR;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::setSrcPos(uInt32 x, uInt32 y) void FBSurfaceSDL::setSrcPos(uInt32 x, uInt32 y)
{ {
if(setSrcPosInternal(x, y)) if(setSrcPosInternal(x, y))
reinitializeBlitter(); reinitializeBlitter();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::setSrcSize(uInt32 w, uInt32 h) void FBSurfaceSDL::setSrcSize(uInt32 w, uInt32 h)
{ {
if(setSrcSizeInternal(w, h)) if(setSrcSizeInternal(w, h))
reinitializeBlitter(); reinitializeBlitter();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::setSrcRect(const Common::Rect& r) void FBSurfaceSDL::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 FBSurfaceSDL2::setSrcRect(const Common::Rect& r)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::setDstPos(uInt32 x, uInt32 y) void FBSurfaceSDL::setDstPos(uInt32 x, uInt32 y)
{ {
if(setDstPosInternal(x, y)) if(setDstPosInternal(x, y))
reinitializeBlitter(); reinitializeBlitter();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::setDstSize(uInt32 w, uInt32 h) void FBSurfaceSDL::setDstSize(uInt32 w, uInt32 h)
{ {
if(setDstSizeInternal(w, h)) if(setDstSizeInternal(w, h))
reinitializeBlitter(); reinitializeBlitter();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::setDstRect(const Common::Rect& r) void FBSurfaceSDL::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,22 +151,23 @@ void FBSurfaceSDL2::setDstRect(const Common::Rect& r)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::setVisible(bool visible) void FBSurfaceSDL::setVisible(bool visible)
{ {
myIsVisible = visible; myIsVisible = visible;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::translateCoords(Int32& x, Int32& y) const void FBSurfaceSDL::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 FBSurfaceSDL2::render() bool FBSurfaceSDL::render()
{ {
if (!myBlitter) reinitializeBlitter(); if(!myBlitter)
reinitializeBlitter();
if(myIsVisible && myBlitter) if(myIsVisible && myBlitter)
{ {
@ -178,15 +179,15 @@ bool FBSurfaceSDL2::render()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::invalidate() void FBSurfaceSDL::invalidate()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
SDL_FillRect(mySurface, nullptr, 0); SDL_FillSurfaceRect(mySurface, nullptr, 0);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::invalidateRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) void FBSurfaceSDL::invalidateRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
@ -198,22 +199,22 @@ void FBSurfaceSDL2::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_FillRect(mySurface, &tmp, 0); SDL_FillSurfaceRect(mySurface, &tmp, 0);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::reload() void FBSurfaceSDL::reload()
{ {
reinitializeBlitter(true); reinitializeBlitter(true);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::resize(uInt32 width, uInt32 height) void FBSurfaceSDL::resize(uInt32 width, uInt32 height)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
if(mySurface) if(mySurface)
SDL_FreeSurface(mySurface); SDL_DestroySurface(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
@ -224,17 +225,13 @@ void FBSurfaceSDL2::resize(uInt32 width, uInt32 height)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::createSurface(uInt32 width, uInt32 height, void FBSurfaceSDL::createSurface(uInt32 width, uInt32 height, const uInt32* data)
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_PixelFormat& pf = myBackend.pixelFormat(); const SDL_PixelFormatDetails& 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
@ -246,7 +243,7 @@ void FBSurfaceSDL2::createSurface(uInt32 width, uInt32 height,
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
// 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.BytesPerPixel; myPitch = mySurface->pitch / pf.bytes_per_pixel;
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
myIsStatic = data != nullptr; myIsStatic = data != nullptr;
@ -258,30 +255,40 @@ void FBSurfaceSDL2::createSurface(uInt32 width, uInt32 height,
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::reinitializeBlitter(bool force) void FBSurfaceSDL::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, myAttributes, myBlitter->reinitialize(mySrcR, myDstR, myEnableBlend, myBlendLevel,
myIsStatic ? mySurface : nullptr); myIsStatic ? mySurface : nullptr);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::applyAttributes() void FBSurfaceSDL::setBlendLevel(uInt32 percent)
{ {
myBlendLevel = BSPF::clamp(percent, 0U, 100U);
reinitializeBlitter(); reinitializeBlitter();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::setScalingInterpolation(ScalingInterpolation interpolation) void FBSurfaceSDL::enableBlend(bool enableBlend)
{ {
if (interpolation == ScalingInterpolation::sharp && myEnableBlend = enableBlend;
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())
@ -289,7 +296,8 @@ void FBSurfaceSDL2::setScalingInterpolation(ScalingInterpolation interpolation)
) )
interpolation = ScalingInterpolation::blur; interpolation = ScalingInterpolation::blur;
if (interpolation == myInterpolationMode) return; if(interpolation == myInterpolationMode)
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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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_SDL2_HXX #ifndef FBSURFACE_SDL_HXX
#define FBSURFACE_SDL2_HXX #define FBSURFACE_SDL_HXX
#include "bspf.hxx" #include "bspf.hxx"
#include "FBSurface.hxx" #include "FBSurface.hxx"
#include "FBBackendSDL2.hxx" #include "FBBackendSDL.hxx"
#include "sdl_blitter/Blitter.hxx" #include "sdl_blitter/Blitter.hxx"
/** /**
An FBSurface suitable for the SDL2 Render2D API, making use of hardware An FBSurface suitable for the SDL Render2D API, making use of hardware
acceleration behind the scenes. acceleration behind the scenes.
@author Stephen Anthony @author Stephen Anthony
*/ */
class FBSurfaceSDL2 : public FBSurface class FBSurfaceSDL : public FBSurface
{ {
public: public:
FBSurfaceSDL2(FBBackendSDL2& backend, uInt32 width, uInt32 height, FBSurfaceSDL(FBBackendSDL& backend, uInt32 width, uInt32 height,
ScalingInterpolation inter, const uInt32* staticData); ScalingInterpolation inter, const uInt32* staticData);
~FBSurfaceSDL2() override; ~FBSurfaceSDL() 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,14 +63,13 @@ class FBSurfaceSDL2 : 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(x != static_cast<uInt32>(mySrcR.x) || y != static_cast<uInt32>(mySrcR.y)) if(std::cmp_not_equal(x, mySrcR.x) || std::cmp_not_equal(y, mySrcR.y))
{ {
mySrcR.x = x; mySrcR.y = y; mySrcR.x = x; mySrcR.y = y;
mySrcGUIR.moveTo(x, y); mySrcGUIR.moveTo(x, y);
@ -79,7 +78,7 @@ class FBSurfaceSDL2 : public FBSurface
return false; return false;
} }
bool setSrcSizeInternal(uInt32 w, uInt32 h) { bool setSrcSizeInternal(uInt32 w, uInt32 h) {
if(w != static_cast<uInt32>(mySrcR.w) || h != static_cast<uInt32>(mySrcR.h)) if(std::cmp_not_equal(w, mySrcR.w) || std::cmp_not_equal(h, 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);
@ -88,7 +87,7 @@ class FBSurfaceSDL2 : public FBSurface
return false; return false;
} }
bool setDstPosInternal(uInt32 x, uInt32 y) { bool setDstPosInternal(uInt32 x, uInt32 y) {
if(x != static_cast<uInt32>(myDstR.x) || y != static_cast<uInt32>(myDstR.y)) if(std::cmp_not_equal(x, myDstR.x) || std::cmp_not_equal(y, myDstR.y))
{ {
myDstR.x = x; myDstR.y = y; myDstR.x = x; myDstR.y = y;
myDstGUIR.moveTo(x, y); myDstGUIR.moveTo(x, y);
@ -97,7 +96,7 @@ class FBSurfaceSDL2 : public FBSurface
return false; return false;
} }
bool setDstSizeInternal(uInt32 w, uInt32 h) { bool setDstSizeInternal(uInt32 w, uInt32 h) {
if(w != static_cast<uInt32>(myDstR.w) || h != static_cast<uInt32>(myDstR.h)) if(std::cmp_not_equal(w, myDstR.w) || std::cmp_not_equal(h, 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);
@ -111,18 +110,17 @@ class FBSurfaceSDL2 : 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
FBSurfaceSDL2() = delete; FBSurfaceSDL() = delete;
FBSurfaceSDL2(const FBSurfaceSDL2&) = delete; FBSurfaceSDL(const FBSurfaceSDL&) = delete;
FBSurfaceSDL2(FBSurfaceSDL2&&) = delete; FBSurfaceSDL(FBSurfaceSDL&&) = delete;
FBSurfaceSDL2& operator=(const FBSurfaceSDL2&) = delete; FBSurfaceSDL& operator=(const FBSurfaceSDL&) = delete;
FBSurfaceSDL2& operator=(FBSurfaceSDL2&&) = delete; FBSurfaceSDL& operator=(FBSurfaceSDL&&) = delete;
private: private:
FBBackendSDL2& myBackend; FBBackendSDL& myBackend;
unique_ptr<Blitter> myBlitter; unique_ptr<Blitter> myBlitter;
ScalingInterpolation myInterpolationMode ScalingInterpolation myInterpolationMode{ScalingInterpolation::none};
{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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,8 +381,7 @@ string HighScoresManager::formattedScore(Int32 score, Int32 width) const
if(scoreBCD(jprops)) if(scoreBCD(jprops))
{ {
if(width > digits) digits = std::max(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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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 uInt32 numAddrBytes(Int32 digits, Int32 trailing) { static constexpr 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,27 +188,26 @@ 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::sort(sortedMap.begin(), sortedMap.end(), std::ranges::sort(sortedMap, [](const MapType& a, const MapType& b)
[](const MapType& a, const MapType& b) {
{ // Event::Type first
// Event::Type first if(a.first.button != b.first.button)
if(a.first.button != b.first.button) return 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();
@ -285,17 +284,17 @@ int JoyMap::loadMapping(const json& eventMappings, EventMode mode)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
json JoyMap::convertLegacyMapping(string list) json JoyMap::convertLegacyMapping(string lst)
{ {
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::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(), ',', ' '); std::ranges::replace(lst, ',', ' ');
istringstream buf(list); istringstream buf(lst);
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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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 list); static nlohmann::json convertLegacyMapping(string lst);
/** 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) // 2 bits + (((static_cast<uInt64>(m.axis)) << 0) // 3 bits
| ((static_cast<uInt64>(m.adir)) << 2) // 2 bits | ((static_cast<uInt64>(m.adir)) << 3) // 2 bits
| ((static_cast<uInt64>(m.hat )) << 4) // 1 bit | ((static_cast<uInt64>(m.hat )) << 5) // 1 bit
| ((static_cast<uInt64>(m.hdir)) << 5) // 2 bits | ((static_cast<uInt64>(m.hdir)) << 6) // 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,8 +43,7 @@ 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;
@ -222,8 +221,7 @@ 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::sort(sortedMap.begin(), sortedMap.end(), std::ranges::sort(sortedMap, [](const MapType& a, const MapType& b)
[](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)
@ -290,11 +288,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 list{lm}; string lst{lm};
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(), ',', ' '); std::ranges::replace(lst, ',', ' ');
istringstream buf(list); istringstream buf(lst);
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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,6 +17,10 @@
#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()
{ {
@ -52,6 +56,10 @@ 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,21 +28,13 @@
#include "SerialPort.hxx" #include "SerialPort.hxx"
#if defined(BSPF_UNIX) #if defined(BSPF_UNIX)
#include "SerialPortUNIX.hxx" #include "SerialPortUNIX.hxx"
#if defined(RETRON77) #include "OSystemUNIX.hxx"
#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
@ -53,8 +45,8 @@
#include "EventHandlerLIBRETRO.hxx" #include "EventHandlerLIBRETRO.hxx"
#include "FBBackendLIBRETRO.hxx" #include "FBBackendLIBRETRO.hxx"
#elif defined(SDL_SUPPORT) #elif defined(SDL_SUPPORT)
#include "EventHandlerSDL2.hxx" #include "EventHandlerSDL.hxx"
#include "FBBackendSDL2.hxx" #include "FBBackendSDL.hxx"
#else #else
#error Unsupported backend! #error Unsupported backend!
#endif #endif
@ -63,7 +55,7 @@
#if defined(__LIB_RETRO__) #if defined(__LIB_RETRO__)
#include "SoundLIBRETRO.hxx" #include "SoundLIBRETRO.hxx"
#elif defined(SDL_SUPPORT) #elif defined(SDL_SUPPORT)
#include "SoundSDL2.hxx" #include "SoundSDL.hxx"
#else #else
#include "SoundNull.hxx" #include "SoundNull.hxx"
#endif #endif
@ -78,10 +70,6 @@ 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
@ -90,11 +78,7 @@ class MediaFactory
static unique_ptr<OSystem> createOSystem() static unique_ptr<OSystem> createOSystem()
{ {
#if defined(BSPF_UNIX) #if defined(BSPF_UNIX)
#if defined(RETRON77) return make_unique<OSystemUNIX>();
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)
@ -108,11 +92,7 @@ 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()
@ -133,7 +113,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<FBBackendSDL2>(osystem); return make_unique<FBBackendSDL>(osystem);
#else #else
#error Unsupported platform for FrameBuffer! #error Unsupported platform for FrameBuffer!
#endif #endif
@ -145,7 +125,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<SoundSDL2>(osystem, audioSettings); return make_unique<SoundSDL>(osystem, audioSettings);
#else #else
return make_unique<SoundNull>(osystem); return make_unique<SoundNull>(osystem);
#endif #endif
@ -159,7 +139,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<EventHandlerSDL2>(osystem); return make_unique<EventHandlerSDL>(osystem);
#else #else
#error Unsupported platform for EventHandler! #error Unsupported platform for EventHandler!
#endif #endif
@ -181,15 +161,6 @@ 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,18 +405,11 @@ 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 R77 controllers support two players each when // devices. The Stelladaptor and 2600-daptor controllers support
// used as paddles, so are different. Similarly, stelladaptors // two players each when used as paddles, so are different.
// and 2600-daptors support two players natively. const int paddlesPerJoystick = (j->type == PhysicalJoystick::Type::REGULAR) ? 1 : 2;
const int paddlesPerJoystick = (j->type == PhysicalJoystick::Type::REGULAR && !retron77) ? 1 : 2;
if(paddlesPerJoystick == 2) if(paddlesPerJoystick == 2)
{ {
@ -1355,17 +1348,11 @@ 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},
@ -1375,17 +1362,11 @@ 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},
@ -1485,14 +1466,6 @@ 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"
@ -1500,7 +1473,6 @@ 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,18 +806,10 @@ 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
}; };
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -862,24 +854,12 @@ 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
@ -1037,6 +1017,8 @@ 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},
@ -1046,6 +1028,8 @@ 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},
@ -1097,10 +1081,16 @@ 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},
}; };
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1157,9 +1147,7 @@ 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,13 +449,15 @@ 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 degrees // YIQ is YUV shifted by 33°
constexpr float offset = 33 * BSPF::PI_f / 180; // -90° + 33° = -57° would create a greenish yellow
// -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
@ -478,9 +480,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);
if(R < 0) R = 0; R = std::max(R, 0.F);
if(G < 0) G = 0; G = std::max(G, 0.F);
if(B < 0) B = 0; B = std::max(B, 0.F);
R = powf(R, 0.9F); R = powf(R, 0.9F);
G = powf(G, 0.9F); G = powf(G, 0.9F);
@ -496,6 +498,7 @@ 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;
@ -669,9 +672,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
0x8b8b8b, 0, 0xaaaaaa, 0, 0xc7c7c7, 0, 0xe3e3e3, 0, 0x999999, 0, 0xb6b6b6, 0, 0xcfcfcf, 0, 0xe6e6e6, 0,
0x000000, 0, 0x272727, 0, 0x404040, 0, 0x696969, 0, // 1 0x0b0b0b, 0, 0x333333, 0, 0x595959, 0, 0x7b7b7b, 0, // 1
0x8b8b8b, 0, 0xaaaaaa, 0, 0xc7c7c7, 0, 0xe3e3e3, 0, 0x999999, 0, 0xb6b6b6, 0, 0xcfcfcf, 0, 0xe6e6e6, 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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.2F; static constexpr float DEF_NTSC_SHIFT = 26.7F; // makes color $fx fall between $1x and $2x
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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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 >> 16), const auto rc = static_cast<uInt8>(c),
gc = static_cast<uInt8>(c >> 8), gc = static_cast<uInt8>(c >> 8),
bc = static_cast<uInt8>(c), bc = static_cast<uInt8>(c >> 16),
rp = static_cast<uInt8>(p >> 16), rp = static_cast<uInt8>(p),
gp = static_cast<uInt8>(p >> 8), gp = static_cast<uInt8>(p >> 8),
bp = static_cast<uInt8>(p); bp = static_cast<uInt8>(p >> 16);
return (ourPhosphorLUT[rc][rp] << 16) | (ourPhosphorLUT[gc][gp] << 8) | return ourPhosphorLUT[rc][rp] | (ourPhosphorLUT[gc][gp] << 8) |
ourPhosphorLUT[bc][bp]; (ourPhosphorLUT[bc][bp] << 16);
} }
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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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 map; string lmap;
// Skip joystick name // Skip joystick name
getline(buf, map, MODE_DELIM); getline(buf, lmap, MODE_DELIM);
while (getline(buf, map, MODE_DELIM)) while (getline(buf, lmap, MODE_DELIM))
{ {
int mode{0}; int mode{0};
// Get event mode // Get event mode
std::replace(map.begin(), map.end(), '|', ' '); std::ranges::replace(lmap, '|', ' ');
istringstream modeBuf(map); istringstream modeBuf(lmap);
modeBuf >> mode; modeBuf >> mode;
// Remove leading "<mode>|" string // Remove leading "<mode>|" string
map.erase(0, 2); lmap.erase(0, 2);
const json mappingForMode = JoyMap::convertLegacyMapping(map); const json lmappingForMode = JoyMap::convertLegacyMapping(lmap);
convertedMapping[jsonName(static_cast<EventMode>(mode))] = mappingForMode; convertedMapping[jsonName(static_cast<EventMode>(mode))] = lmappingForMode;
} }
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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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().tia().cycles() - lastState.cycles < interval) if(myOSystem.console().system().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().tia().cycles(); state.cycles = myOSystem.console().system().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().tia().cycles(); const uInt64 startCycles = myOSystem.console().system().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().tia().cycles(); const uInt64 startCycles = myOSystem.console().system().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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,48 +33,30 @@
#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 <SDL.h> #include <SDL3/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 <SDL.h> #include <SDL3/SDL.h>
#pragma warning(pop) #pragma warning(pop)
#else #else
#include <SDL.h> #include <SDL3/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;
SDL_version ver; const int ver = SDL_GetVersion();
SDL_GetVersion(&ver); buf << "SDL "
buf << "SDL " << static_cast<int>(ver.major) << "." << static_cast<int>(ver.minor) << SDL_VERSIONNUM_MAJOR(ver) << "."
<< "." << static_cast<int>(ver.patch); << SDL_VERSIONNUM_MINOR(ver) << "."
<< 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)
{ {
#if SDL_VERSION_ATLEAST(2,0,14) return SDL_OpenURL(url.c_str());
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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,9 +90,10 @@ 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) override { } void setVolume(uInt32 volume, bool persist = true) 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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 "SoundSDL2.hxx" #include "SoundSDL.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL2::SoundSDL2(OSystem& osystem, AudioSettings& audioSettings) SoundSDL::SoundSDL(OSystem& osystem, AudioSettings& audioSettings)
: Sound{osystem}, : Sound{osystem},
myAudioSettings{audioSettings} myAudioSettings{audioSettings}
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
Logger::debug("SoundSDL2::SoundSDL2 started ..."); Logger::debug("SoundSDL::SoundSDL started ...");
if(SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) if(!SDL_InitSubSystem(SDL_INIT_AUDIO))
{ {
ostringstream buf; ostringstream buf;
@ -52,77 +52,35 @@ SoundSDL2::SoundSDL2(OSystem& osystem, AudioSettings& audioSettings)
return; return;
} }
queryHardware(myDevices); // NOLINT SDL_zero(mySpec);
if(!myAudioSettings.enabled())
Logger::info("Sound disabled\n");
SDL_zero(myHardwareSpec); Logger::debug("SoundSDL::SoundSDL initialized");
if(!openDevice())
return;
Logger::debug("SoundSDL2::SoundSDL2 initialized"); // Reserve 8K for the audio buffer; seems to be enough on most systems
myBuffer.reserve(8_KB);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL2::~SoundSDL2() SoundSDL::~SoundSDL()
{ {
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);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::queryHardware(VariantList& devices) bool SoundSDL::openDevice()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
const int numDevices = SDL_GetNumAudioDevices(0); auto SOUND_ERROR = [this]() -> bool
// 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;
@ -131,49 +89,72 @@ bool SoundSDL2::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 SoundSDL2::setEnabled(bool enable) void SoundSDL::setEnabled(bool enable)
{ {
mute(!enable); if(myIsInitializedFlag)
pause(!enable); {
mute(!enable);
pause(!enable);
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue, void SoundSDL::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
@ -184,20 +165,20 @@ void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue,
// And start the SDL sound subsystem ... // And start the SDL sound subsystem ...
pause(false); pause(false);
Logger::debug("SoundSDL2::open finished"); Logger::debug("SoundSDL::open finished");
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::mute(bool enable) void SoundSDL::mute(bool enable)
{ {
if(enable) if(enable)
myVolumeFactor = 0; setVolume(0, false);
else else
setVolume(myAudioSettings.volume()); setVolume(myAudioSettings.volume());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::toggleMute() void SoundSDL::toggleMute()
{ {
const bool wasMuted = myVolumeFactor == 0; const bool wasMuted = myVolumeFactor == 0;
mute(!wasMuted); mute(!wasMuted);
@ -211,31 +192,40 @@ void SoundSDL2::toggleMute()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL2::pause(bool enable) bool SoundSDL::pause(bool enable)
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
const bool wasPaused = SDL_GetAudioDeviceStatus(myDevice) == SDL_AUDIO_PAUSED; const bool wasPaused = SDL_AudioStreamDevicePaused(myStream);
if(myIsInitializedFlag) if(myIsInitializedFlag)
{ {
SDL_PauseAudioDevice(myDevice, enable ? 1 : 0); if(enable) SDL_PauseAudioStreamDevice(myStream);
else SDL_ResumeAudioStreamDevice(myStream);
myWavHandler.pause(enable); myWavHandler.pause(enable);
} }
return wasPaused; return wasPaused;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::setVolume(uInt32 volume) void SoundSDL::setVolume(uInt32 volume, bool persist)
{ {
if(myIsInitializedFlag && (volume <= 100)) if(myIsInitializedFlag && (volume <= 100))
{ {
myAudioSettings.setVolume(volume); myVolumeFactor = myAudioSettings.enabled()
myVolumeFactor = myAudioSettings.enabled() ? static_cast<float>(volume) / 100.F : 0; ? static_cast<float>(volume) / 100.F
: 0.F;
SDL_SetAudioStreamGain(myStream, myVolumeFactor);
myWavHandler.setVolumeFactor(myVolumeFactor);
if(persist)
myAudioSettings.setVolume(volume);
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::adjustVolume(int direction) void SoundSDL::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);
@ -257,13 +247,12 @@ void SoundSDL2::adjustVolume(int direction)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string SoundSDL2::about() const string SoundSDL::about() const
{ {
ostringstream buf; ostringstream buf;
buf << "Sound enabled:\n" buf << "Sound enabled:\n"
<< " Volume: " << myAudioSettings.volume() << "%\n" << " Volume: " << myAudioSettings.volume() << "%\n"
<< " Device: " << myDevices.at(myDeviceId).first << '\n' << " Channels: " << static_cast<uInt32>(mySpec.channels)
<< " Channels: " << static_cast<uInt32>(myHardwareSpec.channels)
<< (myAudioQueue->isStereo() ? " (Stereo)" : " (Mono)") << '\n' << (myAudioQueue->isStereo() ? " (Stereo)" : " (Mono)") << '\n'
<< " Preset: "; << " Preset: ";
switch(myAudioSettings.preset()) switch(myAudioSettings.preset())
@ -287,10 +276,7 @@ string SoundSDL2::about() const
default: default:
break; // Not supposed to get here break; // Not supposed to get here
} }
buf << " Fragment size: " << static_cast<uInt32>(myHardwareSpec.samples) buf << " Sample rate: " << static_cast<uInt32>(mySpec.freq) << " Hz\n";
<< " bytes\n"
<< " Sample rate: " << static_cast<uInt32>(myHardwareSpec.freq)
<< " Hz\n";
buf << " Resampling: "; buf << " Resampling: ";
switch(myAudioSettings.resamplingQuality()) switch(myAudioSettings.resamplingQuality())
{ {
@ -315,7 +301,7 @@ string SoundSDL2::about() const
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::initResampler() void SoundSDL::initResampler()
{ {
const Resampler::NextFragmentCallback nextFragmentCallback = [this] () -> Int16* { const Resampler::NextFragmentCallback nextFragmentCallback = [this] () -> Int16* {
Int16* nextFragment = nullptr; Int16* nextFragment = nullptr;
@ -333,13 +319,11 @@ void SoundSDL2::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(myHardwareSpec.freq, myHardwareSpec.samples, Resampler::Format(mySpec.freq, 1024, mySpec.channels > 1);
myHardwareSpec.channels > 1);
switch(myAudioSettings.resamplingQuality()) switch(myAudioSettings.resamplingQuality())
{ {
@ -365,183 +349,147 @@ void SoundSDL2::initResampler()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::callback(void* object, uInt8* stream, int len) void SoundSDL::audioCallback(void* object, SDL_AudioStream* stream,
int additional_amt, int)
{ {
auto* self = static_cast<SoundSDL2*>(object); auto* self = static_cast<SoundSDL*>(object);
std::vector<uInt8>& buf = self->myBuffer;
if(self->myAudioQueue && self->myResampler) // Make sure we always have enough room in the buffer
{ if(std::cmp_greater_equal(additional_amt, buf.capacity()))
// The stream is 32-bit float (even though this callback is 8-bits), since buf.resize(additional_amt);
// 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);
for(uInt32 i = 0; i < length; ++i) // The stream is 32-bit float (even though this callback is 8-bits), since
s[i] *= SoundSDL2::myVolumeFactor; // the resampler and TIA audio subsystem always generate float samples
} 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
SDL_memset(stream, 0, len); return false;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL2::playWav(const string& fileName, uInt32 position, uInt32 length) void SoundSDL::stopWav()
{
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 SoundSDL2::wavSize() const uInt32 SoundSDL::wavSize() const
{ {
return myWavHandler.size(); return myWavHandler.size();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL2::WavHandlerSDL2::play( bool SoundSDL::WavHandler::play(SDL_AudioDeviceID device,
const string& fileName, const char* device, uInt32 position, uInt32 length) const string& fileName, 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_FreeWAV(myBuffer); SDL_free(myBuffer);
myBuffer = nullptr; myBuffer = nullptr;
} }
SDL_zero(mySpec); SDL_zero(mySpec);
if(SDL_LoadWAV(fileName.c_str(), &mySpec, &myBuffer, &myLength) == nullptr) if(!SDL_LoadWAV(fileName.c_str(), &mySpec, &myBuffer, &myLength))
return false; return false;
// Set the callback function
mySpec.callback = callback;
mySpec.userdata = this;
} }
if(position > myLength) if(position > myLength)
return false; return false;
myFilename = fileName; 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;
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 SoundSDL2::WavHandlerSDL2::stop() void SoundSDL::WavHandler::stop()
{ {
if(myBuffer) if(myBuffer)
{ {
// Clean up // Clean up
myRemaining = 0; myRemaining = 0;
SDL_CloseAudioDevice(myDevice); myDevice = 0; SDL_UnbindAudioStream(myStream); myStream = nullptr;
SDL_FreeWAV(myBuffer); myBuffer = nullptr; SDL_free(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)
{ {
myCvtBuffer.reset(); if(state) SDL_PauseAudioStreamDevice(myStream);
myCvtBufferSize = 0; else SDL_ResumeAudioStreamDevice(myStream);
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::WavHandlerSDL2::processWav(uInt8* stream, uInt32 len) void SoundSDL::WavHandler::wavCallback(void* object, SDL_AudioStream* stream,
int additional_amt, int)
{ {
SDL_memset(stream, mySpec.silence, len); auto* self = static_cast<WavHandler*>(object);
if(myRemaining) auto len = static_cast<uInt32>(additional_amt);
auto& remaining = self->myRemaining;
if(remaining)
{ {
if(mySpeed != 1.0) if(self->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 > myRemaining) if(len > remaining) // NOLINT(readability-use-std-min-max)
len = myRemaining; len = remaining;
SDL_AudioCVT cvt; SDL_PutAudioStreamData(stream, self->myPos, len);
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
if(!myCvtBuffer || self->myPos += len;
myCvtBufferSize < static_cast<uInt32>(cvt.len * cvt.len_mult)) remaining -= len;
{
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;
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::WavHandlerSDL2::callback(void* object, uInt8* stream, int len) SoundSDL::WavHandler::~WavHandler()
{ {
static_cast<WavHandlerSDL2*>(object)->processWav( ASSERT_MAIN_THREAD;
stream, static_cast<uInt32>(len));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if(myBuffer)
SoundSDL2::WavHandlerSDL2::~WavHandlerSDL2() SDL_free(myBuffer);
{
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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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_SDL2_HXX #ifndef SOUND_SDL_HXX
#define SOUND_SDL2_HXX #define SOUND_SDL_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 SoundSDL2 : public Sound class SoundSDL : 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.
*/ */
SoundSDL2(OSystem& osystem, AudioSettings& audioSettings); SoundSDL(OSystem& osystem, AudioSettings& audioSettings);
~SoundSDL2() override; ~SoundSDL() override;
public: public:
/** /**
@ -92,9 +92,10 @@ class SoundSDL2 : 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) override; void setVolume(uInt32 volume, bool persist = true) 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.
@ -133,13 +134,6 @@ class SoundSDL2 : 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
@ -156,10 +150,14 @@ class SoundSDL2 : public Sound
bool myIsInitializedFlag{false}; bool myIsInitializedFlag{false};
// Audio specification structure // Audio specification structure
SDL_AudioSpec myHardwareSpec{}; SDL_AudioSpec mySpec{};
SDL_AudioDeviceID myDevice{0}; // Audio device and stream, which handles all interaction with SDL sound backend
uInt32 myDeviceId{0}; SDL_AudioDeviceID myDevice{};
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;
@ -169,65 +167,66 @@ class SoundSDL2 : 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 SDL2 sound API. This class implements WAV file playback using the SDL sound API.
*/ */
class WavHandlerSDL2 class WavHandler
{ {
public: public:
explicit WavHandlerSDL2() = default; explicit WavHandler() = default;
~WavHandlerSDL2(); ~WavHandler();
bool play(const string& fileName, const char* device, bool play(SDL_AudioDeviceID device, const string& fileName,
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 setSpeed(const double speed) { mySpeed = speed; } void setVolumeFactor(float volumeFactor);
void pause(bool state) const; void pause(bool state) const;
private: private:
string myFilename; string myFilename;
uInt32 myLength{0}; SDL_AudioStream* myStream{nullptr};
SDL_AudioDeviceID myDevice{0}; SDL_AudioSpec mySpec{};
uInt8* myBuffer{nullptr}; uInt8* myBuffer{nullptr};
uInt32 myLength{0};
double mySpeed{1.0}; double mySpeed{1.0};
unique_ptr<uInt8[]> myCvtBuffer; float myVolumeFactor{1.F}; // Current volume level (0.F - 1.F)
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
void processWav(uInt8* stream, uInt32 len); static void wavCallback(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
WavHandlerSDL2(const WavHandlerSDL2&) = delete; WavHandler(const WavHandler&) = delete;
WavHandlerSDL2(WavHandlerSDL2&&) = delete; WavHandler(WavHandler&&) = delete;
WavHandlerSDL2& operator=(const WavHandlerSDL2&) = delete; WavHandler& operator=(const WavHandler&) = delete;
WavHandlerSDL2& operator=(WavHandlerSDL2&&) = delete; WavHandler& operator=(WavHandler&&) = delete;
}; };
WavHandlerSDL2 myWavHandler; WavHandler 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 callback(void* object, uInt8* stream, int len); static void audioCallback(void* object, SDL_AudioStream* stream,
int additional_amt, int);
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
SoundSDL2() = delete; SoundSDL() = delete;
SoundSDL2(const SoundSDL2&) = delete; SoundSDL(const SoundSDL&) = delete;
SoundSDL2(SoundSDL2&&) = delete; SoundSDL(SoundSDL&&) = delete;
SoundSDL2& operator=(const SoundSDL2&) = delete; SoundSDL& operator=(const SoundSDL&) = delete;
SoundSDL2& operator=(SoundSDL2&&) = delete; SoundSDL& operator=(SoundSDL&&) = delete;
}; };
#endif #endif // SOUND_SDL_HXX
#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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,7 +116,9 @@ 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 (msecSinceLastIntervalEnd > myCooldownTime && myCurrentIntervalFactor > 1) { while (std::cmp_greater(msecSinceLastIntervalEnd, myCooldownTime) &&
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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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, KBDK_HELP = 117, /**< AL Integrated Help Center */
KBDK_MENU = 118, KBDK_MENU = 118, /**< Menu (show menu) */
KBDK_SELECT = 119, KBDK_SELECT = 119,
KBDK_STOP = 120, KBDK_STOP = 120, /**< AC Stop */
KBDK_AGAIN = 121, /**< redo */ KBDK_AGAIN = 121, /**< AC Redo/Repeat */
KBDK_UNDO = 122, KBDK_UNDO = 122, /**< AC Undo */
KBDK_CUT = 123, KBDK_CUT = 123, /**< AC Cut */
KBDK_COPY = 124, KBDK_COPY = 124, /**< AC Copy */
KBDK_PASTE = 125, KBDK_PASTE = 125, /**< AC Paste */
KBDK_FIND = 126, KBDK_FIND = 126, /**< AC Find */
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, KBDK_CANCEL = 155, /**< AC Cancel */
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, /**< ALT-GR(aph) key on non-American keyboards KBDK_MODE = 257, /**< I'm not sure if this is really not covered
* This is like pressing KBDK_RALT + KBDK_RCTRL * by any of the above, but since there's a
* on some keyboards * special SDL_KMOD_MODE for it I'm adding it here
*/ */
/* @} *//* Usage page 0x07 */ /* @} *//* Usage page 0x07 */
@ -341,77 +341,98 @@ 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_AUDIONEXT = 258, KBDK_SLEEP = 258, /**< Sleep */
KBDK_AUDIOPREV = 259, KBDK_WAKE = 259, /**< Wake */
KBDK_AUDIOSTOP = 260,
KBDK_AUDIOPLAY = 261, KBDK_CHANNEL_INCREMENT = 260, /**< Channel Increment */
KBDK_AUDIOMUTE = 262, KBDK_CHANNEL_DECREMENT = 261, /**< Channel Decrement */
KBDK_MEDIASELECT = 263,
KBDK_WWW = 264, KBDK_MEDIA_PLAY = 262, /**< Play */
KBDK_MAIL = 265, KBDK_MEDIA_PAUSE = 263, /**< Pause */
KBDK_CALCULATOR = 266, KBDK_MEDIA_RECORD = 264, /**< Record */
KBDK_COMPUTER = 267, KBDK_MEDIA_FAST_FORWARD = 265, /**< Fast Forward */
KBDK_AC_SEARCH = 268, KBDK_MEDIA_REWIND = 266, /**< Rewind */
KBDK_AC_HOME = 269, KBDK_MEDIA_NEXT_TRACK = 267, /**< Next Track */
KBDK_AC_BACK = 270, KBDK_MEDIA_PREVIOUS_TRACK = 268, /**< Previous Track */
KBDK_AC_FORWARD = 271, KBDK_MEDIA_STOP = 269, /**< Stop */
KBDK_AC_STOP = 272, KBDK_MEDIA_EJECT = 270, /**< Eject */
KBDK_AC_REFRESH = 273, KBDK_MEDIA_PLAY_PAUSE = 271, /**< Play / Pause */
KBDK_AC_BOOKMARKS = 274, KBDK_MEDIA_SELECT = 272, /* Media Select */
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 Walther keys * \name Mobile keys
* *
* These are values that Christian Walther added (for mac keyboard?). * These are values that are often used on mobile phones.
*/ */
/* @{ */ /* @{ */
KBDK_BRIGHTNESSDOWN = 275, KBDK_SOFTLEFT = 287, /**< Usually situated below the display on phones and
KBDK_BRIGHTNESSUP = 276, used as a multi-function feature key for selecting
KBDK_DISPLAYSWITCH = 277, /**< display mirroring/dual display a software defined function shown on the bottom left
switch, video mode switch */ of the display. */
KBDK_KBDILLUMTOGGLE = 278, KBDK_SOFTRIGHT = 288, /**< Usually situated below the display on phones and
KBDK_KBDILLUMDOWN = 279, used as a multi-function feature key for selecting
KBDK_KBDILLUMUP = 280, a software defined function shown on the bottom right
KBDK_EJECT = 281, of the display. */
KBDK_SLEEP = 282, KBDK_CALL = 289, /**< Used for accepting phone calls. */
KBDK_ENDCALL = 290, /**< Used for rejecting phone calls. */
KBDK_APP1 = 283, /* @} *//* Mobile keys */
KBDK_APP2 = 284,
/* @} *//* Walther keys */
/* Add any other keys here. */ /* Add any other keys here. */
KBDK_LAST = 512 /**< not a key, just marks the number of scancodes KBDK_RESERVED = 400, /**< 400-500 reserved for dynamic keycodes */
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 = 0x0000, KBDM_NONE = 0x0000U, /**< no modifier is applicable. */
KBDM_LSHIFT = 0x0001, KBDM_LSHIFT = 0x0001U, /**< the left Shift key is down. */
KBDM_RSHIFT = 0x0002, KBDM_RSHIFT = 0x0002U, /**< the right Shift key is down. */
KBDM_LCTRL = 0x0040, KBDM_LEVEL5 = 0x0004U, /**< the Level 5 Shift key is down. */
KBDM_RCTRL = 0x0080, KBDM_LCTRL = 0x0040U, /**< the left Ctrl (Control) key is down. */
KBDM_LALT = 0x0100, KBDM_RCTRL = 0x0080U, /**< the right Ctrl (Control) key is down. */
KBDM_RALT = 0x0200, KBDM_LALT = 0x0100U, /**< the left Alt key is down. */
KBDM_LGUI = 0x0400, KBDM_RALT = 0x0200U, /**< the right Alt key is down. */
KBDM_RGUI = 0x0800, KBDM_LGUI = 0x0400U, /**< the left GUI key (often the Windows key) is down. */
KBDM_NUM = 0x1000, KBDM_RGUI = 0x0800U, /**< the right GUI key (often the Windows key) is down. */
KBDM_CAPS = 0x2000, KBDM_NUM = 0x1000U, /**< the Num Lock key (may be located on an extended keypad) is down. */
KBDM_MODE = 0x4000, KBDM_CAPS = 0x2000U, /**< the Caps Lock key is down. */
KBDM_RESERVED = 0x8000, KBDM_MODE = 0x4000U, /**< the !AltGr key is down. */
KBDM_CTRL = (KBDM_LCTRL|KBDM_RCTRL), KBDM_SCROLL = 0x8000U, /**< the Scroll Lock key is down. */
KBDM_SHIFT = (KBDM_LSHIFT|KBDM_RSHIFT), KBDM_CTRL = (KBDM_LCTRL | KBDM_RCTRL), /**< Any Ctrl key is down. */
KBDM_ALT = (KBDM_LALT|KBDM_RALT), KBDM_SHIFT = (KBDM_LSHIFT | KBDM_RSHIFT), /**< Any Shift key is down. */
KBDM_GUI = (KBDM_LGUI|KBDM_RGUI) KBDM_ALT = (KBDM_LALT | KBDM_RALT), /**< Any Alt key is down. */
KBDM_GUI = (KBDM_LGUI | KBDM_RGUI) /**< Any GUI key is down. */
}; };
// Test if specified modifier is pressed // Test if specified modifier is pressed
@ -442,7 +463,7 @@ namespace StellaKeyName
inline string_view forKey(StellaKey key) inline string_view forKey(StellaKey key)
{ {
#ifdef SDL_SUPPORT #ifdef SDL_SUPPORT
return SDL_GetScancodeName(SDL_Scancode(key)); return SDL_GetScancodeName(static_cast<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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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 "7.0" #define STELLA_VERSION "8.0_pre"
#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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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, Int32 fsIndex) void VideoModeHandler::setDisplaySize(const Common::Size& display, bool fullscreen)
{ {
myDisplay = display; myDisplay = display;
myFSIndex = fsIndex; myFullscreen = fullscreen;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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 = myFSIndex == -1; const bool windowedRequested = !myFullscreen;
// 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, myFSIndex, Mode::Stretch::Fill, myFullscreen,
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, myFSIndex, Mode::Stretch::Preserve, myFullscreen,
"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, myFSIndex, Mode::Stretch::Fill, myFullscreen,
"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, myFSIndex); Mode::Stretch::None, myFullscreen);
} }
return myMode; return myMode;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, Stretch smode, VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, Stretch smode,
Int32 fsindex, string_view desc, bool fullscreen, string_view desc,
double zoomLevel, Bezel::Info bezelInfo) double zoomLevel, Bezel::Info bezelInfo)
: Mode(iw, ih, iw, ih, smode, fsindex, desc, zoomLevel, 1., bezelInfo) : Mode(iw, ih, iw, ih, smode, fullscreen, 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, Int32 fsindex, string_view desc, Stretch smode, bool fullscreen, 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},
fsIndex{fsindex} fullscreen{fullscreen}
{ {
// Now resize based on windowed/fullscreen mode and stretch factor // Now resize based on windowed/fullscreen mode and stretch factor
if(fsIndex != -1) // fullscreen mode if(fullscreen)
{ {
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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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.};
Int32 fsIndex{-1}; // -1 indicates windowed mode bool fullscreen{false}; // false 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,
Int32 fsindex = -1, string_view desc = "", bool fullscreen = false, 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, Int32 fsindex = -1, Mode(uInt32 iw, uInt32 ih, Stretch smode, bool fullscreen = false,
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
<< " fsIndex= " << vm.fsIndex; << " fullscreen= " << vm.fullscreen;
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 fsIndex Fullscreen mode in use (-1 indicates windowed mode) @param fullscreen Whether to use fullscreen or windowed mode
*/ */
void setDisplaySize(const Common::Size& display, Int32 fsIndex = -1); void setDisplaySize(const Common::Size& display, bool fullscreen);
/** /**
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;
Int32 myFSIndex{-1}; bool myFullscreen{false};
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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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,8 +266,7 @@ 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
if(buflen > myLength) buflen = std::min(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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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-2024 by Bradford W. Mott, Stephen Anthony // Copyright (c) 1995-2025 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