mirror of
https://github.com/visualboyadvance-m/visualboyadvance-m
synced 2025-10-06 08:02:50 +02:00
Compare commits
118 Commits
user-input
...
libretro-m
Author | SHA1 | Date | |
---|---|---|---|
|
074ab8cd5e | ||
|
d5e1a1f36b | ||
|
821b9176bd | ||
|
9031103c9a | ||
|
557f897ead | ||
|
c8a4f66cf8 | ||
|
ddc93ec6e1 | ||
|
ab38ae8f24 | ||
|
d337688fa7 | ||
|
3eea90afb6 | ||
|
b3952d74a8 | ||
|
be09125d52 | ||
|
f264e7f807 | ||
|
fe0791762a | ||
|
61f427dec1 | ||
|
d619ee2bb1 | ||
|
7128e6dd08 | ||
|
0decffea8d | ||
|
9b60e17746 | ||
|
26207038c9 | ||
|
90867bc802 | ||
|
7f49cd33ad | ||
|
d8342d3d67 | ||
|
12e840a8fa | ||
|
6ca609ac7e | ||
|
2b8f9f71ff | ||
|
e76cef79d2 | ||
|
7f06428df8 | ||
|
e4a9340409 | ||
|
5d7023a5d6 | ||
|
3bd7c918cc | ||
|
67e4944cdb | ||
|
df4ff16e2c | ||
|
d4805065b3 | ||
|
e39b1f2c53 | ||
|
73b65a6553 | ||
|
9e556e6a56 | ||
|
11e73f2c61 | ||
|
e97b0448b7 | ||
|
aa6ed14b2a | ||
|
2ce20c4f59 | ||
|
e4ef4aa625 | ||
|
cf5cb40cb9 | ||
|
c450d14311 | ||
|
41572be3f2 | ||
|
4f8da1c574 | ||
|
32091669d4 | ||
|
abd72a5b2e | ||
|
7e6349b19f | ||
|
0782be749e | ||
|
a7b545ab1a | ||
|
1a564f900c | ||
|
3d4e03f85f | ||
|
fc17209ac7 | ||
|
38877ef209 | ||
|
8691a15be8 | ||
|
961fd0304c | ||
|
e2cf6ecba6 | ||
|
b5a4788747 | ||
|
e63acb9545 | ||
|
7a0826a60c | ||
|
1c7db77192 | ||
|
02f3bd6765 | ||
|
d516683a77 | ||
|
834c7de86c | ||
|
4f1a5dd726 | ||
|
5766b9b9c7 | ||
|
5d8426d317 | ||
|
63ec3528f1 | ||
|
f646c3848c | ||
|
09433875bc | ||
|
05c09ff506 | ||
|
7f78fbb3c5 | ||
|
261e26f488 | ||
|
ed820708af | ||
|
5b8b6a0b47 | ||
|
8809ce26b3 | ||
|
a48625855b | ||
|
55c1477d69 | ||
|
2d7a1ea25b | ||
|
244149c00e | ||
|
c0bcf3bfdf | ||
|
13756bcbf9 | ||
|
fc82e06270 | ||
|
df89beb256 | ||
|
82eda48e8f | ||
|
b47787b317 | ||
|
c9668d9a88 | ||
|
90a56c6937 | ||
|
d377f7abff | ||
|
1fac129746 | ||
|
af7d5f7b89 | ||
|
28f7c2010b | ||
|
486330f23d | ||
|
1ae78a04a8 | ||
|
c776da7120 | ||
|
d543784a3d | ||
|
902c6c8e4b | ||
|
d32be9ddbe | ||
|
b776509287 | ||
|
56eb97c846 | ||
|
9f46c575fd | ||
|
e0402a9b0b | ||
|
3e30f54d5f | ||
|
d73085a88c | ||
|
8eb6a6900f | ||
|
3615137c12 | ||
|
bbd5b76f2a | ||
|
bb3604f333 | ||
|
18a0067ca7 | ||
|
cfdbdc4ec2 | ||
|
32ca2ae42f | ||
|
bad96cf91e | ||
|
62294702e4 | ||
|
72c4f33d63 | ||
|
3fe57f540d | ||
|
1e1a369c8d | ||
|
3c16671e39 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
github: rkitover
|
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -11,7 +11,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please try the nightly build from: https://nightly.vba-m.com and factory resetting the emulator from the Help menu.
|
||||
Please try the nightly build from: https://nightly.visualboyadvance-m.org and factory resetting the emulator from the Help menu.
|
||||
On Linux build master from source or use the edge snap.
|
||||
And last but not least, search for existing reports via the filters bar on the issues page.
|
||||
- type: markdown
|
||||
|
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,7 +1,7 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: VBA-M Forum
|
||||
url: https://board.vba-m.com/
|
||||
url: https://board.visualboyadvance-m.org/
|
||||
about: For general questions and support please join our forum or our
|
||||
- name: VBA-M IRC Channel
|
||||
url: https://web.libera.chat/#vba-m
|
||||
|
2
.github/ISSUE_TEMPLATE/feature.yml
vendored
2
.github/ISSUE_TEMPLATE/feature.yml
vendored
@@ -14,7 +14,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please try a nightly build from: https://nightly.vba-m.com to see if your idea has already been implemented.
|
||||
Please try a nightly build from: https://nightly.visualboyadvance-m.org to see if your idea has already been implemented.
|
||||
On Linux build master from source or use the edge snap.
|
||||
And last but not least, search for existing requests via the filters bar on the issues page.
|
||||
- type: textarea
|
||||
|
33
.github/workflows/devkitpro-build.yml
vendored
Normal file
33
.github/workflows/devkitpro-build.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Libretro Devkitpro
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
target_name: [ngc, wii, wiiu, switch]
|
||||
build_type: [release, debug]
|
||||
include:
|
||||
- libretro_build: 'DEBUG=0'
|
||||
build_type: release
|
||||
- libretro_build: 'DEBUG=1'
|
||||
build_type: debug
|
||||
- devkit_container: 'devkitpro/devkitppc:latest'
|
||||
target_name: ngc
|
||||
- devkit_container: 'devkitpro/devkitppc:latest'
|
||||
target_name: wii
|
||||
- devkit_container: 'devkitpro/devkitppc:latest'
|
||||
target_name: wiiu
|
||||
- devkit_container: 'devkitpro/devkita64:latest'
|
||||
target_name: switch
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ matrix.devkit_container }}
|
||||
steps:
|
||||
- name: Checkout the code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
# Libretro build
|
||||
- name: Build libretro core
|
||||
run: make -C src/libretro ${{ matrix.libretro_build }} platform=${{ matrix.target_name }}
|
8
.github/workflows/macos-build.yml
vendored
8
.github/workflows/macos-build.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: macOS Latest Build
|
||||
name: macOS Latest
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
@@ -55,3 +55,9 @@ jobs:
|
||||
name: Build libretro core
|
||||
run: >-
|
||||
nix-shell --command 'make -C src/libretro ${{ matrix.libretro_build }}'
|
||||
|
||||
# Run tests
|
||||
- if: matrix.build_options == 'default'
|
||||
name: Run tests
|
||||
run: >-
|
||||
nix-shell --command 'cd build && ctest -j --output-on-failure'
|
||||
|
2
.github/workflows/msys2-build.yml
vendored
2
.github/workflows/msys2-build.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: MSYS2 Build
|
||||
name: MSYS2
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
14
.github/workflows/ubuntu-build.yml
vendored
14
.github/workflows/ubuntu-build.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Ubuntu Latest Build
|
||||
name: Ubuntu Latest
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
@@ -45,6 +45,9 @@ jobs:
|
||||
run: >-
|
||||
bash installdeps; if [ "${{ matrix.build_compiler }}" = clang ]; then sudo apt -y install clang; fi
|
||||
|
||||
- name: Install xvfb
|
||||
run: sudo apt -y install xvfb
|
||||
|
||||
# CMake build
|
||||
- if: matrix.build_options != 'libretro'
|
||||
name: Configure CMake
|
||||
@@ -58,6 +61,11 @@ jobs:
|
||||
run: sudo ninja -C build install
|
||||
|
||||
# Libretro build
|
||||
- name: Build libretro core
|
||||
if: matrix.build_options == 'libretro'
|
||||
- if: matrix.build_options == 'libretro'
|
||||
name: Build libretro core
|
||||
run: make -C src/libretro ${{ matrix.libretro_build }}
|
||||
|
||||
# Run tests
|
||||
- if: matrix.build_options == 'default'
|
||||
name: Run tests
|
||||
run: cd build && xvfb-run ctest -j --output-on-failure
|
||||
|
7
.github/workflows/visual-studio-build.yml
vendored
7
.github/workflows/visual-studio-build.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Visual Studio Build
|
||||
name: Visual Studio
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
@@ -53,3 +53,8 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build
|
||||
|
||||
# Run tests
|
||||
- if: matrix.build_options == 'default' && matrix.msvc_arch != 'amd64_arm64'
|
||||
name: Run tests
|
||||
run: cd build && ctest -j --output-on-failure
|
||||
|
62
CHANGELOG.md
62
CHANGELOG.md
@@ -4,6 +4,68 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [2.1.11] - 2024-09-15
|
||||
|
||||
==========================
|
||||
* 3eea90af - build: set BUILD_TESTING=OFF when not git checkout [rkitover]
|
||||
* b3952d74 - build: fix ENABLE_LIRC=ON [rkitover]
|
||||
* f264e7f8 - Fix Help -> Translations URL [rkitover]
|
||||
* 61f427de - Write shortcuts in the proper section (#1335) [Steelskin]
|
||||
* d619ee2b - build: fix installing GoogleTest [rkitover]
|
||||
* 26207038 - Update WinSparkle to 0.8.1 and add ARM64 [rkitover]
|
||||
|
||||
## [2.1.10] - 2024-09-08
|
||||
|
||||
==========================
|
||||
* 7f06428d - Disable dialog position save/restore on wxGTK (#1331) [Steelskin]
|
||||
* e4ef4aa6 - Propagate key events (#1323) [Steelskin]
|
||||
* e2cf6ecb - Add option to mute sound during speedup [rkitover]
|
||||
* d516683a - build: fix for wx using GTK2 [rkitover]
|
||||
* 834c7de8 - build: update macOS builder dists [rkitover]
|
||||
* fc82e062 - build: do not build SDL bin on Windows or macOS [rkitover]
|
||||
* d543784a - [UserInput] Filter key events globally [steelskin]
|
||||
* 902c6c8e - [UserInput] Only process shortcut commands once [steelskin]
|
||||
* b7765092 - [bindings] Set default shortcut for recent file 3 [steelskin]
|
||||
* cc65ef28 - doc: add system requirements to README.md [danialhorton]
|
||||
* 32627f6b - [Dialogs] Save and restore dialog positions [steelskin]
|
||||
* 41952d06 - build: update macOS linker tool to 1.5 [rkitover]
|
||||
* 0c39a5ba - build: override FindGettext to not update po files [rkitover]
|
||||
* 1b77d659 - build: update macOS build to ffmpeg 7.0 [rkitover]
|
||||
* 8d08223d - build: fix compatibility with older ffmpeg [rkitover]
|
||||
* af6028a9 - build: fix build for nix on macOS [rkitover]
|
||||
* b52edf52 - build: fix building on macOS with Homebrew [rkitover]
|
||||
* 6766b9ca - build: fix ffmpeg 7.x compat [rkitover]
|
||||
* 795f25bb - build: fix nix deps for OpenGL [rkitover]
|
||||
* 647be137 - gba: set cpsr=spsr when switching to FIQ mode [40356555+Aikku93]
|
||||
* 8abe3e79 - build: remove -lgcc from static link flags [rkitover]
|
||||
* f4835674 - [Audio] Rework audio devices enumeration [steelskin]
|
||||
* 775a571f - build: fix detecting Visual Studio default vcpkg [rkitover]
|
||||
* 64abd3e8 - [Audio] Remove manual memory allocations [steelskin]
|
||||
* 047ad277 - [Dialogs] Prevent viewers from causing a crash [steelskin]
|
||||
* 045c98d8 - build: only use -Werror=lto-type-mismatch on gcc [rkitover]
|
||||
* 3518dc6a - build: fix LTO on Linux [rkitover]
|
||||
* cc9a03ce - Add toggle: SDL GameController mode for joysticks [rkitover]
|
||||
* 8576733c - [Build] Remove lingering references to OpenAl [steelskin]
|
||||
* 05561922 - build: fix MSYS2 check [rkitover]
|
||||
* d9432ebb - build: fix build on MINGW{64,32}/UCRT64 on MSYS2 [rkitover]
|
||||
* f57cad67 - build: fix static linking on MSYS2 CLANG64 [rkitover]
|
||||
* 23e15734 - build: set wxWidgets_DIR with vcpkg [rkitover]
|
||||
* 98b51910 - [Build] Remove ENABLE_NLS, fix TRANSLATIONS_ONLY [steelskin]
|
||||
* a565cea8 - [Build] Remove the OpenGL check [steelskin]
|
||||
* f96e42fe - build: cmake refactor and improvements [Steelskin]
|
||||
* 07e49025 - Fix most remaining release warnings (#1243) [Steelskin]
|
||||
* 18b97b43 - Fix various build warnings (#1242) [Steelskin]
|
||||
* 13a16eb7 - Fix various warnings in filters and headers (#1241) [Steelskin]
|
||||
* f46da1c5 - build: remove our version of FindSDL2.cmake [rkitover]
|
||||
* 404e9a1a - build: add clang to ./installdeps for MSYS2 [rkitover]
|
||||
* 613bd403 - Make menu more reasonably organized (#1230) [wwrustc]
|
||||
* 215e3c5a - build: use find_program() to find powershell [rkitover]
|
||||
* e5aa685f - build: don't use wx utils as UNIX cmds on Windows [rkitover]
|
||||
* 9e4c8e17 - build: fix gentoo dependency namespaces [68k]
|
||||
* 5f853b99 - Update metainfo.xml to new standards [jhonny.oliveira]
|
||||
* e7d135db - Update links to new domain visualboyadvance-m.org [rkitover]
|
||||
* 60fc096f - build: add libglu-devel for solus in installdeps [rkitover]
|
||||
|
||||
## [2.1.9] - 2024-02-03
|
||||
=======================
|
||||
* 84b0a3e3 - Remove SDL sound driver [rkitover]
|
||||
|
@@ -5,6 +5,13 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||
|
||||
if(WIN32)
|
||||
include(RemoveStrawberryPerlFromPATH)
|
||||
|
||||
find_program(POWERSHELL
|
||||
NAMES powershell.exe pwsh.exe
|
||||
HINTS "/Windows/System32/WindowsPowerShell/v1.0"
|
||||
REQUIRED)
|
||||
else()
|
||||
find_program(POWERSHELL pwsh)
|
||||
endif()
|
||||
|
||||
if(UPDATE_APPCAST)
|
||||
@@ -23,15 +30,6 @@ set(VCPKG_DEPS_OPTIONAL
|
||||
faudio ENABLE_FAUDIO
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
find_program(POWERSHELL
|
||||
NAMES powershell.exe pwsh.exe
|
||||
HINTS "/Windows/System32/WindowsPowerShell/v1.0"
|
||||
REQUIRED)
|
||||
else()
|
||||
find_program(POWERSHELL pwsh)
|
||||
endif()
|
||||
|
||||
include(Set-Toolchain-vcpkg)
|
||||
|
||||
# Use ccache if available and not already enabled on the command line.
|
||||
@@ -77,6 +75,32 @@ set(CMAKE_C_STANDARD_REQUIRED True)
|
||||
|
||||
project(VBA-M C CXX)
|
||||
|
||||
include(CTest)
|
||||
include(FetchContent)
|
||||
include(GNUInstallDirs)
|
||||
include(Architecture)
|
||||
include(Options)
|
||||
include(Toolchain)
|
||||
include(Dependencies)
|
||||
|
||||
# Disable tests when not in a git checkout.
|
||||
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
set(BUILD_TESTING OFF)
|
||||
endif()
|
||||
|
||||
# Configure gtest
|
||||
if(BUILD_TESTING)
|
||||
FetchContent_Declare(googletest
|
||||
URL https://github.com/google/googletest/archive/2d16ed055d09c3689d44b272adc097393de948a0.zip
|
||||
EXCLUDE_FROM_ALL
|
||||
)
|
||||
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
FetchContent_MakeAvailable(googletest)
|
||||
|
||||
include(GoogleTest)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_PREFIX_PATH AND (NOT ("$ENV{CMAKE_PREFIX_PATH}" STREQUAL "")))
|
||||
set(CMAKE_PREFIX_PATH "$ENV{CMAKE_PREFIX_PATH}")
|
||||
endif()
|
||||
@@ -87,27 +111,11 @@ elseif(NOT CMAKE_BUILD_TYPE MATCHES "^(Release|Debug|RelWithDebInfo|MinSizeRel)$
|
||||
message(FATAL_ERROR "Invalid CMAKE_BUILD_TYPE: '${CMAKE_BUILD_TYPE}', must be one of: 'Release', 'Debug', 'RelWithDebInfo' or 'MinSizeRel'")
|
||||
endif()
|
||||
|
||||
# Link debug libs for RelWithDebInfo
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO "Debug")
|
||||
endif()
|
||||
|
||||
set(MSYS OFF)
|
||||
if(NOT "$ENV{MSYSTEM_PREFIX}" STREQUAL "")
|
||||
set(MSYS ON)
|
||||
endif()
|
||||
|
||||
include(CTest)
|
||||
if(BUILD_TESTING)
|
||||
enable_testing()
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(Options)
|
||||
include(Architecture)
|
||||
include(Toolchain)
|
||||
include(Dependencies)
|
||||
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
include(GitTagVersion)
|
||||
git_version(VBAM_VERSION VBAM_REVISION VBAM_VERSION_RELEASE)
|
||||
|
@@ -9,17 +9,15 @@
|
||||
- [Commit Message](#commit-message)
|
||||
- [Collaboration on a Branch](#collaboration-on-a-branch)
|
||||
- [Commits from Maintainers](#commits-from-maintainers)
|
||||
- [Strings, Character Sets and Translations](#strings-character-sets-and-translations)
|
||||
- [Pulling Updated Translations](#pulling-updated-translations)
|
||||
- [Translations Message Catalog](#translations-message-catalog)
|
||||
- [Interaction with non-wxWidgets Code](#interaction-with-non-wxwidgets-code)
|
||||
- [Windows Native Development Environment Setup](#windows-native-development-environment-setup)
|
||||
- [Miscellaneous](#miscellaneous)
|
||||
- [Debug Messages](#debug-messages)
|
||||
- [Release Process](#release-process)
|
||||
- [Environment](#environment)
|
||||
- [Certificates](#certificates)
|
||||
- [Release Commit and Tag](#release-commit-and-tag)
|
||||
- [64-bit Windows Binary](#64-bit-windows-binary)
|
||||
- [32-bit Windows Binary](#32-bit-windows-binary)
|
||||
- [64-bit Mac Binary](#64-bit-mac-binary)
|
||||
- [ARM64 Windows Binary](#arm64-windows-binary)
|
||||
- [macOS Binary](#macos-binary)
|
||||
- [Final steps](#final-steps)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
@@ -53,11 +51,12 @@ Follow the following steps to process newly submitted issues:
|
||||
- An issue is resolved by closing it in github. A commit that fixes the issue
|
||||
should have the following line near the end of the body of the commit message:
|
||||
```
|
||||
- Fix #999.
|
||||
Fix #999.
|
||||
```
|
||||
This will automatically close the issue and assign the closing commit in the
|
||||
github metadata when it is merged to master. The issue can be reopened if
|
||||
needed.
|
||||
|
||||
- A commit that is work towards resolving an issue, should have the issue number
|
||||
preceded by a pound sign either at the end of a commit message title, if it is
|
||||
of primary relevance to the issue, or the body otherwise.
|
||||
@@ -70,7 +69,7 @@ Follow these guidelines always:
|
||||
|
||||
https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
||||
|
||||
the description of your work should be in the **commit message NOT the pull
|
||||
, the description of your work should be in the **commit message NOT the pull
|
||||
request description**.
|
||||
|
||||
The title line must be no more than 50 characters and the description must be
|
||||
@@ -95,16 +94,21 @@ other projects, fewer is better.
|
||||
The commit title line should be prefixed with an area, unless it involves the
|
||||
wxWidgets GUI app, in which case it should **NOT** have a prefix.
|
||||
|
||||
If the commit is a user-facing functionality change or enhancement, the title
|
||||
line of the commit must be a non-technical description of this change. For
|
||||
example "Mute on speedup" because this will go into the changelog.
|
||||
|
||||
The text after the area prefix should not be capitalized.
|
||||
|
||||
Please use one of these area prefixes otherwise:
|
||||
Please use one of these area prefixes for non-main-GUI-app commits:
|
||||
|
||||
- doc: documentation, README.md etc.
|
||||
- build: cmake, installdeps, preprocessor compatibility defines, etc.
|
||||
- gb-core: the GameBoy emulator core
|
||||
- gba-core: the GameBoy Advance emulator core
|
||||
- build: cmake, installdeps, preprocessor compatibility defines, compatibility
|
||||
headers, etc.
|
||||
- gb: the GameBoy emulator core
|
||||
- gba: the GameBoy Advance emulator core
|
||||
- libretro: the libretro core glue and build
|
||||
- sdl-app: anything for the SDL app
|
||||
- sdl: anything for the SDL port
|
||||
- translations: anything related to translations
|
||||
|
||||
. Add other areas here if needed.
|
||||
@@ -138,137 +142,75 @@ things in mind:
|
||||
better to edit the history than to add more commits. Never add commits fixing
|
||||
previous commits, only improving or adding to them.
|
||||
|
||||
- To update when someone else (very rudely you might say) did a `push -f`, `pull
|
||||
--rebase` will **USUALLY** work. Verify the log, and if necessary do this
|
||||
instead:
|
||||
|
||||
- To update when someone else updated the branch with a `push -f`
|
||||
```bash
|
||||
git status # should be clean, with your work having been already pushed
|
||||
git fetch --all --prune
|
||||
git reset --hard origin/<branch-name>
|
||||
```
|
||||
.
|
||||
|
||||
While actively working on a branch, keep it rebased on top of master.
|
||||
- While actively working on a branch, keep it rebased on top of master.
|
||||
|
||||
#### Commits from Maintainers
|
||||
|
||||
Maintainers have the power to commit directly to master. This power must be
|
||||
used responsibly, something I fail to do myself often, and will try to improve
|
||||
upon.
|
||||
Maintainers and project members have the power to commit directly to master.
|
||||
This power must be used responsibly.
|
||||
|
||||
Make your most earnest attempt to follow these general guidelines to keep our
|
||||
Make your best attempt to follow these general guidelines to keep our
|
||||
history clean:
|
||||
|
||||
- Things that are a straight fix or improvement that does not require discussion
|
||||
- Things that are a minor fix or improvement that does not require discussion
|
||||
can be committed directly, keeping the following guidelines in mind.
|
||||
|
||||
- Bigger new features, code refactors and changes in architecture should go
|
||||
through the PR process.
|
||||
|
||||
- Push code changes to a branch first, so they can run through the CI.
|
||||
Differences in what different compilers allow is a problem that comes up
|
||||
**VERY** frequently. As well as incompatibilities between different
|
||||
configurations for both the C++ code and any supporting code.
|
||||
- Push code changes to a branch first, so they can run through the CI. When you
|
||||
open the commit in GitHub there is a little icon in the upper left corner that
|
||||
shows the CI status for this commit. Differences in what different compilers
|
||||
allow is a problem that comes up **VERY** frequently. As well as
|
||||
incompatibilities between different configurations for both the C++ code and
|
||||
any supporting code.
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
### Strings, Character Sets and Translations
|
||||
#### Debug Messages
|
||||
|
||||
#### Pulling Updated Translations
|
||||
We have an override for `wxLogDebug()` to make it work even in non-debug builds
|
||||
of wx and on windows, even in mintty.
|
||||
|
||||
Once in a while it is necessary to pull new and updated translations from
|
||||
transifex.
|
||||
|
||||
For this you need the transifex client, available for Windows as well from
|
||||
chocolatey as `transifex-client`.
|
||||
|
||||
To pull translations run:
|
||||
|
||||
```bash
|
||||
tx pull -af
|
||||
```
|
||||
|
||||
then check `git status` and if any message catalogs were updated, commit the
|
||||
result as:
|
||||
|
||||
```bash
|
||||
git commit -a --signoff -S -m'Transifex pull.'
|
||||
git push
|
||||
```
|
||||
|
||||
#### Translations Message Catalog
|
||||
|
||||
Strings that need to be translated by our wonderful translators on transifex
|
||||
(thank you guys very much) need to be enclosed in `_("...")`, for example:
|
||||
It works like `printf()`, e.g.:
|
||||
|
||||
```cpp
|
||||
wxLogError(_("error: something very wrong"));
|
||||
int foo = 42;
|
||||
wxLogDebug(wxT("the value of foo = %d"), foo);
|
||||
```
|
||||
|
||||
The next time you run cmake after adding a string to be translated, the `.pot`
|
||||
message catalog source will be regenerated, and you will see a loud message
|
||||
telling you to push to transifex.
|
||||
|
||||
Strings in the XRC XML GUI definition files are automatically added to the
|
||||
message catalog as well.
|
||||
|
||||
If you are working on a branch or a PR, don't push to transifex until it has
|
||||
been merged to master.
|
||||
|
||||
Once it is, push it with:
|
||||
|
||||
```bash
|
||||
tx push -s
|
||||
```
|
||||
|
||||
#### Interaction with non-wxWidgets Code
|
||||
|
||||
Use our `UTF8(...)` function to force any `wxString` to UTF-8 for use by other
|
||||
libraries, screen output or OS APIs. For example:
|
||||
From the core etc. the usual:
|
||||
|
||||
```cpp
|
||||
fprintf(STDERR, "Error: %s\n", UTF8(err_msg));
|
||||
fprintf(stderr, "...", ...);
|
||||
```
|
||||
, will work fine.
|
||||
|
||||
There is one exception to this, when using `wxString::Printf()` and such, you
|
||||
can't pass another `wxString` to the `%s` format directly, use something like
|
||||
this:
|
||||
|
||||
```cpp
|
||||
wxString err;
|
||||
err.Printf("Cannot read file: %s", fname.wc_str());
|
||||
```
|
||||
|
||||
this uses the `wchar_t` UTF-16 representation on Windows and does nothing
|
||||
elsewhere.
|
||||
|
||||
For calling Windows APIs with strings, use the wide char `W` variants and the
|
||||
`wc_str()` method as well.
|
||||
|
||||
### Windows Native Development Environment Setup
|
||||
|
||||
This guide has been moved to:
|
||||
|
||||
https://github.com/rkitover/windows-dev-guide
|
||||
You need a debug build for this to work or to even have a console on Windows.
|
||||
Pass `-DCMAKE_BUILD_TYPE=Debug` to cmake.
|
||||
|
||||
### Release Process
|
||||
|
||||
#### Environment
|
||||
#### GnuPG Key
|
||||
|
||||
The variable `VBAM_NO_PAUSE`, if set, will cause cmake to not pause before gpg
|
||||
signing operations, you want to set this if you've disabled your gpg passphrase
|
||||
to not require interaction during release builds.
|
||||
You will need to create a GnuPG key for signing your commits and release tags,
|
||||
and upload it to a keyserver.
|
||||
|
||||
gpg set up with your key is helpful for the release process on all environments
|
||||
where a binary is built, but you can also make the detached signature files
|
||||
yourself at the end of the process.
|
||||
Make sure to install GnuPG on all environments where you will be making commits
|
||||
and tags.
|
||||
|
||||
For codesigning windows binaries, put your certificate into
|
||||
`~/.codesign/windows_comodo.pkcs12`.
|
||||
#### Certificates
|
||||
|
||||
On Mac the 'Developer ID Application' certificate stored in your login keychain
|
||||
is used, `keychain unlock` will prompt you for your login keychain password, to
|
||||
avoid that set the `LOGIN_KEYCHAIN_PASSWORD` environment variable to your
|
||||
password.
|
||||
Make sure you have set up a Windows code signing certificate with the right
|
||||
password and a Mac 'Developer ID Application' certificate.
|
||||
|
||||
#### Release Commit and Tag
|
||||
|
||||
@@ -280,8 +222,10 @@ tag:
|
||||
mkdir build && cd build
|
||||
cmake .. -DTAG_RELEASE=TRUE
|
||||
```
|
||||
, follow the instructions to edit the `CHANGELOG.md` and then push the release:
|
||||
|
||||
then push the release:
|
||||
To reiterate, **make sure you edit the `CHANGELOG.md`** to remove any
|
||||
non-user-facing changes before you make the release commit.
|
||||
|
||||
```bash
|
||||
git push
|
||||
@@ -296,84 +240,126 @@ cmake .. -DTAG_RELEASE=UNDO
|
||||
|
||||
#### 64-bit Windows Binary
|
||||
|
||||
For this you will preferably need the powershell environment setup described
|
||||
earlier, however you can use a regular Visual Studio 64 bit native developer
|
||||
command prompt as well.
|
||||
For this you will preferably need the PowerShell environment setup described
|
||||
[here](https://github.com/rkitover/windows-dev-guide), or by starting the `x64
|
||||
Native Tools Command Prompt` from your Start Menu.
|
||||
|
||||
```powershell
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_BUILD_TYPE=Release -DUPSTREAM_RELEASE=TRUE -G Ninja
|
||||
mkdir build-msvc64
|
||||
cd build-msvc64
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DUPSTREAM_RELEASE=TRUE -G Ninja
|
||||
ninja
|
||||
```
|
||||
|
||||
Collect the following files for the release:
|
||||
|
||||
- `visualboyadvance-m-Win-64bit.zip`
|
||||
- `visualboyadvance-m-Win-64bit.zip.asc`
|
||||
- `visualboyadvance-m-Win-x86_64.zip`
|
||||
- `translations.zip`
|
||||
- `translations.zip.asc`
|
||||
|
||||
Repeat the process for the debug build, with `-DCMAKE_BUILD_TYPE=Debug` and
|
||||
collect this file:
|
||||
|
||||
- `visualboyadvance-m-Win-x86_64-debug.zip`
|
||||
.
|
||||
|
||||
#### 32-bit Windows Binary
|
||||
|
||||
For this the optimal environment is a linux distribution with the mingw
|
||||
toolchain, I use fedora.
|
||||
The 32-bit build is a legacy build for Windows XP compatibility. You will need
|
||||
the MinGW toolchain to build it. The easiest method is to use the MINGW32 MSYS2
|
||||
environment.
|
||||
|
||||
You can set up a shell on a fedora distribution with docker as described here:
|
||||
Make sure the Visual Studio `signtool.exe` is in your path, you can start MSYS2
|
||||
with an inherited `PATH` from a Visual Studio enabled environment or add it to
|
||||
your shell configuration.
|
||||
|
||||
https://gist.github.com/rkitover/6379764c619c10e829e4b2fa0ae243fd
|
||||
|
||||
If using fedora, the cross script will install all necessary dependencies, if
|
||||
not install the base toolchain (mingw gcc, binutils, winpthreads) using the
|
||||
preferred method for your distribution, you can also use mxe for this.
|
||||
|
||||
https://mxe.cc/
|
||||
First install dependencies with:
|
||||
|
||||
```bash
|
||||
sh tools/win/linux-cross-builder -32
|
||||
./installdeps
|
||||
```
|
||||
|
||||
You can also use msys2 on Windows, this is not recommended:
|
||||
. Then build the 32-bit binary as follows:
|
||||
|
||||
```bash
|
||||
sh tools/win/msys2-builder -32
|
||||
mkdir build-mingw32
|
||||
cd build-mingw32
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DUPSTREAM_RELEASE=TRUE -G Ninja
|
||||
ninja
|
||||
```
|
||||
. Collect this file for the release:
|
||||
|
||||
To set up msys2, see this guide:
|
||||
- `visualboyadvance-m-Win-x86_32.zip`
|
||||
|
||||
https://gist.github.com/rkitover/d008324309044fc0cc742bdb16064454
|
||||
. Then repeat the process for the debug build with `-DCMAKE_BUILD_TYPE=Debug`,
|
||||
and collect this file:
|
||||
|
||||
Collect the following files from `~/vbam-build-mingw32/project` if using linux,
|
||||
or `~/vbam-build-msys2-x86_64/project` if using msys2:
|
||||
- `visualboyadvance-m-Win-x86_32-debug.zip`
|
||||
.
|
||||
|
||||
- `visualboyadvance-m-Win-32bit.zip`
|
||||
- `visualboyadvance-m-Win-32bit.zip.asc`
|
||||
#### ARM64 Windows Binary
|
||||
|
||||
#### 64-bit Mac Binary
|
||||
You will need the MSVC ARM64 cross toolchain to build this binary, if you used
|
||||
the install script from [here](https://github.com/rkitover/windows-dev-guide)
|
||||
you will have it installed, otherwise run Visual Studio Installer and install
|
||||
the component.
|
||||
|
||||
To enter the ARM64 cross environment, edit the PowerShell profile described
|
||||
[here](https://github.com/rkitover/windows-dev-guide) or use the `vcvarsall.bat`
|
||||
script with the `amd64_arm64` argument as described
|
||||
[here](https://learn.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=msvc-170).
|
||||
|
||||
From there the process is the same as for the 64-bit build, collect the
|
||||
following files for the release:
|
||||
|
||||
- `visualboyadvance-m-Win-arm64.zip`
|
||||
- 'visualboyadvance-m-Win-arm64-debug.zip'
|
||||
.
|
||||
|
||||
#### macOS Binary
|
||||
|
||||
Install the latest Xcode for your OS.
|
||||
|
||||
You will need bash and (optionally) gpg from homebrew (which you will also need
|
||||
to install):
|
||||
|
||||
```bash
|
||||
brew install bash gnupg
|
||||
```
|
||||
You will need bash from Homebrew/nix/MacPorts/whatever to run the build script.
|
||||
|
||||
You will need a codesigning certificate from Apple, which you will be able to
|
||||
generate once you join their developer program. This is the certificate of the
|
||||
type 'Developer ID Application' stored in your login keychain. `keychain
|
||||
unlock` will prompt you for your login keychain password, to avoid that set the
|
||||
`LOGIN_KEYCHAIN_PASSWORD` environment variable to your password.
|
||||
generate once you join their developer program from XCode. This is the
|
||||
certificate of the type 'Developer ID Application' stored in your login
|
||||
keychain.
|
||||
|
||||
If you are not using a GUI session, you will need to use a method to unlock your
|
||||
login keychain before building so that your codesigning certificate can be used.
|
||||
Adding the certificate and key to the System keychain is also a method that some
|
||||
people use.
|
||||
|
||||
To unlock your keychain on login, you can add something like this to your
|
||||
`~/.zshrc`:
|
||||
|
||||
```bash
|
||||
/usr/local/bin/bash tools/osx/builder -64
|
||||
security unlock-keychain -p "$(cat ~/.login-keychain-password)" login.keychain
|
||||
```
|
||||
, with your login password in that file.
|
||||
|
||||
For notarization to work, you will need to create an app-specific password on
|
||||
https://appleid.apple.com , get your Team ID from your Apple Developer account,
|
||||
and store them with this command:
|
||||
|
||||
```bash
|
||||
xcrun notarytool store-credentials AC_PASSWORD \
|
||||
--apple-id you@domain.com \
|
||||
--team-id <DeveloperTeamID> \
|
||||
--password <secret_app_specific_2FA_password>
|
||||
```
|
||||
. Once all of this is set up, run:
|
||||
|
||||
```bash
|
||||
tools/osx/builder
|
||||
```
|
||||
, this will take a while because it builds all of the dependencies.
|
||||
|
||||
Collect the following files from `~/vbam-build-mac-64bit/project`:
|
||||
|
||||
- `visualboyadvance-m-Mac-64bit.zip`
|
||||
- `visualboyadvance-m-Mac-64bit.zip.asc`
|
||||
- `visualboyadvance-m-Mac-x86_64.zip`
|
||||
- `visualboyadvance-m-Mac-x86_64-debug.zip`
|
||||
.
|
||||
|
||||
#### Final steps
|
||||
|
||||
@@ -381,27 +367,26 @@ Go to the github releases tab, and make a release for the tag you pushed
|
||||
earlier.
|
||||
|
||||
Put any notes to users and distro maintainers into the description as well as
|
||||
the entries from `CHANGELOG.md` generated earlier from git by the release
|
||||
commit script.
|
||||
the generated entries from `CHANGELOG.md` you edited earlier.
|
||||
|
||||
Upload all files collected during the earlier builds, the complete list is:
|
||||
|
||||
|
||||
- `translations.zip`
|
||||
- `translations.zip.asc`
|
||||
- `visualboyadvance-m-Mac-64bit.zip`
|
||||
- `visualboyadvance-m-Mac-64bit.zip.asc`
|
||||
- `visualboyadvance-m-Win-32bit.zip`
|
||||
- `visualboyadvance-m-Win-32bit.zip.asc`
|
||||
- `visualboyadvance-m-Win-64bit.zip`
|
||||
- `visualboyadvance-m-Win-64bit.zip.asc`
|
||||
- `visualboyadvance-m-Win-x86_64.zip`
|
||||
- `visualboyadvance-m-Win-x86_64-debug.zip`
|
||||
- `visualboyadvance-m-Win-x86_32.zip`
|
||||
- `visualboyadvance-m-Win-x86_32-debug.zip`
|
||||
- `visualboyadvance-m-Win-arm64.zip`
|
||||
- 'visualboyadvance-m-Win-arm64-debug.zip'
|
||||
- `visualboyadvance-m-Mac-x86_64.zip`
|
||||
- `visualboyadvance-m-Mac-x86_64-debug.zip`
|
||||
|
||||
Update the winsparkle appcast.xml by running this cmake command:
|
||||
Update the winsparkle `appcast.xml` by running this cmake command:
|
||||
|
||||
```bash
|
||||
cmake .. -DUPDATE_APPCAST=TRUE
|
||||
```
|
||||
|
||||
follow the instructions to push the change to the web data repo.
|
||||
, follow the instructions to push the change to the web data repo.
|
||||
|
||||
Announce the release on reddit r/emulation and the forum.
|
||||
|
172
README.md
172
README.md
@@ -19,8 +19,6 @@
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
[](https://gitter.im/visualboyadvance-m/Lobby)
|
||||
|
||||
Our bridged Discord server is [Here](https://discord.gg/EpfxEuGMKH).
|
||||
|
||||
We are also on *`#vba-m`* on [Libera IRC](https://libera.chat/) which has a [Web
|
||||
@@ -59,35 +57,39 @@ Factory Reset`.
|
||||
|
||||
## System Requirements
|
||||
|
||||
Windows 7, 8.1 or 10/11, Linux distro's or macOS.
|
||||
Windows XP, Vista, 7, 8.1 or 10/11, Linux distros or macOS.
|
||||
|
||||
2Ghz x86 (or x86-64) Intel Core 2 or AMD Athlon processor with SSE, Snapdragon 835
|
||||
or newer CPU compatible with Arm for Windows.
|
||||
|
||||
2Ghz x86(or x86-64) Intel Core 2 or AMD Athlon processor with SSE, Snapdragon 835
|
||||
or newer cpu compatible with Arm for Windows.
|
||||
- Just a guideline, lower clock speeds and Celeron processors may be able to run at full
|
||||
speed on lower settings, and Linux based ARM Operating systems have wider cpu support.
|
||||
speed on lower settings, and Linux based ARM Operating systems have wider CPU support.
|
||||
|
||||
DirectX June 2010 Redist [Full](https://www.microsoft.com/en-au/download/details.aspx?id=8109) / [Websetup](https://www.microsoft.com/en-au/download/details.aspx?id=35) for Xaudio (Remember to uncheck Bing on the websetup)
|
||||
DirectX June 2010 Redist
|
||||
[Full](https://www.microsoft.com/en-au/download/details.aspx?id=8109) /
|
||||
[Websetup](https://www.microsoft.com/en-au/download/details.aspx?id=35) for
|
||||
Xaudio (Remember to uncheck Bing on the websetup.)
|
||||
|
||||
## Building
|
||||
|
||||
The basic formula to build vba-m is:
|
||||
|
||||
```shell
|
||||
```bash
|
||||
cd ~ && mkdir src && cd src
|
||||
git clone https://github.com/visualboyadvance-m/visualboyadvance-m.git
|
||||
cd visualboyadvance-m
|
||||
./installdeps
|
||||
|
||||
# ./installdeps will give you build instructions, which will be similar to:
|
||||
./installdeps # On Linux or macOS
|
||||
|
||||
mkdir build && cd build
|
||||
cmake .. -G Ninja
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -G Ninja
|
||||
ninja
|
||||
```
|
||||
|
||||
`./installdeps` is supported on MSys2, Linux (Debian/Ubuntu, Fedora, Arch,
|
||||
Solus, OpenSUSE, Gentoo and RHEL/CentOS) and Mac OS X (homebrew, macports or
|
||||
fink.)
|
||||
`./installdeps` is supported on MSYS2, Linux (Debian/Ubuntu, Fedora, Arch,
|
||||
Solus, OpenSUSE, Gentoo and RHEL/CentOS) and Mac OS X (homebrew, MacPorts or
|
||||
Fink.)
|
||||
|
||||
## Building a Libretro core
|
||||
|
||||
@@ -98,7 +100,7 @@ cd src/libretro
|
||||
make -j`nproc`
|
||||
```
|
||||
|
||||
Copy vbam_libretro.so to your RetroArch cores directory.
|
||||
Copy `vbam_libretro.so` to your RetroArch cores directory.
|
||||
|
||||
## Visual Studio Support
|
||||
|
||||
@@ -118,14 +120,16 @@ environment loaded.
|
||||
Using your own user-wide installation of vcpkg is supported, just make sure the
|
||||
environment variable `VCPKG_ROOT` is set.
|
||||
|
||||
To build in the visual studio command prompt, use something like this:
|
||||
To build in the Visual Studio `x64 Native Tools Command Prompt`, use something
|
||||
like this:
|
||||
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_BUILD_TYPE=Debug -G Ninja
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -G Ninja
|
||||
ninja
|
||||
```
|
||||
.
|
||||
|
||||
## Visual Studio Code Support
|
||||
|
||||
@@ -163,7 +167,7 @@ And the following development libraries:
|
||||
- [gettext](https://www.gnu.org/software/gettext/) and gettext-tools
|
||||
- [SDL2](https://www.libsdl.org/) (required)
|
||||
- [SFML](https://www.sfml-dev.org/) (optional, for link)
|
||||
- [OpenAL](https://www.openal.org/) or [openal-soft](https://kcat.strangesoft.net/openal.html) (required, a sound interface)
|
||||
- [openal-soft](https://kcat.strangesoft.net/openal.html) (required, a sound interface)
|
||||
- [wxWidgets](https://wxwidgets.org/) (required for GUI, 2.8 and non-stl builds are no longer supported)
|
||||
|
||||
On Linux and similar, you also need the version of GTK your wxWidgets is linked
|
||||
@@ -183,61 +187,47 @@ This is supported on Fedora, Arch, Solus and MSYS2.
|
||||
may be `win32` which is an alias for `mingw-w64-i686` to target 32 bit Windows,
|
||||
or `mingw-w64-x86_64` for 64 bit Windows targets.
|
||||
|
||||
The target is implicit on MSys2 depending on which MINGW shell you started (the
|
||||
The target is implicit on MSYS2 depending on which MINGW shell you started (the
|
||||
value of `$MSYSTEM`.)
|
||||
|
||||
On Debian/Ubuntu this uses the MXE apt repository and works quite well.
|
||||
|
||||
On Fedora it can build using the Fedora MinGW packages, albeit with wx 2.8, no
|
||||
OpenGL support, and no Link support for lack of SFML.
|
||||
|
||||
On Arch it currently doesn't work at all because the AUR stuff is completely
|
||||
broken, I will at some point redo the arch stuff to use MXE as well.
|
||||
|
||||
## CMake Options
|
||||
|
||||
The CMake code tries to guess reasonable defaults for options, but you can
|
||||
override them, for example:
|
||||
|
||||
```shell
|
||||
cmake .. -DENABLE_LINK=NO -G Ninja
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DENABLE_LINK=NO -G Ninja
|
||||
```
|
||||
|
||||
Of particular interest is making **Release** or **Debug** builds, the default
|
||||
mode is **Release**, to make a **Debug** build use something like:
|
||||
|
||||
```shell
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -G Ninja
|
||||
```
|
||||
|
||||
Here is the complete list:
|
||||
. Here is the complete list:
|
||||
|
||||
| **CMake Option** | **What it Does** | **Defaults** |
|
||||
|-----------------------|----------------------------------------------------------------------|-----------------------|
|
||||
| ENABLE_SDL | Build the SDL port | OFF |
|
||||
| ENABLE_WX | Build the wxWidgets port | ON |
|
||||
| ENABLE_DEBUGGER | Enable the debugger | ON |
|
||||
| ENABLE_ASM_CORE | Enable x86 ASM CPU cores (**BUGGY AND DANGEROUS**) | OFF |
|
||||
| ENABLE_ASM | Enable the following two ASM options | ON for 32 bit builds |
|
||||
| ENABLE_ASM_SCALERS | Enable x86 ASM graphic filters | ON for 32 bit builds |
|
||||
| ENABLE_MMX | Enable MMX | ON for 32 bit builds |
|
||||
| ENABLE_LINK | Enable GBA linking functionality (requires SFML) | AUTO |
|
||||
| ENABLE_LIRC | Enable LIRC support | OFF |
|
||||
| ENABLE_FFMPEG | Enable ffmpeg A/V recording | AUTO |
|
||||
| ENABLE_ONLINEUPDATES | Enable online update checks | ON |
|
||||
| ENABLE_LTO | Compile with Link Time Optimization (gcc and clang only) | ON for release build |
|
||||
| ENABLE_GBA_LOGGING | Enable extended GBA logging | ON |
|
||||
| ENABLE_DIRECT3D | Direct3D rendering for wxWidgets (Windows, **NOT IMPLEMENTED!!!**) | ON |
|
||||
| ENABLE_XAUDIO2 | Enable xaudio2 sound output for wxWidgets (Windows only) | ON |
|
||||
| ENABLE_ASAN | Enable libasan sanitizers (by default address, only in debug mode) | OFF |
|
||||
| UPSTREAM_RELEASE | Do some release tasks, like codesigning, making zip and gpg sigs. | OFF |
|
||||
| BUILD_TESTING | Build the tests and enable ctest support. | ON |
|
||||
| VBAM_STATIC | Try link all libs statically (the following are set to ON if ON) | OFF |
|
||||
| SDL2_STATIC | Try to link static SDL2 libraries | OFF |
|
||||
| SFML_STATIC_LIBRARIES | Try to link static SFML libraries | OFF |
|
||||
| FFMPEG_STATIC | Try to link static ffmpeg libraries | OFF |
|
||||
| OPENAL_STATIC | Try to link static OpenAL libraries | OFF |
|
||||
| TRANSLATIONS_ONLY | Build only the translations.zip and nothing else | OFF |
|
||||
| `ENABLE_SDL` | Build the SDL port | OFF |
|
||||
| `ENABLE_WX` | Build the wxWidgets port | ON |
|
||||
| `ENABLE_DEBUGGER` | Enable the debugger | ON |
|
||||
| `ENABLE_ASM_CORE` | Enable x86 ASM CPU cores (**BUGGY AND DANGEROUS**) | OFF |
|
||||
| `ENABLE_ASM` | Enable the following two ASM options | ON for 32 bit builds |
|
||||
| `ENABLE_ASM_SCALERS` | Enable x86 ASM graphic filters | ON for 32 bit builds |
|
||||
| `ENABLE_MMX` | Enable MMX | ON for 32 bit builds |
|
||||
| `ENABLE_LINK` | Enable GBA linking functionality (requires SFML) | AUTO |
|
||||
| `ENABLE_LIRC` | Enable LIRC support | OFF |
|
||||
| `ENABLE_FFMPEG` | Enable ffmpeg A/V recording | AUTO |
|
||||
| `ENABLE_ONLINEUPDATES` | Enable online update checks | ON |
|
||||
| `ENABLE_LTO` | Compile with Link Time Optimization (gcc and clang only) | ON for release build |
|
||||
| `ENABLE_GBA_LOGGING` | Enable extended GBA logging | ON |
|
||||
| `ENABLE_XAUDIO2` | Enable xaudio2 sound output for wxWidgets (Windows only) | ON |
|
||||
| `ENABLE_FAUDIO` | Enable faudio sound output for wxWidgets, | ON, not 32 bit Win |
|
||||
| `ENABLE_ASAN` | Enable libasan sanitizers (by default address, only in debug mode) | OFF |
|
||||
| `UPSTREAM_RELEASE` | Do some release tasks, like codesigning, making zip and gpg sigs. | OFF |
|
||||
| `BUILD_TESTING` | Build the tests and enable ctest support. | ON |
|
||||
| `VBAM_STATIC` | Try link all libs statically (the following are set to ON if ON) | OFF |
|
||||
| `SDL2_STATIC` | Try to link static SDL2 libraries | OFF |
|
||||
| `SFML_STATIC_LIBRARIES` | Try to link static SFML libraries | OFF |
|
||||
| `FFMPEG_STATIC` | Try to link static ffmpeg libraries | OFF |
|
||||
| `OPENAL_STATIC` | Try to link static OpenAL libraries | OFF |
|
||||
| `TRANSLATIONS_ONLY` | Build only the translations.zip and nothing else | OFF |
|
||||
|
||||
Note for distro packagers, we use the CMake module
|
||||
[GNUInstallDirs](https://cmake.org/cmake/help/v2.8.12/cmake.html#module:GNUInstallDirs)
|
||||
@@ -247,52 +237,6 @@ On Unix to use a different version of wxWidgets, set
|
||||
`wxWidgets_CONFIG_EXECUTABLE` to the path to the `wx-config` script you want to
|
||||
use.
|
||||
|
||||
## MSys2 Notes
|
||||
|
||||
To run the resulting binary, you can simply type:
|
||||
|
||||
```shell
|
||||
./visualboyadvance-m
|
||||
```
|
||||
|
||||
in the shell where you built it.
|
||||
|
||||
If you built with `-DCMAKE_BUILD_TYPE=Debug`, you will get a console app and
|
||||
will see debug messages, even in mintty.
|
||||
|
||||
If you want to start the binary from e.g. a shortcut or Explorer, you will need
|
||||
to put `c:\msys64\mingw32\bin` for 32 bit builds and `c:\msys64\mingw64\bin`
|
||||
for 64 bit builds in your PATH (to edit system PATH, go to Control Panel ->
|
||||
System -> Advanced system settings -> Environment Variables.)
|
||||
|
||||
If you want to package the binary, you will need to include the MinGW DLLs it
|
||||
depends on, they can install to the same directory as the binary.
|
||||
|
||||
Our own builds are static.
|
||||
|
||||
## Debug Messages
|
||||
|
||||
We have an override for `wxLogDebug()` to make it work even in non-debug builds
|
||||
of wx and on windows, even in mintty.
|
||||
|
||||
It works like `printf()`, e.g.:
|
||||
|
||||
```cpp
|
||||
int foo = 42;
|
||||
wxLogDebug(wxT("the value of foo = %d"), foo);
|
||||
```
|
||||
|
||||
From the core etc. the usual:
|
||||
|
||||
```cpp
|
||||
fprintf(stderr, "...", ...);
|
||||
```
|
||||
|
||||
will work fine.
|
||||
|
||||
You need a debug build for this to work or to even have a console on Windows.
|
||||
Pass `-DCMAKE_BUILD_TYPE=Debug` to cmake.
|
||||
|
||||
## Reporting Crash Bugs
|
||||
|
||||
If the emulator crashes and you wish to report the bug, a backtrace made with
|
||||
@@ -311,27 +255,25 @@ do something such as:
|
||||
```shell
|
||||
ulimit -c unlimited
|
||||
```
|
||||
|
||||
in your shell to enable coredump files.
|
||||
, in your shell to enable core files.
|
||||
|
||||
[This
|
||||
post](https://ask.fedoraproject.org/en/question/98776/where-is-core-dump-located/?answer=98779#post-id-98779)
|
||||
explains how to retrieve core dump on Fedora Linux (and possibly other
|
||||
distributions.)
|
||||
explains how to retrieve core dump on some distributions, when they are managed
|
||||
by systemd.
|
||||
|
||||
Once you have the core dump file, open it with `gdb`, for example:
|
||||
Once you have the core file, open it with `gdb`, for example:
|
||||
|
||||
```shell
|
||||
gdb -c core ./visualboyadvance-m
|
||||
```
|
||||
|
||||
In the `gdb` shell, to print the backtrace, type:
|
||||
. In the `gdb` shell, to start the process and print the backtrace, type:
|
||||
|
||||
```
|
||||
run
|
||||
bt
|
||||
```
|
||||
|
||||
This may be a bit of a hassle, but it helps us out immensely.
|
||||
. This may be a bit of a hassle, but it helps us out immensely.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
@@ -56,9 +56,11 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "[xX]86|i[3-9]86|[aA][mM][dD]64")
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "[aA][aA][rR][cC][hH]|[aA][rR][mM]")
|
||||
if(CMAKE_C_SIZEOF_DATA_PTR EQUAL 4) # 32 bit
|
||||
set(ARM32 ON)
|
||||
set(ARCH_NAME arm32)
|
||||
set(WINARCH arm)
|
||||
elseif(CMAKE_C_SIZEOF_DATA_PTR EQUAL 8)
|
||||
set(ARM64 ON)
|
||||
set(ARCH_NAME arm64)
|
||||
set(WINARCH arm64)
|
||||
endif()
|
||||
|
@@ -23,7 +23,7 @@ if(CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg")
|
||||
if(VCPKG_TARGET_TRIPLET MATCHES -static)
|
||||
set(arch_suffix -static)
|
||||
endif()
|
||||
if(CMAKE_BUILD_TYPE MATCHES "^(Debug|RelWithDebInfo)$")
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(path_prefix debug)
|
||||
endif()
|
||||
set(installed_prefix ${_VCPKG_INSTALLED_DIR}/${WINARCH}-windows${arch_suffix}/${path_prefix})
|
||||
|
@@ -10,10 +10,6 @@ if(NOT EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
message(FATAL_ERROR "releases can only be done from a git clone")
|
||||
endif()
|
||||
|
||||
if(NOT GIT_FOUND)
|
||||
message(FATAL_ERROR "git is required to make a release")
|
||||
endif()
|
||||
|
||||
find_program(GPG_EXECUTABLE gpg)
|
||||
|
||||
if(NOT GPG_EXECUTABLE)
|
||||
@@ -35,7 +31,7 @@ function(make_release_commit_and_tag)
|
||||
# First make sure we are on master.
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --short --abbrev-ref=strict HEAD
|
||||
COMMAND git rev-parse --short --abbrev-ref=strict HEAD
|
||||
OUTPUT_VARIABLE git_branch
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
@@ -46,7 +42,7 @@ function(make_release_commit_and_tag)
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} status --porcelain=2
|
||||
COMMAND git status --porcelain=2
|
||||
OUTPUT_VARIABLE git_status
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
@@ -59,7 +55,7 @@ function(make_release_commit_and_tag)
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} tag --sort=-v:refname
|
||||
COMMAND git tag --sort=-v:refname
|
||||
OUTPUT_VARIABLE git_tags
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
@@ -80,7 +76,7 @@ function(make_release_commit_and_tag)
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} log ${git_last_tag}.. "--pretty=format:* %h - %s [%aL]"
|
||||
COMMAND git log ${git_last_tag}.. "--pretty=format:* %h - %s [%aL]"
|
||||
OUTPUT_VARIABLE release_log
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
@@ -90,17 +86,17 @@ function(make_release_commit_and_tag)
|
||||
|
||||
if(TAG_RELEASE STREQUAL UNDO)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} tag -d ${git_last_tag}
|
||||
COMMAND git tag -d ${git_last_tag}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} reset HEAD~1
|
||||
COMMAND git reset HEAD~1
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} checkout HEAD CHANGELOG.md
|
||||
COMMAND git checkout HEAD CHANGELOG.md
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
|
||||
@@ -212,45 +208,37 @@ Ignore the following cmake error.
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} add CHANGELOG.md
|
||||
COMMAND git add CHANGELOG.md
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
|
||||
# Make the release commit.
|
||||
message(FATAL_ERROR "
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} commit -m "release ${new_tag}" --signoff -S
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
Release prepared.
|
||||
|
||||
# Make release tag.
|
||||
Edit CHANGELOG.md to remove any non-user-facing commits, and optionally add any
|
||||
release notes.
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} tag -s -m${new_tag} ${new_tag}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
Run the following commands to commit the change:
|
||||
|
||||
message(FATAL_ERROR [=[
|
||||
|
||||
Release commit and tag generated.
|
||||
|
||||
**** IF YOU ARE SURE YOU WANT TO RELEASE ****
|
||||
|
||||
Run the following commands to push the release commit and tag:
|
||||
|
||||
git push
|
||||
git push --tags
|
||||
|
||||
**** TO UNDO THE RELEASE ****
|
||||
git commit -m'release ${new_tag}' --signoff -S
|
||||
git tag -s -m'${new_tag}' ${new_tag}
|
||||
|
||||
To rollback these changes, run this command:
|
||||
|
||||
cmake .. -DTAG_RELEASE=UNDO
|
||||
|
||||
Ignore the "configuration incomplete" message following, this mode does not
|
||||
==== IF YOU ARE SURE YOU WANT TO RELEASE, THIS **CANNOT** BE REVERSED ====
|
||||
|
||||
Run the following to push the release commit and tag:
|
||||
|
||||
git push
|
||||
git push --tags
|
||||
|
||||
Ignore the 'configuration incomplete' message following, this mode does not
|
||||
build anything.
|
||||
|
||||
]=])
|
||||
")
|
||||
endfunction()
|
||||
|
||||
make_release_commit_and_tag()
|
||||
|
@@ -8,7 +8,12 @@ else()
|
||||
set(BUILD_DEFAULT ON)
|
||||
endif()
|
||||
|
||||
option(ENABLE_SDL "Build the SDL port" ${BUILD_DEFAULT})
|
||||
set(ENABLE_SDL_DEFAULT ${BUILD_DEFAULT})
|
||||
if(WIN32 OR APPLE)
|
||||
set(ENABLE_SDL_DEFAULT OFF)
|
||||
endif()
|
||||
|
||||
option(ENABLE_SDL "Build the SDL port" ${ENABLE_SDL_DEFAULT})
|
||||
option(ENABLE_WX "Build the wxWidgets port" ${BUILD_DEFAULT})
|
||||
option(ENABLE_DEBUGGER "Enable the debugger" ON)
|
||||
option(ENABLE_ASAN "Enable -fsanitize=address by default. Requires debug build with GCC/Clang" OFF)
|
||||
@@ -67,13 +72,6 @@ if(TRANSLATIONS_ONLY)
|
||||
set(ENABLE_LINK_DEFAULT OFF)
|
||||
else()
|
||||
find_package(SFML 2.4 COMPONENTS network system)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
if(SFML_STATIC_LIBRARIES AND SFML_NETWORK_LIBRARY_STATIC_DEBUG AND SFML_SYSTEM_LIBRARY_STATIC_DEBUG)
|
||||
set(SFML_LIBRARIES ${SFML_NETWORK_LIBRARY_STATIC_DEBUG} ${SFML_SYSTEM_LIBRARY_STATIC_DEBUG})
|
||||
elseif(SFML_NETWORK_LIBRARY_DYNAMIC_DEBUG AND SFML_SYSTEM_LIBRARY_DYNAMIC_DEBUG)
|
||||
set(SFML_LIBRARIES ${SFML_NETWORK_LIBRARY_DYNAMIC_DEBUG} ${SFML_SYSTEM_LIBRARY_DYNAMIC_DEBUG})
|
||||
endif()
|
||||
endif()
|
||||
set(ENABLE_LINK_DEFAULT OFF)
|
||||
if(SFML_FOUND)
|
||||
set(ENABLE_LINK_DEFAULT ON)
|
||||
@@ -86,7 +84,7 @@ set(FFMPEG_DEFAULT OFF)
|
||||
set(FFMPEG_COMPONENTS AVCODEC AVFORMAT SWSCALE AVUTIL SWRESAMPLE)
|
||||
set(FFMPEG_COMPONENT_VERSIONS AVCODEC>=58.18.100 AVFORMAT>=58.12.100 SWSCALE>=5.1.100 AVUTIL>=56.14.100 SWRESAMPLE>=3.1.100)
|
||||
|
||||
if(NOT TRANSLATIONS_ONLY AND NOT DEFINED ENABLE_FFMPEG OR ENABLE_FFMPEG)
|
||||
if(NOT TRANSLATIONS_ONLY AND (NOT DEFINED ENABLE_FFMPEG OR ENABLE_FFMPEG) AND (NOT (X86 AND MINGW)))
|
||||
set(FFMPEG_DEFAULT ON)
|
||||
|
||||
find_package(FFmpeg COMPONENTS ${FFMPEG_COMPONENTS})
|
||||
@@ -146,7 +144,7 @@ set(ENABLE_FAUDIO_DEFAULT OFF)
|
||||
|
||||
find_package(FAudio QUIET)
|
||||
|
||||
if(FAudio_FOUND)
|
||||
if(FAudio_FOUND AND NOT (MINGW AND X86))
|
||||
set(ENABLE_FAUDIO_DEFAULT ON)
|
||||
endif()
|
||||
|
||||
@@ -162,3 +160,5 @@ endif()
|
||||
if(TRANSLATIONS_ONLY AND (ENABLE_SDL OR ENABLE_WX))
|
||||
message(FATAL_ERROR "The SDL and wxWidgets ports can't be built when TRANSLATIONS_ONLY is enabled")
|
||||
endif()
|
||||
|
||||
option(GPG_SIGNATURES "Create GPG signatures for release files" OFF)
|
||||
|
@@ -10,19 +10,28 @@ if(NOT DEFINED VCPKG_TARGET_TRIPLET)
|
||||
# Check if we are in an MSVC environment.
|
||||
find_program(cl_exe_path NAME cl.exe HINTS ENV PATH)
|
||||
|
||||
if($ENV{CXX} MATCHES "cl.exe$" OR cl_exe_path)
|
||||
if(ENV{CXX} MATCHES "cl.exe$" OR cl_exe_path)
|
||||
# Infer the architecture from the LIB folders.
|
||||
foreach(LIB $ENV{LIB})
|
||||
if(${LIB} MATCHES "x64$")
|
||||
foreach(lib $ENV{LIB})
|
||||
if(lib MATCHES "x64$")
|
||||
set(VBAM_VCPKG_PLATFORM "x64-windows-static")
|
||||
break()
|
||||
endif()
|
||||
if(${LIB} MATCHES "x86$")
|
||||
if(lib MATCHES "x86$")
|
||||
set(VBAM_VCPKG_PLATFORM "x86-windows-static")
|
||||
break()
|
||||
endif()
|
||||
if(${LIB} MATCHES "ARM64$")
|
||||
if(lib MATCHES "ARM64$")
|
||||
set(VBAM_VCPKG_PLATFORM "arm64-windows-static")
|
||||
|
||||
foreach(path $ENV{PATH})
|
||||
if(path MATCHES "[Hh]ost[Xx]64")
|
||||
set(VCPKG_HOST_TRIPLET "x64-windows-static" CACHE STRING "Vcpkg host triplet" FORCE)
|
||||
set(VCPKG_USE_HOST_TOOLS ON CACHE BOOL "Use vcpkg host tools" FORCE)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
@@ -51,6 +60,28 @@ if(NOT DEFINED VCPKG_TARGET_TRIPLET)
|
||||
message(STATUS "Inferred VCPKG_TARGET_TRIPLET=${VCPKG_TARGET_TRIPLET}")
|
||||
endif()
|
||||
|
||||
function(vcpkg_seconds)
|
||||
if(CMAKE_HOST_SYSTEM MATCHES Windows OR ((NOT DEFINED CMAKE_HOST_SYSTEM) AND WIN32))
|
||||
execute_process(
|
||||
COMMAND cmd /c echo %TIME:~0,8%
|
||||
OUTPUT_VARIABLE time
|
||||
)
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND date +%H:%M:%S
|
||||
OUTPUT_VARIABLE time
|
||||
)
|
||||
endif()
|
||||
|
||||
string(SUBSTRING "${time}" 0 2 hours)
|
||||
string(SUBSTRING "${time}" 3 2 minutes)
|
||||
string(SUBSTRING "${time}" 6 2 secs)
|
||||
|
||||
math(EXPR seconds "(${hours} * 60 * 60) + (${minutes} * 60) + ${secs}")
|
||||
|
||||
set(seconds ${seconds} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(vcpkg_check_git_status git_status)
|
||||
# The VS vcpkg component cannot be written to without elevation.
|
||||
if(NOT git_status EQUAL 0 AND NOT VCPKG_ROOT MATCHES "Visual Studio")
|
||||
@@ -545,14 +576,4 @@ endfunction()
|
||||
|
||||
vcpkg_set_toolchain()
|
||||
|
||||
# Make vcpkg use debug libs for RelWithDebInfo
|
||||
set(orig_build_type ${CMAKE_BUILD_TYPE})
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo)
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
endif()
|
||||
|
||||
include(${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake)
|
||||
|
||||
set(CMAKE_BUILD_TYPE ${orig_build_type})
|
||||
unset(orig_build_type)
|
||||
|
@@ -16,7 +16,7 @@ Ignore the following cmake error.
|
||||
# Get last tag.
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} tag --sort=-v:refname
|
||||
COMMAND git tag --sort=-v:refname
|
||||
OUTPUT_VARIABLE git_tags
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
@@ -43,7 +43,7 @@ Ignore the following cmake error.
|
||||
# Clone repo.
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} clone git@github.com:visualboyadvance-m/visualboyadvance-m.github.io web-data
|
||||
COMMAND git clone git@github.com:visualboyadvance-m/visualboyadvance-m.github.io web-data
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
|
||||
@@ -103,21 +103,21 @@ Ignore the following cmake error.
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} add appcast.xml
|
||||
COMMAND git add appcast.xml
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/web-data
|
||||
)
|
||||
|
||||
# Commit the change.
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} commit -m "release ${new_tag}" --signoff -S
|
||||
COMMAND git commit -m "release ${new_tag}" --signoff -S
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/web-data
|
||||
)
|
||||
|
||||
# Make release tag.
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} tag -s -m${new_tag} ${new_tag}
|
||||
COMMAND git tag -s -m${new_tag} ${new_tag}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/web-data
|
||||
)
|
||||
|
||||
|
@@ -1,175 +0,0 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
doctest
|
||||
-----
|
||||
|
||||
This module defines a function to help use the doctest test framework.
|
||||
|
||||
The :command:`doctest_discover_tests` discovers tests by asking the compiled test
|
||||
executable to enumerate its tests. This does not require CMake to be re-run
|
||||
when tests change. However, it may not work in a cross-compiling environment,
|
||||
and setting test properties is less convenient.
|
||||
|
||||
This command is intended to replace use of :command:`add_test` to register
|
||||
tests, and will create a separate CTest test for each doctest test case. Note
|
||||
that this is in some cases less efficient, as common set-up and tear-down logic
|
||||
cannot be shared by multiple test cases executing in the same instance.
|
||||
However, it provides more fine-grained pass/fail information to CTest, which is
|
||||
usually considered as more beneficial. By default, the CTest test name is the
|
||||
same as the doctest name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
|
||||
|
||||
.. command:: doctest_discover_tests
|
||||
|
||||
Automatically add tests with CTest by querying the compiled test executable
|
||||
for available tests::
|
||||
|
||||
doctest_discover_tests(target
|
||||
[TEST_SPEC arg1...]
|
||||
[EXTRA_ARGS arg1...]
|
||||
[WORKING_DIRECTORY dir]
|
||||
[TEST_PREFIX prefix]
|
||||
[TEST_SUFFIX suffix]
|
||||
[PROPERTIES name1 value1...]
|
||||
[TEST_LIST var]
|
||||
)
|
||||
|
||||
``doctest_discover_tests`` sets up a post-build command on the test executable
|
||||
that generates the list of tests by parsing the output from running the test
|
||||
with the ``--list-test-cases`` argument. This ensures that the full
|
||||
list of tests is obtained. Since test discovery occurs at build time, it is
|
||||
not necessary to re-run CMake when the list of tests changes.
|
||||
However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set
|
||||
in order to function in a cross-compiling environment.
|
||||
|
||||
Additionally, setting properties on tests is somewhat less convenient, since
|
||||
the tests are not available at CMake time. Additional test properties may be
|
||||
assigned to the set of tests as a whole using the ``PROPERTIES`` option. If
|
||||
more fine-grained test control is needed, custom content may be provided
|
||||
through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES`
|
||||
directory property. The set of discovered tests is made accessible to such a
|
||||
script via the ``<target>_TESTS`` variable.
|
||||
|
||||
The options are:
|
||||
|
||||
``target``
|
||||
Specifies the doctest executable, which must be a known CMake executable
|
||||
target. CMake will substitute the location of the built executable when
|
||||
running the test.
|
||||
|
||||
``TEST_SPEC arg1...``
|
||||
Specifies test cases, wildcarded test cases, tags and tag expressions to
|
||||
pass to the doctest executable with the ``--list-test-cases`` argument.
|
||||
|
||||
``EXTRA_ARGS arg1...``
|
||||
Any extra arguments to pass on the command line to each test case.
|
||||
|
||||
``WORKING_DIRECTORY dir``
|
||||
Specifies the directory in which to run the discovered test cases. If this
|
||||
option is not provided, the current binary directory is used.
|
||||
|
||||
``TEST_PREFIX prefix``
|
||||
Specifies a ``prefix`` to be prepended to the name of each discovered test
|
||||
case. This can be useful when the same test executable is being used in
|
||||
multiple calls to ``doctest_discover_tests()`` but with different
|
||||
``TEST_SPEC`` or ``EXTRA_ARGS``.
|
||||
|
||||
``TEST_SUFFIX suffix``
|
||||
Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of
|
||||
every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may
|
||||
be specified.
|
||||
|
||||
``PROPERTIES name1 value1...``
|
||||
Specifies additional properties to be set on all tests discovered by this
|
||||
invocation of ``doctest_discover_tests``.
|
||||
|
||||
``TEST_LIST var``
|
||||
Make the list of tests available in the variable ``var``, rather than the
|
||||
default ``<target>_TESTS``. This can be useful when the same test
|
||||
executable is being used in multiple calls to ``doctest_discover_tests()``.
|
||||
Note that this variable is only available in CTest.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
function(doctest_discover_tests TARGET)
|
||||
cmake_parse_arguments(
|
||||
""
|
||||
""
|
||||
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST"
|
||||
"TEST_SPEC;EXTRA_ARGS;PROPERTIES"
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
if(NOT _WORKING_DIRECTORY)
|
||||
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
if(NOT _TEST_LIST)
|
||||
set(_TEST_LIST ${TARGET}_TESTS)
|
||||
endif()
|
||||
|
||||
## Generate a unique name based on the extra arguments
|
||||
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS}")
|
||||
string(SUBSTRING ${args_hash} 0 7 args_hash)
|
||||
|
||||
# Define rule to generate test list for aforementioned test executable
|
||||
set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake")
|
||||
set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake")
|
||||
get_property(crosscompiling_emulator
|
||||
TARGET ${TARGET}
|
||||
PROPERTY CROSSCOMPILING_EMULATOR
|
||||
)
|
||||
add_custom_command(
|
||||
TARGET ${TARGET} POST_BUILD
|
||||
BYPRODUCTS "${ctest_tests_file}"
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-D "TEST_TARGET=${TARGET}"
|
||||
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
|
||||
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
|
||||
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
|
||||
-D "TEST_SPEC=${_TEST_SPEC}"
|
||||
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
|
||||
-D "TEST_PROPERTIES=${_PROPERTIES}"
|
||||
-D "TEST_PREFIX=${_TEST_PREFIX}"
|
||||
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
|
||||
-D "TEST_LIST=${_TEST_LIST}"
|
||||
-D "CTEST_FILE=${ctest_tests_file}"
|
||||
-P "${_DOCTEST_DISCOVER_TESTS_SCRIPT}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
file(WRITE "${ctest_include_file}"
|
||||
"if(EXISTS \"${ctest_tests_file}\")\n"
|
||||
" include(\"${ctest_tests_file}\")\n"
|
||||
"else()\n"
|
||||
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
|
||||
"endif()\n"
|
||||
)
|
||||
|
||||
if(NOT CMAKE_VERSION VERSION_LESS 3.10)
|
||||
# Add discovered tests to directory TEST_INCLUDE_FILES
|
||||
set_property(DIRECTORY
|
||||
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
|
||||
)
|
||||
else()
|
||||
# Add discovered tests as directory TEST_INCLUDE_FILE if possible
|
||||
get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET)
|
||||
if(NOT ${test_include_file_set})
|
||||
set_property(DIRECTORY
|
||||
PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}"
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"Cannot set more than one TEST_INCLUDE_FILE"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
|
||||
###############################################################################
|
||||
|
||||
set(_DOCTEST_DISCOVER_TESTS_SCRIPT
|
||||
${CMAKE_CURRENT_LIST_DIR}/doctestAddTests.cmake
|
||||
)
|
@@ -1,81 +0,0 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
set(prefix "${TEST_PREFIX}")
|
||||
set(suffix "${TEST_SUFFIX}")
|
||||
set(spec ${TEST_SPEC})
|
||||
set(extra_args ${TEST_EXTRA_ARGS})
|
||||
set(properties ${TEST_PROPERTIES})
|
||||
set(script)
|
||||
set(suite)
|
||||
set(tests)
|
||||
|
||||
function(add_command NAME)
|
||||
set(_args "")
|
||||
foreach(_arg ${ARGN})
|
||||
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
|
||||
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
|
||||
else()
|
||||
set(_args "${_args} ${_arg}")
|
||||
endif()
|
||||
endforeach()
|
||||
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Run test executable to get list of available tests
|
||||
if(NOT EXISTS "${TEST_EXECUTABLE}")
|
||||
message(FATAL_ERROR
|
||||
"Specified test executable '${TEST_EXECUTABLE}' does not exist"
|
||||
)
|
||||
endif()
|
||||
|
||||
if("${spec}" MATCHES .)
|
||||
set(spec "--test-case=${spec}")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-cases
|
||||
OUTPUT_VARIABLE output
|
||||
RESULT_VARIABLE result
|
||||
)
|
||||
if(NOT ${result} EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"Error running test executable '${TEST_EXECUTABLE}':\n"
|
||||
" Result: ${result}\n"
|
||||
" Output: ${output}\n"
|
||||
)
|
||||
endif()
|
||||
|
||||
string(REPLACE "\n" ";" output "${output}")
|
||||
|
||||
# Parse output
|
||||
foreach(line ${output})
|
||||
if("${line}" STREQUAL "===============================================================================" OR "${line}" MATCHES [==[^\[doctest\] ]==])
|
||||
continue()
|
||||
endif()
|
||||
set(test ${line})
|
||||
# use escape commas to handle properly test cases with commas inside the name
|
||||
string(REPLACE "," "\\," test_name ${test})
|
||||
# ...and add to script
|
||||
add_command(add_test
|
||||
"${prefix}${test}${suffix}"
|
||||
${TEST_EXECUTOR}
|
||||
"${TEST_EXECUTABLE}"
|
||||
"--test-case=${test_name}"
|
||||
${extra_args}
|
||||
)
|
||||
add_command(set_tests_properties
|
||||
"${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
${properties}
|
||||
)
|
||||
list(APPEND tests "${prefix}${test}${suffix}")
|
||||
endforeach()
|
||||
|
||||
# Create a list of all discovered tests, which users may use to e.g. set
|
||||
# properties on the tests
|
||||
add_command(set ${TEST_LIST} ${tests})
|
||||
|
||||
# Write CTest script
|
||||
file(WRITE "${CTEST_FILE}" "${script}")
|
Submodule dependencies updated: e8ce758a98...0a1cdd9c04
@@ -1118,9 +1118,12 @@ windows_installdeps() {
|
||||
*i686*)
|
||||
pkgs="$pkgs nasm"
|
||||
;;
|
||||
*)
|
||||
pkgs="$pkgs FAudio"
|
||||
;;
|
||||
esac
|
||||
|
||||
pkgs="$pkgs SDL2 sfml FAudio wxWidgets3.2 zlib binutils cmake crt-git headers-git make pkgconf tools-git windows-default-manifest libmangle-git ninja gdb ccache openal"
|
||||
pkgs="$pkgs SDL2 sfml wxWidgets3.2 zlib binutils cmake crt-git headers-git make pkgconf tools-git windows-default-manifest libmangle-git ninja gdb ccache openal"
|
||||
|
||||
case "$target" in
|
||||
*x86_64)
|
||||
|
1963
po/wxvbam/bg.po
1963
po/wxvbam/bg.po
File diff suppressed because it is too large
Load Diff
1963
po/wxvbam/br.po
1963
po/wxvbam/br.po
File diff suppressed because it is too large
Load Diff
1963
po/wxvbam/cs.po
1963
po/wxvbam/cs.po
File diff suppressed because it is too large
Load Diff
2280
po/wxvbam/de.po
2280
po/wxvbam/de.po
File diff suppressed because it is too large
Load Diff
1963
po/wxvbam/el.po
1963
po/wxvbam/el.po
File diff suppressed because it is too large
Load Diff
1963
po/wxvbam/es.po
1963
po/wxvbam/es.po
File diff suppressed because it is too large
Load Diff
1963
po/wxvbam/es_419.po
1963
po/wxvbam/es_419.po
File diff suppressed because it is too large
Load Diff
2506
po/wxvbam/fr_FR.po
2506
po/wxvbam/fr_FR.po
File diff suppressed because it is too large
Load Diff
1963
po/wxvbam/gl.po
1963
po/wxvbam/gl.po
File diff suppressed because it is too large
Load Diff
1963
po/wxvbam/he_IL.po
1963
po/wxvbam/he_IL.po
File diff suppressed because it is too large
Load Diff
1965
po/wxvbam/hu_HU.po
1965
po/wxvbam/hu_HU.po
File diff suppressed because it is too large
Load Diff
2084
po/wxvbam/id.po
2084
po/wxvbam/id.po
File diff suppressed because it is too large
Load Diff
1963
po/wxvbam/it_IT.po
1963
po/wxvbam/it_IT.po
File diff suppressed because it is too large
Load Diff
1963
po/wxvbam/ja.po
1963
po/wxvbam/ja.po
File diff suppressed because it is too large
Load Diff
1943
po/wxvbam/ja_JP.po
1943
po/wxvbam/ja_JP.po
File diff suppressed because it is too large
Load Diff
2439
po/wxvbam/ko_KR.po
2439
po/wxvbam/ko_KR.po
File diff suppressed because it is too large
Load Diff
1963
po/wxvbam/ms_MY.po
1963
po/wxvbam/ms_MY.po
File diff suppressed because it is too large
Load Diff
1963
po/wxvbam/nb.po
1963
po/wxvbam/nb.po
File diff suppressed because it is too large
Load Diff
1963
po/wxvbam/nl.po
1963
po/wxvbam/nl.po
File diff suppressed because it is too large
Load Diff
1965
po/wxvbam/pl_PL.po
1965
po/wxvbam/pl_PL.po
File diff suppressed because it is too large
Load Diff
1959
po/wxvbam/pt_BR.po
1959
po/wxvbam/pt_BR.po
File diff suppressed because it is too large
Load Diff
1943
po/wxvbam/pt_PT.po
1943
po/wxvbam/pt_PT.po
File diff suppressed because it is too large
Load Diff
1963
po/wxvbam/ru_RU.po
1963
po/wxvbam/ru_RU.po
File diff suppressed because it is too large
Load Diff
1963
po/wxvbam/sv.po
1963
po/wxvbam/sv.po
File diff suppressed because it is too large
Load Diff
1963
po/wxvbam/tr.po
1963
po/wxvbam/tr.po
File diff suppressed because it is too large
Load Diff
1965
po/wxvbam/uk.po
1965
po/wxvbam/uk.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1943
po/wxvbam/wxvbam.pot
1943
po/wxvbam/wxvbam.pot
File diff suppressed because it is too large
Load Diff
1953
po/wxvbam/zh-Hans.po
1953
po/wxvbam/zh-Hans.po
File diff suppressed because it is too large
Load Diff
1990
po/wxvbam/zh_CN.po
1990
po/wxvbam/zh_CN.po
File diff suppressed because it is too large
Load Diff
@@ -136,3 +136,5 @@ if(ENABLE_LINK)
|
||||
PRIVATE ${NLS_LIBS}
|
||||
)
|
||||
endif()
|
||||
|
||||
add_subdirectory(test)
|
||||
|
@@ -5,7 +5,9 @@
|
||||
# Generate version_gen.h
|
||||
string(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+).*" "\\1,\\2,\\3,0" VBAM_WIN_VERSION "${VBAM_VERSION}")
|
||||
set(VBAM_GENERATED_VERSION_H ${VBAM_GENERATED_DIR}/core/base/version_gen.h)
|
||||
add_custom_target(vbam-core-base-generated
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${VBAM_GENERATED_VERSION_H}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DVBAM_GENERATED_VERSION_H=${VBAM_GENERATED_VERSION_H}
|
||||
-DVBAM_VERSION=${VBAM_VERSION}
|
||||
@@ -13,8 +15,6 @@ add_custom_target(vbam-core-base-generated
|
||||
-DVBAM_VERSION_RELEASE=${VBAM_VERSION_RELEASE}
|
||||
-DVBAM_WIN_VERSION=${VBAM_WIN_VERSION}
|
||||
-P ${CMAKE_CURRENT_SOURCE_DIR}/build-version.cmake
|
||||
BYPRODUCTS
|
||||
${VBAM_GENERATED_VERSION_H}
|
||||
DEPENDS
|
||||
version_gen.h.in
|
||||
build-version.cmake
|
||||
@@ -22,8 +22,6 @@ add_custom_target(vbam-core-base-generated
|
||||
|
||||
add_library(vbam-core-base OBJECT)
|
||||
|
||||
add_dependencies(vbam-core-base vbam-core-base-generated)
|
||||
|
||||
target_sources(vbam-core-base
|
||||
PRIVATE
|
||||
file_util_common.cpp
|
||||
@@ -37,6 +35,7 @@ target_sources(vbam-core-base
|
||||
version.cpp
|
||||
|
||||
PUBLIC
|
||||
check.h
|
||||
array.h
|
||||
file_util.h
|
||||
image_util.h
|
||||
@@ -48,6 +47,8 @@ target_sources(vbam-core-base
|
||||
sound_driver.h
|
||||
system.h
|
||||
version.h
|
||||
# Generated file.
|
||||
${VBAM_GENERATED_VERSION_H}
|
||||
)
|
||||
|
||||
target_include_directories(vbam-core-base
|
||||
@@ -59,3 +60,5 @@ target_link_libraries(vbam-core-base
|
||||
PRIVATE vbam-fex stb-image
|
||||
PUBLIC ${ZLIB_LIBRARY}
|
||||
)
|
||||
|
||||
add_subdirectory(test)
|
||||
|
58
src/core/base/check.h
Normal file
58
src/core/base/check.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#ifndef VBAM_CORE_BASE_CHECK_H_
|
||||
#define VBAM_CORE_BASE_CHECK_H_
|
||||
|
||||
// This header defines a number of macros for checking conditions and crashing
|
||||
// the program if they are not met.
|
||||
// * VBAM_CHECK(condition) - crashes the program if the condition is not met.
|
||||
// * VBAM_NOTREACHED() - crashes the program if this line of code is reached.
|
||||
// In release builds, this macro also tells the compiler that this code path
|
||||
// is unreachable, which can help the compiler generate better code.
|
||||
// * VBAM_STRINGIFY(x) - converts the argument to a string literal.
|
||||
// While a number of other macros are defined in this file, they are not
|
||||
// intended for general use and should be avoided.
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
|
||||
// GCC/Clang.
|
||||
#define VBAM_IMMEDIATE_CRASH_DETAIL() __builtin_trap()
|
||||
#define VBAM_INTRINSIC_UNREACHABLE_DETAIL() __builtin_unreachable()
|
||||
|
||||
#elif defined(_MSC_VER) // defined(__GNUC__) || defined(__clang__)
|
||||
|
||||
// MSVC.
|
||||
#define VBAM_IMMEDIATE_CRASH_DETAIL() __debugbreak()
|
||||
#define VBAM_INTRINSIC_UNREACHABLE_DETAIL() __assume(0)
|
||||
|
||||
#else // defined(__GNUC__) || defined(__clang__)
|
||||
|
||||
#error "Unsupported compiler"
|
||||
|
||||
#endif // defined(__GNUC__) || defined(__clang__)
|
||||
|
||||
#define VBAM_STRINGIFY_DETAIL(x) #x
|
||||
#define VBAM_STRINGIFY(x) VBAM_STRINGIFY_DETAIL(x)
|
||||
|
||||
#define VBAM_REQUIRE_SEMICOLON_DETAIL() \
|
||||
static_assert(true, "Require a semicolon after macros invocation.")
|
||||
|
||||
#define VBAM_CHECK(condition) \
|
||||
if (!(condition)) { \
|
||||
fputs("CHECK failed at " __FILE__ ":" VBAM_STRINGIFY(__LINE__) ": " #condition "\n", \
|
||||
stderr); \
|
||||
VBAM_IMMEDIATE_CRASH_DETAIL(); \
|
||||
} \
|
||||
VBAM_REQUIRE_SEMICOLON_DETAIL()
|
||||
|
||||
#if defined(DEBUG)
|
||||
|
||||
#define VBAM_NOTREACHED() \
|
||||
fputs("NOTREACHED code reached at " __FILE__ ":" VBAM_STRINGIFY(__LINE__) "\n", stderr); \
|
||||
VBAM_IMMEDIATE_CRASH_DETAIL()
|
||||
|
||||
#else // defined(DEBUG)
|
||||
|
||||
#define VBAM_NOTREACHED() VBAM_INTRINSIC_UNREACHABLE_DETAIL()
|
||||
|
||||
#endif // defined(DEBUG)
|
||||
|
||||
#endif // VBAM_CORE_BASE_CHECK_H_
|
@@ -59,6 +59,7 @@ extern struct CoreOptions {
|
||||
bool speedHack = false;
|
||||
bool speedup = false;
|
||||
bool speedup_throttle_frame_skip = false;
|
||||
bool speedup_mute = true;
|
||||
int cheatsEnabled = 1;
|
||||
int cpuDisableSfx = 0;
|
||||
int cpuSaveType = 0;
|
||||
|
12
src/core/base/test/CMakeLists.txt
Normal file
12
src/core/base/test/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
# This defines the `vbam-core-base-test` library.
|
||||
|
||||
if(NOT BUILD_TESTING)
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_library(vbam-core-base-test
|
||||
INTERFACE
|
||||
notreached.h)
|
||||
|
||||
target_link_libraries(vbam-core-base-test
|
||||
INTERFACE GTest::gtest)
|
22
src/core/base/test/notreached.h
Normal file
22
src/core/base/test/notreached.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef VBAM_CORE_BASE_TEST_NOTREACHED_H_
|
||||
#define VBAM_CORE_BASE_TEST_NOTREACHED_H_
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#if defined(DEBUG)
|
||||
|
||||
#define VBAM_EXPECT_NOTREACHED(statement) EXPECT_DEATH(statement, "NOTREACHED")
|
||||
|
||||
#else // defined(DEBUG)
|
||||
|
||||
// There is no way to test this in release builds as the compiler might optimize
|
||||
// this code path away. Eat the statement to avoid unused variable warnings.
|
||||
#define VBAM_EXPECT_NOTREACHED(statement) \
|
||||
if constexpr (false) { \
|
||||
statement; \
|
||||
} \
|
||||
static_assert(true, "")
|
||||
|
||||
#endif // defined(DEBUG)
|
||||
|
||||
#endif // VBAM_CORE_BASE_TEST_NOTREACHED_H_
|
@@ -1,4 +1,7 @@
|
||||
#include "core/base/version.h"
|
||||
|
||||
#include "core/base/version_gen.h"
|
||||
|
||||
const std::string kVbamMainVersion = VBAM_CURRENT_VERSION;
|
||||
const std::string kVbamNameAndSubversion = VBAM_NAME_AND_SUBVERSION;
|
||||
const std::string kVbamVersion = VBAM_VERSION;
|
||||
|
@@ -3,7 +3,9 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "core/base/version_gen.h"
|
||||
// Main version information, generated by the build system.
|
||||
// e.g. "2.1.9"
|
||||
extern const std::string kVbamMainVersion;
|
||||
|
||||
// Full version information, generated by the build system.
|
||||
// e.g. "VisualBoyAdvance-M 2.1.9-316e4a43 msvc-316e4a43"
|
||||
|
@@ -1,13 +1,13 @@
|
||||
#include "core/gb/gb.h"
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "core/base/check.h"
|
||||
#include "core/base/file_util.h"
|
||||
#include "core/base/message.h"
|
||||
#include "core/base/sizes.h"
|
||||
@@ -291,8 +291,7 @@ bool gbInitializeRom(size_t romSize) {
|
||||
switch (g_gbCartData.validity()) {
|
||||
case gbCartData::Validity::kValid:
|
||||
case gbCartData::Validity::kUninitialized:
|
||||
// Unreachable.
|
||||
assert(false);
|
||||
VBAM_NOTREACHED();
|
||||
break;
|
||||
case gbCartData::Validity::kSizeTooSmall:
|
||||
systemMessage(MSG_UNSUPPORTED_ROM_SIZE,
|
||||
@@ -2970,8 +2969,13 @@ void gbReset()
|
||||
inBios = true;
|
||||
} else if (gbHardware & 0xa) {
|
||||
// Set compatibility mode if it is a DMG ROM.
|
||||
const uint8_t gbcFlag = g_gbCartData.SupportsCGB() ? 0x80 : 0x00;
|
||||
gbMemory[0xff6c] = 0xfe | gbcFlag;
|
||||
if (g_gbCartData.SupportsCGB()) {
|
||||
// OPRI with bit 0 set to 0 = CGB mode.
|
||||
gbMemory[0xff6c] = 0xfe;
|
||||
} else {
|
||||
// OPRI with bit 0 set to 1 = DMG mode.
|
||||
gbMemory[0xff6c] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
gbLine99Ticks = 1;
|
||||
|
@@ -2,8 +2,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
|
||||
#include "core/base/check.h"
|
||||
#include "core/base/sizes.h"
|
||||
|
||||
namespace {
|
||||
@@ -62,7 +62,7 @@ char byte_to_char(uint8_t byte) {
|
||||
} else if (byte < 16) {
|
||||
return 'A' + (byte - 10);
|
||||
} else {
|
||||
assert(false);
|
||||
VBAM_NOTREACHED();
|
||||
return '\0';
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ bool is_valid_manufacturer_code(const std::string& manufacturer_code) {
|
||||
constexpr size_t kHeaderGlobalChecksumAdress = 0x14e;
|
||||
|
||||
uint16_t get_rom_checksum(const uint8_t* romData, size_t romDataSize) {
|
||||
assert(romData);
|
||||
VBAM_CHECK(romData);
|
||||
|
||||
uint16_t checksum = 0;
|
||||
for (size_t i = 0; i < romDataSize; i++) {
|
||||
@@ -160,7 +160,7 @@ constexpr size_t kHeaderChecksumEndAdress = 0x14c;
|
||||
} // namespace
|
||||
|
||||
gbCartData::gbCartData(const uint8_t* romData, size_t romDataSize) {
|
||||
assert(romData);
|
||||
VBAM_CHECK(romData);
|
||||
|
||||
if (romDataSize < sizeof(gbRomHeader)) {
|
||||
validity_ = Validity::kSizeTooSmall;
|
||||
|
@@ -3845,6 +3845,8 @@ void CPULoop(int ticks)
|
||||
bool turbo_button_pressed = (joy >> 10) & 1;
|
||||
#ifndef __LIBRETRO__
|
||||
static uint32_t last_throttle;
|
||||
static bool current_volume_saved = false;
|
||||
static float current_volume;
|
||||
|
||||
if (turbo_button_pressed) {
|
||||
if (coreOptions.speedup_frame_skip)
|
||||
@@ -3859,11 +3861,24 @@ void CPULoop(int ticks)
|
||||
if (coreOptions.speedup_throttle_frame_skip)
|
||||
framesToSkip += static_cast<int>(std::ceil(double(coreOptions.speedup_throttle) / 100.0) - 1);
|
||||
}
|
||||
|
||||
if (coreOptions.speedup_mute && !current_volume_saved) {
|
||||
current_volume = soundGetVolume();
|
||||
current_volume_saved = true;
|
||||
soundSetVolume(0);
|
||||
}
|
||||
else if (speedup_throttle_set) {
|
||||
}
|
||||
else {
|
||||
if (current_volume_saved) {
|
||||
soundSetVolume(current_volume);
|
||||
current_volume_saved = false;
|
||||
}
|
||||
|
||||
if (speedup_throttle_set) {
|
||||
soundSetThrottle(DowncastU16(last_throttle));
|
||||
speedup_throttle_set = false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (turbo_button_pressed)
|
||||
framesToSkip = 9;
|
||||
|
13
src/core/test/CMakeLists.txt
Normal file
13
src/core/test/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
# This defines the `vbam-core-fake` library, which is used for providing a fake
|
||||
# implementation of the core library for testing purposes.
|
||||
|
||||
if(NOT BUILD_TESTING)
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_library(vbam-core-fake OBJECT)
|
||||
|
||||
target_sources(vbam-core-fake
|
||||
PRIVATE
|
||||
fake_core.cpp
|
||||
)
|
94
src/core/test/fake_core.cpp
Normal file
94
src/core/test/fake_core.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "core/base/system.h"
|
||||
|
||||
void systemMessage(int, const char*, ...) {}
|
||||
|
||||
void log(const char*, ...) {}
|
||||
|
||||
bool systemPauseOnFrame() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void systemGbPrint(uint8_t*, int, int, int, int, int) {}
|
||||
|
||||
void systemScreenCapture(int) {}
|
||||
|
||||
void systemDrawScreen() {}
|
||||
|
||||
void systemSendScreen() {}
|
||||
|
||||
bool systemReadJoypads() {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t systemReadJoypad(int) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t systemGetClock() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void systemSetTitle(const char*) {}
|
||||
|
||||
std::unique_ptr<SoundDriver> systemSoundInit() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void systemOnWriteDataToSoundBuffer(const uint16_t* /*finalWave*/, int /*length*/) {}
|
||||
|
||||
void systemOnSoundShutdown() {}
|
||||
|
||||
void systemScreenMessage(const char*) {}
|
||||
|
||||
void systemUpdateMotionSensor() {}
|
||||
|
||||
int systemGetSensorX() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int systemGetSensorY() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int systemGetSensorZ() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t systemGetSensorDarkness() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void systemCartridgeRumble(bool) {}
|
||||
|
||||
void systemPossibleCartridgeRumble(bool) {}
|
||||
|
||||
void updateRumbleFrame() {}
|
||||
|
||||
bool systemCanChangeSoundQuality() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void systemShowSpeed(int) {}
|
||||
|
||||
void system10Frames() {}
|
||||
|
||||
void systemFrame() {}
|
||||
|
||||
void systemGbBorderOn() {}
|
||||
|
||||
void (*dbgOutput)(const char* s, uint32_t addr);
|
||||
void (*dbgSignal)(int sig, int number);
|
||||
|
||||
uint16_t systemColorMap16[0x10000];
|
||||
uint32_t systemColorMap32[0x10000];
|
||||
uint16_t systemGbPalette[24];
|
||||
int systemRedShift;
|
||||
int systemGreenShift;
|
||||
int systemBlueShift;
|
||||
int systemColorDepth;
|
||||
int systemVerbose;
|
||||
int systemFrameSkip;
|
||||
int systemSaveUpdateCounter;
|
||||
int systemSpeed;
|
||||
|
||||
int emulating = 0;
|
@@ -311,8 +311,8 @@ else ifeq ($(platform), wiiu)
|
||||
CXX = $(DEVKITPPC)/bin/powerpc-eabi-g++$(EXE_EXT)
|
||||
AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)
|
||||
ENDIANNESS_DEFINES += -DMSB_FIRST -DWORDS_BIGENDIAN=1
|
||||
PLATFORM_DEFINES += -DGEKKO -DWIIU -DHW_RVL -mwup -mcpu=750 -meabi -mhard-float -D__ppc__
|
||||
PLATFORM_DEFINES += -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int
|
||||
PLATFORM_DEFINES += -DGEKKO -DWIIU -DHW_WUP -mcpu=750 -meabi -mhard-float -D__ppc__
|
||||
PLATFORM_DEFINES += -ffunction-sections -fdata-sections -D__wiiu__ -D__wut__
|
||||
STATIC_LINKING=1
|
||||
TILED_RENDERING=1
|
||||
|
||||
@@ -325,14 +325,15 @@ else ifeq ($(platform), libnx)
|
||||
-fPIE -I$(LIBNX)/include/ -ffunction-sections -fdata-sections -ftls-model=local-exec -Wl,--allow-multiple-definition -specs=$(LIBNX)/switch.specs
|
||||
CFLAGS += $(INCDIRS)
|
||||
CFLAGS += $(INCLUDE) -D__SWITCH__ -DHAVE_LIBNX
|
||||
CXXFLAGS := $(ASFLAGS) $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
||||
CFLAGS += -std=gnu11
|
||||
CXXFLAGS := $(ASFLAGS) $(CFLAGS) -fno-rtti -fno-exceptions
|
||||
STATIC_LINKING=1
|
||||
|
||||
# Nintendo Switch (libtransistor)
|
||||
# Nintendo Switch (devkitpro)
|
||||
else ifeq ($(platform), switch)
|
||||
TARGET := $(TARGET_NAME)_libretro_$(platform).a
|
||||
include $(LIBTRANSISTOR_HOME)/libtransistor.mk
|
||||
CC = $(DEVKITPRO)/devkitA64/bin/aarch64-none-elf-gcc$(EXE_EXT)
|
||||
CXX = $(DEVKITPRO)/devkitA64/bin/aarch64-none-elf-g++$(EXE_EXT)
|
||||
AR = $(DEVKITPRO)/devkitA64/bin/aarch64-none-elf-ar$(EXE_EXT)
|
||||
STATIC_LINKING=1
|
||||
TILED_RENDERING=1
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#include <cassert>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
@@ -12,6 +11,7 @@
|
||||
|
||||
#include "components/filters_agb/filters_agb.h"
|
||||
#include "components/filters_interframe/interframe.h"
|
||||
#include "core/base/check.h"
|
||||
#include "core/base/system.h"
|
||||
#include "core/base/file_util.h"
|
||||
#include "core/base/sizes.h"
|
||||
@@ -177,8 +177,7 @@ static void* gb_rtcdata_prt(void)
|
||||
case gbCartData::MapperType::kGameGenie:
|
||||
case gbCartData::MapperType::kGameShark:
|
||||
case gbCartData::MapperType::kUnknown:
|
||||
// Unreachable.
|
||||
assert(false);
|
||||
VBAM_NOTREACHED();
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
@@ -205,8 +204,7 @@ static size_t gb_rtcdata_size(void)
|
||||
case gbCartData::MapperType::kGameGenie:
|
||||
case gbCartData::MapperType::kGameShark:
|
||||
case gbCartData::MapperType::kUnknown:
|
||||
// Unreachable.
|
||||
assert(false);
|
||||
VBAM_NOTREACHED();
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
@@ -272,8 +270,7 @@ static void gbInitRTC(void)
|
||||
case gbCartData::MapperType::kGameGenie:
|
||||
case gbCartData::MapperType::kGameShark:
|
||||
case gbCartData::MapperType::kUnknown:
|
||||
// Unreachable.
|
||||
assert(false);
|
||||
VBAM_NOTREACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1456,8 +1453,7 @@ void retro_run(void)
|
||||
case gbCartData::MapperType::kGameGenie:
|
||||
case gbCartData::MapperType::kGameShark:
|
||||
case gbCartData::MapperType::kUnknown:
|
||||
// Unreachable.
|
||||
assert(false);
|
||||
VBAM_NOTREACHED();
|
||||
break;
|
||||
}
|
||||
/* Initialize RTC using local time if needed */
|
||||
|
@@ -61,7 +61,7 @@ endif()
|
||||
|
||||
if(ENABLE_LIRC)
|
||||
target_link_libraries(vbam lirc_client)
|
||||
target_compile_definitions(vbam VBAM_ENABLE_LIRC)
|
||||
target_compile_definitions(vbam PUBLIC VBAM_ENABLE_LIRC)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
@@ -71,15 +71,15 @@ endif()
|
||||
# Installation scripts.
|
||||
install(
|
||||
PROGRAMS ${PROJECT_BINARY_DIR}/vbam${CMAKE_EXECUTABLE_SUFFIX}
|
||||
DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_LIST_DIR}/vbam.cfg-example
|
||||
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}
|
||||
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}
|
||||
RENAME vbam.cfg
|
||||
)
|
||||
|
||||
if(UNIX)
|
||||
# man pages.
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/src/debian/vbam.6 DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man6)
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/src/debian/vbam.6 DESTINATION ${CMAKE_INSTALL_MANDIR}/man6)
|
||||
endif()
|
||||
|
@@ -98,7 +98,8 @@ enum named_opts
|
||||
OPT_SOUND_FILTERING,
|
||||
OPT_SPEEDUP_THROTTLE,
|
||||
OPT_SPEEDUP_FRAME_SKIP,
|
||||
OPT_NO_SPEEDUP_THROTTLE_FRAME_SKIP
|
||||
OPT_NO_SPEEDUP_THROTTLE_FRAME_SKIP,
|
||||
OPT_NO_SPEEDUP_MUTE
|
||||
};
|
||||
|
||||
#define SOUND_MAX_VOLUME 2.0
|
||||
@@ -232,6 +233,7 @@ struct option argOptions[] = {
|
||||
{ "speedup-throttle", required_argument, 0, OPT_SPEEDUP_THROTTLE },
|
||||
{ "speedup-frame-skip", required_argument, 0, OPT_SPEEDUP_FRAME_SKIP },
|
||||
{ "no-speedup-throttle-frame-skip", no_argument, 0, OPT_NO_SPEEDUP_THROTTLE_FRAME_SKIP },
|
||||
{ "no-speedup-mute", no_argument, 0, OPT_NO_SPEEDUP_MUTE },
|
||||
{ "use-bios", no_argument, &coreOptions.useBios, 1 },
|
||||
{ "verbose", required_argument, 0, 'v' },
|
||||
{ "win-gb-printer-enabled", no_argument, &coreOptions.winGbPrinterEnabled, 1 },
|
||||
@@ -347,6 +349,7 @@ void LoadConfig()
|
||||
coreOptions.speedup_throttle = ReadPref("speedupThrottle", 100);
|
||||
coreOptions.speedup_frame_skip = ReadPref("speedupFrameSkip", 9);
|
||||
coreOptions.speedup_throttle_frame_skip = ReadPref("speedupThrottleFrameSkip", 0);
|
||||
coreOptions.speedup_mute = ReadPref("speedupMute", 1);
|
||||
coreOptions.useBios = ReadPrefHex("useBiosGBA");
|
||||
coreOptions.winGbPrinterEnabled = ReadPref("gbPrinter", 0);
|
||||
|
||||
@@ -990,6 +993,9 @@ int ReadOpts(int argc, char ** argv)
|
||||
case OPT_NO_SPEEDUP_THROTTLE_FRAME_SKIP:
|
||||
coreOptions.speedup_throttle_frame_skip = false;
|
||||
break;
|
||||
case OPT_NO_SPEEDUP_MUTE:
|
||||
coreOptions.speedup_mute = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return op;
|
||||
|
@@ -13,22 +13,6 @@ set(VBAM_WX_COMMON
|
||||
background-input.cpp
|
||||
background-input.h
|
||||
cmdevents.cpp
|
||||
config/game-control.cpp
|
||||
config/game-control.h
|
||||
config/internal/option-internal.cpp
|
||||
config/internal/option-internal.h
|
||||
config/internal/shortcuts-internal.cpp
|
||||
config/internal/shortcuts-internal.h
|
||||
config/option-id.h
|
||||
config/option-observer.cpp
|
||||
config/option-observer.h
|
||||
config/option-proxy.h
|
||||
config/option.cpp
|
||||
config/option.h
|
||||
config/shortcuts.cpp
|
||||
config/shortcuts.h
|
||||
config/user-input.cpp
|
||||
config/user-input.h
|
||||
dialogs/accel-config.cpp
|
||||
dialogs/accel-config.h
|
||||
dialogs/base-dialog.cpp
|
||||
@@ -47,6 +31,8 @@ set(VBAM_WX_COMMON
|
||||
dialogs/joypad-config.h
|
||||
dialogs/sound-config.cpp
|
||||
dialogs/sound-config.h
|
||||
dialogs/speedup-config.cpp
|
||||
dialogs/speedup-config.h
|
||||
drawing.h
|
||||
extra-translations.cpp
|
||||
gfxviewers.cpp
|
||||
@@ -56,39 +42,14 @@ set(VBAM_WX_COMMON
|
||||
opts.h
|
||||
panel.cpp
|
||||
rpi.h
|
||||
strutils.cpp
|
||||
strutils.h
|
||||
sys.cpp
|
||||
viewers.cpp
|
||||
viewsupt.cpp
|
||||
viewsupt.h
|
||||
wayland.cpp
|
||||
wayland.h
|
||||
# from external source with minor modifications
|
||||
widgets/checkedlistctrl.cpp
|
||||
widgets/checkedlistctrl.h
|
||||
widgets/dpi-support.h
|
||||
widgets/group-check-box.cpp
|
||||
widgets/group-check-box.h
|
||||
widgets/keep-on-top-styler.cpp
|
||||
widgets/keep-on-top-styler.h
|
||||
widgets/option-validator.cpp
|
||||
widgets/option-validator.h
|
||||
widgets/render-plugin.cpp
|
||||
widgets/render-plugin.h
|
||||
widgets/user-input-ctrl.cpp
|
||||
widgets/user-input-ctrl.h
|
||||
widgets/sdljoy.cpp
|
||||
widgets/sdljoy.h
|
||||
widgets/utils.cpp
|
||||
widgets/utils.h
|
||||
widgets/webupdatedef.h
|
||||
widgets/wxmisc.h
|
||||
widgets/wxmisc.cpp
|
||||
wxhead.h
|
||||
wxlogdebug.h
|
||||
wxutil.cpp
|
||||
wxutil.h
|
||||
wxvbam.cpp
|
||||
wxvbam.h
|
||||
x11keymap.h
|
||||
@@ -98,7 +59,6 @@ set(VBAM_WX_COMMON
|
||||
${VBAM_GENERATED_DIR}/wx/builtin-over.h
|
||||
${VBAM_GENERATED_DIR}/wx/cmdhandlers.h
|
||||
${VBAM_GENERATED_DIR}/wx/cmd-evtable.h
|
||||
${VBAM_GENERATED_DIR}/wx/cmdtab.cpp
|
||||
)
|
||||
|
||||
if(NOT ZIP_PROGRAM)
|
||||
@@ -179,6 +139,165 @@ if(TRANSLATIONS_ONLY)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# wxWidgets configuration.
|
||||
set(wxWidgets_USE_UNICODE ON)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(wxWidgets_USE_DEBUG ON) # noop if wx is compiled with --disable-debug, like in Mac Homebrew atm
|
||||
endif()
|
||||
if(VBAM_STATIC)
|
||||
set(wxWidgets_USE_STATIC ON)
|
||||
endif()
|
||||
|
||||
unset(wx_find_extra)
|
||||
if(CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg")
|
||||
set(wx_find_extra NO_DEFAULT_PATH)
|
||||
set(wxWidgets_DIR "${VCPKG_ROOT}/installed/${VCPKG_TARGET_TRIPLET}/share/wxwidgets")
|
||||
endif()
|
||||
|
||||
set(ENABLE_OPENGL TRUE)
|
||||
find_package(wxWidgets COMPONENTS xrc xml html adv net core base gl ${wx_find_extra})
|
||||
|
||||
if(NOT wxWidgets_FOUND)
|
||||
find_package(wxWidgets COMPONENTS xrc xml html adv net core base ${wx_find_extra} REQUIRED)
|
||||
set(ENABLE_OPENGL FALSE)
|
||||
endif()
|
||||
|
||||
# Fixup wxWidgets paths for vcpkg debug builds.
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg")
|
||||
set(wxWidgets_ROOT_DIR "${wxWidgets_ROOT_DIR}/debug" CACHE INTERNAL "wxWidgets root directory" FORCE)
|
||||
string(REGEX REPLACE "/lib$" "/debug/lib" wxWidgets_LIB_DIR "${wxWidgets_LIB_DIR}")
|
||||
set(wxWidgets_LIB_DIR "${wxWidgets_LIB_DIR}" CACHE INTERNAL "wxWidgets library directory" FORCE)
|
||||
endif()
|
||||
|
||||
# Find OpenAL (required).
|
||||
find_package(OpenAL REQUIRED)
|
||||
|
||||
# Workaround of static liblzma not being found on MSYS2.
|
||||
if(VBAM_STATIC AND MSYS)
|
||||
unset(cleaned_up_wx_libs)
|
||||
foreach(lib ${wxWidgets_LIBRARIES})
|
||||
if(lib STREQUAL "-llzma")
|
||||
set(lib "liblzma.a")
|
||||
endif()
|
||||
|
||||
list(APPEND cleaned_up_wx_libs "${lib}")
|
||||
endforeach()
|
||||
|
||||
set(wxWidgets_LIBRARIES "${cleaned_up_wx_libs}")
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES ${wxWidgets_LIBRARIES})
|
||||
list(APPEND CMAKE_REQUIRED_INCLUDES ${wxWidgets_INCLUDE_DIRS})
|
||||
list(APPEND CMAKE_REQUIRED_FLAGS ${wxWidgets_CXX_FLAGS})
|
||||
foreach(DEF ${wxWidgets_DEFINITIONS})
|
||||
list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D${DEF}")
|
||||
endforeach()
|
||||
|
||||
# Configure common settings for wx-based targets, like linking, include
|
||||
# directories, compile options, and definitions.
|
||||
function(configure_wx_target target)
|
||||
get_target_property(target_type ${target} TYPE)
|
||||
if(target_type STREQUAL "EXECUTABLE")
|
||||
set(target_is_executable TRUE)
|
||||
else()
|
||||
set(target_is_executable FALSE)
|
||||
endif()
|
||||
|
||||
function(_add_link_libraries)
|
||||
if(${target_is_executable})
|
||||
target_link_libraries(${target} ${ARGN})
|
||||
else()
|
||||
target_link_libraries(${target} PUBLIC ${ARGN})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(_add_include_directories)
|
||||
if(${target_is_executable})
|
||||
target_include_directories(${target} PRIVATE ${ARGN})
|
||||
else()
|
||||
target_include_directories(${target} PUBLIC ${ARGN})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(_add_compile_options)
|
||||
if(${target_is_executable})
|
||||
target_compile_options(${target} PRIVATE ${ARGN})
|
||||
else()
|
||||
target_compile_options(${target} PUBLIC ${ARGN})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(_add_compile_definitions)
|
||||
if(${target_is_executable})
|
||||
target_compile_definitions(${target} PRIVATE ${ARGN})
|
||||
else()
|
||||
target_compile_definitions(${target} PUBLIC ${ARGN})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Core emulator.
|
||||
_add_link_libraries(vbam-core)
|
||||
|
||||
# Nonstd.
|
||||
_add_link_libraries(nonstd-lib)
|
||||
_add_include_directories(${NONSTD_INCLUDE_DIR})
|
||||
|
||||
# wxWidgets.
|
||||
_add_link_libraries(${wxWidgets_LIBRARIES})
|
||||
_add_include_directories(${wxWidgets_INCLUDE_DIRS})
|
||||
_add_compile_options(${wxWidgets_CXX_FLAGS})
|
||||
_add_compile_definitions(${wxWidgets_DEFINITIONS})
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
_add_compile_definitions(${wxWidgets_DEFINITIONS_DEBUG})
|
||||
endif()
|
||||
|
||||
# OpenAL.
|
||||
if(OPENAL_STATIC)
|
||||
_add_compile_definitions(AL_LIBTYPE_STATIC)
|
||||
endif()
|
||||
_add_include_directories(${OPENAL_INCLUDE_DIR})
|
||||
_add_link_libraries(${OPENAL_LIBRARY})
|
||||
|
||||
# XAudio2.
|
||||
if(ENABLE_XAUDIO2)
|
||||
_add_compile_definitions(VBAM_ENABLE_XAUDIO2)
|
||||
endif()
|
||||
|
||||
# FAudio.
|
||||
if(ENABLE_FAUDIO)
|
||||
_add_compile_definitions(VBAM_ENABLE_FAUDIO)
|
||||
if(MSVC)
|
||||
_add_link_libraries(FAudio::FAudio)
|
||||
else()
|
||||
_add_link_libraries(FAudio)
|
||||
if(WIN32)
|
||||
_add_link_libraries(dxguid uuid winmm ole32 advapi32 user32 mfplat mfreadwrite mfuuid propsys)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Direct3D.
|
||||
if(NOT ENABLE_DIRECT3D)
|
||||
_add_compile_definitions(NO_D3D)
|
||||
endif()
|
||||
|
||||
# SDL2.
|
||||
_add_link_libraries(${VBAM_SDL2_LIBS})
|
||||
|
||||
# OpenGL.
|
||||
if(ENABLE_OPENGL)
|
||||
_add_link_libraries(${OPENGL_LIBRARIES})
|
||||
else()
|
||||
_add_compile_definitions(NO_OGL)
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
|
||||
# Sub-projects.
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(config)
|
||||
add_subdirectory(widgets)
|
||||
|
||||
set(VBAM_ICON visualboyadvance-m.icns)
|
||||
set(VBAM_ICON_PATH ${CMAKE_CURRENT_SOURCE_DIR}/icons/${VBAM_ICON})
|
||||
|
||||
@@ -189,19 +308,17 @@ add_executable(
|
||||
)
|
||||
|
||||
target_sources(visualboyadvance-m PRIVATE ${VBAM_WX_COMMON} ${VBAM_ICON_PATH})
|
||||
target_include_directories(visualboyadvance-m PRIVATE ${NONSTD_INCLUDE_DIR} ${SDL2_INCLUDE_DIRS})
|
||||
target_include_directories(visualboyadvance-m PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(
|
||||
visualboyadvance-m
|
||||
nonstd-lib
|
||||
vbam-core
|
||||
vbam-components-draw-text
|
||||
vbam-components-filters
|
||||
vbam-components-filters-agb
|
||||
vbam-components-filters-interframe
|
||||
vbam-components-user-config
|
||||
${OPENGL_LIBRARIES}
|
||||
${VBAM_SDL2_LIBS}
|
||||
vbam-wx-config
|
||||
vbam-wx-widgets
|
||||
)
|
||||
|
||||
# adjust link command when making a static binary for gcc
|
||||
@@ -270,20 +387,11 @@ if(WIN32)
|
||||
# Disable the auto-generated manifest from CMake.
|
||||
target_link_options(visualboyadvance-m PRIVATE "/MANIFEST:NO")
|
||||
endif()
|
||||
|
||||
# wxWidgets fails to bring in its dependency.
|
||||
find_library(PCRE_LIB pcre2-16 REQUIRED)
|
||||
target_link_libraries(visualboyadvance-m ${PCRE_LIB})
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
target_sources(visualboyadvance-m PRIVATE
|
||||
macsupport.mm
|
||||
widgets/dpi-support-mac.mm
|
||||
)
|
||||
else()
|
||||
target_sources(visualboyadvance-m PRIVATE
|
||||
widgets/dpi-support.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -308,82 +416,17 @@ if(CMAKE_COMPILER_IS_GNUCXX AND VBAM_STATIC)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# OpenAL.
|
||||
find_package(OpenAL REQUIRED)
|
||||
if(OPENAL_STATIC)
|
||||
target_compile_definitions(visualboyadvance-m PRIVATE AL_LIBTYPE_STATIC)
|
||||
endif()
|
||||
target_include_directories(visualboyadvance-m PRIVATE ${OPENAL_INCLUDE_DIR})
|
||||
target_link_libraries(visualboyadvance-m ${OPENAL_LIBRARY})
|
||||
|
||||
# XAudio2.
|
||||
if(ENABLE_XAUDIO2)
|
||||
target_sources(visualboyadvance-m PRIVATE audio/internal/xaudio2.cpp)
|
||||
target_compile_definitions(visualboyadvance-m PRIVATE VBAM_ENABLE_XAUDIO2)
|
||||
endif()
|
||||
|
||||
# Direct3D.
|
||||
if(NOT ENABLE_DIRECT3D)
|
||||
target_compile_definitions(visualboyadvance-m PRIVATE NO_D3D)
|
||||
endif()
|
||||
|
||||
# FAudio.
|
||||
if(ENABLE_FAUDIO)
|
||||
target_sources(visualboyadvance-m PRIVATE audio/internal/faudio.cpp)
|
||||
target_link_libraries(visualboyadvance-m FAudio::FAudio)
|
||||
target_compile_definitions(visualboyadvance-m PRIVATE VBAM_ENABLE_FAUDIO)
|
||||
endif()
|
||||
|
||||
# wxWidgets.
|
||||
set(wxWidgets_USE_UNICODE ON)
|
||||
if(CMAKE_BUILD_TYPE MATCHES "^(Debug|RelWithDebInfo)$")
|
||||
set(wxWidgets_USE_DEBUG ON) # noop if wx is compiled with --disable-debug, like in Mac Homebrew atm
|
||||
endif()
|
||||
if(VBAM_STATIC)
|
||||
set(wxWidgets_USE_STATIC ON)
|
||||
endif()
|
||||
|
||||
unset(wx_find_extra)
|
||||
if(CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg")
|
||||
set(wx_find_extra NO_DEFAULT_PATH)
|
||||
set(wxWidgets_DIR "${VCPKG_ROOT}/installed/${VCPKG_TARGET_TRIPLET}/share/wxwidgets")
|
||||
endif()
|
||||
|
||||
find_package(wxWidgets COMPONENTS xrc xml html adv net core base gl ${wx_find_extra})
|
||||
|
||||
if(NOT wxWidgets_FOUND)
|
||||
find_package(wxWidgets COMPONENTS xrc xml html adv net core base ${wx_find_extra} REQUIRED)
|
||||
target_compile_definitions(visualboyadvance-m PRIVATE NO_OGL)
|
||||
endif()
|
||||
|
||||
# Workaround of static liblzma not being found on MSYS2.
|
||||
if(VBAM_STATIC AND MSYS)
|
||||
unset(cleaned_up_wx_libs)
|
||||
foreach(lib ${wxWidgets_LIBRARIES})
|
||||
if(lib STREQUAL "-llzma")
|
||||
set(lib "liblzma.a")
|
||||
endif()
|
||||
|
||||
list(APPEND cleaned_up_wx_libs "${lib}")
|
||||
endforeach()
|
||||
|
||||
set(wxWidgets_LIBRARIES "${cleaned_up_wx_libs}")
|
||||
endif()
|
||||
|
||||
target_link_libraries(visualboyadvance-m ${wxWidgets_LIBRARIES})
|
||||
target_include_directories(visualboyadvance-m PRIVATE ${wxWidgets_INCLUDE_DIRS})
|
||||
target_compile_options(visualboyadvance-m PRIVATE ${wxWidgets_CXX_FLAGS})
|
||||
target_compile_definitions(visualboyadvance-m PRIVATE ${wxWidgets_DEFINITIONS})
|
||||
if(CMAKE_BUILD_TYPE MATCHES "^(Debug|RelWithDebInfo)$")
|
||||
target_compile_definitions(visualboyadvance-m PRIVATE ${wxWidgets_DEFINITIONS_DEBUG})
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES ${wxWidgets_LIBRARIES})
|
||||
list(APPEND CMAKE_REQUIRED_INCLUDES ${wxWidgets_INCLUDE_DIRS})
|
||||
list(APPEND CMAKE_REQUIRED_FLAGS ${wxWidgets_CXX_FLAGS})
|
||||
foreach(DEF ${wxWidgets_DEFINITIONS})
|
||||
list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D${DEF}")
|
||||
endforeach()
|
||||
configure_wx_target(visualboyadvance-m)
|
||||
|
||||
# we make some direct gtk/gdk calls on linux and such
|
||||
# so need to link the gtk that wx was built with
|
||||
@@ -593,24 +636,7 @@ add_custom_command(
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/src/vba-over.ini ${BIN2C}
|
||||
)
|
||||
|
||||
# I don't like duplicating/triplicating code, so I only declare
|
||||
# event handlers once, and copy them in other places they are needed
|
||||
# all using portable cmake code
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${VBAM_GENERATED_DIR}/wx/cmdtab.cpp
|
||||
${VBAM_GENERATED_DIR}/wx/cmdhandlers.h
|
||||
${VBAM_GENERATED_DIR}/wx/cmd-evtable.h
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -D OUTDIR=${VBAM_GENERATED_DIR}/wx/ -P copy-events.cmake
|
||||
WORKING_DIRECTORY
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS
|
||||
cmdevents.cpp
|
||||
copy-events.cmake
|
||||
)
|
||||
|
||||
set(VBAM_LOCALIZABLE_FILES ${VBAM_WX_COMMON})
|
||||
set(VBAM_LOCALIZABLE_FILES ${VBAM_WX_COMMON} ${VBAM_LOCALIZABLE_WX_CONFIG_FILES})
|
||||
list(APPEND VBAM_LOCALIZABLE_FILES
|
||||
audio/internal/dsound.cpp
|
||||
audio/internal/faudio.cpp
|
||||
@@ -632,9 +658,9 @@ if(APPLE)
|
||||
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks")
|
||||
endif()
|
||||
|
||||
if(WIN32 AND (X86_64 OR X86_32) AND ENABLE_ONLINEUPDATES)
|
||||
if(WIN32 AND (X86_64 OR ARM64 OR X86_32) AND ENABLE_ONLINEUPDATES)
|
||||
if(NOT DEFINED WINSPARKLE_BIN_RELEASE_DIR)
|
||||
set(WINSPARKLE_BIN_RELEASE_DIR ${CMAKE_SOURCE_DIR}/dependencies/WinSparkle-0.6.0)
|
||||
set(WINSPARKLE_BIN_RELEASE_DIR ${CMAKE_SOURCE_DIR}/dependencies/WinSparkle-0.8.1)
|
||||
endif()
|
||||
|
||||
target_include_directories(
|
||||
@@ -643,9 +669,11 @@ if(WIN32 AND (X86_64 OR X86_32) AND ENABLE_ONLINEUPDATES)
|
||||
)
|
||||
|
||||
if(X86_64)
|
||||
set(WINSPARKLE_DLL ${CMAKE_SOURCE_DIR}/dependencies/WinSparkle-0.6.0/x64/Release/WinSparkle.dll)
|
||||
else()
|
||||
set(WINSPARKLE_DLL ${CMAKE_SOURCE_DIR}/dependencies/WinSparkle-0.6.0/Release/WinSparkle.dll)
|
||||
set(WINSPARKLE_DLL ${WINSPARKLE_BIN_RELEASE_DIR}/x64/Release/WinSparkle.dll)
|
||||
elseif(ARM64)
|
||||
set(WINSPARKLE_DLL ${WINSPARKLE_BIN_RELEASE_DIR}/ARM64/Release/WinSparkle.dll)
|
||||
elseif(X86_32)
|
||||
set(WINSPARKLE_DLL ${WINSPARKLE_BIN_RELEASE_DIR}/Release/WinSparkle.dll)
|
||||
endif()
|
||||
|
||||
configure_file(autoupdater/wxmsw/winsparkle-path.h.in ${CMAKE_BINARY_DIR}/winsparkle-path.h)
|
||||
@@ -805,7 +833,7 @@ if(UPSTREAM_RELEASE AND WIN32)
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
|
||||
if(GPG_KEYS)
|
||||
if(GPG_SIGNATURES AND GPG_KEYS)
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/translations.zip.asc
|
||||
COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_BINARY_DIR}/translations.zip.asc
|
||||
@@ -982,20 +1010,16 @@ install(
|
||||
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
if(BUILD_TESTING AND (NOT CMAKE_CROSSCOMPILING))
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
# Installation scripts.
|
||||
install(
|
||||
PROGRAMS ${PROJECT_BINARY_DIR}/visualboyadvance-m${CMAKE_EXECUTABLE_SUFFIX}
|
||||
DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
install(
|
||||
FILES ${CMAKE_SOURCE_DIR}/src/vba-over.ini
|
||||
DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/vbam
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/vbam
|
||||
)
|
||||
|
||||
if (UNIX)
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/src/debian/visualboyadvance-m.6 DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man6)
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/src/debian/visualboyadvance-m.6 DESTINATION ${CMAKE_INSTALL_MANDIR}/man6)
|
||||
endif()
|
||||
|
@@ -1,12 +1,12 @@
|
||||
#include "wx/audio/audio.h"
|
||||
|
||||
#include "core/base/check.h"
|
||||
#include "wx/audio/internal/openal.h"
|
||||
|
||||
#if defined(__WXMSW__)
|
||||
#include "wx/audio/internal/dsound.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(VBAM_ENABLE_FAUDIO)
|
||||
#include "wx/audio/internal/faudio.h"
|
||||
#endif
|
||||
@@ -39,8 +39,7 @@ std::vector<AudioDevice> EnumerateAudioDevices(const config::AudioApi& audio_api
|
||||
|
||||
case config::AudioApi::kLast:
|
||||
default:
|
||||
// This should never happen.
|
||||
assert(false);
|
||||
VBAM_NOTREACHED();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -67,8 +66,7 @@ std::unique_ptr<SoundDriver> CreateSoundDriver(const config::AudioApi& api) {
|
||||
|
||||
case config::AudioApi::kLast:
|
||||
default:
|
||||
// This should never happen.
|
||||
assert(false);
|
||||
VBAM_NOTREACHED();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@@ -5,8 +5,6 @@
|
||||
|
||||
#include "wx/audio/internal/faudio.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
@@ -18,9 +16,11 @@
|
||||
#include <wx/log.h>
|
||||
#include <wx/translation.h>
|
||||
|
||||
#include "core/base/check.h"
|
||||
#include "core/base/system.h"
|
||||
#include "core/gba/gbaGlobals.h"
|
||||
#include "wx/config/option-proxy.h"
|
||||
#include "wx/config/strutils.h"
|
||||
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
@@ -28,7 +28,7 @@ namespace internal {
|
||||
namespace {
|
||||
|
||||
int FAGetDev(FAudio* fa) {
|
||||
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||
const wxString audio_device = OPTION(kSoundAudioDevice).Get();
|
||||
if (audio_device.empty()) {
|
||||
// Just use the default device.
|
||||
return 0;
|
||||
@@ -48,7 +48,7 @@ int FAGetDev(FAudio* fa) {
|
||||
if (hr != 0) {
|
||||
continue;
|
||||
}
|
||||
const wxString device_id(reinterpret_cast<wchar_t*>(dd.DeviceID));
|
||||
const wxString device_id = config::utf16_to_utf8(dd.DeviceID);
|
||||
if (audio_device == device_id) {
|
||||
return i;
|
||||
}
|
||||
@@ -120,8 +120,6 @@ public:
|
||||
FAudio_Output();
|
||||
~FAudio_Output();
|
||||
|
||||
void device_change();
|
||||
|
||||
private:
|
||||
void close();
|
||||
|
||||
@@ -176,7 +174,7 @@ void FAudio_Output::close() {
|
||||
|
||||
if (sVoice) {
|
||||
if (playing) {
|
||||
assert(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == 0);
|
||||
VBAM_CHECK(FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW) == 0);
|
||||
}
|
||||
|
||||
FAudioVoice_DestroyVoice(sVoice);
|
||||
@@ -194,10 +192,6 @@ void FAudio_Output::close() {
|
||||
}
|
||||
}
|
||||
|
||||
void FAudio_Output::device_change() {
|
||||
device_changed = true;
|
||||
}
|
||||
|
||||
bool FAudio_Output::init(long sampleRate) {
|
||||
if (failed || initialized)
|
||||
return false;
|
||||
@@ -259,7 +253,7 @@ bool FAudio_Output::init(long sampleRate) {
|
||||
if (OPTION(kSoundUpmix)) {
|
||||
// set up stereo upmixing
|
||||
FAudioDeviceDetails dd{};
|
||||
assert(FAudio_GetDeviceDetails(faud, 0, &dd) == 0);
|
||||
VBAM_CHECK(FAudio_GetDeviceDetails(faud, 0, &dd) == 0);
|
||||
std::vector<float> matrix(sizeof(float) * 2 * dd.OutputFormat.Format.nChannels);
|
||||
|
||||
bool matrixAvailable = true;
|
||||
@@ -353,12 +347,12 @@ bool FAudio_Output::init(long sampleRate) {
|
||||
if (matrixAvailable) {
|
||||
hr = FAudioVoice_SetOutputMatrix(sVoice, nullptr, 2, dd.OutputFormat.Format.nChannels,
|
||||
matrix.data(), FAUDIO_DEFAULT_CHANNELS);
|
||||
assert(hr == 0);
|
||||
VBAM_CHECK(hr == 0);
|
||||
}
|
||||
}
|
||||
|
||||
hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
|
||||
assert(hr == 0);
|
||||
VBAM_CHECK(hr == 0);
|
||||
playing = true;
|
||||
currentBuffer = 0;
|
||||
device_changed = false;
|
||||
@@ -380,7 +374,7 @@ void FAudio_Output::write(uint16_t* finalWave, int) {
|
||||
}
|
||||
|
||||
FAudioSourceVoice_GetState(sVoice, &vState, flags);
|
||||
assert(vState.BuffersQueued <= buffer_count_);
|
||||
VBAM_CHECK(vState.BuffersQueued <= buffer_count_);
|
||||
|
||||
if (vState.BuffersQueued < buffer_count_) {
|
||||
if (vState.BuffersQueued == 0) {
|
||||
@@ -414,7 +408,7 @@ void FAudio_Output::write(uint16_t* finalWave, int) {
|
||||
currentBuffer++;
|
||||
currentBuffer %= (buffer_count_ + 1); // + 1 because we need one temporary buffer
|
||||
[[maybe_unused]] uint32_t hr = FAudioSourceVoice_SubmitSourceBuffer(sVoice, &buf, nullptr);
|
||||
assert(hr == 0);
|
||||
VBAM_CHECK(hr == 0);
|
||||
}
|
||||
|
||||
void FAudio_Output::pause() {
|
||||
@@ -423,7 +417,7 @@ void FAudio_Output::pause() {
|
||||
|
||||
if (playing) {
|
||||
[[maybe_unused]] uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
|
||||
assert(hr == 0);
|
||||
VBAM_CHECK(hr == 0);
|
||||
playing = false;
|
||||
}
|
||||
}
|
||||
@@ -434,7 +428,7 @@ void FAudio_Output::resume() {
|
||||
|
||||
if (!playing) {
|
||||
[[maybe_unused]] int32_t hr = FAudioSourceVoice_Start(sVoice, 0, FAUDIO_COMMIT_NOW);
|
||||
assert(hr == 0);
|
||||
VBAM_CHECK(hr == 0);
|
||||
playing = true;
|
||||
}
|
||||
}
|
||||
@@ -445,7 +439,7 @@ void FAudio_Output::reset() {
|
||||
|
||||
if (playing) {
|
||||
[[maybe_unused]] uint32_t hr = FAudioSourceVoice_Stop(sVoice, 0, FAUDIO_COMMIT_NOW);
|
||||
assert(hr == 0);
|
||||
VBAM_CHECK(hr == 0);
|
||||
}
|
||||
|
||||
FAudioSourceVoice_FlushSourceBuffers(sVoice);
|
||||
@@ -462,7 +456,7 @@ void FAudio_Output::setThrottle(unsigned short throttle_) {
|
||||
|
||||
[[maybe_unused]] uint32_t hr =
|
||||
FAudioSourceVoice_SetFrequencyRatio(sVoice, (float)throttle_ / 100.0f, FAUDIO_COMMIT_NOW);
|
||||
assert(hr == 0);
|
||||
VBAM_CHECK(hr == 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -489,8 +483,11 @@ std::vector<AudioDevice> GetFAudioDevices() {
|
||||
}
|
||||
|
||||
std::vector<AudioDevice> devices;
|
||||
#if defined(__WXMSW__)
|
||||
// Add a separate default device on Windows.
|
||||
devices.reserve(dev_count + 1);
|
||||
devices.push_back({_("Default device"), wxEmptyString});
|
||||
#endif
|
||||
|
||||
for (uint32_t i = 0; i < dev_count; i++) {
|
||||
FAudioDeviceDetails dd;
|
||||
@@ -499,9 +496,9 @@ std::vector<AudioDevice> GetFAudioDevices() {
|
||||
continue;
|
||||
}
|
||||
|
||||
const wxString display_name(reinterpret_cast<wchar_t*>(dd.DisplayName));
|
||||
const wxString device_id(reinterpret_cast<wchar_t*>(dd.DeviceID));
|
||||
|
||||
// Convert to UTF-8.
|
||||
const wxString display_name = config::utf16_to_utf8(dd.DisplayName);
|
||||
const wxString device_id = config::utf16_to_utf8(dd.DeviceID);
|
||||
devices.push_back({display_name, device_id});
|
||||
}
|
||||
|
||||
|
@@ -30,13 +30,12 @@ typedef ALCboolean(ALC_APIENTRY* LPALCISEXTENSIONPRESENT)(ALCdevice* device,
|
||||
typedef const ALCchar*(ALC_APIENTRY* LPALCGETSTRING)(ALCdevice* device, ALCenum param);
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <wx/arrstr.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/translation.h>
|
||||
#include <wx/utils.h>
|
||||
|
||||
#include "core/base/check.h"
|
||||
#include "core/gba/gbaGlobals.h"
|
||||
#include "core/gba/gbaSound.h"
|
||||
#include "wx/config/option-proxy.h"
|
||||
@@ -47,7 +46,7 @@ namespace internal {
|
||||
namespace {
|
||||
|
||||
// Debug
|
||||
#define ASSERT_SUCCESS assert(AL_NO_ERROR == alGetError())
|
||||
#define ASSERT_SUCCESS VBAM_CHECK(AL_NO_ERROR == alGetError())
|
||||
|
||||
#ifndef LOGALL
|
||||
// replace logging functions with comments
|
||||
@@ -171,7 +170,7 @@ void OpenAL::debugState() {
|
||||
|
||||
bool OpenAL::init(long sampleRate) {
|
||||
winlog("OpenAL::init\n");
|
||||
assert(initialized == false);
|
||||
VBAM_CHECK(initialized == false);
|
||||
|
||||
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||
if (!audio_device.empty()) {
|
||||
@@ -191,9 +190,9 @@ bool OpenAL::init(long sampleRate) {
|
||||
}
|
||||
|
||||
context = alcCreateContext(device, nullptr);
|
||||
assert(context != nullptr);
|
||||
VBAM_CHECK(context != nullptr);
|
||||
ALCboolean retVal = alcMakeContextCurrent(context);
|
||||
assert(ALC_TRUE == retVal);
|
||||
VBAM_CHECK(ALC_TRUE == retVal);
|
||||
alGenBuffers(OPTION(kSoundBuffers), buffer);
|
||||
ASSERT_SUCCESS;
|
||||
alGenSources(1, &source);
|
||||
@@ -338,7 +337,7 @@ void OpenAL::write(uint16_t* finalWave, int length) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(nBuffersProcessed > 0);
|
||||
VBAM_CHECK(nBuffersProcessed > 0);
|
||||
|
||||
// unqueue buffer
|
||||
tempBuffer = 0;
|
||||
|
@@ -1,10 +1,6 @@
|
||||
#ifndef WX_AUDIO_INTERNAL_XAUDIO2_H_
|
||||
#define WX_AUDIO_INTERNAL_XAUDIO2_H_
|
||||
|
||||
#if !defined(VBAM_ENABLE_FAUDIO)
|
||||
#error "This file should only be included if FAudio is enabled"
|
||||
#endif
|
||||
|
||||
#include "wx/audio/audio.h"
|
||||
|
||||
namespace audio {
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#ifndef AUTOUPDATER_H
|
||||
#define AUTOUPDATER_H
|
||||
#ifndef VBAM_WX_AUTOUPDATER_AUTOUPDATER_H_
|
||||
#define VBAM_WX_AUTOUPDATER_AUTOUPDATER_H_
|
||||
|
||||
void initAutoupdater();
|
||||
void checkUpdatesUi();
|
||||
void shutdownAutoupdater();
|
||||
|
||||
#endif // AUTOUPDATER_H
|
||||
#endif // VBAM_WX_AUTOUPDATER_AUTOUPDATER_H_
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#include "../autoupdater.h"
|
||||
#include "sparkle-wrapper.h"
|
||||
#include "wx/autoupdater/autoupdater.h"
|
||||
|
||||
#include "wx/autoupdater/macos/sparkle-wrapper.h"
|
||||
|
||||
SparkleWrapper autoupdater;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
#ifndef SPARKLE_WRAPPER_H
|
||||
#define SPARKLE_WRAPPER_H
|
||||
#ifndef VBAM_WX_AUTOUPDATER_MACOS_SPARKLE_WRAPPER_H_
|
||||
#define VBAM_WX_AUTOUPDATER_MACOS_SPARKLE_WRAPPER_H_
|
||||
|
||||
class SparkleWrapper
|
||||
{
|
||||
@@ -14,4 +14,4 @@ class SparkleWrapper
|
||||
Private* d;
|
||||
};
|
||||
|
||||
#endif // SPARKLE_WRAPPER_H
|
||||
#endif // VBAM_WX_AUTOUPDATER_MACOS_SPARKLE_WRAPPER_H_
|
||||
|
@@ -1,14 +1,13 @@
|
||||
#include "../autoupdater.h"
|
||||
#include "wx/autoupdater/autoupdater.h"
|
||||
|
||||
#include "core/base/version.h"
|
||||
#include "../../strutils.h"
|
||||
#include "winsparkle-wrapper.h"
|
||||
#include "wx/autoupdater/wxmsw/winsparkle-wrapper.h"
|
||||
|
||||
|
||||
void initAutoupdater()
|
||||
{
|
||||
// even if we are a nightly, only check latest stable version
|
||||
wxString version = strutils::split(kVbamVersion, '-')[0];
|
||||
const wxString version(kVbamMainVersion);
|
||||
#ifndef NO_HTTPS
|
||||
win_sparkle_set_appcast_url("https://data.visualboyadvance-m.org/appcast.xml");
|
||||
#else
|
||||
|
@@ -1,12 +1,13 @@
|
||||
#include "wx/autoupdater/wxmsw/winsparkle-wrapper.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <wx/file.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/msw/private.h>
|
||||
#include <wx/utils.h>
|
||||
#include "wx/wxvbam.h"
|
||||
#include "winsparkle-wrapper.h"
|
||||
#include "wx/msw/private.h"
|
||||
|
||||
#include "wx/autoupdater/wxmsw/winsparkle-rc.h"
|
||||
|
||||
WinSparkleDllWrapper *WinSparkleDllWrapper::GetInstance()
|
||||
{
|
||||
|
@@ -4,8 +4,6 @@
|
||||
#include <wx/string.h>
|
||||
#include <wx/dynlib.h>
|
||||
|
||||
#include "winsparkle-rc.h"
|
||||
|
||||
class WinSparkleDllWrapper {
|
||||
public:
|
||||
static WinSparkleDllWrapper *GetInstance();
|
||||
|
@@ -547,17 +547,17 @@ wxThread::ExitCode BackgroundInput::CheckKeyboard()
|
||||
// virtual key "i" is pressed
|
||||
if ((bits & 0x8000) && (previousState[i] & 0x8000) == 0) {
|
||||
if (handler && !wxWindow::FindFocus()) {
|
||||
wxKeyEvent ev(wxEVT_KEY_DOWN);
|
||||
ev.m_keyCode = xKeySym;
|
||||
handler->AddPendingEvent(ev);
|
||||
wxKeyEvent* event = new wxKeyEvent(wxEVT_KEY_DOWN);
|
||||
event->m_keyCode = xKeySym;
|
||||
handler->QueueEvent(event);
|
||||
}
|
||||
}
|
||||
// virtual key "i" is released
|
||||
else if (((bits & 0x8000) == 0) && (previousState[i] & 0x8000)) {
|
||||
if (handler && !wxWindow::FindFocus()) {
|
||||
wxKeyEvent ev(wxEVT_KEY_UP);
|
||||
ev.m_keyCode = xKeySym;
|
||||
handler->AddPendingEvent(ev);
|
||||
wxKeyEvent* event = new wxKeyEvent(wxEVT_KEY_UP);
|
||||
event->m_keyCode = xKeySym;
|
||||
handler->QueueEvent(event);
|
||||
}
|
||||
}
|
||||
previousState[i] = bits;
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
#include "components/filters_interframe/interframe.h"
|
||||
#include "core/base/check.h"
|
||||
#include "core/base/version.h"
|
||||
#include "core/gb/gb.h"
|
||||
#include "core/gb/gbCheats.h"
|
||||
@@ -22,6 +23,7 @@
|
||||
#include "core/gba/gbaGlobals.h"
|
||||
#include "core/gba/gbaPrint.h"
|
||||
#include "core/gba/gbaSound.h"
|
||||
#include "wx/config/cmdtab.h"
|
||||
#include "wx/config/option-proxy.h"
|
||||
#include "wx/config/option.h"
|
||||
#include "wx/dialogs/game-maker.h"
|
||||
@@ -29,14 +31,9 @@
|
||||
#define GetXRCDialog(n) \
|
||||
wxStaticCast(wxGetApp().frame->FindWindowByName(n), wxDialog)
|
||||
|
||||
bool cmditem_lt(const struct cmditem& cmd1, const struct cmditem& cmd2)
|
||||
{
|
||||
return wxStrcmp(cmd1.cmd, cmd2.cmd) < 0;
|
||||
}
|
||||
|
||||
void MainFrame::GetMenuOptionBool(const wxString& menuName, bool* field)
|
||||
{
|
||||
assert(field);
|
||||
VBAM_CHECK(field);
|
||||
*field = !*field;
|
||||
int id = wxXmlResource::GetXRCID(menuName);
|
||||
|
||||
@@ -52,7 +49,7 @@ void MainFrame::GetMenuOptionBool(const wxString& menuName, bool* field)
|
||||
void MainFrame::GetMenuOptionConfig(const wxString& menu_name,
|
||||
const config::OptionID& option_id) {
|
||||
config::Option* option = config::Option::ByID(option_id);
|
||||
assert(option);
|
||||
VBAM_CHECK(option);
|
||||
|
||||
int id = wxXmlResource::GetXRCID(menu_name);
|
||||
for (size_t i = 0; i < checkable_mi.size(); i++) {
|
||||
@@ -68,7 +65,7 @@ void MainFrame::GetMenuOptionConfig(const wxString& menu_name,
|
||||
option->SetInt(is_checked);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
VBAM_CHECK(false);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -77,7 +74,7 @@ void MainFrame::GetMenuOptionConfig(const wxString& menu_name,
|
||||
|
||||
void MainFrame::GetMenuOptionInt(const wxString& menuName, int* field, int mask)
|
||||
{
|
||||
assert(field);
|
||||
VBAM_CHECK(field);
|
||||
int value = mask;
|
||||
bool is_checked = ((*field) & (mask)) != (value);
|
||||
int id = wxXmlResource::GetXRCID(menuName);
|
||||
@@ -129,7 +126,7 @@ static void toggleBitVar(bool *menuValue, int *globalVar, int mask)
|
||||
EVT_HANDLER(wxID_OPEN, "Open ROM...")
|
||||
{
|
||||
static int open_ft = 0;
|
||||
const wxString& gba_rom_dir = OPTION(kGBAROMDir);
|
||||
const wxString gba_rom_dir = OPTION(kGBAROMDir);
|
||||
|
||||
// FIXME: ignore if non-existent or not a dir
|
||||
wxString pats = _(
|
||||
@@ -161,7 +158,7 @@ EVT_HANDLER(wxID_OPEN, "Open ROM...")
|
||||
EVT_HANDLER(OpenGB, "Open GB...")
|
||||
{
|
||||
static int open_ft = 0;
|
||||
const wxString& gb_rom_dir = OPTION(kGBROMDir);
|
||||
const wxString gb_rom_dir = OPTION(kGBROMDir);
|
||||
|
||||
// FIXME: ignore if non-existent or not a dir
|
||||
wxString pats = _(
|
||||
@@ -188,7 +185,7 @@ EVT_HANDLER(OpenGB, "Open GB...")
|
||||
EVT_HANDLER(OpenGBC, "Open GBC...")
|
||||
{
|
||||
static int open_ft = 0;
|
||||
const wxString& gbc_rom_dir = OPTION(kGBGBCROMDir);
|
||||
const wxString gbc_rom_dir = OPTION(kGBGBCROMDir);
|
||||
|
||||
// FIXME: ignore if non-existent or not a dir
|
||||
wxString pats = _(
|
||||
@@ -365,7 +362,7 @@ EVT_HANDLER_MASK(SetLoadingDotCodeFile, "Load e-Reader Dot Code...", CMDEN_GBA)
|
||||
static wxString loaddotcodefile_path;
|
||||
wxFileDialog dlg(this, _("Select Dot Code file"), loaddotcodefile_path, wxEmptyString,
|
||||
_(
|
||||
"e-Reader Dot Code (*.bin;*.raw)|"
|
||||
"E-Reader Dot Code (*.bin;*.raw)|"
|
||||
"*.bin;*.raw"),
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
int ret = ShowModal(&dlg);
|
||||
@@ -387,7 +384,7 @@ EVT_HANDLER_MASK(SetSavingDotCodeFile, "Save e-Reader Dot Code...", CMDEN_GBA)
|
||||
static wxString savedotcodefile_path;
|
||||
wxFileDialog dlg(this, _("Select Dot Code file"), savedotcodefile_path, wxEmptyString,
|
||||
_(
|
||||
"e-Reader Dot Code (*.bin;*.raw)|"
|
||||
"E-Reader Dot Code (*.bin;*.raw)|"
|
||||
"*.bin;*.raw"),
|
||||
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
int ret = ShowModal(&dlg);
|
||||
@@ -1184,7 +1181,7 @@ EVT_HANDLER(AllowKeyboardBackgroundInput, "Allow keyboard background input (togg
|
||||
disableKeyboardBackgroundInput();
|
||||
if (OPTION(kUIAllowKeyboardBackgroundInput)) {
|
||||
if (panel && panel->panel) {
|
||||
enableKeyboardBackgroundInput(panel->panel->GetWindow());
|
||||
enableKeyboardBackgroundInput(panel->panel->GetWindow()->GetEventHandler());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2173,44 +2170,17 @@ EVT_HANDLER(EmulatorDirectories, "Directories...")
|
||||
|
||||
EVT_HANDLER(JoypadConfigure, "Joypad options...")
|
||||
{
|
||||
joy.PollAllJoysticks();
|
||||
|
||||
auto frame = wxGetApp().frame;
|
||||
bool joy_timer = frame->IsJoyPollTimerRunning();
|
||||
|
||||
if (!joy_timer) {
|
||||
frame->StartJoyPollTimer();
|
||||
}
|
||||
|
||||
if (ShowModal(GetXRCDialog("JoypadConfig")) == wxID_OK) {
|
||||
update_joypad_opts();
|
||||
update_shortcut_opts();
|
||||
}
|
||||
|
||||
if (!joy_timer) {
|
||||
frame->StopJoyPollTimer();
|
||||
}
|
||||
|
||||
SetJoystick();
|
||||
}
|
||||
|
||||
EVT_HANDLER(Customize, "Customize UI...")
|
||||
{
|
||||
wxDialog* dlg = GetXRCDialog("AccelConfig");
|
||||
joy.PollAllJoysticks();
|
||||
|
||||
auto frame = wxGetApp().frame;
|
||||
bool joy_timer = frame->IsJoyPollTimerRunning();
|
||||
|
||||
if (!joy_timer) frame->StartJoyPollTimer();
|
||||
|
||||
if (ShowModal(dlg) == wxID_OK) {
|
||||
if (ShowModal(GetXRCDialog("AccelConfig")) == wxID_OK) {
|
||||
update_shortcut_opts();
|
||||
ResetMenuAccelerators();
|
||||
}
|
||||
|
||||
if (!joy_timer) frame->StopJoyPollTimer();
|
||||
|
||||
SetJoystick();
|
||||
}
|
||||
|
||||
#ifndef NO_ONLINEUPDATES
|
||||
@@ -2249,7 +2219,7 @@ EVT_HANDLER(FAQ, "VBA-M support forum")
|
||||
|
||||
EVT_HANDLER(Translate, "Translations")
|
||||
{
|
||||
wxLaunchDefaultBrowser(wxT("http://www.transifex.com/projects/p/vba-m"));
|
||||
wxLaunchDefaultBrowser(wxT("https://explore.transifex.com/bgk/vba-m/"));
|
||||
}
|
||||
|
||||
// was About
|
||||
|
@@ -0,0 +1,89 @@
|
||||
# This defines the vbam-wx-config target and the
|
||||
# `VBAM_LOCALIZABLE_WX_CONFIG_FILES` variable, containing the list of
|
||||
# localizable files in the vbam-wx-config target.
|
||||
|
||||
# I don't like duplicating/triplicating code, so I only declare
|
||||
# event handlers once, and copy them in other places they are needed
|
||||
# all using portable cmake code
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${VBAM_GENERATED_DIR}/wx/config/internal/cmdtab.cpp
|
||||
${VBAM_GENERATED_DIR}/wx/cmdhandlers.h
|
||||
${VBAM_GENERATED_DIR}/wx/cmd-evtable.h
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -D OUTDIR=${VBAM_GENERATED_DIR}/wx/ -P copy-events.cmake
|
||||
WORKING_DIRECTORY
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS
|
||||
../cmdevents.cpp
|
||||
copy-events.cmake
|
||||
)
|
||||
|
||||
add_library(vbam-wx-config OBJECT)
|
||||
|
||||
# Export the localizable files to the parent scope.
|
||||
set(VBAM_LOCALIZABLE_WX_CONFIG_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/bindings.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/command.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/cmdtab.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/emulated-gamepad.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/bindings-internal.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/bindings-internal.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/option-internal.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/option-internal.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/option-observer.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/option.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/strutils.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/user-input.cpp
|
||||
# Generated file.
|
||||
${VBAM_GENERATED_DIR}/wx/config/internal/cmdtab.cpp
|
||||
)
|
||||
|
||||
set(VBAM_LOCALIZABLE_WX_CONFIG_FILES
|
||||
${VBAM_LOCALIZABLE_WX_CONFIG_FILES}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
target_sources(vbam-wx-config
|
||||
PRIVATE
|
||||
${VBAM_LOCALIZABLE_WX_CONFIG_FILES}
|
||||
|
||||
PUBLIC
|
||||
bindings.h
|
||||
command.h
|
||||
cmdtab.h
|
||||
emulated-gamepad.h
|
||||
option-id.h
|
||||
option-observer.h
|
||||
option-proxy.h
|
||||
option.h
|
||||
strutils.h
|
||||
user-input.h
|
||||
)
|
||||
|
||||
configure_wx_target(vbam-wx-config)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_executable(vbam-wx-config-tests
|
||||
bindings-test.cpp
|
||||
command-test.cpp
|
||||
emulated-gamepad-test.cpp
|
||||
option-test.cpp
|
||||
strutils-test.cpp
|
||||
user-input-test.cpp
|
||||
)
|
||||
target_link_libraries(vbam-wx-config-tests
|
||||
# Test deps.
|
||||
vbam-core-fake
|
||||
vbam-wx-fake-opts
|
||||
|
||||
# Target deps.
|
||||
vbam-wx-config
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
configure_wx_target(vbam-wx-config-tests)
|
||||
if (NOT CMAKE_CROSSCOMPILING)
|
||||
gtest_discover_tests(vbam-wx-config-tests)
|
||||
endif()
|
||||
endif()
|
||||
|
152
src/wx/config/bindings-test.cpp
Normal file
152
src/wx/config/bindings-test.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include "wx/config/bindings.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include "wx/config/command.h"
|
||||
#include "wx/config/user-input.h"
|
||||
|
||||
TEST(BindingsTest, Default) {
|
||||
const config::Bindings bindings;
|
||||
|
||||
// Check that the default bindings are set up correctly.
|
||||
auto inputs =
|
||||
bindings.InputsForCommand(config::GameCommand(config::GameJoy(0), config::GameKey::Up));
|
||||
EXPECT_TRUE(inputs.find(config::KeyboardInput('W')) != inputs.end());
|
||||
EXPECT_TRUE(inputs.find(config::JoyInput(config::JoyId(0), config::JoyControl::HatNorth, 0)) !=
|
||||
inputs.end());
|
||||
|
||||
inputs = bindings.InputsForCommand(config::ShortcutCommand(wxID_CLOSE));
|
||||
EXPECT_TRUE(inputs.find(config::KeyboardInput('W', wxMOD_CMD)) != inputs.end());
|
||||
|
||||
inputs = bindings.InputsForCommand(config::ShortcutCommand(XRCID("LoadGame01")));
|
||||
EXPECT_TRUE(inputs.find(config::KeyboardInput(WXK_F1)) != inputs.end());
|
||||
|
||||
// Check that the INI configuration for the keyboard is empty.
|
||||
const auto config = bindings.GetKeyboardConfiguration();
|
||||
EXPECT_TRUE(config.empty());
|
||||
}
|
||||
|
||||
// Tests that assigning a default input to another command generates the right
|
||||
// configuration.
|
||||
TEST(BindingsTest, AssignDefault) {
|
||||
config::Bindings bindings;
|
||||
|
||||
// Assign F1 to the "Close" command.
|
||||
bindings.AssignInputToCommand(config::KeyboardInput(WXK_F1),
|
||||
config::ShortcutCommand(wxID_CLOSE));
|
||||
|
||||
// The INI configuration should have NOOP set to F1, and Close set to F1.
|
||||
const auto config = bindings.GetKeyboardConfiguration();
|
||||
EXPECT_EQ(config.size(), 2);
|
||||
EXPECT_EQ(config[0].first, "Keyboard/NOOP");
|
||||
EXPECT_EQ(config[0].second, "F1");
|
||||
EXPECT_EQ(config[1].first, "Keyboard/CLOSE");
|
||||
EXPECT_EQ(config[1].second, "F1");
|
||||
}
|
||||
|
||||
// Tests that unassigning a default input generates the right configuration.
|
||||
TEST(BindingsTest, UnassignDefault) {
|
||||
config::Bindings bindings;
|
||||
|
||||
// Unassign F1.
|
||||
bindings.UnassignInput(config::KeyboardInput(WXK_F1));
|
||||
|
||||
// The INI configuration should have NOOP set to F1.
|
||||
const auto config = bindings.GetKeyboardConfiguration();
|
||||
EXPECT_EQ(config.size(), 1);
|
||||
EXPECT_EQ(config[0].first, "Keyboard/NOOP");
|
||||
EXPECT_EQ(config[0].second, "F1");
|
||||
}
|
||||
|
||||
// Tests that re-assigning a default input to its default command generates the
|
||||
// right configuration.
|
||||
TEST(BindingsTest, ReassignDefault) {
|
||||
config::Bindings bindings;
|
||||
|
||||
// Assign F1 to the "Close" command.
|
||||
bindings.AssignInputToCommand(config::KeyboardInput(WXK_F1),
|
||||
config::ShortcutCommand(wxID_CLOSE));
|
||||
|
||||
// Re-assign F1 to the "LoadGame01" command.
|
||||
bindings.AssignInputToCommand(config::KeyboardInput(WXK_F1),
|
||||
config::ShortcutCommand(XRCID("LoadGame01")));
|
||||
|
||||
// The INI configuration should be empty.
|
||||
const auto config = bindings.GetKeyboardConfiguration();
|
||||
EXPECT_TRUE(config.empty());
|
||||
}
|
||||
|
||||
// Tests that assigning an input to "NOOP" properly disables the default input.
|
||||
TEST(BindingsTest, AssignToNoop) {
|
||||
config::Bindings bindings;
|
||||
|
||||
// Assign F1 to the "NOOP" command.
|
||||
bindings.AssignInputToCommand(config::KeyboardInput(WXK_F1),
|
||||
config::ShortcutCommand(XRCID("NOOP")));
|
||||
|
||||
const auto command = bindings.CommandForInput(config::KeyboardInput(WXK_F1));
|
||||
EXPECT_FALSE(command.has_value());
|
||||
|
||||
// The INI configuration should have NOOP set to F1 and nothing more.
|
||||
const auto config = bindings.GetKeyboardConfiguration();
|
||||
EXPECT_EQ(config.size(), 1);
|
||||
EXPECT_EQ(config[0].first, "Keyboard/NOOP");
|
||||
EXPECT_EQ(config[0].second, "F1");
|
||||
}
|
||||
|
||||
// Tests that assigning an input not used as a default shortcut to "NOOP" does
|
||||
// nothing.
|
||||
TEST(BindingsTest, AssignUnusedToNoop) {
|
||||
config::Bindings bindings;
|
||||
|
||||
// Assign "T" to the "NOOP" command.
|
||||
bindings.AssignInputToCommand(config::KeyboardInput('T'), config::ShortcutCommand(XRCID("NOOP")));
|
||||
|
||||
// The INI configuration should be empty.
|
||||
const auto config = bindings.GetKeyboardConfiguration();
|
||||
EXPECT_TRUE(config.empty());
|
||||
|
||||
// "T" should have no assignment.
|
||||
const auto command = bindings.CommandForInput(config::KeyboardInput('T'));
|
||||
EXPECT_FALSE(command.has_value());
|
||||
}
|
||||
|
||||
// Tests that assigning a default input to a Game command works as expected.
|
||||
TEST(BindingsTest, AssignDefaultToGame) {
|
||||
config::Bindings bindings;
|
||||
|
||||
// Assign F1 to the "Up" command and clear all of the default input for the
|
||||
// "Up" command.
|
||||
bindings.AssignInputsToCommand({config::KeyboardInput(WXK_F1)},
|
||||
config::GameCommand(config::GameJoy(0), config::GameKey::Up));
|
||||
|
||||
// The INI configuration should have NOOP set to F1.
|
||||
const auto config = bindings.GetKeyboardConfiguration();
|
||||
EXPECT_EQ(config.size(), 1);
|
||||
EXPECT_EQ(config[0].first, "Keyboard/NOOP");
|
||||
EXPECT_EQ(config[0].second, "F1");
|
||||
|
||||
EXPECT_EQ(
|
||||
bindings.InputsForCommand(config::GameCommand(config::GameJoy(0), config::GameKey::Up)),
|
||||
std::unordered_set<config::UserInput>{config::KeyboardInput(WXK_F1)});
|
||||
|
||||
EXPECT_EQ(bindings.CommandForInput(config::KeyboardInput(WXK_F1)),
|
||||
config::Command(config::GameCommand(config::GameJoy(0), config::GameKey::Up)));
|
||||
}
|
||||
|
||||
// Tests the "ClearCommandAssignments" method.
|
||||
TEST(BindingsTest, ClearCommand) {
|
||||
config::Bindings bindings;
|
||||
|
||||
// Clear "CLOSE" assignments.
|
||||
bindings.ClearCommandAssignments(config::ShortcutCommand(wxID_CLOSE));
|
||||
|
||||
// The INI configuration should only have the NOOP assignment.
|
||||
const auto config = bindings.GetKeyboardConfiguration();
|
||||
EXPECT_EQ(config.size(), 1);
|
||||
EXPECT_EQ(config[0].first, "Keyboard/NOOP");
|
||||
EXPECT_EQ(config[0].second, "CTRL+W");
|
||||
|
||||
EXPECT_TRUE(bindings.InputsForCommand(config::ShortcutCommand(wxID_CLOSE)).empty());
|
||||
}
|
252
src/wx/config/bindings.cpp
Normal file
252
src/wx/config/bindings.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
#include "wx/config/bindings.h"
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <wx/translation.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
|
||||
#include "wx/config/user-input.h"
|
||||
|
||||
#define VBAM_BINDINGS_INTERNAL_INCLUDE
|
||||
#include "wx/config/internal/bindings-internal.h"
|
||||
#undef VBAM_BINDINGS_INTERNAL_INCLUDE
|
||||
|
||||
namespace config {
|
||||
|
||||
namespace {
|
||||
|
||||
int NoopCommand() {
|
||||
static const int noop = XRCID("NOOP");
|
||||
return noop;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
const std::unordered_set<UserInput>& Bindings::DefaultInputsForCommand(const Command& command) {
|
||||
return internal::DefaultInputsForCommand(command);
|
||||
}
|
||||
|
||||
Bindings::Bindings() {
|
||||
// Set up default shortcuts.
|
||||
for (const auto& iter : internal::DefaultInputs()) {
|
||||
for (const auto& input : iter.second) {
|
||||
AssignInputToCommand(input, iter.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bindings::Bindings(
|
||||
const std::unordered_map<Command, std::unordered_set<UserInput>>& control_to_inputs,
|
||||
const std::unordered_map<UserInput, Command>& input_to_control,
|
||||
const std::unordered_map<UserInput, ShortcutCommand>& disabled_defaults)
|
||||
: control_to_inputs_(control_to_inputs.begin(), control_to_inputs.end()),
|
||||
input_to_control_(input_to_control.begin(), input_to_control.end()),
|
||||
disabled_defaults_(disabled_defaults.begin(), disabled_defaults.end()) {}
|
||||
|
||||
std::vector<std::pair<wxString, wxString>> Bindings::GetKeyboardConfiguration() const {
|
||||
std::vector<std::pair<wxString, wxString>> config;
|
||||
config.reserve(control_to_inputs_.size() + 1);
|
||||
|
||||
if (!disabled_defaults_.empty()) {
|
||||
std::unordered_set<UserInput> noop_inputs;
|
||||
for (const auto& iter : disabled_defaults_) {
|
||||
noop_inputs.insert(iter.first);
|
||||
}
|
||||
config.push_back(std::make_pair(ShortcutCommand(NoopCommand()).ToConfigString(),
|
||||
UserInput::SpanToConfigString(noop_inputs)));
|
||||
}
|
||||
|
||||
for (const auto& iter : control_to_inputs_) {
|
||||
if (iter.first.is_game()) {
|
||||
// We only consider shortcut assignments here.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Gather the inputs for this command.
|
||||
std::unordered_set<UserInput> inputs;
|
||||
for (const auto& input : iter.second) {
|
||||
if (internal::IsDefaultInputForCommand(iter.first, input)) {
|
||||
// Default assignments are ignored.
|
||||
continue;
|
||||
}
|
||||
// Not a default input.
|
||||
inputs.insert(input);
|
||||
}
|
||||
|
||||
if (!inputs.empty()) {
|
||||
config.push_back(std::make_pair(iter.first.shortcut().ToConfigString(),
|
||||
UserInput::SpanToConfigString(inputs)));
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
std::vector<std::pair<GameCommand, wxString>> Bindings::GetJoypadConfiguration() const {
|
||||
std::vector<std::pair<GameCommand, wxString>> config;
|
||||
config.reserve(kNbGameKeys * kNbJoypads);
|
||||
|
||||
for (const auto& game_command : internal::kOrderedGameCommands) {
|
||||
const auto iter = control_to_inputs_.find(Command(game_command));
|
||||
if (iter == control_to_inputs_.end()) {
|
||||
config.push_back(std::make_pair(game_command, wxEmptyString));
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::unordered_set<UserInput>& inputs = iter->second;
|
||||
config.push_back(std::make_pair(game_command, UserInput::SpanToConfigString(inputs)));
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
std::unordered_set<UserInput> Bindings::InputsForCommand(const Command& command) const {
|
||||
if (command.is_shortcut() && command.shortcut().id() == NoopCommand()) {
|
||||
std::unordered_set<UserInput> noop_inputs;
|
||||
for (const auto& iter : disabled_defaults_) {
|
||||
noop_inputs.insert(iter.first);
|
||||
}
|
||||
return noop_inputs;
|
||||
}
|
||||
|
||||
auto iter = control_to_inputs_.find(command);
|
||||
if (iter == control_to_inputs_.end()) {
|
||||
return {};
|
||||
}
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
nonstd::optional<Command> Bindings::CommandForInput(const UserInput& input) const {
|
||||
const auto iter = input_to_control_.find(input);
|
||||
if (iter == input_to_control_.end()) {
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
Bindings Bindings::Clone() const {
|
||||
return Bindings(this->control_to_inputs_, this->input_to_control_, this->disabled_defaults_);
|
||||
}
|
||||
|
||||
void Bindings::AssignInputToCommand(const UserInput& input, const Command& command) {
|
||||
if (command.is_shortcut() && command.shortcut().id() == NoopCommand()) {
|
||||
// "Assigning to Noop" means unassinging the default binding.
|
||||
UnassignDefaultBinding(input);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the existing binding if it exists.
|
||||
auto iter = input_to_control_.find(input);
|
||||
if (iter != input_to_control_.end()) {
|
||||
UnassignInput(input);
|
||||
}
|
||||
|
||||
if (command.is_shortcut()) {
|
||||
const ShortcutCommand& shortcut_command = command.shortcut();
|
||||
auto disabled_iter = disabled_defaults_.find(input);
|
||||
if (disabled_iter != disabled_defaults_.end()) {
|
||||
const ShortcutCommand& original_command = disabled_iter->second;
|
||||
if (original_command == shortcut_command) {
|
||||
// Restoring a disabled input. Remove from the disabled set.
|
||||
disabled_defaults_.erase(disabled_iter);
|
||||
}
|
||||
// Then, just continue normally.
|
||||
}
|
||||
}
|
||||
|
||||
control_to_inputs_[command].emplace(input);
|
||||
input_to_control_.emplace(std::make_pair(input, command));
|
||||
}
|
||||
|
||||
void Bindings::AssignInputsToCommand(const std::unordered_set<UserInput>& inputs,
|
||||
const Command& command) {
|
||||
// Remove the existing binding if it exists.
|
||||
const auto iter = control_to_inputs_.find(command);
|
||||
if (iter != control_to_inputs_.end()) {
|
||||
// We need to make a copy here because the iterator is going to be invalidated.
|
||||
const std::unordered_set<UserInput> inputs_to_unassign = iter->second;
|
||||
for (const UserInput& user_input : inputs_to_unassign) {
|
||||
UnassignInput(user_input);
|
||||
}
|
||||
}
|
||||
|
||||
for (const UserInput& user_input : inputs) {
|
||||
AssignInputToCommand(user_input, command);
|
||||
}
|
||||
}
|
||||
|
||||
void Bindings::UnassignInput(const UserInput& input) {
|
||||
VBAM_CHECK(input);
|
||||
|
||||
auto iter = input_to_control_.find(input);
|
||||
if (iter == input_to_control_.end()) {
|
||||
// Input not found, nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
if (iter->second.is_shortcut()) {
|
||||
if (internal::IsDefaultInputForCommand(iter->second, input)) {
|
||||
// Unassigning a default binding has some special handling.
|
||||
UnassignDefaultBinding(input);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, just remove it from the 2 maps.
|
||||
auto command_iter = control_to_inputs_.find(iter->second);
|
||||
VBAM_CHECK(command_iter != control_to_inputs_.end());
|
||||
|
||||
command_iter->second.erase(input);
|
||||
if (command_iter->second.empty()) {
|
||||
// Remove empty set.
|
||||
control_to_inputs_.erase(command_iter);
|
||||
}
|
||||
input_to_control_.erase(iter);
|
||||
}
|
||||
|
||||
void Bindings::ClearCommandAssignments(const Command& command) {
|
||||
const auto iter = control_to_inputs_.find(command);
|
||||
if (iter == control_to_inputs_.end()) {
|
||||
// Command not found, nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep a copy of the inputs to unassign.
|
||||
std::unordered_set<UserInput> inputs_to_unassign(iter->second);
|
||||
|
||||
// Unassign all inputs.
|
||||
for (const UserInput& input : inputs_to_unassign) {
|
||||
UnassignInput(input);
|
||||
}
|
||||
}
|
||||
|
||||
void Bindings::UnassignDefaultBinding(const UserInput& input) {
|
||||
auto input_iter = input_to_control_.find(input);
|
||||
if (input_iter == input_to_control_.end()) {
|
||||
// This can happen if the INI file provided by the user has an invalid
|
||||
// option. In this case, just silently ignore it.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!input_iter->second.is_shortcut()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!internal::IsDefaultInputForCommand(input_iter->second, input)) {
|
||||
// As above, we have already removed the default binding, ignore it.
|
||||
return;
|
||||
}
|
||||
|
||||
auto command_iter = control_to_inputs_.find(input_iter->second);
|
||||
VBAM_CHECK(command_iter != control_to_inputs_.end());
|
||||
|
||||
command_iter->second.erase(input);
|
||||
if (command_iter->second.empty()) {
|
||||
control_to_inputs_.erase(command_iter);
|
||||
}
|
||||
|
||||
disabled_defaults_.emplace(std::make_pair(input, input_iter->second.shortcut()));
|
||||
input_to_control_.erase(input_iter);
|
||||
}
|
||||
|
||||
} // namespace config
|
104
src/wx/config/bindings.h
Normal file
104
src/wx/config/bindings.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#ifndef VBAM_WX_CONFIG_BINDINGS_H_
|
||||
#define VBAM_WX_CONFIG_BINDINGS_H_
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "wx/config/command.h"
|
||||
#include "wx/config/user-input.h"
|
||||
|
||||
// wxWidgets only goes up to `wxID_FILE9` but we want 10 recent files.
|
||||
#define wxID_FILE10 (wxID_FILE9 + 1)
|
||||
|
||||
namespace config {
|
||||
|
||||
// Bindings is a class that manages the association between commands and
|
||||
// user inputs. It is used to manage the shortcuts configuration. The class
|
||||
// provides methods to assign and unassign inputs to commands, as well as
|
||||
// retrieve the current configuration for the INI file.
|
||||
class Bindings {
|
||||
public:
|
||||
// Returns the list of default inputs for `command`.
|
||||
static const std::unordered_set<UserInput>& DefaultInputsForCommand(const Command& command);
|
||||
|
||||
Bindings();
|
||||
~Bindings() = default;
|
||||
|
||||
Bindings(Bindings&&) noexcept = default;
|
||||
Bindings& operator=(Bindings&&) noexcept = default;
|
||||
|
||||
// Disable copy and copy assignment operator.
|
||||
// `Clone()` is provided only for the configuration window, this class
|
||||
// should otherwise be treated as move-only. If you wish to access the
|
||||
// Bindings configuration, do it from `wxGetApp().bindings()`.
|
||||
Bindings(const Bindings&) = delete;
|
||||
Bindings& operator=(const Bindings&) = delete;
|
||||
|
||||
// Returns the shortcuts configuration for the INI file.
|
||||
// Internally, there are global default system inputs that are immediately
|
||||
// available on first run. For the configuration saved in the [Keyboard]
|
||||
// section of the vbam.ini file, we only keep track of the following:
|
||||
// - Disabled default input. These appear under [Keyboard/NOOP].
|
||||
// - User-added custom bindings. These appear under [Keyboard/CommandName].
|
||||
// Essentially, this is a diff between the default shortcuts and the user
|
||||
// configuration.
|
||||
std::vector<std::pair<wxString, wxString>> GetKeyboardConfiguration() const;
|
||||
|
||||
// Returns the game control configuration for the INI file. These go in the
|
||||
// [Joypad] section of the INI file.
|
||||
std::vector<std::pair<GameCommand, wxString>> GetJoypadConfiguration() const;
|
||||
|
||||
// Returns the list of input currently configured for `command`.
|
||||
std::unordered_set<UserInput> InputsForCommand(const Command& command) const;
|
||||
|
||||
// Returns the Command currently assigned to `input` or nullopt if none.
|
||||
nonstd::optional<Command> CommandForInput(const UserInput& input) const;
|
||||
|
||||
// Returns a copy of this object. This can be an expensive operation and
|
||||
// should only be used to modify the currently active shortcuts
|
||||
// configuration.
|
||||
Bindings Clone() const;
|
||||
|
||||
// Assigns `input` to `command`. Silently unassigns `input` if it is already
|
||||
// assigned to another command.
|
||||
void AssignInputToCommand(const UserInput& input, const Command& command);
|
||||
|
||||
// Assigns `inputs` to `command`. Silently unassigns any of `inputs` if they
|
||||
// are already assigned to another command. Any input previously assigned to
|
||||
// `command` will be cleared.
|
||||
void AssignInputsToCommand(const std::unordered_set<UserInput>& inputs, const Command& command);
|
||||
|
||||
// Removes `input` assignment. No-op if `input` is not assigned. `input`
|
||||
// must be a valid UserInput. Call will assert otherwise. Call will assert otherwise.
|
||||
void UnassignInput(const UserInput& input);
|
||||
|
||||
// Removes all assignments for `command`. No-op if `command` has no assignment.
|
||||
void ClearCommandAssignments(const Command& command);
|
||||
|
||||
private:
|
||||
// Faster constructor for explicit copy.
|
||||
Bindings(
|
||||
const std::unordered_map<Command, std::unordered_set<UserInput>>& control_to_inputs,
|
||||
const std::unordered_map<UserInput, Command>& input_to_control,
|
||||
const std::unordered_map<UserInput, ShortcutCommand>& disabled_defaults);
|
||||
|
||||
// Helper method to unassign a binding used by the default configuration.
|
||||
// This requires special handling since the INI configuration is a diff
|
||||
// between the default bindings and the user configuration.
|
||||
void UnassignDefaultBinding(const UserInput& input);
|
||||
|
||||
// Map of command to their associated input set.
|
||||
std::unordered_map<Command, std::unordered_set<UserInput>> control_to_inputs_;
|
||||
// Reverse map of the above. An input can only map to a single command.
|
||||
std::unordered_map<UserInput, Command> input_to_control_;
|
||||
// Disabled default shortcuts. This is used to easily retrieve the
|
||||
// configuration to save in the INI file.
|
||||
std::unordered_map<UserInput, ShortcutCommand> disabled_defaults_;
|
||||
};
|
||||
|
||||
using BindingsProvider = std::function<Bindings*()>;
|
||||
|
||||
} // namespace config
|
||||
|
||||
#endif // VBAM_WX_CONFIG_BINDINGS_H_
|
55
src/wx/config/cmdtab.cpp
Normal file
55
src/wx/config/cmdtab.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "wx/config/cmdtab.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <wx/wxcrt.h>
|
||||
|
||||
#include "core/base/check.h"
|
||||
|
||||
// Initializer for struct cmditem
|
||||
cmditem new_cmditem(const wxString cmd,
|
||||
const wxString name,
|
||||
int cmd_id,
|
||||
int mask_flags,
|
||||
wxMenuItem* mi) {
|
||||
return cmditem {cmd, name, cmd_id, mask_flags, mi};
|
||||
}
|
||||
|
||||
namespace config {
|
||||
wxString GetCommandINIEntry(int command) {
|
||||
for (const auto& cmd_item : cmdtab) {
|
||||
if (cmd_item.cmd_id == command) {
|
||||
return wxString::Format("Keyboard/%s", cmd_item.cmd);
|
||||
}
|
||||
}
|
||||
|
||||
// Command not found. This should never happen.
|
||||
VBAM_NOTREACHED();
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
wxString GetCommandHelper(int command) {
|
||||
for (const auto& cmd_item : cmdtab) {
|
||||
if (cmd_item.cmd_id == command) {
|
||||
return cmd_item.name;
|
||||
}
|
||||
}
|
||||
|
||||
// Command not found. This should never happen.
|
||||
VBAM_NOTREACHED();
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
nonstd::optional<int> CommandFromConfigString(const wxString& config) {
|
||||
const cmditem dummy = new_cmditem(config);
|
||||
const auto iter = std::lower_bound(cmdtab.begin(), cmdtab.end(), dummy, [](const cmditem& cmd1, const cmditem& cmd2) {
|
||||
return wxStrcmp(cmd1.cmd, cmd2.cmd) < 0;
|
||||
});
|
||||
|
||||
if (iter == cmdtab.end() || iter->cmd != config) {
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
return iter->cmd_id;
|
||||
}
|
||||
} // namespace config
|
85
src/wx/config/cmdtab.h
Normal file
85
src/wx/config/cmdtab.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#ifndef VBAM_WX_CONFIG_CMDTAB_H_
|
||||
#define VBAM_WX_CONFIG_CMDTAB_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <optional.hpp>
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
// Forward declaration.
|
||||
class wxMenuItem;
|
||||
|
||||
// List of all commands with their descriptions
|
||||
// sorted by cmd field for binary searching
|
||||
// filled in by copy-events.cmake
|
||||
struct cmditem {
|
||||
const wxString cmd;
|
||||
const wxString name;
|
||||
int cmd_id;
|
||||
int mask_flags; // if non-0, one of the flags must be turned on in win
|
||||
// to enable this command
|
||||
wxMenuItem* mi; // the menu item to invoke command, if present
|
||||
};
|
||||
|
||||
extern std::vector<cmditem> cmdtab;
|
||||
|
||||
// Initializer for struct cmditem
|
||||
cmditem new_cmditem(const wxString cmd = "",
|
||||
const wxString name = "",
|
||||
int cmd_id = 0,
|
||||
int mask_flags = 0,
|
||||
wxMenuItem* mi = nullptr);
|
||||
|
||||
namespace config {
|
||||
// Returns the command INI entry name for the given XRC ID. Will assert if
|
||||
// the command is not found. The INI entry name is the command name prefixed
|
||||
// with "Keyboard/". This is used to store the command in the INI file.
|
||||
// Examples:
|
||||
// * wxID_OPEN -> "Keyboard/OPEN"
|
||||
// * XRCID("NOOP") -> "Keyboard/NOOP"
|
||||
// O(n) search.
|
||||
wxString GetCommandINIEntry(int xrc_id);
|
||||
|
||||
// Returns the command helper string for the given XRC ID. Will assert if
|
||||
// the command is not found.
|
||||
// O(n) search.
|
||||
wxString GetCommandHelper(int xrc_id);
|
||||
|
||||
// Returns the XRC ID for the given command config name, without the
|
||||
// "Keyboard/" prefix. Examples:
|
||||
// * "OPEN" -> wxID_OPEN
|
||||
// * "Keyboard/OPEN" -> nonstd::nullopt
|
||||
// * "NOOP" -> XRCID("NOOP")
|
||||
// O(log(n)) search.
|
||||
nonstd::optional<int> CommandFromConfigString(const wxString& config);
|
||||
}
|
||||
|
||||
// here are those conditions
|
||||
enum { CMDEN_GB = (1 << 0), // GB ROM loaded
|
||||
CMDEN_GBA = (1 << 1), // GBA ROM loaded
|
||||
// the rest imply the above, unless:
|
||||
// _ANY -> does not imply either
|
||||
// _GBA -> only implies GBA
|
||||
CMDEN_REWIND = (1 << 2), // rewind states available
|
||||
CMDEN_SREC = (1 << 3), // sound recording in progress
|
||||
CMDEN_NSREC = (1 << 4), // no sound recording
|
||||
CMDEN_VREC = (1 << 5), // video recording
|
||||
CMDEN_NVREC = (1 << 6), // no video recording
|
||||
CMDEN_GREC = (1 << 7), // game recording
|
||||
CMDEN_NGREC = (1 << 8), // no game recording
|
||||
CMDEN_GPLAY = (1 << 9), // game playback
|
||||
CMDEN_NGPLAY = (1 << 10), // no game playback
|
||||
CMDEN_SAVST = (1 << 11), // any save states
|
||||
CMDEN_GDB = (1 << 12), // gdb connected
|
||||
CMDEN_NGDB_GBA = (1 << 13), // gdb not connected
|
||||
CMDEN_NGDB_ANY = (1 << 14), // gdb not connected
|
||||
CMDEN_NREC_ANY = (1 << 15), // not a/v recording
|
||||
CMDEN_LINK_ANY = (1 << 16), // link enabled
|
||||
|
||||
CMDEN_NEVER = (1 << 31) // never (for NOOP)
|
||||
};
|
||||
#define ONLOAD_CMDEN (CMDEN_NSREC | CMDEN_NVREC | CMDEN_NGREC | CMDEN_NGPLAY)
|
||||
#define UNLOAD_CMDEN_KEEP (CMDEN_NGDB_ANY | CMDEN_NREC_ANY | CMDEN_LINK_ANY)
|
||||
|
||||
#endif // VBAM_WX_CONFIG_CMDTAB_H_
|
51
src/wx/config/command-test.cpp
Normal file
51
src/wx/config/command-test.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "wx/config/command.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <wx/log.h>
|
||||
|
||||
TEST(GameCommandTest, Basic) {
|
||||
config::GameCommand command(config::GameJoy(0), config::GameKey::Up);
|
||||
|
||||
EXPECT_EQ(command.joypad(), config::GameJoy(0));
|
||||
EXPECT_EQ(command.game_key(), config::GameKey::Up);
|
||||
EXPECT_EQ(command.ToConfigString(), "Joypad/1/Up");
|
||||
}
|
||||
|
||||
TEST(ShortcutCommandTest, Basic) {
|
||||
config::ShortcutCommand command(wxID_OPEN);
|
||||
|
||||
EXPECT_EQ(command.id(), wxID_OPEN);
|
||||
EXPECT_EQ(command.ToConfigString(), "Keyboard/OPEN");
|
||||
}
|
||||
|
||||
TEST(CommandTest, FromString) {
|
||||
const auto game_command = config::Command::FromString("Joypad/1/Up");
|
||||
ASSERT_TRUE(game_command.has_value());
|
||||
ASSERT_TRUE(game_command->is_game());
|
||||
|
||||
const config::GameCommand& game = game_command->game();
|
||||
EXPECT_EQ(game.joypad(), config::GameJoy(0));
|
||||
EXPECT_EQ(game.game_key(), config::GameKey::Up);
|
||||
|
||||
const auto shortcut_command = config::Command::FromString("Keyboard/OPEN");
|
||||
ASSERT_TRUE(shortcut_command.has_value());
|
||||
ASSERT_TRUE(shortcut_command->is_shortcut());
|
||||
|
||||
const config::ShortcutCommand& shortcut = shortcut_command->shortcut();
|
||||
EXPECT_EQ(shortcut.id(), wxID_OPEN);
|
||||
}
|
||||
|
||||
TEST(CommandTest, FromStringInvalid) {
|
||||
// Need to disable logging to test for errors.
|
||||
const wxLogNull disable_logging;
|
||||
|
||||
const auto game_command = config::Command::FromString("Joypad/1/Invalid");
|
||||
EXPECT_FALSE(game_command.has_value());
|
||||
|
||||
const auto shortcut_command = config::Command::FromString("Keyboard/INVALID");
|
||||
EXPECT_FALSE(shortcut_command.has_value());
|
||||
|
||||
const auto invalid_command = config::Command::FromString("INVALID");
|
||||
EXPECT_FALSE(invalid_command.has_value());
|
||||
}
|
162
src/wx/config/command.cpp
Normal file
162
src/wx/config/command.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
#include "wx/config/command.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <wx/log.h>
|
||||
#include <wx/translation.h>
|
||||
|
||||
#include "wx/config/cmdtab.h"
|
||||
#include "wx/config/strutils.h"
|
||||
|
||||
namespace config {
|
||||
namespace {
|
||||
|
||||
constexpr int GameKeyToInt(const GameKey& game_key) {
|
||||
return static_cast<int>(game_key);
|
||||
}
|
||||
|
||||
// Returns true if `joypad` is in a valid joypad range.
|
||||
constexpr bool JoypadInRange(const int& joypad) {
|
||||
constexpr size_t kMinJoypadIndex = 0;
|
||||
return static_cast<size_t>(joypad) >= kMinJoypadIndex &&
|
||||
static_cast<size_t>(joypad) < kNbJoypads;
|
||||
}
|
||||
|
||||
wxString GameKeyToUxString(const GameKey& game_key) {
|
||||
// Note: this must match GUI widget names or GUI won't work
|
||||
// This array's order determines tab order as well
|
||||
static const std::array<wxString, kNbGameKeys> kGameKeyStrings = {
|
||||
_("Up"), _("Down"), _("Left"), _("Right"), _("A"),
|
||||
_("B"), _("L"), _("R"), _("Select"), _("Start"),
|
||||
_("Motion Up"), _("Motion Down"), _("Motion Left"), _("Motion Right"), _("Motion In"),
|
||||
_("Motion Out"), _("Auto A"), _("Auto B"), _("Speed"), _("Capture"),
|
||||
_("GameShark"),
|
||||
};
|
||||
return kGameKeyStrings[GameKeyToInt(game_key)];
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// clang-format off
|
||||
wxString GameKeyToString(const GameKey& game_key) {
|
||||
// Note: this must match GUI widget names or GUI won't work
|
||||
// This array's order determines tab order as well
|
||||
static const std::array<wxString, kNbGameKeys> kGameKeyStrings = {
|
||||
"Up",
|
||||
"Down",
|
||||
"Left",
|
||||
"Right",
|
||||
"A",
|
||||
"B",
|
||||
"L",
|
||||
"R",
|
||||
"Select",
|
||||
"Start",
|
||||
"MotionUp",
|
||||
"MotionDown",
|
||||
"MotionLeft",
|
||||
"MotionRight",
|
||||
"MotionIn",
|
||||
"MotionOut",
|
||||
"AutoA",
|
||||
"AutoB",
|
||||
"Speed",
|
||||
"Capture",
|
||||
"GS",
|
||||
};
|
||||
return kGameKeyStrings[GameKeyToInt(game_key)];
|
||||
}
|
||||
|
||||
nonstd::optional<GameKey> StringToGameKey(const wxString& input) {
|
||||
static const std::map<wxString, GameKey> kStringToGameKey = {
|
||||
{ "Up", GameKey::Up },
|
||||
{ "Down", GameKey::Down },
|
||||
{ "Left", GameKey::Left },
|
||||
{ "Right", GameKey::Right },
|
||||
{ "A", GameKey::A },
|
||||
{ "B", GameKey::B },
|
||||
{ "L", GameKey::L },
|
||||
{ "R", GameKey::R },
|
||||
{ "Select", GameKey::Select },
|
||||
{ "Start", GameKey::Start },
|
||||
{ "MotionUp", GameKey::MotionUp },
|
||||
{ "MotionDown", GameKey::MotionDown },
|
||||
{ "MotionLeft", GameKey::MotionLeft },
|
||||
{ "MotionRight", GameKey::MotionRight },
|
||||
{ "MotionIn", GameKey::MotionIn },
|
||||
{ "MotionOut", GameKey::MotionOut },
|
||||
{ "AutoA", GameKey::AutoA },
|
||||
{ "AutoB", GameKey::AutoB },
|
||||
{ "Speed", GameKey::Speed },
|
||||
{ "Capture", GameKey::Capture },
|
||||
{ "GS", GameKey::Gameshark },
|
||||
};
|
||||
|
||||
const auto iter = kStringToGameKey.find(input);
|
||||
if (iter == kStringToGameKey.end()) {
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
return iter->second;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
wxString GameCommand::ToConfigString() const {
|
||||
return wxString::Format("Joypad/%zu/%s", joypad_.ux_index(), GameKeyToString(game_key_));
|
||||
}
|
||||
wxString GameCommand::ToUXString() const {
|
||||
return wxString::Format(_("Joypad %zu %s"), joypad_.ux_index(), GameKeyToUxString(game_key()));
|
||||
}
|
||||
|
||||
wxString ShortcutCommand::ToConfigString() const {
|
||||
return GetCommandINIEntry(id_);
|
||||
}
|
||||
|
||||
// static
|
||||
nonstd::optional<Command> Command::FromString(const wxString& name) {
|
||||
static const wxString kKeyboard("Keyboard");
|
||||
static const wxString kJoypad("Joypad");
|
||||
|
||||
bool is_keyboard = !wxStrncmp(name, kKeyboard, kKeyboard.size());
|
||||
bool is_joypad = !wxStrncmp(name, kJoypad, kJoypad.size());
|
||||
if (!is_keyboard && !is_joypad) {
|
||||
wxLogDebug("Doesn't start with joypad or keyboard");
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
auto parts = config::str_split(name, "/");
|
||||
if (is_joypad) {
|
||||
if (parts.size() != 3) {
|
||||
wxLogDebug("Wrong split size: %d", parts.size());
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
const int joypad = parts[1][0] - '1';
|
||||
if (!JoypadInRange(joypad)) {
|
||||
wxLogDebug("Wrong joypad index: %d", joypad);
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
const nonstd::optional<GameKey> game_key = StringToGameKey(parts[2]);
|
||||
if (!game_key) {
|
||||
wxLogDebug("Failed to parse game_key: %s", parts[2]);
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
return Command(GameCommand(GameJoy(joypad), *game_key));
|
||||
} else {
|
||||
if (parts.size() != 2) {
|
||||
wxLogDebug("Wrong split size: %d", parts.size());
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
const auto xrc_id = CommandFromConfigString(parts[1]);
|
||||
if (!xrc_id.has_value()) {
|
||||
wxLogDebug("Command ID %s not found", parts[1]);
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
return Command(ShortcutCommand(xrc_id.value()));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace config
|
268
src/wx/config/command.h
Normal file
268
src/wx/config/command.h
Normal file
@@ -0,0 +1,268 @@
|
||||
#ifndef VBAM_WX_CONFIG_COMMAND_H_
|
||||
#define VBAM_WX_CONFIG_COMMAND_H_
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
|
||||
#include <optional.hpp>
|
||||
#include <variant.hpp>
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
#include "core/base/check.h"
|
||||
|
||||
namespace config {
|
||||
|
||||
// clang-format off
|
||||
// Represents an in-game input.
|
||||
enum class GameKey {
|
||||
Up = 0,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
A,
|
||||
B,
|
||||
L,
|
||||
R,
|
||||
Select,
|
||||
Start,
|
||||
MotionUp,
|
||||
MotionDown,
|
||||
MotionLeft,
|
||||
MotionRight,
|
||||
MotionIn,
|
||||
MotionOut,
|
||||
AutoA,
|
||||
AutoB,
|
||||
Speed,
|
||||
Capture,
|
||||
Gameshark,
|
||||
Last = Gameshark
|
||||
};
|
||||
static constexpr size_t kNbGameKeys = static_cast<size_t>(GameKey::Last) + 1;
|
||||
|
||||
static constexpr std::array<GameKey, config::kNbGameKeys> kAllGameKeys = {
|
||||
GameKey::Up, GameKey::Down, GameKey::Left,
|
||||
GameKey::Right, GameKey::A, GameKey::B,
|
||||
GameKey::L, GameKey::R, GameKey::Select,
|
||||
GameKey::Start, GameKey::MotionUp, GameKey::MotionDown,
|
||||
GameKey::MotionLeft, GameKey::MotionRight, GameKey::MotionIn,
|
||||
GameKey::MotionOut, GameKey::AutoA, GameKey::AutoB,
|
||||
GameKey::Speed, GameKey::Capture, GameKey::Gameshark,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static constexpr size_t kNbJoypads = 4;
|
||||
|
||||
// Represents an emulated joypad. The internal index is zero-based.
|
||||
class GameJoy {
|
||||
public:
|
||||
constexpr explicit GameJoy(size_t index) : index_(index) { VBAM_CHECK(index < kNbJoypads); }
|
||||
|
||||
// The underlying zero-based index for this emulated joypad.
|
||||
constexpr size_t index() const { return index_; }
|
||||
|
||||
// For display and INI purposes, the index is one-based.
|
||||
constexpr size_t ux_index() const { return index_ + 1; }
|
||||
|
||||
constexpr bool operator==(const GameJoy& other) const { return index_ == other.index_; }
|
||||
constexpr bool operator!=(const GameJoy& other) const { return !(*this == other); }
|
||||
constexpr bool operator<(const GameJoy& other) const { return index_ < other.index_; }
|
||||
constexpr bool operator<=(const GameJoy& other) const {
|
||||
return *this < other || *this == other;
|
||||
}
|
||||
constexpr bool operator>(const GameJoy& other) const { return !(*this <= other); }
|
||||
constexpr bool operator>=(const GameJoy& other) const { return !(*this < other); }
|
||||
|
||||
private:
|
||||
const size_t index_;
|
||||
};
|
||||
|
||||
static constexpr std::array<GameJoy, config::kNbJoypads> kAllGameJoys = {
|
||||
GameJoy(0),
|
||||
GameJoy(1),
|
||||
GameJoy(2),
|
||||
GameJoy(3),
|
||||
};
|
||||
|
||||
// A Game Command is represented by a `joypad` number (1 to 4) and a
|
||||
// `game_key`. `joypad` MUST be in the 1-4 range. Debug checks will assert
|
||||
// otherwise.
|
||||
class GameCommand final {
|
||||
public:
|
||||
constexpr GameCommand(GameJoy joypad, GameKey game_key)
|
||||
: joypad_(joypad), game_key_(game_key) {}
|
||||
|
||||
constexpr GameJoy joypad() const { return joypad_; }
|
||||
constexpr GameKey game_key() const { return game_key_; }
|
||||
wxString ToConfigString() const;
|
||||
wxString ToUXString() const;
|
||||
|
||||
constexpr bool operator==(const GameCommand& other) const {
|
||||
return joypad_ == other.joypad_ && game_key_ == other.game_key_;
|
||||
}
|
||||
constexpr bool operator!=(const GameCommand& other) const { return !(*this == other); }
|
||||
constexpr bool operator<(const GameCommand& other) const {
|
||||
if (joypad_ == other.joypad_) {
|
||||
return game_key_ < other.game_key_;
|
||||
} else {
|
||||
return joypad_ < other.joypad_;
|
||||
}
|
||||
}
|
||||
constexpr bool operator<=(const GameCommand& other) const {
|
||||
return *this < other || *this == other;
|
||||
}
|
||||
constexpr bool operator>(const GameCommand& other) const { return !(*this <= other); }
|
||||
constexpr bool operator>=(const GameCommand& other) const { return !(*this < other); }
|
||||
|
||||
private:
|
||||
const GameJoy joypad_;
|
||||
const GameKey game_key_;
|
||||
};
|
||||
|
||||
// A Shortcut Command is represented by the wx command ID.
|
||||
class ShortcutCommand final {
|
||||
public:
|
||||
constexpr explicit ShortcutCommand(int id) : id_(id) {}
|
||||
|
||||
constexpr int id() const { return id_; }
|
||||
wxString ToConfigString() const;
|
||||
|
||||
constexpr bool operator==(const ShortcutCommand& other) const { return id_ == other.id_; }
|
||||
constexpr bool operator!=(const ShortcutCommand& other) const { return !(*this == other); }
|
||||
constexpr bool operator<(const ShortcutCommand& other) const { return id_ < other.id_; }
|
||||
constexpr bool operator<=(const ShortcutCommand& other) const {
|
||||
return *this < other || *this == other;
|
||||
}
|
||||
constexpr bool operator>(const ShortcutCommand& other) const { return !(*this <= other); }
|
||||
constexpr bool operator>=(const ShortcutCommand& other) const { return !(*this < other); }
|
||||
|
||||
private:
|
||||
const int id_;
|
||||
};
|
||||
|
||||
// Conversion utility method. Returns empty string on failure.
|
||||
// This is O(1).
|
||||
wxString GameKeyToString(const GameKey& game_key);
|
||||
|
||||
// Conversion utility method. Returns std::nullopt on failure.
|
||||
// This is O(log(kNbGameKeys)).
|
||||
nonstd::optional<GameKey> StringToGameKey(const wxString& input);
|
||||
|
||||
// Represents a Command for the emulator software. This can be either a Game
|
||||
// Command (a button is pressed on the emulated device) or a Shortcut Command
|
||||
// (a user-specified shortcut is activated).
|
||||
// This is mainly used by the Bindings class to handle assignment in a common
|
||||
// manner and prevent the user from assigning the same input to multiple
|
||||
// Commands, Game or Shortcut.
|
||||
class Command final {
|
||||
public:
|
||||
enum class Tag {
|
||||
kGame = 0,
|
||||
kShortcut,
|
||||
};
|
||||
|
||||
// Converts a string to a Command. Returns std::nullopt on failure.
|
||||
static nonstd::optional<Command> FromString(const wxString& name);
|
||||
|
||||
// Game Command constructor.
|
||||
Command(GameCommand game_control) : tag_(Tag::kGame), control_(game_control) {}
|
||||
|
||||
// Shortcut Command constructors.
|
||||
Command(ShortcutCommand shortcut_control) : tag_(Tag::kShortcut), control_(shortcut_control) {}
|
||||
|
||||
~Command() = default;
|
||||
|
||||
// Returns the type of the value stored by the current object.
|
||||
Tag tag() const { return tag_; }
|
||||
|
||||
bool is_game() const { return tag() == Tag::kGame; }
|
||||
bool is_shortcut() const { return tag() == Tag::kShortcut; }
|
||||
|
||||
const GameCommand& game() const {
|
||||
VBAM_CHECK(is_game());
|
||||
return nonstd::get<GameCommand>(control_);
|
||||
}
|
||||
|
||||
const ShortcutCommand& shortcut() const {
|
||||
VBAM_CHECK(is_shortcut());
|
||||
return nonstd::get<ShortcutCommand>(control_);
|
||||
}
|
||||
|
||||
bool operator==(const Command& other) const {
|
||||
return tag_ == other.tag_ && control_ == other.control_;
|
||||
}
|
||||
bool operator!=(const Command& other) const { return !(*this == other); }
|
||||
bool operator<(const Command& other) const {
|
||||
if (tag_ == other.tag_) {
|
||||
switch (tag_) {
|
||||
case Tag::kGame:
|
||||
return game() < other.game();
|
||||
case Tag::kShortcut:
|
||||
return shortcut() < other.shortcut();
|
||||
}
|
||||
|
||||
VBAM_NOTREACHED();
|
||||
return false;
|
||||
} else {
|
||||
return tag_ < other.tag_;
|
||||
}
|
||||
}
|
||||
bool operator<=(const Command& other) const { return *this < other || *this == other; }
|
||||
bool operator>(const Command& other) const { return !(*this <= other); }
|
||||
bool operator>=(const Command& other) const { return !(*this < other); }
|
||||
|
||||
private:
|
||||
const Tag tag_;
|
||||
const nonstd::variant<GameCommand, ShortcutCommand> control_;
|
||||
};
|
||||
|
||||
} // namespace config
|
||||
|
||||
// Specializations for hash functions for all of the above classes.
|
||||
template <>
|
||||
struct std::hash<config::GameKey> {
|
||||
std::size_t operator()(const config::GameKey& game_key) const noexcept {
|
||||
return std::hash<size_t>{}(static_cast<size_t>(game_key));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::hash<config::GameJoy> {
|
||||
std::size_t operator()(const config::GameJoy& game_joy) const noexcept {
|
||||
return std::hash<size_t>{}(static_cast<size_t>(game_joy.index()));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::hash<config::GameCommand> {
|
||||
std::size_t operator()(const config::GameCommand& game_control) const noexcept {
|
||||
const std::size_t hash1 = std::hash<config::GameJoy>{}(game_control.joypad());
|
||||
const std::size_t hash2 = std::hash<config::GameKey>{}(game_control.game_key());
|
||||
return hash1 ^ hash2;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::hash<config::ShortcutCommand> {
|
||||
std::size_t operator()(const config::ShortcutCommand& shortcut) const noexcept {
|
||||
return std::hash<int>{}(shortcut.id());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::hash<config::Command> {
|
||||
std::size_t operator()(const config::Command& control) const noexcept {
|
||||
switch (control.tag()) {
|
||||
case config::Command::Tag::kGame:
|
||||
return std::hash<config::GameCommand>{}(control.game());
|
||||
case config::Command::Tag::kShortcut:
|
||||
return std::hash<config::ShortcutCommand>{}(control.shortcut());
|
||||
}
|
||||
|
||||
VBAM_NOTREACHED();
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // VBAM_WX_CONFIG_COMMAND_H_
|
@@ -4,16 +4,20 @@ IF(NOT OUTDIR)
|
||||
SET(OUTDIR ".")
|
||||
ENDIF(NOT OUTDIR)
|
||||
|
||||
SET(CMDTAB "${OUTDIR}/cmdtab.cpp")
|
||||
SET(CMDTAB "${OUTDIR}/config/internal/cmdtab.cpp")
|
||||
SET(EVPROTO "${OUTDIR}/cmdhandlers.h")
|
||||
SET(EVTABLE "${OUTDIR}/cmd-evtable.h")
|
||||
|
||||
FILE(READ cmdevents.cpp MW)
|
||||
FILE(READ ../cmdevents.cpp MW)
|
||||
STRING(REGEX MATCHALL "\nEVT_HANDLER([^\")]|\"[^\"]*\")*\\)" MW "${MW}")
|
||||
|
||||
# cmdtab.cpp is a table of cmd-id-name/cmd-name pairs
|
||||
# sorted for binary searching
|
||||
FILE(WRITE "${CMDTAB}" "// Generated from cmdevents.cpp; do not edit\n#include <wx/xrc/xmlres.h>\n\n#include \"wx/wxvbam.h\"\n#include \"wx/wxhead.h\"\n\nstruct cmditem cmdtab[] = {\n")
|
||||
FILE(WRITE "${CMDTAB}" "// Generated from cmdevents.cpp; do not edit\n\n")
|
||||
FILE(APPEND "${CMDTAB}" "#include \"wx/config/cmdtab.h\"\n\n")
|
||||
FILE(APPEND "${CMDTAB}" "#include <wx/xrc/xmlres.h>\n\n")
|
||||
FILE(APPEND "${CMDTAB}" "#include \"wx/config/bindings.h\"\n\n")
|
||||
FILE(APPEND "${CMDTAB}" "std::vector<cmditem> cmdtab = {\n")
|
||||
SET(EVLINES )
|
||||
FOREACH(EV ${MW})
|
||||
# stripping the wxID_ makes it look better, but it's still all-caps
|
||||
@@ -26,7 +30,8 @@ ENDFOREACH(EV)
|
||||
LIST(SORT EVLINES)
|
||||
STRING(REGEX REPLACE ",\n\$" "\n" EVLINES "${EVLINES}")
|
||||
FILE(APPEND "${CMDTAB}" ${EVLINES})
|
||||
FILE(APPEND "${CMDTAB}" "};\nconst int ncmds = sizeof(cmdtab) / sizeof(cmdtab[0]);\n")
|
||||
FILE(APPEND "${CMDTAB}" "};\n")
|
||||
FILE(APPEND "${CMDTAB}" "const int ncmds = sizeof(cmdtab) / sizeof(cmdtab[0]);\n")
|
||||
|
||||
# cmdhandlers.h contains prototypes for all handlers
|
||||
FILE(WRITE "${EVPROTO}" "// Generated from cmdevents.cpp; do not edit\n")
|
148
src/wx/config/emulated-gamepad-test.cpp
Normal file
148
src/wx/config/emulated-gamepad-test.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
#include "wx/config/emulated-gamepad.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <wx/string.h>
|
||||
#include <memory>
|
||||
#include "wx/config/bindings.h"
|
||||
#include "wx/config/command.h"
|
||||
#include "wx/config/user-input.h"
|
||||
|
||||
#define KEYM_UP 1 << 6
|
||||
|
||||
// Test fixture to set up the EmulatedGamepad with default bindings.
|
||||
class GamepadTest : public ::testing::Test {
|
||||
public:
|
||||
GamepadTest() = default;
|
||||
~GamepadTest() override = default;
|
||||
|
||||
protected:
|
||||
void SetUp() override {
|
||||
gamepad_ =
|
||||
std::make_unique<config::EmulatedGamepad>(std::bind(&GamepadTest::bindings, this));
|
||||
}
|
||||
|
||||
void TearDown() override { gamepad_.reset(); }
|
||||
|
||||
config::Bindings* bindings() { return &bindings_; }
|
||||
config::EmulatedGamepad* gamepad() { return gamepad_.get(); }
|
||||
|
||||
private:
|
||||
config::Bindings bindings_;
|
||||
std::unique_ptr<config::EmulatedGamepad> gamepad_;
|
||||
};
|
||||
|
||||
TEST_F(GamepadTest, Basic) {
|
||||
// No input should be pressed initially.
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), 0);
|
||||
|
||||
const config::JoyInput up(config::JoyId(0), config::JoyControl::HatNorth, 0);
|
||||
|
||||
// Press up, the up key should be pressed.
|
||||
EXPECT_TRUE(gamepad()->OnInputPressed(up));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), KEYM_UP);
|
||||
|
||||
// Release up, the up key should be released.
|
||||
EXPECT_TRUE(gamepad()->OnInputReleased(up));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), 0);
|
||||
}
|
||||
|
||||
// Tests that pressing multiple bindings at the same time keeps the keys pressed
|
||||
// until all corresponding bindings are released.
|
||||
TEST_F(GamepadTest, ManyBindingsPressed) {
|
||||
const config::JoyInput up(config::JoyId(0), config::JoyControl::HatNorth, 0);
|
||||
const config::KeyboardInput w('W');
|
||||
|
||||
// Press up, the up key should be pressed.
|
||||
EXPECT_TRUE(gamepad()->OnInputPressed(up));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), KEYM_UP);
|
||||
|
||||
// Press W, the up key should still be pressed.
|
||||
EXPECT_TRUE(gamepad()->OnInputPressed(w));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), KEYM_UP);
|
||||
|
||||
// Release up, the up key should still be pressed.
|
||||
EXPECT_TRUE(gamepad()->OnInputReleased(up));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), KEYM_UP);
|
||||
|
||||
// Release w, the up key should be released.
|
||||
EXPECT_TRUE(gamepad()->OnInputReleased(w));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), 0);
|
||||
}
|
||||
|
||||
// Tests that pressing the same binding twice is a noop.
|
||||
TEST_F(GamepadTest, DoublePress) {
|
||||
const config::JoyInput up(config::JoyId(0), config::JoyControl::HatNorth, 0);
|
||||
|
||||
// Press up, the up key should be pressed.
|
||||
EXPECT_TRUE(gamepad()->OnInputPressed(up));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), KEYM_UP);
|
||||
|
||||
// Press up again, the up key should still be pressed.
|
||||
EXPECT_TRUE(gamepad()->OnInputPressed(up));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), KEYM_UP);
|
||||
|
||||
// Release up, the up key should be released.
|
||||
EXPECT_TRUE(gamepad()->OnInputReleased(up));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), 0);
|
||||
}
|
||||
|
||||
// Tests that releasing the same biniding twice is a noop.
|
||||
TEST_F(GamepadTest, DoubleRelease) {
|
||||
const config::JoyInput up(config::JoyId(0), config::JoyControl::HatNorth, 0);
|
||||
|
||||
// Press up, the up key should be pressed.
|
||||
EXPECT_TRUE(gamepad()->OnInputPressed(up));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), KEYM_UP);
|
||||
|
||||
// Release up, the up key should be released.
|
||||
EXPECT_TRUE(gamepad()->OnInputReleased(up));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), 0);
|
||||
|
||||
// Release up again, the up key should still be released.
|
||||
EXPECT_TRUE(gamepad()->OnInputReleased(up));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), 0);
|
||||
}
|
||||
|
||||
// Tests that pressing an unbound input is a noop.
|
||||
TEST_F(GamepadTest, UnassignedInput) {
|
||||
const config::KeyboardInput f1(WXK_F1);
|
||||
const config::JoyInput up(config::JoyId(0), config::JoyControl::HatNorth, 0);
|
||||
|
||||
// Press F1, nothing should happen.
|
||||
EXPECT_FALSE(gamepad()->OnInputPressed(f1));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), 0);
|
||||
|
||||
// Press up, the up key should be pressed.
|
||||
EXPECT_TRUE(gamepad()->OnInputPressed(up));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), KEYM_UP);
|
||||
|
||||
// Release F1, nothing should happen.
|
||||
EXPECT_FALSE(gamepad()->OnInputReleased(f1));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), KEYM_UP);
|
||||
|
||||
// Release up, the up key should be released.
|
||||
EXPECT_TRUE(gamepad()->OnInputReleased(up));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), 0);
|
||||
|
||||
// Release F1 again, nothing should happen.
|
||||
EXPECT_FALSE(gamepad()->OnInputReleased(f1));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), 0);
|
||||
}
|
||||
|
||||
// Tests that assigning an input to a game command properly triggers the game
|
||||
// command.
|
||||
TEST_F(GamepadTest, NonDefaultInput) {
|
||||
const config::KeyboardInput f1(WXK_F1);
|
||||
|
||||
// Assign F1 to "Up".
|
||||
bindings()->AssignInputToCommand(f1,
|
||||
config::GameCommand(config::GameJoy(0), config::GameKey::Up));
|
||||
|
||||
// Press F1, the up key should be pressed.
|
||||
EXPECT_TRUE(gamepad()->OnInputPressed(f1));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), KEYM_UP);
|
||||
|
||||
// Release F1, the up key should be released.
|
||||
EXPECT_TRUE(gamepad()->OnInputReleased(f1));
|
||||
EXPECT_EQ(gamepad()->GetJoypad(0), 0);
|
||||
}
|
127
src/wx/config/emulated-gamepad.cpp
Normal file
127
src/wx/config/emulated-gamepad.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#include "wx/config/emulated-gamepad.h"
|
||||
|
||||
#include "core/base/check.h"
|
||||
|
||||
namespace config {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t kBitKeyA = (1 << 0);
|
||||
constexpr uint32_t kBitKeyB = (1 << 1);
|
||||
constexpr uint32_t kBitKeySelect = (1 << 2);
|
||||
constexpr uint32_t kBitKeyStart = (1 << 3);
|
||||
constexpr uint32_t kBitKeyRight = (1 << 4);
|
||||
constexpr uint32_t kBitKeyLeft = (1 << 5);
|
||||
constexpr uint32_t kBitKeyUp = (1 << 6);
|
||||
constexpr uint32_t kBitKeyDown = (1 << 7);
|
||||
constexpr uint32_t kBitKeyR = (1 << 8);
|
||||
constexpr uint32_t kBitKeyL = (1 << 9);
|
||||
constexpr uint32_t kBitKeySpeed = (1 << 10);
|
||||
constexpr uint32_t kBitKeyCapture = (1 << 11);
|
||||
constexpr uint32_t kBitKeyGameShark = (1 << 12);
|
||||
constexpr uint32_t kBitKeyAutoA = (1 << 13);
|
||||
constexpr uint32_t kBitKeyAutoB = (1 << 14);
|
||||
constexpr uint32_t kBitKeyMotionUp = (1 << 15);
|
||||
constexpr uint32_t kBitKeyMotionDown = (1 << 16);
|
||||
constexpr uint32_t kBitKeyMotionLeft = (1 << 17);
|
||||
constexpr uint32_t kBitKeyMotionRight = (1 << 18);
|
||||
constexpr uint32_t kBitKeyMotionIn = (1 << 19);
|
||||
constexpr uint32_t kBitKeyMotionOut = (1 << 20);
|
||||
|
||||
// clang-format off
|
||||
constexpr std::array<uint32_t, kNbGameKeys> kBitMask = {
|
||||
kBitKeyUp,
|
||||
kBitKeyDown,
|
||||
kBitKeyLeft,
|
||||
kBitKeyRight,
|
||||
kBitKeyA,
|
||||
kBitKeyB,
|
||||
kBitKeyL,
|
||||
kBitKeyR,
|
||||
kBitKeySelect,
|
||||
kBitKeyStart,
|
||||
kBitKeyMotionUp,
|
||||
kBitKeyMotionDown,
|
||||
kBitKeyMotionLeft,
|
||||
kBitKeyMotionRight,
|
||||
kBitKeyMotionIn,
|
||||
kBitKeyMotionOut,
|
||||
kBitKeyAutoA,
|
||||
kBitKeyAutoB,
|
||||
kBitKeySpeed,
|
||||
kBitKeyCapture,
|
||||
kBitKeyGameShark,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
inline size_t GameKeyToInt(const GameKey& game_key) {
|
||||
return static_cast<size_t>(game_key);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EmulatedGamepad::EmulatedGamepad(const BindingsProvider bindings_provider)
|
||||
: joypads_({0, 0, 0, 0}), bindings_provider_(bindings_provider) {}
|
||||
|
||||
bool EmulatedGamepad::OnInputPressed(const config::UserInput& user_input) {
|
||||
VBAM_CHECK(user_input);
|
||||
|
||||
const auto command = bindings_provider_()->CommandForInput(user_input);
|
||||
if (!command || !command->is_game()) {
|
||||
// No associated game control for `user_input`.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the corresponding control.
|
||||
auto iter = active_controls_.find(command->game());
|
||||
if (iter == active_controls_.end()) {
|
||||
iter = active_controls_
|
||||
.insert(std::make_pair(command->game(), std::unordered_set<UserInput>()))
|
||||
.first;
|
||||
}
|
||||
|
||||
iter->second.emplace(user_input);
|
||||
joypads_[command->game().joypad().index()] |=
|
||||
kBitMask[GameKeyToInt(command->game().game_key())];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmulatedGamepad::OnInputReleased(const config::UserInput& user_input) {
|
||||
VBAM_CHECK(user_input);
|
||||
|
||||
const auto command = bindings_provider_()->CommandForInput(user_input);
|
||||
if (!command || !command->is_game()) {
|
||||
// No associated game control for `user_input`.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the corresponding control.
|
||||
auto iter = active_controls_.find(command->game());
|
||||
if (iter == active_controls_.end()) {
|
||||
// Double release is noop.
|
||||
return true;
|
||||
}
|
||||
|
||||
iter->second.erase(user_input);
|
||||
if (iter->second.empty()) {
|
||||
// Actually release control.
|
||||
active_controls_.erase(iter);
|
||||
joypads_[command->game().joypad().index()] &=
|
||||
~kBitMask[GameKeyToInt(command->game().game_key())];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void EmulatedGamepad::Reset() {
|
||||
active_controls_.clear();
|
||||
joypads_.fill(0);
|
||||
}
|
||||
|
||||
uint32_t EmulatedGamepad::GetJoypad(size_t joypad) const {
|
||||
if (joypad >= kNbJoypads) {
|
||||
return 0;
|
||||
}
|
||||
return joypads_[joypad];
|
||||
}
|
||||
|
||||
} // namespace config
|
49
src/wx/config/emulated-gamepad.h
Normal file
49
src/wx/config/emulated-gamepad.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef VBAM_WX_CONFIG_EMULATED_GAMEPAD_H_
|
||||
#define VBAM_WX_CONFIG_EMULATED_GAMEPAD_H_
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
#include "wx/config/bindings.h"
|
||||
#include "wx/config/command.h"
|
||||
#include "wx/config/user-input.h"
|
||||
|
||||
namespace config {
|
||||
|
||||
// Tracks in-game input and computes the joypad value used to send control input
|
||||
// data to the emulator. This class should be kept as a singleton owned by the
|
||||
// application.
|
||||
class EmulatedGamepad final {
|
||||
public:
|
||||
explicit EmulatedGamepad(const BindingsProvider bindings_provider);
|
||||
~EmulatedGamepad() = default;
|
||||
|
||||
// Disable copy constructor and assignment operator.
|
||||
EmulatedGamepad(const EmulatedGamepad&) = delete;
|
||||
EmulatedGamepad& operator=(const EmulatedGamepad&) = delete;
|
||||
|
||||
// Processes `user_input` and updates the internal tracking state.
|
||||
// Returns true if `user_input` corresponds to a game input.
|
||||
bool OnInputPressed(const UserInput& user_input);
|
||||
bool OnInputReleased(const UserInput& user_input);
|
||||
|
||||
// Clears all input.
|
||||
void Reset();
|
||||
|
||||
uint32_t GetJoypad(size_t joypad) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<GameCommand, std::unordered_set<UserInput>> active_controls_;
|
||||
std::array<uint32_t, kNbJoypads> joypads_;
|
||||
const BindingsProvider bindings_provider_;
|
||||
};
|
||||
|
||||
using EmulatedGamepadProvider = std::function<EmulatedGamepad*()>;
|
||||
|
||||
} // namespace config
|
||||
|
||||
#endif // VBAM_WX_CONFIG_EMULATED_GAMEPAD_H_
|
@@ -1,297 +0,0 @@
|
||||
#include "wx/config/game-control.h"
|
||||
|
||||
#include "wx/opts.h"
|
||||
#include "wx/strutils.h"
|
||||
#include "wx/wxlogdebug.h"
|
||||
|
||||
namespace config {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t kBitKeyA = (1 << 0);
|
||||
constexpr uint32_t kBitKeyB = (1 << 1);
|
||||
constexpr uint32_t kBitKeySelect = (1 << 2);
|
||||
constexpr uint32_t kBitKeyStart = (1 << 3);
|
||||
constexpr uint32_t kBitKeyRight = (1 << 4);
|
||||
constexpr uint32_t kBitKeyLeft = (1 << 5);
|
||||
constexpr uint32_t kBitKeyUp = (1 << 6);
|
||||
constexpr uint32_t kBitKeyDown = (1 << 7);
|
||||
constexpr uint32_t kBitKeyR = (1 << 8);
|
||||
constexpr uint32_t kBitKeyL = (1 << 9);
|
||||
constexpr uint32_t kBitKeySpeed = (1 << 10);
|
||||
constexpr uint32_t kBitKeyCapture = (1 << 11);
|
||||
constexpr uint32_t kBitKeyGameShark = (1 << 12);
|
||||
constexpr uint32_t kBitKeyAutoA = (1 << 13);
|
||||
constexpr uint32_t kBitKeyAutoB = (1 << 14);
|
||||
constexpr uint32_t kBitKeyMotionUp = (1 << 15);
|
||||
constexpr uint32_t kBitKeyMotionDown = (1 << 16);
|
||||
constexpr uint32_t kBitKeyMotionLeft = (1 << 17);
|
||||
constexpr uint32_t kBitKeyMotionRight = (1 << 18);
|
||||
constexpr uint32_t kBitKeyMotionIn = (1 << 19);
|
||||
constexpr uint32_t kBitKeyMotionOut = (1 << 20);
|
||||
|
||||
// clang-format off
|
||||
constexpr std::array<uint32_t, kNbGameKeys> kBitMask = {
|
||||
kBitKeyUp,
|
||||
kBitKeyDown,
|
||||
kBitKeyLeft,
|
||||
kBitKeyRight,
|
||||
kBitKeyA,
|
||||
kBitKeyB,
|
||||
kBitKeyL,
|
||||
kBitKeyR,
|
||||
kBitKeySelect,
|
||||
kBitKeyStart,
|
||||
kBitKeyMotionUp,
|
||||
kBitKeyMotionDown,
|
||||
kBitKeyMotionLeft,
|
||||
kBitKeyMotionRight,
|
||||
kBitKeyMotionIn,
|
||||
kBitKeyMotionOut,
|
||||
kBitKeyAutoA,
|
||||
kBitKeyAutoB,
|
||||
kBitKeySpeed,
|
||||
kBitKeyCapture,
|
||||
kBitKeyGameShark,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
inline int GameKeyToInt(const GameKey& game_key) {
|
||||
return static_cast<int>(game_key);
|
||||
}
|
||||
|
||||
// Returns true if `joypad` is in a valid joypad range.
|
||||
inline bool JoypadInRange(const int& joypad) {
|
||||
constexpr int kMinJoypadIndex = 0;
|
||||
return joypad >= kMinJoypadIndex && joypad < kNbJoypads;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// clang-format off
|
||||
wxString GameKeyToString(const GameKey& game_key) {
|
||||
// Note: this must match GUI widget names or GUI won't work
|
||||
// This array's order determines tab order as well
|
||||
static const std::array<wxString, kNbGameKeys> kGameKeyStrings = {
|
||||
"Up",
|
||||
"Down",
|
||||
"Left",
|
||||
"Right",
|
||||
"A",
|
||||
"B",
|
||||
"L",
|
||||
"R",
|
||||
"Select",
|
||||
"Start",
|
||||
"MotionUp",
|
||||
"MotionDown",
|
||||
"MotionLeft",
|
||||
"MotionRight",
|
||||
"MotionIn",
|
||||
"MotionOut",
|
||||
"AutoA",
|
||||
"AutoB",
|
||||
"Speed",
|
||||
"Capture",
|
||||
"GS",
|
||||
};
|
||||
return kGameKeyStrings[GameKeyToInt(game_key)];
|
||||
}
|
||||
|
||||
nonstd::optional<GameKey> StringToGameKey(const wxString& input) {
|
||||
static const std::map<wxString, GameKey> kStringToGameKey = {
|
||||
{ "Up", GameKey::Up },
|
||||
{ "Down", GameKey::Down },
|
||||
{ "Left", GameKey::Left },
|
||||
{ "Right", GameKey::Right },
|
||||
{ "A", GameKey::A },
|
||||
{ "B", GameKey::B },
|
||||
{ "L", GameKey::L },
|
||||
{ "R", GameKey::R },
|
||||
{ "Select", GameKey::Select },
|
||||
{ "Start", GameKey::Start },
|
||||
{ "MotionUp", GameKey::MotionUp },
|
||||
{ "MotionDown", GameKey::MotionDown },
|
||||
{ "MotionLeft", GameKey::MotionLeft },
|
||||
{ "MotionRight", GameKey::MotionRight },
|
||||
{ "MotionIn", GameKey::MotionIn },
|
||||
{ "MotionOut", GameKey::MotionOut },
|
||||
{ "AutoA", GameKey::AutoA },
|
||||
{ "AutoB", GameKey::AutoB },
|
||||
{ "Speed", GameKey::Speed },
|
||||
{ "Capture", GameKey::Capture },
|
||||
{ "GS", GameKey::Gameshark },
|
||||
};
|
||||
|
||||
const auto iter = kStringToGameKey.find(input);
|
||||
if (iter == kStringToGameKey.end()) {
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
return iter->second;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
// static
|
||||
nonstd::optional<GameControl> GameControl::FromString(const wxString& name) {
|
||||
static const wxString kJoypad("Joypad");
|
||||
if (!wxStrncmp(name, kJoypad, kJoypad.size())) {
|
||||
wxLogDebug("Doesn't start with joypad");
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
auto parts = strutils::split(name, "/");
|
||||
if (parts.size() != 3) {
|
||||
wxLogDebug("Wrong split size: %d", parts.size());
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
const int joypad = parts[1][0] - wxT('1');
|
||||
if (!JoypadInRange(joypad)) {
|
||||
wxLogDebug("Wrong joypad index: %d", joypad);
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
nonstd::optional<GameKey> game_key = StringToGameKey(parts[2]);
|
||||
if (!game_key) {
|
||||
wxLogDebug("Failed to parse game_key: %s", parts[2]);
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
return GameControl(joypad, game_key.value());
|
||||
}
|
||||
|
||||
GameControl::GameControl(int joypad, GameKey game_key)
|
||||
: joypad_(joypad),
|
||||
game_key_(game_key),
|
||||
config_string_(wxString::Format("Joypad/%d/%s",
|
||||
joypad_ + 1,
|
||||
GameKeyToString(game_key_))) {
|
||||
assert(JoypadInRange(joypad_));
|
||||
}
|
||||
GameControl::~GameControl() = default;
|
||||
|
||||
bool GameControl::operator==(const GameControl& other) const {
|
||||
return joypad_ == other.joypad_ && game_key_ == other.game_key_;
|
||||
}
|
||||
bool GameControl::operator!=(const GameControl& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
bool GameControl::operator<(const GameControl& other) const {
|
||||
if (joypad_ != other.joypad_) {
|
||||
return joypad_ < other.joypad_;
|
||||
}
|
||||
if (game_key_ != other.game_key_) {
|
||||
return game_key_ < other.game_key_;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool GameControl::operator<=(const GameControl& other) const {
|
||||
return !(*this > other);
|
||||
}
|
||||
bool GameControl::operator>(const GameControl& other) const {
|
||||
return other < *this;
|
||||
}
|
||||
bool GameControl::operator>=(const GameControl& other) const {
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
GameControlState& GameControlState::Instance() {
|
||||
static GameControlState g_game_control_state;
|
||||
return g_game_control_state;
|
||||
}
|
||||
|
||||
GameControlState::GameControlState() : joypads_({0, 0, 0, 0}) {}
|
||||
GameControlState::~GameControlState() = default;
|
||||
|
||||
bool GameControlState::OnInputPressed(const config::UserInput& user_input) {
|
||||
assert(user_input);
|
||||
|
||||
const auto& game_keys = input_bindings_.find(user_input);
|
||||
if (game_keys == input_bindings_.end()) {
|
||||
// No associated game control for `user_input`.
|
||||
return false;
|
||||
}
|
||||
|
||||
auto iter = keys_pressed_.find(user_input);
|
||||
if (iter != keys_pressed_.end()) {
|
||||
// Double press is noop.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remember the key pressed.
|
||||
keys_pressed_.emplace(user_input);
|
||||
|
||||
// Update all corresponding controls.
|
||||
for (const GameControl& game_control : game_keys->second) {
|
||||
active_controls_[game_control].emplace(user_input);
|
||||
joypads_[game_control.joypad_] |=
|
||||
kBitMask[GameKeyToInt(game_control.game_key_)];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameControlState::OnInputReleased(const config::UserInput& user_input) {
|
||||
assert(user_input);
|
||||
|
||||
const auto& game_keys = input_bindings_.find(user_input);
|
||||
if (game_keys == input_bindings_.end()) {
|
||||
// No associated game control for `user_input`.
|
||||
return false;
|
||||
}
|
||||
|
||||
auto iter = keys_pressed_.find(user_input);
|
||||
if (iter == keys_pressed_.end()) {
|
||||
// Double release is noop.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Release the key pressed.
|
||||
keys_pressed_.erase(iter);
|
||||
|
||||
// Update all corresponding controls.
|
||||
for (const GameControl& game_control : game_keys->second) {
|
||||
auto active_controls = active_controls_.find(game_control);
|
||||
if (active_controls == active_controls_.end()) {
|
||||
// This should never happen.
|
||||
assert(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
active_controls->second.erase(user_input);
|
||||
if (active_controls->second.empty()) {
|
||||
// Actually release control.
|
||||
active_controls_.erase(active_controls);
|
||||
joypads_[game_control.joypad_] &=
|
||||
~kBitMask[GameKeyToInt(game_control.game_key_)];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameControlState::Reset() {
|
||||
active_controls_.clear();
|
||||
keys_pressed_.clear();
|
||||
joypads_.fill(0);
|
||||
}
|
||||
|
||||
void GameControlState::OnGameBindingsChanged() {
|
||||
// We should reset to ensure no key remains accidentally pressed following a
|
||||
// configuration change.
|
||||
Reset();
|
||||
|
||||
input_bindings_.clear();
|
||||
for (const auto& iter : gopts.game_control_bindings) {
|
||||
for (const auto& user_input : iter.second) {
|
||||
input_bindings_[user_input].emplace(iter.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GameControlState::GetJoypad(int joypad) const {
|
||||
assert(JoypadInRange(joypad));
|
||||
return joypads_[joypad];
|
||||
}
|
||||
|
||||
} // namespace config
|
@@ -1,146 +0,0 @@
|
||||
#ifndef VBAM_WX_CONFIG_GAME_CONTROL_H_
|
||||
#define VBAM_WX_CONFIG_GAME_CONTROL_H_
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <optional.hpp>
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
#include "wx/config/user-input.h"
|
||||
|
||||
namespace config {
|
||||
|
||||
// Forward declaration.
|
||||
class GameControlState;
|
||||
|
||||
//clang-format off
|
||||
// Represents an in-game input.
|
||||
enum class GameKey {
|
||||
Up = 0,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
A,
|
||||
B,
|
||||
L,
|
||||
R,
|
||||
Select,
|
||||
Start,
|
||||
MotionUp,
|
||||
MotionDown,
|
||||
MotionLeft,
|
||||
MotionRight,
|
||||
MotionIn,
|
||||
MotionOut,
|
||||
AutoA,
|
||||
AutoB,
|
||||
Speed,
|
||||
Capture,
|
||||
Gameshark,
|
||||
Last = Gameshark
|
||||
};
|
||||
|
||||
inline constexpr int kNbGameKeys = static_cast<size_t>(GameKey::Last) + 1;
|
||||
inline constexpr int kNbJoypads = 4;
|
||||
|
||||
inline constexpr std::array<GameKey, kNbGameKeys> kAllGameKeys = {
|
||||
GameKey::Up,
|
||||
GameKey::Down,
|
||||
GameKey::Left,
|
||||
GameKey::Right,
|
||||
GameKey::A,
|
||||
GameKey::B,
|
||||
GameKey::L,
|
||||
GameKey::R,
|
||||
GameKey::Select,
|
||||
GameKey::Start,
|
||||
GameKey::MotionUp,
|
||||
GameKey::MotionDown,
|
||||
GameKey::MotionLeft,
|
||||
GameKey::MotionRight,
|
||||
GameKey::MotionIn,
|
||||
GameKey::MotionOut,
|
||||
GameKey::AutoA,
|
||||
GameKey::AutoB,
|
||||
GameKey::Speed,
|
||||
GameKey::Capture,
|
||||
GameKey::Gameshark,
|
||||
};
|
||||
//clang-format on
|
||||
|
||||
// Conversion utility method. Returns empty string on failure.
|
||||
// This is O(1).
|
||||
wxString GameKeyToString(const GameKey& game_key);
|
||||
|
||||
// Conversion utility method. Returns std::nullopt on failure.
|
||||
// This is O(log(kNbGameKeys)).
|
||||
nonstd::optional<GameKey> StringToGameKey(const wxString& input);
|
||||
|
||||
// Abstraction for an in-game control, wich is made of a player index (from 0
|
||||
// to 3), and a GameKey.
|
||||
class GameControl {
|
||||
public:
|
||||
// Converts a string to a GameControl. Returns std::nullopt on failure.
|
||||
static nonstd::optional<GameControl> FromString(const wxString& name);
|
||||
|
||||
GameControl(int joypad, GameKey game_key);
|
||||
~GameControl();
|
||||
|
||||
wxString ToString() const { return config_string_; };
|
||||
|
||||
bool operator==(const GameControl& other) const;
|
||||
bool operator!=(const GameControl& other) const;
|
||||
bool operator<(const GameControl& other) const;
|
||||
bool operator<=(const GameControl& other) const;
|
||||
bool operator>(const GameControl& other) const;
|
||||
bool operator>=(const GameControl& other) const;
|
||||
|
||||
private:
|
||||
const int joypad_;
|
||||
const GameKey game_key_;
|
||||
const wxString config_string_;
|
||||
|
||||
friend class GameControlState;
|
||||
};
|
||||
|
||||
// Tracks in-game input and computes the joypad value used to send control input
|
||||
// data to the emulator.
|
||||
class GameControlState {
|
||||
public:
|
||||
// This is a global singleton.
|
||||
static GameControlState& Instance();
|
||||
|
||||
// Disable copy constructor and assignment operator.
|
||||
GameControlState(const GameControlState&) = delete;
|
||||
GameControlState& operator=(const GameControlState&) = delete;
|
||||
|
||||
// Processes `user_input` and updates the internal tracking state.
|
||||
// Returns true if `user_input` corresponds to a game input.
|
||||
bool OnInputPressed(const config::UserInput& user_input);
|
||||
bool OnInputReleased(const config::UserInput& user_input);
|
||||
|
||||
// Clears all input.
|
||||
void Reset();
|
||||
|
||||
// Recomputes internal bindinds. This is a potentially slow operation and
|
||||
// should only be called when the game input configuration has been changed.
|
||||
void OnGameBindingsChanged();
|
||||
|
||||
uint32_t GetJoypad(int joypad) const;
|
||||
|
||||
private:
|
||||
GameControlState();
|
||||
~GameControlState();
|
||||
|
||||
std::map<config::UserInput, std::set<GameControl>> input_bindings_;
|
||||
std::map<GameControl, std::set<config::UserInput>> active_controls_;
|
||||
std::set<config::UserInput> keys_pressed_;
|
||||
std::array<uint32_t, kNbJoypads> joypads_;
|
||||
};
|
||||
|
||||
} // namespace config
|
||||
|
||||
#endif // VBAM_WX_CONFIG_GAME_CONTROL_H_
|
615
src/wx/config/internal/bindings-internal.cpp
Normal file
615
src/wx/config/internal/bindings-internal.cpp
Normal file
@@ -0,0 +1,615 @@
|
||||
#include "wx/config/bindings.h"
|
||||
#include "wx/config/command.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include <wx/xrc/xmlres.h>
|
||||
|
||||
#define VBAM_BINDINGS_INTERNAL_INCLUDE
|
||||
#include "wx/config/internal/bindings-internal.h"
|
||||
#undef VBAM_BINDINGS_INTERNAL_INCLUDE
|
||||
|
||||
namespace config {
|
||||
namespace internal {
|
||||
|
||||
const std::unordered_map<Command, std::unordered_set<UserInput>>& DefaultInputs() {
|
||||
// clang-format off
|
||||
static const std::unordered_map<Command, std::unordered_set<UserInput>> kDefaultInputs = {
|
||||
{ShortcutCommand(XRCID("CheatsList")),
|
||||
{
|
||||
KeyboardInput('C', wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("NextFrame")),
|
||||
{
|
||||
KeyboardInput('N', wxMOD_CMD)
|
||||
}},
|
||||
// this was annoying people A LOT #334
|
||||
// {ShortcutCommand(wxID_EXIT),
|
||||
// {
|
||||
// KeyboardInput(WXK_ESCAPE)
|
||||
// }},
|
||||
// this was annoying people #298
|
||||
// {ShortcutCommand(wxID_EXIT),
|
||||
// {
|
||||
// KeyboardInput('X', wxMOD_CMD)
|
||||
// }},
|
||||
{ShortcutCommand(wxID_EXIT),
|
||||
{
|
||||
KeyboardInput('Q', wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(wxID_CLOSE),
|
||||
{
|
||||
KeyboardInput('W', wxMOD_CMD)
|
||||
}},
|
||||
// load most recent is more commonly used than load state
|
||||
// {ShortcutCommand(XRCID("Load")),
|
||||
// {
|
||||
// KeyboardInput('L', wxMOD_CMD)
|
||||
// }},
|
||||
{ShortcutCommand(XRCID("LoadGameRecent")),
|
||||
{
|
||||
KeyboardInput('L', wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("LoadGame01")),
|
||||
{
|
||||
KeyboardInput(WXK_F1)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("LoadGame02")),
|
||||
{
|
||||
KeyboardInput(WXK_F2)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("LoadGame03")),
|
||||
{
|
||||
KeyboardInput(WXK_F3)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("LoadGame04")),
|
||||
{
|
||||
KeyboardInput(WXK_F4)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("LoadGame05")),
|
||||
{
|
||||
KeyboardInput(WXK_F5)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("LoadGame06")),
|
||||
{
|
||||
KeyboardInput(WXK_F6)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("LoadGame07")),
|
||||
{
|
||||
KeyboardInput(WXK_F7)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("LoadGame08")),
|
||||
{
|
||||
KeyboardInput(WXK_F8)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("LoadGame09")),
|
||||
{
|
||||
KeyboardInput(WXK_F9)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("LoadGame10")),
|
||||
{
|
||||
KeyboardInput(WXK_F10)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("Pause")),
|
||||
{KeyboardInput(WXK_PAUSE), KeyboardInput('P', wxMOD_CMD)}},
|
||||
{ShortcutCommand(XRCID("Reset")),
|
||||
{
|
||||
KeyboardInput('R', wxMOD_CMD)
|
||||
}},
|
||||
// add shortcuts for original size multiplier #415
|
||||
{ShortcutCommand(XRCID("SetSize1x")),
|
||||
{
|
||||
KeyboardInput('1')
|
||||
}},
|
||||
{ShortcutCommand(XRCID("SetSize2x")),
|
||||
{
|
||||
KeyboardInput('2')
|
||||
}},
|
||||
{ShortcutCommand(XRCID("SetSize3x")),
|
||||
{
|
||||
KeyboardInput('3')
|
||||
}},
|
||||
{ShortcutCommand(XRCID("SetSize4x")),
|
||||
{
|
||||
KeyboardInput('4')
|
||||
}},
|
||||
{ShortcutCommand(XRCID("SetSize5x")),
|
||||
{
|
||||
KeyboardInput('5')
|
||||
}},
|
||||
{ShortcutCommand(XRCID("SetSize6x")),
|
||||
{
|
||||
KeyboardInput('6')
|
||||
}},
|
||||
// save oldest is more commonly used than save other
|
||||
// {ShortcutCommand(XRCID("Save")),
|
||||
// {
|
||||
// KeyboardInput('S', wxMOD_CMD)
|
||||
// }},
|
||||
{ShortcutCommand(XRCID("SaveGameOldest")),
|
||||
{
|
||||
KeyboardInput('S', wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("SaveGame01")),
|
||||
{
|
||||
KeyboardInput(WXK_F1, wxMOD_SHIFT)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("SaveGame02")),
|
||||
{
|
||||
KeyboardInput(WXK_F2, wxMOD_SHIFT)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("SaveGame03")),
|
||||
{
|
||||
KeyboardInput(WXK_F3, wxMOD_SHIFT)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("SaveGame04")),
|
||||
{
|
||||
KeyboardInput(WXK_F4, wxMOD_SHIFT)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("SaveGame05")),
|
||||
{
|
||||
KeyboardInput(WXK_F5, wxMOD_SHIFT)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("SaveGame06")),
|
||||
{
|
||||
KeyboardInput(WXK_F6, wxMOD_SHIFT)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("SaveGame07")),
|
||||
{
|
||||
KeyboardInput(WXK_F7, wxMOD_SHIFT)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("SaveGame08")),
|
||||
{
|
||||
KeyboardInput(WXK_F8, wxMOD_SHIFT)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("SaveGame09")),
|
||||
{
|
||||
KeyboardInput(WXK_F9, wxMOD_SHIFT)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("SaveGame10")),
|
||||
{
|
||||
KeyboardInput(WXK_F10, wxMOD_SHIFT)
|
||||
}},
|
||||
// I prefer the SDL ESC key binding
|
||||
// {ShortcutCommand(XRCID("ToggleFullscreen")),
|
||||
// {
|
||||
// KeyboardInput(WXK_ESCAPE)
|
||||
// }},
|
||||
// alt-enter is more standard anyway
|
||||
{ShortcutCommand(XRCID("ToggleFullscreen")),
|
||||
{
|
||||
KeyboardInput(WXK_RETURN, wxMOD_ALT)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("JoypadAutofireA")),
|
||||
{
|
||||
KeyboardInput('1', wxMOD_ALT)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("JoypadAutofireB")),
|
||||
{
|
||||
KeyboardInput('2', wxMOD_ALT)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("JoypadAutofireL")),
|
||||
{
|
||||
KeyboardInput('3', wxMOD_ALT)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("JoypadAutofireR")),
|
||||
{
|
||||
KeyboardInput('4', wxMOD_ALT)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("VideoLayersBG0")),
|
||||
{
|
||||
KeyboardInput('1', wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("VideoLayersBG1")),
|
||||
{
|
||||
KeyboardInput('2', wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("VideoLayersBG2")),
|
||||
{
|
||||
KeyboardInput('3', wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("VideoLayersBG3")),
|
||||
{
|
||||
KeyboardInput('4', wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("VideoLayersOBJ")),
|
||||
{
|
||||
KeyboardInput('5', wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("VideoLayersWIN0")),
|
||||
{
|
||||
KeyboardInput('6', wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("VideoLayersWIN1")),
|
||||
{
|
||||
KeyboardInput('7', wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("VideoLayersOBJWIN")),
|
||||
{
|
||||
KeyboardInput('8', wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("Rewind")),
|
||||
{
|
||||
KeyboardInput('B', wxMOD_CMD)
|
||||
}},
|
||||
// The following commands do not have the dafault wxWidgets shortcut.
|
||||
// The wxID_FILE1 shortcut is active when the first recent menu entry is populated.
|
||||
// The same goes for the others, wxID_FILE2 is active when the second recent menu entry is
|
||||
// populated, etc.
|
||||
{ShortcutCommand(wxID_FILE1),
|
||||
{
|
||||
KeyboardInput(WXK_F1, wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(wxID_FILE2),
|
||||
{
|
||||
KeyboardInput(WXK_F2, wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(wxID_FILE3),
|
||||
{
|
||||
KeyboardInput(WXK_F3, wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(wxID_FILE4),
|
||||
{
|
||||
KeyboardInput(WXK_F4, wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(wxID_FILE5),
|
||||
{
|
||||
KeyboardInput(WXK_F5, wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(wxID_FILE6),
|
||||
{
|
||||
KeyboardInput(WXK_F6, wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(wxID_FILE7),
|
||||
{
|
||||
KeyboardInput(WXK_F7, wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(wxID_FILE8),
|
||||
{
|
||||
KeyboardInput(WXK_F8, wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(wxID_FILE9),
|
||||
{
|
||||
KeyboardInput(WXK_F9, wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(wxID_FILE10),
|
||||
{
|
||||
KeyboardInput(WXK_F10, wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("VideoLayersReset")),
|
||||
{
|
||||
KeyboardInput('0', wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("ChangeFilter")),
|
||||
{
|
||||
KeyboardInput('G', wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("ChangeIFB")),
|
||||
{
|
||||
KeyboardInput('I', wxMOD_CMD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("IncreaseVolume")),
|
||||
{
|
||||
KeyboardInput(WXK_NUMPAD_ADD)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("DecreaseVolume")),
|
||||
{
|
||||
KeyboardInput(WXK_NUMPAD_SUBTRACT)
|
||||
}},
|
||||
{ShortcutCommand(XRCID("ToggleSound")),
|
||||
{
|
||||
KeyboardInput(WXK_NUMPAD_ENTER)
|
||||
}},
|
||||
|
||||
// Player 1 controls.
|
||||
{GameCommand(GameJoy(0), config::GameKey::Up),
|
||||
{
|
||||
KeyboardInput('W'),
|
||||
JoyInput(JoyId(0), JoyControl::Button, 11),
|
||||
JoyInput(JoyId(0), JoyControl::AxisMinus, 1),
|
||||
JoyInput(JoyId(0), JoyControl::AxisMinus, 3),
|
||||
JoyInput(JoyId(0), JoyControl::HatNorth, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::Down),
|
||||
{
|
||||
KeyboardInput('S'),
|
||||
JoyInput(JoyId(0), JoyControl::Button, 12),
|
||||
JoyInput(JoyId(0), JoyControl::AxisPlus, 1),
|
||||
JoyInput(JoyId(0), JoyControl::AxisPlus, 3),
|
||||
JoyInput(JoyId(0), JoyControl::HatSouth, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::Left),
|
||||
{
|
||||
KeyboardInput('A'),
|
||||
JoyInput(JoyId(0), JoyControl::Button, 13),
|
||||
JoyInput(JoyId(0), JoyControl::AxisMinus, 0),
|
||||
JoyInput(JoyId(0), JoyControl::AxisMinus, 2),
|
||||
JoyInput(JoyId(0), JoyControl::HatWest, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::Right),
|
||||
{
|
||||
KeyboardInput('D'),
|
||||
JoyInput(JoyId(0), JoyControl::Button, 14),
|
||||
JoyInput(JoyId(0), JoyControl::AxisPlus, 0),
|
||||
JoyInput(JoyId(0), JoyControl::AxisPlus, 2),
|
||||
JoyInput(JoyId(0), JoyControl::HatEast, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::A),
|
||||
{
|
||||
KeyboardInput('L'),
|
||||
JoyInput(JoyId(0), JoyControl::Button, 1),
|
||||
}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::B),
|
||||
{
|
||||
KeyboardInput('K'),
|
||||
JoyInput(JoyId(0), JoyControl::Button, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::L),
|
||||
{
|
||||
KeyboardInput('I'),
|
||||
JoyInput(JoyId(0), JoyControl::Button, 2),
|
||||
JoyInput(JoyId(0), JoyControl::Button, 9),
|
||||
JoyInput(JoyId(0), JoyControl::AxisPlus, 4),
|
||||
}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::R),
|
||||
{
|
||||
KeyboardInput('O'),
|
||||
JoyInput(JoyId(0), JoyControl::Button, 3),
|
||||
JoyInput(JoyId(0), JoyControl::Button, 10),
|
||||
JoyInput(JoyId(0), JoyControl::AxisPlus, 5),
|
||||
}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::Select),
|
||||
{
|
||||
KeyboardInput(WXK_BACK),
|
||||
JoyInput(JoyId(0), JoyControl::Button, 4),
|
||||
}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::Start),
|
||||
{
|
||||
KeyboardInput(WXK_RETURN),
|
||||
JoyInput(JoyId(0), JoyControl::Button, 6),
|
||||
}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::MotionUp), {}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::MotionDown), {}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::MotionLeft), {}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::MotionRight), {}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::MotionIn), {}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::MotionOut), {}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::AutoA), {}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::AutoB), {}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::Speed),
|
||||
{
|
||||
KeyboardInput(WXK_SPACE),
|
||||
}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::Capture), {}},
|
||||
{GameCommand(GameJoy(0), config::GameKey::Gameshark), {}},
|
||||
|
||||
// Player 2 controls.
|
||||
{GameCommand(GameJoy(1), config::GameKey::Up),
|
||||
{
|
||||
JoyInput(JoyId(1), JoyControl::Button, 11),
|
||||
JoyInput(JoyId(1), JoyControl::AxisMinus, 1),
|
||||
JoyInput(JoyId(1), JoyControl::AxisMinus, 3),
|
||||
JoyInput(JoyId(1), JoyControl::HatNorth, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::Down),
|
||||
{
|
||||
JoyInput(JoyId(1), JoyControl::Button, 12),
|
||||
JoyInput(JoyId(1), JoyControl::AxisPlus, 1),
|
||||
JoyInput(JoyId(1), JoyControl::AxisPlus, 3),
|
||||
JoyInput(JoyId(1), JoyControl::HatSouth, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::Left),
|
||||
{
|
||||
JoyInput(JoyId(1), JoyControl::Button, 13),
|
||||
JoyInput(JoyId(1), JoyControl::AxisMinus, 0),
|
||||
JoyInput(JoyId(1), JoyControl::AxisMinus, 2),
|
||||
JoyInput(JoyId(1), JoyControl::HatWest, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::Right),
|
||||
{
|
||||
JoyInput(JoyId(1), JoyControl::Button, 14),
|
||||
JoyInput(JoyId(1), JoyControl::AxisPlus, 0),
|
||||
JoyInput(JoyId(1), JoyControl::AxisPlus, 2),
|
||||
JoyInput(JoyId(1), JoyControl::HatEast, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::A),
|
||||
{
|
||||
JoyInput(JoyId(1), JoyControl::Button, 1),
|
||||
}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::B),
|
||||
{
|
||||
JoyInput(JoyId(1), JoyControl::Button, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::L),
|
||||
{
|
||||
JoyInput(JoyId(1), JoyControl::Button, 2),
|
||||
JoyInput(JoyId(1), JoyControl::Button, 9),
|
||||
JoyInput(JoyId(1), JoyControl::AxisPlus, 4),
|
||||
}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::R),
|
||||
{
|
||||
JoyInput(JoyId(1), JoyControl::Button, 3),
|
||||
JoyInput(JoyId(1), JoyControl::Button, 10),
|
||||
JoyInput(JoyId(1), JoyControl::AxisPlus, 5),
|
||||
}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::Select),
|
||||
{
|
||||
JoyInput(JoyId(1), JoyControl::Button, 4),
|
||||
}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::Start),
|
||||
{
|
||||
JoyInput(JoyId(1), JoyControl::Button, 6),
|
||||
}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::MotionUp), {}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::MotionDown), {}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::MotionLeft), {}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::MotionRight), {}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::MotionIn), {}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::MotionOut), {}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::AutoA), {}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::AutoB), {}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::Speed), {}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::Capture), {}},
|
||||
{GameCommand(GameJoy(1), config::GameKey::Gameshark), {}},
|
||||
|
||||
// Player 3 controls.
|
||||
{GameCommand(GameJoy(2), config::GameKey::Up),
|
||||
{
|
||||
JoyInput(JoyId(2), JoyControl::Button, 11),
|
||||
JoyInput(JoyId(2), JoyControl::AxisMinus, 1),
|
||||
JoyInput(JoyId(2), JoyControl::AxisMinus, 3),
|
||||
JoyInput(JoyId(2), JoyControl::HatNorth, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::Down),
|
||||
{
|
||||
JoyInput(JoyId(2), JoyControl::Button, 12),
|
||||
JoyInput(JoyId(2), JoyControl::AxisPlus, 1),
|
||||
JoyInput(JoyId(2), JoyControl::AxisPlus, 3),
|
||||
JoyInput(JoyId(2), JoyControl::HatSouth, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::Left),
|
||||
{
|
||||
JoyInput(JoyId(2), JoyControl::Button, 13),
|
||||
JoyInput(JoyId(2), JoyControl::AxisMinus, 0),
|
||||
JoyInput(JoyId(2), JoyControl::AxisMinus, 2),
|
||||
JoyInput(JoyId(2), JoyControl::HatWest, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::Right),
|
||||
{
|
||||
JoyInput(JoyId(2), JoyControl::Button, 14),
|
||||
JoyInput(JoyId(2), JoyControl::AxisPlus, 0),
|
||||
JoyInput(JoyId(2), JoyControl::AxisPlus, 2),
|
||||
JoyInput(JoyId(2), JoyControl::HatEast, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::A),
|
||||
{
|
||||
JoyInput(JoyId(2), JoyControl::Button, 1),
|
||||
}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::B),
|
||||
{
|
||||
JoyInput(JoyId(2), JoyControl::Button, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::L),
|
||||
{
|
||||
JoyInput(JoyId(2), JoyControl::Button, 2),
|
||||
JoyInput(JoyId(2), JoyControl::Button, 9),
|
||||
JoyInput(JoyId(2), JoyControl::AxisPlus, 4),
|
||||
}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::R),
|
||||
{
|
||||
JoyInput(JoyId(2), JoyControl::Button, 3),
|
||||
JoyInput(JoyId(2), JoyControl::Button, 10),
|
||||
JoyInput(JoyId(2), JoyControl::AxisPlus, 5),
|
||||
}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::Select),
|
||||
{
|
||||
JoyInput(JoyId(2), JoyControl::Button, 4),
|
||||
}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::Start),
|
||||
{
|
||||
JoyInput(JoyId(2), JoyControl::Button, 6),
|
||||
}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::MotionUp), {}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::MotionDown), {}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::MotionLeft), {}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::MotionRight), {}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::MotionIn), {}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::MotionOut), {}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::AutoA), {}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::AutoB), {}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::Speed), {}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::Capture), {}},
|
||||
{GameCommand(GameJoy(2), config::GameKey::Gameshark), {}},
|
||||
|
||||
// Player 4 controls.
|
||||
{GameCommand(GameJoy(3), config::GameKey::Up),
|
||||
{
|
||||
JoyInput(JoyId(3), JoyControl::Button, 11),
|
||||
JoyInput(JoyId(3), JoyControl::AxisMinus, 1),
|
||||
JoyInput(JoyId(3), JoyControl::AxisMinus, 3),
|
||||
JoyInput(JoyId(3), JoyControl::HatNorth, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::Down),
|
||||
{
|
||||
JoyInput(JoyId(3), JoyControl::Button, 12),
|
||||
JoyInput(JoyId(3), JoyControl::AxisPlus, 1),
|
||||
JoyInput(JoyId(3), JoyControl::AxisPlus, 3),
|
||||
JoyInput(JoyId(3), JoyControl::HatSouth, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::Left),
|
||||
{
|
||||
JoyInput(JoyId(3), JoyControl::Button, 13),
|
||||
JoyInput(JoyId(3), JoyControl::AxisMinus, 0),
|
||||
JoyInput(JoyId(3), JoyControl::AxisMinus, 2),
|
||||
JoyInput(JoyId(3), JoyControl::HatWest, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::Right),
|
||||
{
|
||||
JoyInput(JoyId(3), JoyControl::Button, 14),
|
||||
JoyInput(JoyId(3), JoyControl::AxisPlus, 0),
|
||||
JoyInput(JoyId(3), JoyControl::AxisPlus, 2),
|
||||
JoyInput(JoyId(3), JoyControl::HatEast, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::A),
|
||||
{
|
||||
JoyInput(JoyId(3), JoyControl::Button, 1),
|
||||
}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::B),
|
||||
{
|
||||
JoyInput(JoyId(3), JoyControl::Button, 0),
|
||||
}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::L),
|
||||
{
|
||||
JoyInput(JoyId(3), JoyControl::Button, 2),
|
||||
JoyInput(JoyId(3), JoyControl::Button, 9),
|
||||
JoyInput(JoyId(3), JoyControl::AxisPlus, 4),
|
||||
}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::R),
|
||||
{
|
||||
JoyInput(JoyId(3), JoyControl::Button, 3),
|
||||
JoyInput(JoyId(3), JoyControl::Button, 10),
|
||||
JoyInput(JoyId(3), JoyControl::AxisPlus, 5),
|
||||
}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::Select),
|
||||
{
|
||||
JoyInput(JoyId(3), JoyControl::Button, 4),
|
||||
}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::Start),
|
||||
{
|
||||
JoyInput(JoyId(3), JoyControl::Button, 6),
|
||||
}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::MotionUp), {}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::MotionDown), {}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::MotionLeft), {}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::MotionRight), {}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::MotionIn), {}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::MotionOut), {}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::AutoA), {}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::AutoB), {}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::Speed), {}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::Capture), {}},
|
||||
{GameCommand(GameJoy(3), config::GameKey::Gameshark), {}},
|
||||
};
|
||||
// clang-format on
|
||||
return kDefaultInputs;
|
||||
}
|
||||
|
||||
const std::unordered_set<UserInput>& DefaultInputsForCommand(const Command& command) {
|
||||
const auto& iter = DefaultInputs().find(command);
|
||||
if (iter != DefaultInputs().end()) {
|
||||
return iter->second;
|
||||
}
|
||||
static const std::unordered_set<UserInput> kEmptySet;
|
||||
return kEmptySet;
|
||||
}
|
||||
|
||||
bool IsDefaultInputForCommand(const Command& command, const UserInput& input) {
|
||||
const auto& iter = DefaultInputs().find(command);
|
||||
if (iter != DefaultInputs().end()) {
|
||||
return iter->second.find(input) != iter->second.end();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace config
|
118
src/wx/config/internal/bindings-internal.h
Normal file
118
src/wx/config/internal/bindings-internal.h
Normal file
@@ -0,0 +1,118 @@
|
||||
#ifndef VBAM_BINDINGS_INTERNAL_INCLUDE
|
||||
#error "Do not include "config/internal/bindings-internal.h" outside of the implementation."
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "wx/config/command.h"
|
||||
#include "wx/config/user-input.h"
|
||||
|
||||
namespace config {
|
||||
namespace internal {
|
||||
|
||||
// Returns the map of commands to their default inputs.
|
||||
const std::unordered_map<Command, std::unordered_set<UserInput>>& DefaultInputs();
|
||||
|
||||
// Returns the default inputs for the given `command`.
|
||||
// Returns an empty set if there are no default inputs for `command`.
|
||||
const std::unordered_set<UserInput>& DefaultInputsForCommand(const Command& command);
|
||||
|
||||
// Returns true if `input` is the default input for `command`.
|
||||
bool IsDefaultInputForCommand(const Command& command, const UserInput& input);
|
||||
|
||||
// clang-format off
|
||||
static constexpr std::array<GameCommand, kNbGameKeys * kNbJoypads> kOrderedGameCommands = {
|
||||
GameCommand(GameJoy(0), GameKey::Up),
|
||||
GameCommand(GameJoy(0), GameKey::Down),
|
||||
GameCommand(GameJoy(0), GameKey::Left),
|
||||
GameCommand(GameJoy(0), GameKey::Right),
|
||||
GameCommand(GameJoy(0), GameKey::A),
|
||||
GameCommand(GameJoy(0), GameKey::B),
|
||||
GameCommand(GameJoy(0), GameKey::L),
|
||||
GameCommand(GameJoy(0), GameKey::R),
|
||||
GameCommand(GameJoy(0), GameKey::Select),
|
||||
GameCommand(GameJoy(0), GameKey::Start),
|
||||
GameCommand(GameJoy(0), GameKey::MotionUp),
|
||||
GameCommand(GameJoy(0), GameKey::MotionDown),
|
||||
GameCommand(GameJoy(0), GameKey::MotionLeft),
|
||||
GameCommand(GameJoy(0), GameKey::MotionRight),
|
||||
GameCommand(GameJoy(0), GameKey::MotionIn),
|
||||
GameCommand(GameJoy(0), GameKey::MotionOut),
|
||||
GameCommand(GameJoy(0), GameKey::AutoA),
|
||||
GameCommand(GameJoy(0), GameKey::AutoB),
|
||||
GameCommand(GameJoy(0), GameKey::Speed),
|
||||
GameCommand(GameJoy(0), GameKey::Capture),
|
||||
GameCommand(GameJoy(0), GameKey::Gameshark),
|
||||
|
||||
GameCommand(GameJoy(1), GameKey::Up),
|
||||
GameCommand(GameJoy(1), GameKey::Down),
|
||||
GameCommand(GameJoy(1), GameKey::Left),
|
||||
GameCommand(GameJoy(1), GameKey::Right),
|
||||
GameCommand(GameJoy(1), GameKey::A),
|
||||
GameCommand(GameJoy(1), GameKey::B),
|
||||
GameCommand(GameJoy(1), GameKey::L),
|
||||
GameCommand(GameJoy(1), GameKey::R),
|
||||
GameCommand(GameJoy(1), GameKey::Select),
|
||||
GameCommand(GameJoy(1), GameKey::Start),
|
||||
GameCommand(GameJoy(1), GameKey::MotionUp),
|
||||
GameCommand(GameJoy(1), GameKey::MotionDown),
|
||||
GameCommand(GameJoy(1), GameKey::MotionLeft),
|
||||
GameCommand(GameJoy(1), GameKey::MotionRight),
|
||||
GameCommand(GameJoy(1), GameKey::MotionIn),
|
||||
GameCommand(GameJoy(1), GameKey::MotionOut),
|
||||
GameCommand(GameJoy(1), GameKey::AutoA),
|
||||
GameCommand(GameJoy(1), GameKey::AutoB),
|
||||
GameCommand(GameJoy(1), GameKey::Speed),
|
||||
GameCommand(GameJoy(1), GameKey::Capture),
|
||||
GameCommand(GameJoy(1), GameKey::Gameshark),
|
||||
|
||||
GameCommand(GameJoy(2), GameKey::Up),
|
||||
GameCommand(GameJoy(2), GameKey::Down),
|
||||
GameCommand(GameJoy(2), GameKey::Left),
|
||||
GameCommand(GameJoy(2), GameKey::Right),
|
||||
GameCommand(GameJoy(2), GameKey::A),
|
||||
GameCommand(GameJoy(2), GameKey::B),
|
||||
GameCommand(GameJoy(2), GameKey::L),
|
||||
GameCommand(GameJoy(2), GameKey::R),
|
||||
GameCommand(GameJoy(2), GameKey::Select),
|
||||
GameCommand(GameJoy(2), GameKey::Start),
|
||||
GameCommand(GameJoy(2), GameKey::MotionUp),
|
||||
GameCommand(GameJoy(2), GameKey::MotionDown),
|
||||
GameCommand(GameJoy(2), GameKey::MotionLeft),
|
||||
GameCommand(GameJoy(2), GameKey::MotionRight),
|
||||
GameCommand(GameJoy(2), GameKey::MotionIn),
|
||||
GameCommand(GameJoy(2), GameKey::MotionOut),
|
||||
GameCommand(GameJoy(2), GameKey::AutoA),
|
||||
GameCommand(GameJoy(2), GameKey::AutoB),
|
||||
GameCommand(GameJoy(2), GameKey::Speed),
|
||||
GameCommand(GameJoy(2), GameKey::Capture),
|
||||
GameCommand(GameJoy(2), GameKey::Gameshark),
|
||||
|
||||
GameCommand(GameJoy(3), GameKey::Up),
|
||||
GameCommand(GameJoy(3), GameKey::Down),
|
||||
GameCommand(GameJoy(3), GameKey::Left),
|
||||
GameCommand(GameJoy(3), GameKey::Right),
|
||||
GameCommand(GameJoy(3), GameKey::A),
|
||||
GameCommand(GameJoy(3), GameKey::B),
|
||||
GameCommand(GameJoy(3), GameKey::L),
|
||||
GameCommand(GameJoy(3), GameKey::R),
|
||||
GameCommand(GameJoy(3), GameKey::Select),
|
||||
GameCommand(GameJoy(3), GameKey::Start),
|
||||
GameCommand(GameJoy(3), GameKey::MotionUp),
|
||||
GameCommand(GameJoy(3), GameKey::MotionDown),
|
||||
GameCommand(GameJoy(3), GameKey::MotionLeft),
|
||||
GameCommand(GameJoy(3), GameKey::MotionRight),
|
||||
GameCommand(GameJoy(3), GameKey::MotionIn),
|
||||
GameCommand(GameJoy(3), GameKey::MotionOut),
|
||||
GameCommand(GameJoy(3), GameKey::AutoA),
|
||||
GameCommand(GameJoy(3), GameKey::AutoB),
|
||||
GameCommand(GameJoy(3), GameKey::Speed),
|
||||
GameCommand(GameJoy(3), GameKey::Capture),
|
||||
GameCommand(GameJoy(3), GameKey::Gameshark),
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
} // namespace internal
|
||||
} // namespace config
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user