Compare commits
118 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 235e41653d | |||
| 433479b1c3 | |||
| 2b4736f224 | |||
| 41f1dcf9d2 | |||
| 45b97cf39b | |||
| 95b1225219 | |||
| 70f6c40b26 | |||
| 43bc54fca9 | |||
| a5ec31ffef | |||
| 81cbf82ddc | |||
| 6cea506c6f | |||
| d7d49f261d | |||
| 4692a5c0b7 | |||
| d32ea32f1e | |||
| f5f053cc4e | |||
| bb3c6dc9fc | |||
| 496d4de1de | |||
| 71f8437ecb | |||
| e7767105d6 | |||
| 9cfda8514e | |||
| e0c6700a4d | |||
| 12bf158398 | |||
| ea01c86821 | |||
| 96459432a6 | |||
| 089194c663 | |||
| 7a70227535 | |||
| 7adfb3123b | |||
| 1d28075601 | |||
| af7aa688bb | |||
| beb524f523 | |||
| 18c9c151a8 | |||
| 83820baf79 | |||
| abf50350e7 | |||
| 6588d0d17a | |||
| 648736df48 | |||
| 6278a443df | |||
| 615295f1f9 | |||
| 593135d02b | |||
| 78ea41a2ca | |||
| 105600878e | |||
| 415202e15f | |||
| 618a6f6c9f | |||
| af70f35bf2 | |||
| 43bb0a3a1c | |||
| 8d9f83b1cc | |||
| 7d713afbdd | |||
| 5044ef8ab1 | |||
| 5b4fe5667a | |||
| a4d3efa087 | |||
| 9cb8adce55 | |||
| c061981de0 | |||
| b2f76fc436 | |||
| dc58fbe900 | |||
| 0e80142da1 | |||
| ee77bc7c9b | |||
| b978248c03 | |||
| 85d2b5be5a | |||
| cee35297d6 | |||
| edde08849f | |||
| f649053e00 | |||
| 6c6b4bcd3a | |||
| 1fef816572 | |||
| a6203cf6e9 | |||
| 200bb0f6e3 | |||
| 1dd2fca3db | |||
| 5444f3b807 | |||
| 897d81a188 | |||
| eae18c446a | |||
| fb7402e394 | |||
| 63dc9cbe7c | |||
| ac63f4809f | |||
| 0bdf3ada25 | |||
| aa684b903b | |||
| db0ae36702 | |||
| 3d5e812c6f | |||
| 07f6810835 | |||
| b3e2a43cf8 | |||
| 96f4abbc07 | |||
| d96746c933 | |||
| 4391ebd94a | |||
| 081029e3d1 | |||
| 541724b63c | |||
| 304afbb50d | |||
| 1ba88f48bb | |||
| 8f8c9c35f1 | |||
| 1c07d032ef | |||
| c500a17b1a | |||
| 6d0347b48a | |||
| 83af0ff4af | |||
| 32c13eee1d | |||
| 42597a76f0 | |||
| 7c59a520aa | |||
| 3c124d8519 | |||
| 76282a11da | |||
| 5249bf6a56 | |||
| 12e0dac777 | |||
| b4e3bede3b | |||
| 679731f004 | |||
| 3bbd29916b | |||
| 6516330289 | |||
| 0628ea920b | |||
| 40a4133565 | |||
| b4425fe1be | |||
| 809cbcbc21 | |||
| 2ae2b994a8 | |||
| 8ce36cdc03 | |||
| 3707a86c79 | |||
| eea3ec7f95 | |||
| 4a2c6761fc | |||
| 7a4e179f4c | |||
| 11eaadb739 | |||
| 41b77b54ea | |||
| 22689bd9ee | |||
| 831535c86f | |||
| ce43955784 | |||
| e85f4f4309 | |||
| 00b3f33530 | |||
| 1b0116a0cb |
@@ -4,7 +4,7 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
Linux:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -14,20 +14,33 @@ jobs:
|
||||
key: Linux
|
||||
- name: Install Packages
|
||||
run: |
|
||||
sudo apt-get purge grub\* --yes --allow-remove-essential
|
||||
sudo add-apt-repository ppa:okirby/qt6-backports --yes
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get upgrade
|
||||
sudo apt-get -y install cmake ninja-build libhidapi-dev libsamplerate0-dev libspeex-dev libminizip-dev libsdl2-dev libfreetype6-dev libgl1-mesa-dev libglu1-mesa-dev pkg-config zlib1g-dev binutils-dev libspeexdsp-dev qt6-base-dev libqt6svg6-dev build-essential nasm git zip
|
||||
sudo apt-get -y install cmake ninja-build libhidapi-dev libsamplerate0-dev libspeex-dev libminizip-dev libsdl2-dev libfreetype6-dev libgl1-mesa-dev libglu1-mesa-dev pkg-config zlib1g-dev binutils-dev libspeexdsp-dev qt6-base-dev libqt6svg6-dev build-essential nasm git zip appstream
|
||||
- name: Prepare Environment
|
||||
run: |
|
||||
echo "GIT_REVISION=$(git describe --tags --always)" >> $GITHUB_ENV
|
||||
- name: Build RMG (Portable)
|
||||
run: ./Source/Script/Build.sh Release
|
||||
- name: Build RMG (AppImage)
|
||||
run: |
|
||||
mkdir Build Build/AppImage Bin/ -p
|
||||
export src_dir="$(pwd)"
|
||||
export build_dir="$(pwd)/build"
|
||||
export bin_dir="$(pwd)/Bin/AppImage"
|
||||
cmake -S "$src_dir" -B "$build_dir" -DCMAKE_BUILD_TYPE="Release" -DPORTABLE_INSTALL="OFF" -DUPDATER=ON -DAPPIMAGE_UPDATER=ON -DCMAKE_INSTALL_PREFIX="/usr" -G "Ninja"
|
||||
cmake --build "$build_dir"
|
||||
cmake --install "$build_dir" --prefix="$bin_dir/usr"
|
||||
shell: bash
|
||||
- name: Upload RMG (Portable)
|
||||
- name: Create AppImage
|
||||
run: |
|
||||
./Package/AppImage/Create.sh
|
||||
shell: bash
|
||||
- name: Upload RMG (AppImage)
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: RMG-Portable-Linux64-${{ env.GIT_REVISION }}
|
||||
path: Bin/Release/*
|
||||
path: Bin/*.AppImage
|
||||
|
||||
Windows:
|
||||
runs-on: windows-2019
|
||||
|
||||
@@ -9,4 +9,7 @@ Package/ArchLinux/rmg/
|
||||
Package/ArchLinux/src/
|
||||
Package/ArchLinux/*.pkg.*
|
||||
# Clangd compile_commands.json
|
||||
compile_commands.json
|
||||
compile_commands.json
|
||||
# AppImage
|
||||
squashfs-root
|
||||
*.AppImage
|
||||
@@ -6,8 +6,10 @@ cmake_minimum_required(VERSION 3.15)
|
||||
option(PORTABLE_INSTALL "Portable Installation" ON)
|
||||
option(NO_GIT_CLONE "Disables git clone usage for 3rdParty dependencies" OFF)
|
||||
option(UPDATER "Enables updater" ${PORTABLE_INSTALL})
|
||||
option(APPIMAGE_UPDATER "Enables AppImage updater" OFF)
|
||||
option(DISCORD_RPC "Enables Discord Rich Presence" ON)
|
||||
option(USE_CCACHE "Enables usage of ccache when ccache has been found" ON)
|
||||
option(FORCE_XCB "Forces Qt to use the xcb platform on linux" ${LINUX})
|
||||
|
||||
project(RMG)
|
||||
|
||||
@@ -34,11 +36,26 @@ if (NOT PORTABLE_INSTALL AND (WIN32 OR MSYS))
|
||||
set(PORTABLE_INSTALL ON)
|
||||
endif()
|
||||
|
||||
if (NOT PORTABLE_INSTALL AND UPDATER)
|
||||
if (NOT PORTABLE_INSTALL AND UPDATER AND NOT APPIMAGE_UPDATER)
|
||||
message(WARNING "Not-Portable builds don't support the updater, disabling UPDATER")
|
||||
set(UPDATER OFF)
|
||||
endif()
|
||||
|
||||
if (APPIMAGE_UPDATER AND (WIN32 OR MSYS))
|
||||
message(WARNING "AppImage updater not supported on windows, disabling APPIMAGE_UPDATER!")
|
||||
set(APPIMAGE_UPDATER OFF)
|
||||
endif()
|
||||
|
||||
if (UPDATER AND NOT (WIN32 OR MSYS) AND NOT APPIMAGE_UPDATER)
|
||||
message(WARNING "Only AppImage updater is supported on linux, disabling UPDATER!")
|
||||
set(UPDATER OFF)
|
||||
endif()
|
||||
|
||||
if (FORCE_XCB AND (WIN32 OR MSYS))
|
||||
message(WARNING "XCB Qt platform not supported on windows, disabling FORCE_XCB!")
|
||||
set(FORCE_XCB OFF)
|
||||
endif()
|
||||
|
||||
if (PORTABLE_INSTALL)
|
||||
set(CMAKE_INSTALL_PREFIX "")
|
||||
set(INSTALL_PATH "Bin/${CMAKE_BUILD_TYPE}")
|
||||
@@ -88,6 +105,9 @@ install(FILES ${MUPEN64PLUSCORE_LIB}
|
||||
install(FILES ${MUPEN64PLUSCORE_INI} Data/font.ttf
|
||||
DESTINATION ${DATA_INSTALL_PATH}
|
||||
)
|
||||
install(FILES ${SDL_GAMECONTROLLERDB}
|
||||
DESTINATION ${DATA_INSTALL_PATH}
|
||||
)
|
||||
file(GLOB GENERATED_CHEAT_FILES ${CMAKE_SOURCE_DIR}/Data/Cheats/Generated/*.cht)
|
||||
file(GLOB CUSTOM_CHEAT_FILES ${CMAKE_SOURCE_DIR}/Data/Cheats/Custom/*.cht)
|
||||
install(FILES ${GENERATED_CHEAT_FILES} ${CUSTOM_CHEAT_FILES}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
cur_dir="$(readlink -f "$(dirname "$0")")"
|
||||
|
||||
exec "$cur_dir/usr/bin/RMG" \
|
||||
--core-path="$cur_dir/usr/lib/RMG/Core" \
|
||||
--plugin-path="$cur_dir/usr/lib/RMG/Plugin" \
|
||||
--shared-data-path="$cur_dir/usr/share/RMG" \
|
||||
"$@"
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
set -ex
|
||||
|
||||
script_dir="$(dirname "$0")"
|
||||
toplvl_dir="$(realpath "$script_dir/../../")"
|
||||
bin_dir="$toplvl_dir/Bin/AppImage" # RMG should be installed here
|
||||
|
||||
export QMAKE="$(which qmake6)"
|
||||
export EXTRA_QT_PLUGINS="imageformats;iconengines;"
|
||||
export VERSION="$(git describe --tags --always)"
|
||||
export OUTPUT="$bin_dir/../RMG-Portable-Linux64-$VERSION.AppImage"
|
||||
|
||||
if [ ! -f "$script_dir/linuxdeploy-x86_64.AppImage" ]
|
||||
then
|
||||
curl -L https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage \
|
||||
-o "$script_dir/linuxdeploy-x86_64.AppImage"
|
||||
chmod +x "$script_dir/linuxdeploy-x86_64.AppImage"
|
||||
fi
|
||||
|
||||
if [ ! -f "$script_dir/linuxdeploy-plugin-qt-x86_64.AppImage" ]
|
||||
then
|
||||
curl -L https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage \
|
||||
-o "$script_dir/linuxdeploy-plugin-qt-x86_64.AppImage"
|
||||
chmod +x "$script_dir/linuxdeploy-plugin-qt-x86_64.AppImage"
|
||||
fi
|
||||
|
||||
"$script_dir/linuxdeploy-plugin-qt-x86_64.AppImage" --appimage-extract
|
||||
"$script_dir/linuxdeploy-x86_64.AppImage" --appimage-extract
|
||||
|
||||
# delete appimages
|
||||
rm "$script_dir/linuxdeploy-x86_64.AppImage" \
|
||||
"$script_dir/linuxdeploy-plugin-qt-x86_64.AppImage"
|
||||
|
||||
"$(pwd)/squashfs-root/AppRun" \
|
||||
--plugin=qt \
|
||||
--appdir="$bin_dir" \
|
||||
--custom-apprun="$script_dir/AppRun" \
|
||||
--output=appimage \
|
||||
--desktop-file="$bin_dir/usr/share/applications/com.github.Rosalie241.RMG.desktop"
|
||||
@@ -1,46 +1,35 @@
|
||||
# Maintainer: Rosalie Wanders <rosalie@mailbox.org>
|
||||
_pkgname=rmg
|
||||
pkgname=${_pkgname}-git
|
||||
pkgver=git
|
||||
pkgname=rmg
|
||||
pkgver=0.3.8
|
||||
pkgrel=1
|
||||
pkgdesc="Rosalie's Mupen GUI"
|
||||
arch=('x86_64')
|
||||
url="https://github.com/Rosalie241/${_pkgname}"
|
||||
license=('GPLv3')
|
||||
|
||||
provides=("$_pkgname")
|
||||
conflicts=("$_pkgname")
|
||||
url="https://github.com/Rosalie241/$pkgname"
|
||||
license=('GPL3')
|
||||
|
||||
depends=("hidapi" "libsamplerate" "minizip" "sdl2" "zlib" "freetype2" "qt6-base" "qt6-svg" "xdg-user-dirs")
|
||||
makedepends=("git" "pkg-config" "nasm" "cmake" "cargo" "ninja")
|
||||
makedepends=("git" "nasm" "cmake" "cargo" "ninja")
|
||||
|
||||
source=("git+https://github.com/Rosalie241/${_pkgname}.git")
|
||||
source=("git+https://github.com/Rosalie241/$pkgname.git#tag=v$pkgver")
|
||||
sha256sums=('SKIP')
|
||||
|
||||
pkgver()
|
||||
{
|
||||
cd "$_pkgname"
|
||||
|
||||
git describe --tags --always | sed -e 's|^v||;s|-|.|g'
|
||||
}
|
||||
|
||||
prepare()
|
||||
{
|
||||
mkdir -p "$srcdir/${_pkgname}/build"
|
||||
mkdir -p "$srcdir/$pkgname/build"
|
||||
}
|
||||
|
||||
build()
|
||||
{
|
||||
cmake -S "$srcdir/${_pkgname}" -B "$srcdir/${_pkgname}/build" \
|
||||
cmake -S "$srcdir/$pkgname" -B "$srcdir/$pkgname/build" \
|
||||
-DCMAKE_BUILD_TYPE="Release" \
|
||||
-DPORTABLE_INSTALL="OFF" \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-G "Ninja" \
|
||||
-G "Ninja"
|
||||
|
||||
cmake --build "$srcdir/${_pkgname}/build"
|
||||
cmake --build "$srcdir/$pkgname/build"
|
||||
}
|
||||
|
||||
package()
|
||||
{
|
||||
cmake --install "$srcdir/${_pkgname}/build" --prefix="$pkgdir/usr"
|
||||
cmake --install "$srcdir/$pkgname/build" --prefix="$pkgdir/usr"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 445 KiB After Width: | Height: | Size: 318 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 274 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 57 KiB |
@@ -31,8 +31,110 @@
|
||||
<image type="source">https://github.com/Rosalie241/RMG/raw/master/Package/Screenshots/InputSettings.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<description/>
|
||||
<releases>
|
||||
<release version="v0.3.8" date="2023-02-24" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix RMG not having the correct video size in some cases (i.e on a WM where resizing is not allowed to be done by an application)</li>
|
||||
<li>Fix translations not working in GLideN64</li>
|
||||
<li>Add -n/--nogui commandline option</li>
|
||||
<li>Add -d/--debug-messages commandline option</li>
|
||||
<li>Add hotkeys for graphics, audio, RSP and input settings</li>
|
||||
<li>Add hotkey for viewing the log</li>
|
||||
<li>Add SIGINT and SIGTERM handling on linux</li>
|
||||
<li>Add "Check For Updates" to "View" in the toolbar</li>
|
||||
<li>Add information about LLE plugins in the settings dialog</li>
|
||||
<li>Add support for toggling visibility of columns in the ROM browser list view (thank you RetraCarteR)</li>
|
||||
<li>Add date and time to the save state slot items in "Current Save State" menu in the menubar</li>
|
||||
<li>Add date and time to the OSD when selecting a save state slot</li>
|
||||
<li>Add failure messages to the OSD when loading/saving a save state fails</li>
|
||||
<li>Improve updater behavior by checking for an update on startup with at least an hour in-between checks</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.7" date="2023-02-19" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix icons not showing up in some cases when migrating from a version before v0.3.6</li>
|
||||
<li>Fix plugin settings not being loaded properly in the settings dialog when migrating from a version before v0.3.6</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.6" date="2023-02-19" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix RMG not using xwayland when using wayland (wayland has lots of issues, can be disabled by building with the "-DFORCE_XCB=OFF" option)</li>
|
||||
<li>Fix some non-fatal mupen64plus errors showing a pop-up</li>
|
||||
<li>Fix window not returning to ROM browser when launching a game through the commandline and the plugins aren't ready</li>
|
||||
<li>Add support for Qt stylesheets (place them in the 'user data directory/Styles' directory with the '.qss' extension, they'll show up in "Themes")</li>
|
||||
<li>Add "Icon Theme" setting</li>
|
||||
<li>Add "Speed Factor" to System menu bar</li>
|
||||
<li>Add support for re-ordering columns in the list view of the ROM browser (thank you RetraCarteR)</li>
|
||||
<li>Add AppImage to github releases (this replaces the old portable linux builds on github releases)</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.5" date="2023-02-14" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix reset button not working properly in RMG-Input</li>
|
||||
<li>Fix profiles not being deleted properly when clicking on cancel/close in some cases in RMG-Input</li>
|
||||
<li>Fix paraLLEl not logging to the log window</li>
|
||||
<li>Fix paraLLEl's odd window behavior by not saving the window size when emulation ends</li>
|
||||
<li>Fix GLideN64 initializing a noise texture being slow</li>
|
||||
<li>Fix issue where an unbound mapping in RMG-Input would respond to an A controller button press in-game</li>
|
||||
<li>Fix issue where RMG-Input wouldn't save anything but the currently opened profile</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.4" date="2023-02-11" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix mapping button size increasing when reloading settings in RMG-Input</li>
|
||||
<li>Fix analog stick mapping in-game when having buttons and axis bound in RMG-Input</li>
|
||||
<li>Fix height mismatch of "Profile" and "Input Device" UI elements in RMG-Input</li>
|
||||
<li>Fix updater script not using the correct path seperators on Windows</li>
|
||||
<li>Fix GLideN64's per game settings being overwritten by the updater on windows (or not being updated on linux)</li>
|
||||
<li>Fix "Automatic" input device not opening controller device when clicking on 'Refresh' in RMG-Input</li>
|
||||
<li>Fix game specific profile having unexpected UI behavior in RMG-Input</li>
|
||||
<li>Fix cancel and close button not working correctly in RMG-Input</li>
|
||||
<li>Fix profile saving not working correctly when having the same profile selected for multiple players in RMG-Input</li>
|
||||
<li>Add "Filter Events Based On Joystick Type For Buttons" setting to RMG-Input</li>
|
||||
<li>Add "Filter Events Based On Joystick Type For Axis" setting to RMG-Input</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.3" date="2023-02-09" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Remove "Controller Is Plugged In" checkbox in RMG-Input</li>
|
||||
<li>Remove text from 'Remove' button in RMG-Input</li>
|
||||
<li>Add "None" input device to RMG-Input (replacing the "Controller Is Plugged In" checkbox)</li>
|
||||
<li>Add "Automatic" input device to RMG-Input</li>
|
||||
<li>Add support for creating profiles in RMG-Input</li>
|
||||
<li>Add icons to 'Remove', 'Refresh', 'Reset' and 'Options' buttons in RMG-Input</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.2" date="2023-02-06" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Fix ndd/d64 files in zip files not working when the disk file is inside a subdirectory inside the zip file</li>
|
||||
<li>Fix ndd/d64 files in zip files not working correctly on windows</li>
|
||||
<li>Fix controller image in RMG-Input sometimes being stuck when having a trigger bound to a button</li>
|
||||
<li>Fix trigger mapping in RMG-Input</li>
|
||||
<li>Remove "Invert Axis Mapped To LRZ" setting in RMG-Input</li>
|
||||
<li>Add "Sort Results After Search" ROM Browser setting</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="v0.3.1" date="2023-02-04" type="stable">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
|
||||
@@ -4,6 +4,17 @@ Rosalie's Mupen GUI is a free and open-source mupen64plus GUI written in C++.
|
||||
|
||||
It offers a simple-to-use user interface.
|
||||
|
||||
## Download
|
||||
|
||||
#### Windows
|
||||
You can download Rosalie's Mupen GUI on 
|
||||
|
||||
#### Linux
|
||||
You can download Rosalie's Mupen GUI on
|
||||
* [Github Releases (AppImage)](https://github.com/Rosalie241/RMG/releases)
|
||||
* [Flathub (Flatpak)](https://flathub.org/apps/details/com.github.Rosalie241.RMG)
|
||||
* [Arch User Repository](https://aur.archlinux.org/packages/rmg)
|
||||
|
||||
## Support
|
||||
|
||||
Join the [Rosalie's Mupen GUI Discord Server](https://discord.gg/k9GuyJ2PpF) for support
|
||||
|
||||
@@ -36,38 +36,41 @@ else(USE_CCACHE)
|
||||
endif(USE_CCACHE)
|
||||
|
||||
if(NOT NO_GIT_CLONE)
|
||||
set(MUPEN64PLUSCORE_URL "https://github.com/Rosalie241/mupen64plus-core")
|
||||
set(MUPEN64PLUSCORE_TAG "fc8edd980a844d11fde64c719591090d09185264")
|
||||
|
||||
set(MUPEN64PLUSCORE_URL "https://github.com/mupen64plus/mupen64plus-core")
|
||||
set(MUPEN64PLUSCORE_TAG "7ee312cb282a7794c62d5d284bf93151c585dd1f")
|
||||
|
||||
set(MUPEN64PLUS_RSP_CXD4_URL "https://github.com/mupen64plus/mupen64plus-rsp-cxd4")
|
||||
set(MUPEN64PLUS_RSP_CXD4_TAG "39f79201baa15890c4cbae92f2215a634cc3ee6d")
|
||||
|
||||
|
||||
set(MUPEN64PLUS_RSP_HLE_URL "https://github.com/mupen64plus/mupen64plus-rsp-hle")
|
||||
set(MUPEN64PLUS_RSP_HLE_TAG "88093cb43499eff53d343653cddcae2132af17ef")
|
||||
|
||||
|
||||
set(MUPEN64PLUS_RSP_PARALLEL_URL "https://github.com/Rosalie241/parallel-rsp")
|
||||
set(MUPEN64PLUS_RSP_PARALLEL_TAG "e67cee3131651c3e48343294d94fa68a6f8ec14c")
|
||||
|
||||
set(MUPEN64PLUS_RSP_PARALLEL_TAG "cfeea3a0f89ba5bc4265acb75778e1267ed1e424")
|
||||
|
||||
set(MUPEN64PLUS_INPUT_RAPHNET_URL "https://github.com/raphnet/mupen64plus-input-raphnetraw")
|
||||
set(MUPEN64PLUS_INPUT_RAPHNET_TAG "86112413e98a8648edb11d199673cc24d5799af8")
|
||||
|
||||
|
||||
set(MUPEN64PLUS_INPUT_GCA_URL "https://github.com/amatho/mupen64plus-input-gca")
|
||||
set(MUPEN64PLUS_INPUT_GCA_TAG "21639fb13dfa797a7c0949ffd9bbda9a3456fc69")
|
||||
|
||||
set(MUPEN64PLUS_GFX_ANGRYLION_URL "https://github.com/Rosalie241/angrylion-rdp-plus")
|
||||
set(MUPEN64PLUS_GFX_ANGRYLION_TAG "158e74d4ec1c818289cd19c3a05e498c3ceff758")
|
||||
|
||||
set(MUPEN64PLUS_GFX_GLIDEN64_URL "https://github.com/gonetz/GLideN64")
|
||||
set(MUPEN64PLUS_GFX_GLIDEN64_TAG "263a7bd2ca81759ece98209bd282a6c1173a129f")
|
||||
set(MUPEN64PLUS_GFX_GLIDEN64_URL "https://github.com/Rosalie241/GLideN64")
|
||||
set(MUPEN64PLUS_GFX_GLIDEN64_TAG "937e2a082c63b917654ff59cfc0b5125ad525dce")
|
||||
|
||||
set(MUPEN64PLUS_GFX_PARALLEL_URL "https://github.com/Rosalie241/parallel-rdp-standalone")
|
||||
set(MUPEN64PLUS_GFX_PARALLEL_TAG "48abaa5e082add7848e6f98648111c69c4a71632")
|
||||
set(MUPEN64PLUS_GFX_PARALLEL_TAG "2c2226517c4c8929e08ec944654867e26efe0cf5")
|
||||
|
||||
set(DISCORD_RPC_URL "https://github.com/discord/discord-rpc")
|
||||
set(DISCORD_RPC_TAG "963aa9f3e5ce81a4682c6ca3d136cddda614db33")
|
||||
|
||||
set(IMGUI_URL "https://github.com/ocornut/imgui")
|
||||
set(IMGUI_TAG "d7c8516a4b848c0291e3d75b627c0843f515f591")
|
||||
|
||||
set(SDL_GAMECONTROLLERDB_URL "https://github.com/gabomdq/SDL_GameControllerDB")
|
||||
set(SDL_GAMECONTROLLERDB_TAG "0562b00eaf5c0308c49d329b79263d2dae1c3a85")
|
||||
endif(NOT NO_GIT_CLONE)
|
||||
|
||||
ExternalProject_Add(mupen64plus-core
|
||||
@@ -286,6 +289,27 @@ ExternalProject_Add(imgui
|
||||
BUILD_BYPRODUCTS ${IMGUI_SOURCES_OUT}
|
||||
)
|
||||
|
||||
set(SDL_GAMECONTROLLERDB_DIR ${THIRDPARTY_DIR}/SDL_GameControllerDB)
|
||||
set(SDL_GAMECONTROLLERDB_BUILD_DIR ${SDL_GAMECONTROLLERDB_DIR}/build)
|
||||
set(SDL_GAMECONTROLLERDB_SOURCES_IN
|
||||
${SDL_GAMECONTROLLERDB_DIR}/gamecontrollerdb.txt
|
||||
)
|
||||
set(SDL_GAMECONTROLLERDB_SOURCES_OUT
|
||||
${SDL_GAMECONTROLLERDB_BUILD_DIR}/gamecontrollerdb.txt
|
||||
)
|
||||
ExternalProject_Add(SDL_GameControllerDB
|
||||
SOURCE_DIR SDL_GameControllerDB
|
||||
|
||||
GIT_REPOSITORY ${SDL_GAMECONTROLLERDB_URL}
|
||||
GIT_TAG ${SDL_GAMECONTROLLERDB_TAG}
|
||||
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E make_directory ${SDL_GAMECONTROLLERDB_BUILD_DIR}
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} -E copy ${SDL_GAMECONTROLLERDB_SOURCES_IN} ${SDL_GAMECONTROLLERDB_SOURCES_OUT}
|
||||
INSTALL_COMMAND ""
|
||||
|
||||
BUILD_BYPRODUCTS ${SDL_GAMECONTROLLERDB_SOURCES_OUT}
|
||||
)
|
||||
|
||||
ExternalProject_Get_property(mupen64plus-core BUILD_BYPRODUCTS)
|
||||
set(MUPEN64PLUSCORE_LIB ${BUILD_BYPRODUCTS} PARENT_SCOPE)
|
||||
set(MUPEN64PLUSCORE_INI ${M64P_CORE_DIR}/data/mupen64plus.ini PARENT_SCOPE)
|
||||
@@ -324,3 +348,6 @@ endif(DISCORD_RPC)
|
||||
ExternalProject_Get_property(imgui BUILD_BYPRODUCTS)
|
||||
set(IMGUI_SOURCES ${BUILD_BYPRODUCTS} PARENT_SCOPE)
|
||||
set(IMGUI_DIR ${THIRDPARTY_DIR}/imgui PARENT_SCOPE)
|
||||
|
||||
ExternalProject_Get_property(SDL_GameControllerDB BUILD_BYPRODUCTS)
|
||||
set(SDL_GAMECONTROLLERDB ${BUILD_BYPRODUCTS} PARENT_SCOPE)
|
||||
|
||||
@@ -24,6 +24,7 @@ set(RMG_CORE_SOURCES
|
||||
ConvertStringEncoding.cpp
|
||||
Settings/Settings.cpp
|
||||
SpeedLimiter.cpp
|
||||
SpeedFactor.cpp
|
||||
RomSettings.cpp
|
||||
Directories.cpp
|
||||
MediaLoader.cpp
|
||||
|
||||
@@ -11,12 +11,16 @@
|
||||
#include "Core.hpp"
|
||||
#include "ConvertStringEncoding.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
//
|
||||
// Local Variables
|
||||
//
|
||||
|
||||
static bool l_SetupCallbacks = false;
|
||||
static std::function<void(enum CoreDebugMessageType, std::string, std::string)> l_DebugCallbackFunc;
|
||||
static std::function<void(enum CoreStateCallbackType, int)> l_StateCallbackFunc;
|
||||
static bool l_PrintCallbacks = false;
|
||||
|
||||
//
|
||||
// Internal Functions
|
||||
@@ -42,21 +46,38 @@ void CoreDebugCallback(void* context, int level, const char* message)
|
||||
messageString = CoreConvertStringEncoding(message, CoreStringEncoding::Shift_JIS);
|
||||
}
|
||||
|
||||
if (l_PrintCallbacks)
|
||||
{
|
||||
std::cout << contextString << messageString << std::endl;
|
||||
}
|
||||
|
||||
l_DebugCallbackFunc((CoreDebugMessageType)level, contextString, messageString);
|
||||
}
|
||||
|
||||
void CoreStateCallback(void* context, m64p_core_param param, int value)
|
||||
{
|
||||
// TODO
|
||||
if (!l_SetupCallbacks)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
l_StateCallbackFunc((CoreStateCallbackType)param, value);
|
||||
}
|
||||
|
||||
//
|
||||
// Exported Functions
|
||||
//
|
||||
|
||||
bool CoreSetupCallbacks(std::function<void(enum CoreDebugMessageType, std::string, std::string)> debugCallbackFunc)
|
||||
bool CoreSetupCallbacks(std::function<void(enum CoreDebugMessageType, std::string, std::string)> debugCallbackFunc,
|
||||
std::function<void(enum CoreStateCallbackType, int)> stateCallbackFunc)
|
||||
{
|
||||
l_DebugCallbackFunc = debugCallbackFunc;
|
||||
l_StateCallbackFunc = stateCallbackFunc;
|
||||
l_SetupCallbacks = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CoreSetPrintDebugCallback(bool enabled)
|
||||
{
|
||||
l_PrintCallbacks = enabled;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,26 @@ enum class CoreDebugMessageType
|
||||
Verbose = 5
|
||||
};
|
||||
|
||||
bool CoreSetupCallbacks(std::function<void(enum CoreDebugMessageType, std::string, std::string)> debugCallbackFunc);
|
||||
enum class CoreStateCallbackType
|
||||
{
|
||||
EmulationState = 1,
|
||||
VideoMode,
|
||||
SaveStateSlot,
|
||||
SpeedFactor,
|
||||
SpeedLimiter,
|
||||
VideoSize,
|
||||
AudioVolume,
|
||||
AudioMute,
|
||||
GamesharkInput,
|
||||
SaveStateLoaded,
|
||||
SaveStateSaved,
|
||||
};
|
||||
|
||||
// attempts to setup callbacks with the provided functions
|
||||
bool CoreSetupCallbacks(std::function<void(enum CoreDebugMessageType, std::string, std::string)> debugCallbackFunc,
|
||||
std::function<void(enum CoreStateCallbackType, int)> stateCallbackFunc);
|
||||
|
||||
// sets whether the debug callbacks will be printed to stdout
|
||||
void CoreSetPrintDebugCallback(bool enabled);
|
||||
|
||||
#endif // CORE_CALLBACK_HPP
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "CachedRomHeaderAndSettings.hpp"
|
||||
#include "Settings/Settings.hpp"
|
||||
#include "SpeedLimiter.hpp"
|
||||
#include "SpeedFactor.hpp"
|
||||
#include "RomSettings.hpp"
|
||||
#include "Directories.hpp"
|
||||
#include "MediaLoader.hpp"
|
||||
|
||||
@@ -18,6 +18,14 @@
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
|
||||
//
|
||||
// Local Variables
|
||||
//
|
||||
|
||||
static std::filesystem::path l_CorePathOverride;
|
||||
static std::filesystem::path l_PluginPathOverride;
|
||||
static std::filesystem::path l_SharedDataPathOverride;
|
||||
|
||||
//
|
||||
// Local Functions
|
||||
//
|
||||
@@ -123,8 +131,15 @@ std::filesystem::path CoreGetCoreDirectory(void)
|
||||
#ifdef PORTABLE_INSTALL
|
||||
directory = "Core";
|
||||
#else // Not Portable
|
||||
directory = CORE_INSTALL_PREFIX;
|
||||
directory += "/lib/RMG/Core";
|
||||
if (!l_CorePathOverride.empty())
|
||||
{
|
||||
directory = l_CorePathOverride;
|
||||
}
|
||||
else
|
||||
{
|
||||
directory = CORE_INSTALL_PREFIX;
|
||||
directory += "/lib/RMG/Core";
|
||||
}
|
||||
#endif // PORTABLE_INSTALL
|
||||
return directory;
|
||||
}
|
||||
@@ -135,8 +150,15 @@ std::filesystem::path CoreGetPluginDirectory(void)
|
||||
#ifdef PORTABLE_INSTALL
|
||||
directory = "Plugin";
|
||||
#else // Not Portable
|
||||
directory = CORE_INSTALL_PREFIX;
|
||||
directory += "/lib/RMG/Plugin";
|
||||
if (!l_PluginPathOverride.empty())
|
||||
{
|
||||
directory = l_PluginPathOverride;
|
||||
}
|
||||
else
|
||||
{
|
||||
directory = CORE_INSTALL_PREFIX;
|
||||
directory += "/lib/RMG/Plugin";
|
||||
}
|
||||
#endif // PORTABLE_INSTALL
|
||||
return directory;
|
||||
}
|
||||
@@ -233,8 +255,15 @@ std::filesystem::path CoreGetSharedDataDirectory(void)
|
||||
#ifdef PORTABLE_INSTALL
|
||||
directory = "Data";
|
||||
#else // Not Portable
|
||||
directory = CORE_INSTALL_PREFIX;
|
||||
directory += "/share/RMG";
|
||||
if (!l_SharedDataPathOverride.empty())
|
||||
{
|
||||
directory = l_SharedDataPathOverride;
|
||||
}
|
||||
else
|
||||
{
|
||||
directory = CORE_INSTALL_PREFIX;
|
||||
directory += "/share/RMG";
|
||||
}
|
||||
#endif // PORTABLE_INSTALL
|
||||
return directory;
|
||||
}
|
||||
@@ -253,3 +282,20 @@ std::filesystem::path CoreGetScreenshotDirectory(void)
|
||||
{
|
||||
return CoreSettingsGetStringValue(SettingsID::Core_ScreenshotPath);
|
||||
}
|
||||
|
||||
#ifndef PORTABLE_INSTALL
|
||||
void CoreSetCorePathOverride(std::filesystem::path path)
|
||||
{
|
||||
l_CorePathOverride = path;
|
||||
}
|
||||
|
||||
void CoreSetPluginPathOverride(std::filesystem::path path)
|
||||
{
|
||||
l_PluginPathOverride = path;
|
||||
}
|
||||
|
||||
void CoreSetSharedDataPathOverride(std::filesystem::path path)
|
||||
{
|
||||
l_SharedDataPathOverride = path;
|
||||
}
|
||||
#endif // PORTABLE_INSTALL
|
||||
|
||||
@@ -73,4 +73,13 @@ std::filesystem::path CoreGetSaveStateDirectory(void);
|
||||
// screenshots should be located
|
||||
std::filesystem::path CoreGetScreenshotDirectory(void);
|
||||
|
||||
// changes the core path to the given path
|
||||
void CoreSetCorePathOverride(std::filesystem::path path);
|
||||
|
||||
// changes the plugin path to the given path
|
||||
void CoreSetPluginPathOverride(std::filesystem::path path);
|
||||
|
||||
// changes the shared data path to the given path
|
||||
void CoreSetSharedDataPathOverride(std::filesystem::path path);
|
||||
|
||||
#endif // DIRECTORIES_HPP
|
||||
|
||||
@@ -90,7 +90,8 @@ static void apply_game_coresettings_overlay(void)
|
||||
bool CoreStartEmulation(std::filesystem::path n64rom, std::filesystem::path n64ddrom)
|
||||
{
|
||||
std::string error;
|
||||
m64p_error ret;
|
||||
m64p_error ret;
|
||||
CoreRomType type;
|
||||
|
||||
if (!CoreOpenRom(n64rom))
|
||||
{
|
||||
@@ -126,8 +127,17 @@ bool CoreStartEmulation(std::filesystem::path n64rom, std::filesystem::path n64d
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CoreGetRomType(type))
|
||||
{
|
||||
CoreClearCheats();
|
||||
CoreDetachPlugins();
|
||||
CoreApplyPluginSettings();
|
||||
CoreCloseRom();
|
||||
return false;
|
||||
}
|
||||
|
||||
// set disk file in media loader when ROM is a cartridge
|
||||
if (CoreGetRomType() == CoreRomType::Cartridge)
|
||||
if (type == CoreRomType::Cartridge)
|
||||
{
|
||||
CoreMediaLoaderSetDiskFile(n64ddrom);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "m64p/PluginApi.hpp"
|
||||
#include "osal/osal_dynlib.hpp"
|
||||
#include "osal/osal_files.hpp"
|
||||
|
||||
#include "m64p/PluginApi.cpp"
|
||||
#include "m64p/Api.hpp"
|
||||
@@ -131,6 +132,54 @@ std::string get_plugin_context_name(CorePluginType type)
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string get_plugin_path(CorePluginType type, std::string settingsValue)
|
||||
{
|
||||
std::string pluginPath;
|
||||
std::string path;
|
||||
std::string typeName;
|
||||
|
||||
// return an empty string when the value is empty
|
||||
if (settingsValue.empty())
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
pluginPath = CoreGetPluginDirectory().string();
|
||||
|
||||
// if the full plugin path is in the settings value,
|
||||
// we know it's the old type
|
||||
if (settingsValue.find(pluginPath) != std::string::npos)
|
||||
{
|
||||
return settingsValue;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CorePluginType::Rsp:
|
||||
typeName = "RSP";
|
||||
break;
|
||||
case CorePluginType::Gfx:
|
||||
typeName = "GFX";
|
||||
break;
|
||||
case CorePluginType::Audio:
|
||||
typeName = "Audio";
|
||||
break;
|
||||
case CorePluginType::Input:
|
||||
typeName = "Input";
|
||||
break;
|
||||
default:
|
||||
return path;
|
||||
}
|
||||
|
||||
path = pluginPath;
|
||||
path += OSAL_FILES_DIR_SEPERATOR_STR;
|
||||
path += typeName;
|
||||
path += OSAL_FILES_DIR_SEPERATOR_STR;
|
||||
path += settingsValue;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
bool apply_plugin_settings(std::string pluginSettings[4])
|
||||
{
|
||||
std::string error;
|
||||
@@ -142,15 +191,14 @@ bool apply_plugin_settings(std::string pluginSettings[4])
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
settingValue = pluginSettings[i];
|
||||
pluginType = (CorePluginType)(i + 1);
|
||||
settingValue = get_plugin_path(pluginType, pluginSettings[i]);
|
||||
if (settingValue.empty() ||
|
||||
!std::filesystem::is_regular_file(settingValue))
|
||||
{ // skip invalid setting value
|
||||
continue;
|
||||
}
|
||||
|
||||
pluginType = (CorePluginType)(i + 1);
|
||||
|
||||
// copy context string to a c string using strcpy
|
||||
std::strcpy(l_PluginContext[(int)pluginType], get_plugin_context_name(pluginType).c_str());
|
||||
|
||||
@@ -252,6 +300,7 @@ std::vector<CorePlugin> CoreGetAllPlugins(void)
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(CoreGetPluginDirectory()))
|
||||
{
|
||||
std::string path = entry.path().string();
|
||||
std::string file = entry.path().filename().string();
|
||||
if (!entry.is_directory() &&
|
||||
path.ends_with(OSAL_DYNLIB_LIB_EXT_STR))
|
||||
{
|
||||
@@ -272,7 +321,7 @@ std::vector<CorePlugin> CoreGetAllPlugins(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
CorePlugin corePlugin = {path, plugin_name, plugin_type};
|
||||
CorePlugin corePlugin = {file, plugin_name, plugin_type};
|
||||
plugins.emplace_back(corePlugin);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,10 +409,8 @@ bool CoreOpenRom(std::filesystem::path file)
|
||||
{
|
||||
std::filesystem::path disk_file;
|
||||
disk_file = CoreGetUserCacheDirectory();
|
||||
disk_file += OSAL_FILES_DIR_SEPERATOR_STR;
|
||||
disk_file += "extracted_disks";
|
||||
disk_file += OSAL_FILES_DIR_SEPERATOR_STR;
|
||||
disk_file += extracted_file;
|
||||
disk_file += "/extracted_disks/";
|
||||
disk_file += extracted_file.filename();
|
||||
|
||||
// attempt to create extraction directory
|
||||
try
|
||||
@@ -503,9 +501,20 @@ bool CoreHasRomOpen(void)
|
||||
return l_HasRomOpen;
|
||||
}
|
||||
|
||||
CoreRomType CoreGetRomType(void)
|
||||
bool CoreGetRomType(CoreRomType& type)
|
||||
{
|
||||
return l_HasDisk ? CoreRomType::Disk : CoreRomType::Cartridge;
|
||||
std::string error;
|
||||
|
||||
if (!l_HasRomOpen)
|
||||
{
|
||||
error = "CoreGetRomType Failed: ";
|
||||
error += "cannot retrieve ROM type when no ROM has been opened!";
|
||||
CoreSetError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
type = l_HasDisk ? CoreRomType::Disk : CoreRomType::Cartridge;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreCloseRom(void)
|
||||
|
||||
@@ -24,8 +24,8 @@ bool CoreOpenRom(std::filesystem::path file);
|
||||
// returns whether core has a ROM opened
|
||||
bool CoreHasRomOpen(void);
|
||||
|
||||
// returns the opened ROM's type
|
||||
CoreRomType CoreGetRomType(void);
|
||||
// attempts to retrieve the ROM type
|
||||
bool CoreGetRomType(CoreRomType& type);
|
||||
|
||||
// closes ROM
|
||||
bool CoreCloseRom(void);
|
||||
|
||||
@@ -8,10 +8,32 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "SaveState.hpp"
|
||||
#include "Directories.hpp"
|
||||
#include "RomSettings.hpp"
|
||||
#include "RomHeader.hpp"
|
||||
#include "Error.hpp"
|
||||
|
||||
#include "m64p/Api.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
//
|
||||
// Local Functions
|
||||
//
|
||||
|
||||
// replaces all occurences of any chars in replace with c inside str
|
||||
static void str_replace_chars(std::string& str, const std::string replace, const char c)
|
||||
{
|
||||
for (int i = 0; i < str.size(); i++)
|
||||
{
|
||||
char str_char = str.at(i);
|
||||
if (replace.find(str_char) != std::string::npos)
|
||||
{
|
||||
str.at(i) = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Exported Functions
|
||||
//
|
||||
@@ -59,6 +81,83 @@ int CoreGetSaveStateSlot(void)
|
||||
return slot;
|
||||
}
|
||||
|
||||
bool CoreGetSaveStatePath(int slot, std::filesystem::path& path)
|
||||
{
|
||||
// TODO: this should probably be an API function
|
||||
// in mupen64plus-core instead
|
||||
|
||||
std::filesystem::path saveStatePath;
|
||||
std::filesystem::path oldSaveStatePath;
|
||||
std::string saveStateFileName;
|
||||
std::filesystem::path saveStateExtension;
|
||||
CoreRomHeader romHeader;
|
||||
CoreRomSettings romSettings;
|
||||
|
||||
// attempt to retrieve the current
|
||||
// rom header and settings
|
||||
if (!CoreGetCurrentRomHeader(romHeader) ||
|
||||
!CoreGetCurrentRomSettings(romSettings))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// retrieve save state directory
|
||||
saveStatePath = CoreGetSaveStateDirectory();
|
||||
saveStatePath += "/";
|
||||
|
||||
// construct extension
|
||||
saveStateExtension = ".st";
|
||||
saveStateExtension += std::to_string(slot);
|
||||
|
||||
// construct old save state path
|
||||
oldSaveStatePath = saveStatePath;
|
||||
oldSaveStatePath += romSettings.GoodName;
|
||||
oldSaveStatePath += saveStateExtension;
|
||||
|
||||
// use old filename if it exists
|
||||
if (std::filesystem::is_regular_file(oldSaveStatePath))
|
||||
{
|
||||
path = oldSaveStatePath;
|
||||
return true;
|
||||
}
|
||||
|
||||
// else use new filename
|
||||
if (romSettings.GoodName.find("(unknown rom)") == std::string::npos)
|
||||
{
|
||||
if (romSettings.GoodName.size() < 32)
|
||||
{
|
||||
saveStatePath += romSettings.GoodName;
|
||||
}
|
||||
else
|
||||
{
|
||||
saveStatePath += romSettings.GoodName.substr(0, 32);
|
||||
}
|
||||
}
|
||||
else if (!romHeader.Name.empty())
|
||||
{
|
||||
saveStatePath += romHeader.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
saveStatePath += "unknown";
|
||||
}
|
||||
saveStatePath += "-";
|
||||
saveStatePath += romSettings.MD5.substr(0, 8);
|
||||
saveStatePath += saveStateExtension;
|
||||
|
||||
// retrieve filename from path
|
||||
saveStateFileName = saveStatePath.filename().string();
|
||||
|
||||
// sanitize filename
|
||||
str_replace_chars(saveStateFileName, ":<>\"/\\|?*", '_');
|
||||
|
||||
// replace filename in path
|
||||
saveStatePath.replace_filename(saveStateFileName);
|
||||
|
||||
path = saveStatePath;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreSaveState(void)
|
||||
{
|
||||
std::string error;
|
||||
|
||||
@@ -19,6 +19,10 @@ bool CoreSetSaveStateSlot(int slot);
|
||||
// returns -1 on error
|
||||
int CoreGetSaveStateSlot(void);
|
||||
|
||||
// retrieves the file path for
|
||||
// the save state in the given slot
|
||||
bool CoreGetSaveStatePath(int slot, std::filesystem::path& path);
|
||||
|
||||
// saves state
|
||||
bool CoreSaveState(void);
|
||||
|
||||
|
||||
@@ -122,6 +122,7 @@ static std::vector<std::string> l_keyList;
|
||||
#define SETTING_SECTION_GB SETTING_SECTION_CORE " Gameboy"
|
||||
#define SETTING_SECTION_M64P "Core"
|
||||
#define SETTING_SECTION_AUDIO SETTING_SECTION_GUI " - Audio Plugin"
|
||||
#define SETTING_SECTION_INPUT SETTING_SECTION_GUI " - Input Plugin"
|
||||
|
||||
// retrieves l_Setting from settingId
|
||||
static l_Setting get_setting(SettingsID settingId)
|
||||
@@ -188,9 +189,15 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::GUI_Theme:
|
||||
setting = {SETTING_SECTION_GUI, "Theme", "Native"};
|
||||
break;
|
||||
case SettingsID::GUI_IconTheme:
|
||||
setting = {SETTING_SECTION_GUI, "IconTheme", "Automatic"};
|
||||
break;
|
||||
case SettingsID::GUI_CheckForUpdates:
|
||||
setting = {SETTING_SECTION_GUI, "CheckForUpdates", true};
|
||||
break;
|
||||
case SettingsID::GUI_LastUpdateCheck:
|
||||
setting = {SETTING_SECTION_GUI, "LastUpdateCheck", ""};
|
||||
break;
|
||||
case SettingsID::GUI_DiscordRpc:
|
||||
setting = {SETTING_SECTION_GUI, "DiscordRpc", true};
|
||||
break;
|
||||
@@ -201,36 +208,36 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::Core_GFX_Plugin:
|
||||
setting = {SETTING_SECTION_CORE, "GFX_Plugin",
|
||||
#ifdef _WIN32
|
||||
CoreGetPluginDirectory().string() + "\\GFX\\mupen64plus-video-GLideN64.dll",
|
||||
"mupen64plus-video-GLideN64.dll",
|
||||
#else
|
||||
CoreGetPluginDirectory().string() + "/GFX/mupen64plus-video-GLideN64.so",
|
||||
"mupen64plus-video-GLideN64.so",
|
||||
#endif // _WIN32
|
||||
};
|
||||
break;
|
||||
case SettingsID::Core_AUDIO_Plugin:
|
||||
setting = {SETTING_SECTION_CORE, "AUDIO_Plugin",
|
||||
#ifdef _WIN32
|
||||
CoreGetPluginDirectory().string() + "\\Audio\\RMG-Audio.dll",
|
||||
"RMG-Audio.dll",
|
||||
#else
|
||||
CoreGetPluginDirectory().string() + "/Audio/RMG-Audio.so",
|
||||
"RMG-Audio.so",
|
||||
#endif // _WIN32
|
||||
};
|
||||
break;
|
||||
case SettingsID::Core_INPUT_Plugin:
|
||||
setting = {SETTING_SECTION_CORE, "INPUT_Plugin",
|
||||
#ifdef _WIN32
|
||||
CoreGetPluginDirectory().string() + "\\Input\\RMG-Input.dll",
|
||||
"RMG-Input.dll",
|
||||
#else
|
||||
CoreGetPluginDirectory().string() + "/Input/RMG-Input.so",
|
||||
"RMG-Input.so",
|
||||
#endif // _WIN32
|
||||
};
|
||||
break;
|
||||
case SettingsID::Core_RSP_Plugin:
|
||||
setting = {SETTING_SECTION_CORE, "RSP_Plugin",
|
||||
#ifdef _WIN32
|
||||
CoreGetPluginDirectory().string() + "\\RSP\\mupen64plus-rsp-hle.dll",
|
||||
"mupen64plus-rsp-hle.dll",
|
||||
#else
|
||||
CoreGetPluginDirectory().string() + "/RSP/mupen64plus-rsp-hle.so",
|
||||
"mupen64plus-rsp-hle.so",
|
||||
#endif // _WIN32
|
||||
};
|
||||
break;
|
||||
@@ -416,6 +423,42 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::KeyBinding_LimitFPS:
|
||||
setting = {SETTING_SECTION_KEYBIND, "LimitFPS", "F4"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor25:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor25", "Alt+0"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor50:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor50", "Alt+1"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor75:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor75", "Alt+2"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor100:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor100", "Alt+3"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor125:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor125", "Alt+4"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor150:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor150", "Alt+5"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor175:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor175", "Alt+6"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor200:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor200", "Alt+7"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor225:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor225", "Alt+8"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor250:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor250", "Alt+9"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor275:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor275", "Alt+["};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SpeedFactor300:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SpeedFactor300", "Alt+]"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_SaveState:
|
||||
setting = {SETTING_SECTION_KEYBIND, "SaveState", "F5"};
|
||||
break;
|
||||
@@ -467,6 +510,21 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::KeyBinding_Fullscreen:
|
||||
setting = {SETTING_SECTION_KEYBIND, "Fullscreen", "Alt+Return"};
|
||||
break;
|
||||
case SettingsID::Keybinding_ViewLog:
|
||||
setting = {SETTING_SECTION_KEYBIND, "ViewLog", "Ctrl+L"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_GraphicsSettings:
|
||||
setting = {SETTING_SECTION_KEYBIND, "GraphicsSettings", "Ctrl+G"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_AudioSettings:
|
||||
setting = {SETTING_SECTION_KEYBIND, "AudioSettings", "Ctrl+A"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_RspSettings:
|
||||
setting = {SETTING_SECTION_KEYBIND, "RspSettings", "Ctrl+R"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_InputSettings:
|
||||
setting = {SETTING_SECTION_KEYBIND, "InputSettings", "Ctrl+I"};
|
||||
break;
|
||||
case SettingsID::KeyBinding_Settings:
|
||||
setting = {SETTING_SECTION_KEYBIND, "Settings", "Ctrl+T"};
|
||||
break;
|
||||
@@ -486,12 +544,18 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::RomBrowser_MaxItems:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "MaxItems", 250};
|
||||
break;
|
||||
case SettingsID::RomBrowser_Columns:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "Columns", std::vector<int>({0, 1, 2})};
|
||||
case SettingsID::RomBrowser_ColumnVisibility:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "ColumnVisibility", std::vector<int>({1, 1, 1})};
|
||||
break;
|
||||
case SettingsID::RomBrowser_ColumnOrder:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "ColumnOrder", std::vector<int>({0, 1, 2})};
|
||||
break;
|
||||
case SettingsID::RomBrowser_ColumnSizes:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "ColumnSizes", std::vector<int>({-1, -1, -1})};
|
||||
break;
|
||||
case SettingsID::RomBrowser_SortAfterSearch:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "SortAfterSearch", true};
|
||||
break;
|
||||
case SettingsID::RomBrowser_ViewMode:
|
||||
setting = {SETTING_SECTION_ROMBROWSER, "ViewMode", 0};
|
||||
break;
|
||||
@@ -543,6 +607,15 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
setting = {SETTING_SECTION_AUDIO, "Synchronize", false};
|
||||
break;
|
||||
|
||||
case SettingsID::Input_Profiles:
|
||||
setting = {SETTING_SECTION_INPUT, "Profiles", ""};
|
||||
break;
|
||||
case SettingsID::Input_UseProfile:
|
||||
setting = {"", "UseProfile"};
|
||||
break;
|
||||
case SettingsID::Input_UseGameProfile:
|
||||
setting = {"", "UseGameProfile"};
|
||||
break;
|
||||
case SettingsID::Input_PluggedIn:
|
||||
setting = {"", "PluggedIn"};
|
||||
break;
|
||||
@@ -570,8 +643,11 @@ static l_Setting get_setting(SettingsID settingId)
|
||||
case SettingsID::Input_RemoveDuplicateMappings:
|
||||
setting = {"", "RemoveDuplicateMappings"};
|
||||
break;
|
||||
case SettingsID::Input_InvertAxis:
|
||||
setting = {"", "InvertAxis"};
|
||||
case SettingsID::Input_FilterEventsForButtons:
|
||||
setting = {"", "FilterEventsForButtons"};
|
||||
break;
|
||||
case SettingsID::Input_FilterEventsForAxis:
|
||||
setting = {"", "FilterEventsForAxis"};
|
||||
break;
|
||||
case SettingsID::Input_A_InputType:
|
||||
setting = {"", "A_InputType"};
|
||||
@@ -1220,6 +1296,27 @@ bool CoreSettingsSectionExists(std::string section)
|
||||
return config_section_exists(section);
|
||||
}
|
||||
|
||||
bool CoreSettingsRevertSection(std::string section)
|
||||
{
|
||||
std::string error;
|
||||
m64p_error ret;
|
||||
|
||||
if (!m64p::Config.IsHooked())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = m64p::Config.RevertChanges(section.c_str());
|
||||
if (ret != M64ERR_SUCCESS)
|
||||
{
|
||||
error = "CoreSettingsRevertSection m64p::Config.RevertChanges() Failed: ";
|
||||
error = m64p::Core.ErrorMessage(ret);
|
||||
CoreSetError(error);
|
||||
}
|
||||
|
||||
return ret == M64ERR_SUCCESS;
|
||||
}
|
||||
|
||||
bool CoreSettingsDeleteSection(std::string section)
|
||||
{
|
||||
std::string error;
|
||||
|
||||
@@ -27,6 +27,9 @@ bool CoreSettingsSetupDefaults(void);
|
||||
// returns whether a section exists
|
||||
bool CoreSettingsSectionExists(std::string section);
|
||||
|
||||
// reverts section to the one from the settings
|
||||
bool CoreSettingsRevertSection(std::string section);
|
||||
|
||||
// deletes given section
|
||||
bool CoreSettingsDeleteSection(std::string section);
|
||||
|
||||
|
||||
@@ -22,10 +22,12 @@ enum class SettingsID
|
||||
GUI_Toolbar,
|
||||
GUI_StatusBar,
|
||||
GUI_Theme,
|
||||
GUI_IconTheme,
|
||||
GUI_CheckForUpdates,
|
||||
GUI_LastUpdateCheck,
|
||||
GUI_DiscordRpc,
|
||||
GUI_Version,
|
||||
|
||||
|
||||
// Core Plugin Settings
|
||||
Core_GFX_Plugin,
|
||||
Core_AUDIO_Plugin,
|
||||
@@ -108,6 +110,18 @@ enum class SettingsID
|
||||
KeyBinding_Resume,
|
||||
KeyBinding_Screenshot,
|
||||
KeyBinding_LimitFPS,
|
||||
KeyBinding_SpeedFactor25,
|
||||
KeyBinding_SpeedFactor50,
|
||||
KeyBinding_SpeedFactor75,
|
||||
KeyBinding_SpeedFactor100,
|
||||
KeyBinding_SpeedFactor125,
|
||||
KeyBinding_SpeedFactor150,
|
||||
KeyBinding_SpeedFactor175,
|
||||
KeyBinding_SpeedFactor200,
|
||||
KeyBinding_SpeedFactor225,
|
||||
KeyBinding_SpeedFactor250,
|
||||
KeyBinding_SpeedFactor275,
|
||||
KeyBinding_SpeedFactor300,
|
||||
KeyBinding_SaveState,
|
||||
KeyBinding_SaveAs,
|
||||
KeyBinding_LoadState,
|
||||
@@ -125,6 +139,11 @@ enum class SettingsID
|
||||
KeyBinding_SaveStateSlot8,
|
||||
KeyBinding_SaveStateSlot9,
|
||||
KeyBinding_Fullscreen,
|
||||
Keybinding_ViewLog,
|
||||
KeyBinding_GraphicsSettings,
|
||||
KeyBinding_AudioSettings,
|
||||
KeyBinding_RspSettings,
|
||||
KeyBinding_InputSettings,
|
||||
KeyBinding_Settings,
|
||||
|
||||
// RomBrowser Settings
|
||||
@@ -133,8 +152,10 @@ enum class SettingsID
|
||||
RomBrowser_Maximized,
|
||||
RomBrowser_Recursive,
|
||||
RomBrowser_MaxItems,
|
||||
RomBrowser_Columns,
|
||||
RomBrowser_ColumnVisibility,
|
||||
RomBrowser_ColumnOrder,
|
||||
RomBrowser_ColumnSizes,
|
||||
RomBrowser_SortAfterSearch,
|
||||
RomBrowser_ViewMode,
|
||||
RomBrowser_ListViewSortSection,
|
||||
RomBrowser_ListViewSortOrder,
|
||||
@@ -157,6 +178,9 @@ enum class SettingsID
|
||||
Audio_Synchronize,
|
||||
|
||||
// Input Plugin Settings
|
||||
Input_Profiles,
|
||||
Input_UseProfile,
|
||||
Input_UseGameProfile,
|
||||
Input_PluggedIn,
|
||||
Input_DeviceType,
|
||||
Input_DeviceName,
|
||||
@@ -166,7 +190,8 @@ enum class SettingsID
|
||||
Input_GameboyRom,
|
||||
Input_GameboySave,
|
||||
Input_RemoveDuplicateMappings,
|
||||
Input_InvertAxis,
|
||||
Input_FilterEventsForButtons,
|
||||
Input_FilterEventsForAxis,
|
||||
Input_A_InputType,
|
||||
Input_A_Name,
|
||||
Input_A_Data,
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "SpeedFactor.hpp"
|
||||
#include "Error.hpp"
|
||||
|
||||
#include "m64p/Api.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
//
|
||||
// Exported Functions
|
||||
//
|
||||
|
||||
int CoreGetSpeedFactor(void)
|
||||
{
|
||||
std::string error;
|
||||
m64p_error ret;
|
||||
int value = 0;
|
||||
|
||||
if (!m64p::Core.IsHooked())
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
ret = m64p::Core.DoCommand(M64CMD_CORE_STATE_QUERY, M64CORE_SPEED_FACTOR, &value);
|
||||
if (ret != M64ERR_SUCCESS)
|
||||
{
|
||||
error = "CoreGetSpeedFactor: m64p::Core.DoCommand(M64CMD_CORE_STATE_QUERY) Failed: ";
|
||||
error += m64p::Core.ErrorMessage(ret);
|
||||
CoreSetError(error);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool CoreSetSpeedFactor(int factor)
|
||||
{
|
||||
std::string error;
|
||||
m64p_error ret;
|
||||
int value = factor;
|
||||
|
||||
if (!m64p::Core.IsHooked())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = m64p::Core.DoCommand(M64CMD_CORE_STATE_SET, M64CORE_SPEED_FACTOR, &value);
|
||||
if (ret != M64ERR_SUCCESS)
|
||||
{
|
||||
error = "CoreSetSpeedFactor: m64p::Core.DoCommand(M64CMD_CORE_STATE_SET) Failed: ";
|
||||
error += m64p::Core.ErrorMessage(ret);
|
||||
CoreSetError(error);
|
||||
}
|
||||
|
||||
return ret == M64ERR_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef CORE_SPEEDFACTOR_HPP
|
||||
#define CORE_SPEEDFACTOR_HPP
|
||||
|
||||
// returns the current speed factor
|
||||
int CoreGetSpeedFactor(void);
|
||||
|
||||
// sets the speed factor
|
||||
bool CoreSetSpeedFactor(int factor);
|
||||
|
||||
#endif // CORE_SPEEDFACTOR_HPP
|
||||
@@ -41,7 +41,7 @@ MainDialog::MainDialog(QWidget* parent, Thread::SDLThread* sdlThread) : QDialog(
|
||||
for (int i = 0; i < this->tabWidget->count(); i++)
|
||||
{
|
||||
Widget::ControllerWidget* widget = new Widget::ControllerWidget(this, this->eventFilter);
|
||||
widget->SetSettingsSection("Rosalie's Mupen GUI - Input Plugin Profile " + QString::number(i));
|
||||
widget->SetSettingsSection("Player " + QString::number(i + 1), "Rosalie's Mupen GUI - Input Plugin Profile " + QString::number(i));
|
||||
widget->LoadSettings();
|
||||
this->tabWidget->widget(i)->layout()->addWidget(widget);
|
||||
controllerWidgets.push_back(widget);
|
||||
@@ -49,12 +49,19 @@ MainDialog::MainDialog(QWidget* parent, Thread::SDLThread* sdlThread) : QDialog(
|
||||
&MainDialog::on_ControllerWidget_CurrentInputDeviceChanged);
|
||||
connect(widget, &Widget::ControllerWidget::RefreshInputDevicesButtonClicked, this,
|
||||
&MainDialog::on_ControllerWidget_RefreshInputDevicesButtonClicked);
|
||||
connect(widget, &Widget::ControllerWidget::UserProfileAdded, this,
|
||||
&MainDialog::on_ControllerWidget_UserProfileAdded);
|
||||
connect(widget, &Widget::ControllerWidget::UserProfileRemoved, this,
|
||||
&MainDialog::on_ControllerWidget_UserProfileRemoved);
|
||||
}
|
||||
|
||||
// always add keyboard device
|
||||
for (auto& controllerWidget : this->controllerWidgets)
|
||||
{
|
||||
controllerWidget->AddInputDevice("Keyboard", -1);
|
||||
controllerWidget->AddInputDevice("None", (int)InputDeviceType::None);
|
||||
controllerWidget->AddInputDevice("Automatic", (int)InputDeviceType::Automatic);
|
||||
controllerWidget->AddInputDevice("Keyboard", (int)InputDeviceType::Keyboard);
|
||||
controllerWidget->SetInitialized(true);
|
||||
}
|
||||
|
||||
// fill device list at least once
|
||||
@@ -90,30 +97,50 @@ void MainDialog::openInputDevice(QString deviceName, int deviceNum)
|
||||
{
|
||||
SDL_JoystickID joystickId;
|
||||
Widget::ControllerWidget* controllerWidget;
|
||||
controllerWidget = controllerWidgets.at(this->tabWidget->currentIndex());
|
||||
controllerWidget = this->controllerWidgets.at(this->tabWidget->currentIndex());
|
||||
|
||||
// we don't need to open a keyboard
|
||||
if (deviceNum == -1)
|
||||
if (deviceNum == (int)InputDeviceType::None ||
|
||||
deviceNum == (int)InputDeviceType::Keyboard)
|
||||
{
|
||||
currentDeviceName = "";
|
||||
currentDeviceNum = -1;
|
||||
controllerWidget->SetCurrentJoystickID(-1);
|
||||
this->currentDeviceName = "";
|
||||
this->currentDeviceNum = deviceNum;
|
||||
controllerWidget->SetCurrentJoystickID(this->currentDeviceNum);
|
||||
return;
|
||||
}
|
||||
|
||||
// handle automatic mode
|
||||
if (deviceNum == (int)InputDeviceType::Automatic)
|
||||
{
|
||||
int currentIndex = this->tabWidget->currentIndex();
|
||||
if (currentIndex < this->inputDeviceList.size())
|
||||
{ // use device when there's one
|
||||
deviceNum = this->inputDeviceList.at(currentIndex).deviceNum;
|
||||
}
|
||||
else
|
||||
{ // no device found, fallback to keyboard
|
||||
this->currentDeviceName = "";
|
||||
this->currentDeviceNum = (int)InputDeviceType::Keyboard;
|
||||
controllerWidget->SetCurrentJoystickID(this->currentDeviceNum);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL_IsGameController(deviceNum) == SDL_TRUE)
|
||||
{
|
||||
currentJoystick = nullptr;
|
||||
currentController = SDL_GameControllerOpen(deviceNum);
|
||||
this->currentJoystick = nullptr;
|
||||
this->currentController = SDL_GameControllerOpen(deviceNum);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentJoystick = SDL_JoystickOpen(deviceNum);
|
||||
currentController = nullptr;
|
||||
this->currentJoystick = SDL_JoystickOpen(deviceNum);
|
||||
this->currentController = nullptr;
|
||||
}
|
||||
currentDeviceNum = deviceNum;
|
||||
|
||||
this->currentDeviceNum = deviceNum;
|
||||
joystickId = SDL_JoystickGetDeviceInstanceID(deviceNum);
|
||||
controllerWidget->SetCurrentJoystickID(joystickId);
|
||||
controllerWidget->SetIsCurrentJoystickGameController(currentController != nullptr);
|
||||
}
|
||||
|
||||
void MainDialog::closeInputDevice()
|
||||
@@ -193,8 +220,9 @@ void MainDialog::on_ControllerWidget_CurrentInputDeviceChanged(ControllerWidget*
|
||||
this->closeInputDevice();
|
||||
|
||||
// only open device,
|
||||
// when it's not a keyboard
|
||||
if (deviceNum != -1)
|
||||
// when needed
|
||||
if (deviceNum != (int)InputDeviceType::None &&
|
||||
deviceNum != (int)InputDeviceType::Keyboard)
|
||||
{
|
||||
this->openInputDevice(deviceName, deviceNum);
|
||||
}
|
||||
@@ -212,21 +240,54 @@ void MainDialog::on_ControllerWidget_RefreshInputDevicesButtonClicked()
|
||||
this->sdlThread->SetAction(SDLThreadAction::GetInputDevices);
|
||||
}
|
||||
|
||||
void MainDialog::on_ControllerWidget_UserProfileAdded(QString name, QString section)
|
||||
{
|
||||
for (auto& controllerWidget : this->controllerWidgets)
|
||||
{
|
||||
controllerWidget->AddUserProfile(name, section);
|
||||
}
|
||||
}
|
||||
|
||||
void MainDialog::on_ControllerWidget_UserProfileRemoved(QString name, QString section)
|
||||
{
|
||||
for (auto& controllerWidget : this->controllerWidgets)
|
||||
{
|
||||
controllerWidget->RemoveUserProfile(name, section);
|
||||
}
|
||||
}
|
||||
|
||||
void MainDialog::on_tabWidget_currentChanged(int index)
|
||||
{
|
||||
QString deviceName;
|
||||
int deviceNum;
|
||||
int deviceNum;
|
||||
Widget::ControllerWidget* controllerWidget;
|
||||
|
||||
Widget::ControllerWidget* controllerWidget = controllerWidgets.at(index);
|
||||
// save previous tab's user profile
|
||||
if (previousTabWidgetIndex != -1 &&
|
||||
previousTabWidgetIndex != index)
|
||||
{
|
||||
controllerWidget = controllerWidgets.at(previousTabWidgetIndex);
|
||||
controllerWidget->SaveUserProfileSettings();
|
||||
}
|
||||
|
||||
// update previous index
|
||||
previousTabWidgetIndex = index;
|
||||
|
||||
// prepare switched tab
|
||||
controllerWidget = controllerWidgets.at(index);
|
||||
controllerWidget->ClearControllerImage();
|
||||
|
||||
this->closeInputDevice();
|
||||
|
||||
// re-load user profile settings
|
||||
controllerWidget->LoadUserProfileSettings();
|
||||
|
||||
// retrieve current input device
|
||||
controllerWidget->GetCurrentInputDevice(deviceName, deviceNum);
|
||||
|
||||
// only open device,
|
||||
// when it's not a keyboard
|
||||
if (deviceNum != -1)
|
||||
// only open device when needed
|
||||
if (deviceNum != (int)InputDeviceType::None &&
|
||||
deviceNum != (int)InputDeviceType::Keyboard)
|
||||
{
|
||||
this->openInputDevice(deviceName, deviceNum);
|
||||
}
|
||||
@@ -309,19 +370,37 @@ void MainDialog::on_EventFilter_KeyReleased(QKeyEvent *event)
|
||||
SDL_PeepEvents(&sdlEvent, 1, SDL_ADDEVENT, 0, 0);
|
||||
}
|
||||
|
||||
void MainDialog::on_buttonBox_clicked(QAbstractButton *button)
|
||||
void MainDialog::accept(void)
|
||||
{
|
||||
QPushButton *pushButton = (QPushButton *)button;
|
||||
QPushButton *okButton = this->buttonBox->button(QDialogButtonBox::Ok);
|
||||
Widget::ControllerWidget* controllerWidget;
|
||||
int currentIndex = this->tabWidget->currentIndex();
|
||||
|
||||
if (pushButton == okButton)
|
||||
for (int i = 0; i < this->controllerWidgets.count(); i++)
|
||||
{
|
||||
for (auto& controllerWidget : this->controllerWidgets)
|
||||
if (i != currentIndex)
|
||||
{
|
||||
controllerWidget = this->controllerWidgets.at(i);
|
||||
controllerWidget->SaveSettings();
|
||||
}
|
||||
|
||||
CoreSettingsSave();
|
||||
}
|
||||
|
||||
// the widget which has focus should be saved last
|
||||
controllerWidget = this->controllerWidgets.at(currentIndex);
|
||||
controllerWidget->SaveSettings();
|
||||
|
||||
CoreSettingsSave();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void MainDialog::reject(void)
|
||||
{
|
||||
for (auto& controllerWidget : this->controllerWidgets)
|
||||
{
|
||||
controllerWidget->RevertSettings();
|
||||
}
|
||||
|
||||
CoreSettingsSave();
|
||||
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
@@ -43,11 +43,13 @@ private:
|
||||
bool updatingDeviceList = false;
|
||||
|
||||
QList<Widget::ControllerWidget*> controllerWidgets;
|
||||
SDL_Joystick* currentJoystick = nullptr;
|
||||
SDL_Joystick* currentJoystick = nullptr;
|
||||
SDL_GameController* currentController = nullptr;
|
||||
QString currentDeviceName;
|
||||
int currentDeviceNum = 0;
|
||||
|
||||
int previousTabWidgetIndex = 0;
|
||||
|
||||
EventFilter* eventFilter;
|
||||
|
||||
void addInputDevice(QString, int);
|
||||
@@ -65,7 +67,10 @@ public slots:
|
||||
|
||||
void on_ControllerWidget_CurrentInputDeviceChanged(ControllerWidget*, QString, int);
|
||||
void on_ControllerWidget_RefreshInputDevicesButtonClicked();
|
||||
|
||||
|
||||
void on_ControllerWidget_UserProfileAdded(QString, QString);
|
||||
void on_ControllerWidget_UserProfileRemoved(QString, QString);
|
||||
|
||||
void on_tabWidget_currentChanged(int);
|
||||
|
||||
void on_SDLThread_DeviceFound(QString, int);
|
||||
@@ -75,7 +80,8 @@ private slots:
|
||||
void on_EventFilter_KeyPressed(QKeyEvent *);
|
||||
void on_EventFilter_KeyReleased(QKeyEvent *);
|
||||
|
||||
void on_buttonBox_clicked(QAbstractButton *);
|
||||
void accept(void) Q_DECL_OVERRIDE;
|
||||
void reject(void) Q_DECL_OVERRIDE;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ OptionsDialog::OptionsDialog(QWidget* parent, OptionsDialogSettings settings) :
|
||||
this->gameboyRomLineEdit->setText(QString::fromStdString(settings.GameboyRom));
|
||||
this->gameboySaveLineEdit->setText(QString::fromStdString(settings.GameboySave));
|
||||
this->removeDuplicateMappingsCheckbox->setChecked(settings.RemoveDuplicateMappings);
|
||||
this->invertAxisCheckBox->setChecked(settings.InvertAxis);
|
||||
this->filterEventsForButtonsCheckBox->setChecked(settings.FilterEventsForButtons);
|
||||
this->filterEventsForAxisCheckBox->setChecked(settings.FilterEventsForAxis);
|
||||
|
||||
if (!CoreIsEmulationRunning() && !CoreIsEmulationPaused())
|
||||
{
|
||||
@@ -84,7 +85,8 @@ void OptionsDialog::on_buttonBox_clicked(QAbstractButton *button)
|
||||
this->settings.GameboyRom = this->gameboyRomLineEdit->text().toStdString();
|
||||
this->settings.GameboySave = this->gameboySaveLineEdit->text().toStdString();
|
||||
this->settings.RemoveDuplicateMappings = this->removeDuplicateMappingsCheckbox->isChecked();
|
||||
this->settings.InvertAxis = this->invertAxisCheckBox->isChecked();
|
||||
this->settings.FilterEventsForButtons = this->filterEventsForButtonsCheckBox->isChecked();
|
||||
this->settings.FilterEventsForAxis = this->filterEventsForAxisCheckBox->isChecked();
|
||||
}
|
||||
|
||||
this->accept();
|
||||
|
||||
@@ -24,7 +24,8 @@ struct OptionsDialogSettings
|
||||
|
||||
// UI settings
|
||||
bool RemoveDuplicateMappings = false;
|
||||
bool InvertAxis = false;
|
||||
bool FilterEventsForButtons = true;
|
||||
bool FilterEventsForAxis = true;
|
||||
};
|
||||
|
||||
namespace UserInterface
|
||||
|
||||
@@ -203,9 +203,16 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="invertAxisCheckBox">
|
||||
<widget class="QCheckBox" name="filterEventsForButtonsCheckBox">
|
||||
<property name="text">
|
||||
<string>Invert Axes Mapped To LRZ</string>
|
||||
<string>Filter Events Based On Joystick Type For Buttons</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="filterEventsForAxisCheckBox">
|
||||
<property name="text">
|
||||
<string>Filter Events Based On Joystick Type For Axis</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <QResizeEvent>
|
||||
#include <QSvgRenderer>
|
||||
#include <QMessageBox>
|
||||
#include <QInputDialog>
|
||||
#include <SDL.h>
|
||||
#include <iostream>
|
||||
|
||||
@@ -122,8 +123,10 @@ ControllerWidget::ControllerWidget(QWidget* parent, EventFilter* eventFilter) :
|
||||
this->setupButtonWidgets.append(button);
|
||||
}
|
||||
|
||||
this->on_controllerPluggedCheckBox_toggled(false);
|
||||
this->initializeButtons();
|
||||
this->setPluggedIn(false);
|
||||
this->initializeMappingButtons();
|
||||
this->initializeProfileButtons();
|
||||
this->initializeMiscButtons();
|
||||
}
|
||||
|
||||
ControllerWidget::~ControllerWidget()
|
||||
@@ -131,7 +134,7 @@ ControllerWidget::~ControllerWidget()
|
||||
|
||||
}
|
||||
|
||||
void ControllerWidget::initializeButtons()
|
||||
void ControllerWidget::initializeMappingButtons()
|
||||
{
|
||||
struct
|
||||
{
|
||||
@@ -180,9 +183,27 @@ void ControllerWidget::initializeButtons()
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerWidget::initializeProfileButtons()
|
||||
{
|
||||
this->addProfileButton->setText("");
|
||||
this->addProfileButton->setIcon(QIcon::fromTheme("add-line"));
|
||||
this->removeProfileButton->setText("");
|
||||
this->removeProfileButton->setIcon(QIcon::fromTheme("delete-bin-line"));
|
||||
}
|
||||
|
||||
void ControllerWidget::initializeMiscButtons()
|
||||
{
|
||||
this->inputDeviceRefreshButton->setIcon(QIcon::fromTheme("refresh-line"));
|
||||
this->resetButton->setIcon(QIcon::fromTheme("restart-line"));
|
||||
this->optionsButton->setIcon(QIcon::fromTheme("settings-3-line"));
|
||||
}
|
||||
|
||||
bool ControllerWidget::isCurrentDeviceKeyboard()
|
||||
{
|
||||
return this->inputDeviceComboBox->currentIndex() == 0;
|
||||
int deviceNum = this->inputDeviceComboBox->currentData().toInt();
|
||||
|
||||
return deviceNum == (int)InputDeviceType::Automatic ||
|
||||
deviceNum == (int)InputDeviceType::Keyboard;
|
||||
}
|
||||
|
||||
bool ControllerWidget::isCurrentDeviceNotFound()
|
||||
@@ -265,11 +286,90 @@ QString ControllerWidget::getCurrentSettingsSection()
|
||||
return this->profileComboBox->currentData().toString();
|
||||
}
|
||||
|
||||
QString ControllerWidget::getUserProfileSectionName(QString profile)
|
||||
{
|
||||
QString profileSection;
|
||||
|
||||
profileSection = "Rosalie's Mupen GUI - Input Plugin User Profile \"";
|
||||
profileSection += profile;
|
||||
profileSection += "\"";
|
||||
|
||||
return profileSection;
|
||||
}
|
||||
|
||||
bool ControllerWidget::isSectionUserProfile(QString section)
|
||||
{
|
||||
return section.contains("User Profile");
|
||||
}
|
||||
|
||||
bool ControllerWidget::isSectionGameProfile(QString section)
|
||||
{
|
||||
return !this->isSectionUserProfile(section) && section.contains("Game");
|
||||
}
|
||||
|
||||
void ControllerWidget::setPluggedIn(bool value)
|
||||
{
|
||||
QWidget* widgetList[] =
|
||||
{
|
||||
// dpad
|
||||
this->dpadGroupBox,
|
||||
this->dpadUpButton, this->dpadUpAddButton, this->dpadUpRemoveButton,
|
||||
this->dpadDownButton, this->dpadDownAddButton, this->dpadDownRemoveButton,
|
||||
this->dpadLeftButton, this->dpadLeftAddButton, this->dpadLeftRemoveButton,
|
||||
this->dpadRightButton, this->dpadRightAddButton, this->dpadRightRemoveButton,
|
||||
// analog stick
|
||||
this->analogStickGroupBox,
|
||||
this->analogStickUpButton, this->analogStickUpAddButton, this->analogStickUpRemoveButton,
|
||||
this->analogStickDownButton, this->analogStickDownAddButton, this->analogStickDownRemoveButton,
|
||||
this->analogStickLeftButton, this->analogStickLeftAddButton, this->analogStickLeftRemoveButton,
|
||||
this->analogStickRightButton, this->analogStickRightAddButton, this->analogStickRightRemoveButton,
|
||||
// cbuttons
|
||||
this->cButtonsGroupBox,
|
||||
this->cbuttonUpButton, this->cbuttonUpAddButton, this->cbuttonUpRemoveButton,
|
||||
this->cbuttonDownButton, this->cbuttonDownAddButton, this->cbuttonDownRemoveButton,
|
||||
this->cbuttonLeftButton, this->cbuttonLeftAddButton, this->cbuttonLeftRemoveButton,
|
||||
this->cbuttonRightButton, this->cbuttonRightAddButton, this->cbuttonRightRemoveButton,
|
||||
// triggers
|
||||
this->analogStickGroupBox,
|
||||
this->leftTriggerButton, this->leftTriggerAddButton, this->leftTriggerRemoveButton,
|
||||
this->rightTriggerButton, this->rightTriggerAddButton, this->rightTriggerRemoveButton,
|
||||
this->zTriggerButton, this->zTriggerAddButton, this->zTriggerRemoveButton,
|
||||
// buttons
|
||||
this->buttonsGroupBox,
|
||||
this->aButton, this->aAddButton, this->aRemoveButton,
|
||||
this->bButton, this->bAddButton, this->bRemoveButton,
|
||||
this->startButton, this->startAddButton, this->startRemoveButton,
|
||||
// misc UI elements
|
||||
this->deadZoneGroupBox,
|
||||
this->deadZoneSlider,
|
||||
this->optionsButton,
|
||||
this->resetButton
|
||||
};
|
||||
|
||||
for (auto& widget : widgetList)
|
||||
{
|
||||
widget->setEnabled(value);
|
||||
}
|
||||
|
||||
this->ClearControllerImage();
|
||||
}
|
||||
|
||||
void ControllerWidget::showErrorMessage(QString text, QString details)
|
||||
{
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setIcon(QMessageBox::Icon::Critical);
|
||||
msgBox.setWindowTitle("Error");
|
||||
msgBox.setText(text);
|
||||
msgBox.setDetailedText(details);
|
||||
msgBox.addButton(QMessageBox::Ok);
|
||||
msgBox.exec();
|
||||
}
|
||||
|
||||
void ControllerWidget::AddInputDevice(QString deviceName, int deviceNum)
|
||||
{
|
||||
QString name = deviceName;
|
||||
|
||||
if (deviceNum != -1)
|
||||
if (deviceNum >= 0)
|
||||
{
|
||||
name += " (";
|
||||
name += QString::number(deviceNum);
|
||||
@@ -301,9 +401,15 @@ void ControllerWidget::CheckInputDeviceSettings()
|
||||
{
|
||||
std::string section = this->getCurrentSettingsSection().toStdString();
|
||||
|
||||
// fallback to main section
|
||||
// fallback to main section unless
|
||||
// it's a user profile
|
||||
if (!CoreSettingsSectionExists(section))
|
||||
{
|
||||
if (this->isSectionUserProfile(this->getCurrentSettingsSection()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CoreSettingsSectionExists(this->settingsSection.toStdString()))
|
||||
{
|
||||
return;
|
||||
@@ -312,8 +418,9 @@ void ControllerWidget::CheckInputDeviceSettings()
|
||||
section = this->settingsSection.toStdString();
|
||||
}
|
||||
|
||||
bool isPluggedIn = CoreSettingsGetBoolValue(SettingsID::Input_PluggedIn, section);
|
||||
std::string deviceName = CoreSettingsGetStringValue(SettingsID::Input_DeviceName, section);
|
||||
int deviceNum = CoreSettingsGetIntValue(SettingsID::Input_DeviceNum, section);
|
||||
int deviceNum = CoreSettingsGetIntValue(SettingsID::Input_DeviceNum, section);
|
||||
|
||||
// do nothing when input device combobox
|
||||
// is empty
|
||||
@@ -322,6 +429,13 @@ void ControllerWidget::CheckInputDeviceSettings()
|
||||
return;
|
||||
}
|
||||
|
||||
// account for old setting
|
||||
if (!isPluggedIn && deviceNum != (int)InputDeviceType::None)
|
||||
{
|
||||
deviceName = "None";
|
||||
deviceNum = (int)InputDeviceType::None;
|
||||
}
|
||||
|
||||
// clear (not found) devices first
|
||||
int notFoundIndex = this->inputDeviceComboBox->findText("(not found)", Qt::MatchFlag::MatchEndsWith);
|
||||
if (notFoundIndex != -1)
|
||||
@@ -331,12 +445,18 @@ void ControllerWidget::CheckInputDeviceSettings()
|
||||
}
|
||||
|
||||
int deviceNameIndex = this->inputDeviceComboBox->findText(QString::fromStdString(deviceName), Qt::MatchFlag::MatchStartsWith);
|
||||
int deviceNumIndex = this->inputDeviceComboBox->findData(deviceNum);
|
||||
int deviceNumIndex = this->inputDeviceComboBox->findData(deviceNum);
|
||||
|
||||
if ((deviceNumIndex != -1) &&
|
||||
(this->inputDeviceComboBox->itemText(deviceNumIndex).startsWith(QString::fromStdString(deviceName))))
|
||||
{ // full match
|
||||
this->inputDeviceComboBox->setCurrentIndex(deviceNumIndex);
|
||||
|
||||
// force-refresh automatic input device
|
||||
if (deviceNum == (int)InputDeviceType::Automatic)
|
||||
{
|
||||
this->on_inputDeviceComboBox_currentIndexChanged(deviceNumIndex);
|
||||
}
|
||||
}
|
||||
else if (deviceNameIndex != -1)
|
||||
{ // name only match
|
||||
@@ -369,18 +489,19 @@ void ControllerWidget::GetCurrentInputDevice(QString& deviceName, int& deviceNum
|
||||
if (this->isCurrentDeviceNotFound() && !ignoreDeviceNotFound)
|
||||
{
|
||||
deviceName = "";
|
||||
deviceNum = -1;
|
||||
deviceNum = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceName = this->inputDeviceNameList.at(currentIndex);
|
||||
deviceNum = this->inputDeviceComboBox->itemData(currentIndex).toInt();
|
||||
deviceNum = this->inputDeviceComboBox->itemData(currentIndex).toInt();
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerWidget::on_deadZoneSlider_valueChanged(int value)
|
||||
{
|
||||
QString title = tr("Deadzone: ");
|
||||
QString title;
|
||||
title = "Deadzone: ";
|
||||
title += QString::number(value);
|
||||
title += "%";
|
||||
|
||||
@@ -390,6 +511,20 @@ void ControllerWidget::on_deadZoneSlider_valueChanged(int value)
|
||||
|
||||
void ControllerWidget::on_profileComboBox_currentIndexChanged(int value)
|
||||
{
|
||||
// save profile when switching profile
|
||||
if (this->initialized && this->previousProfileComboBoxIndex != -1)
|
||||
{
|
||||
// ensure the previous index doesn't go out of bounds
|
||||
if (this->previousProfileComboBoxIndex < this->profileComboBox->count())
|
||||
{
|
||||
QString section = this->profileComboBox->itemData(this->previousProfileComboBoxIndex).toString();
|
||||
this->SaveSettings(section);
|
||||
}
|
||||
}
|
||||
|
||||
// update previous index
|
||||
this->previousProfileComboBoxIndex = value;
|
||||
|
||||
// reload settings from section
|
||||
this->LoadSettings(this->getCurrentSettingsSection());
|
||||
// reload input device settings
|
||||
@@ -405,16 +540,19 @@ void ControllerWidget::on_inputDeviceComboBox_currentIndexChanged(int value)
|
||||
}
|
||||
|
||||
QString deviceName = this->inputDeviceNameList.at(value);
|
||||
int deviceNum = this->inputDeviceComboBox->itemData(value).toInt();
|
||||
int deviceNum = this->inputDeviceComboBox->itemData(value).toInt();
|
||||
|
||||
this->ClearControllerImage();
|
||||
|
||||
if (this->isCurrentDeviceNotFound())
|
||||
{
|
||||
deviceName = "";
|
||||
deviceNum = -1;
|
||||
deviceNum = -1;
|
||||
}
|
||||
|
||||
// set plugged in state
|
||||
this->setPluggedIn(deviceNum != (int)InputDeviceType::None);
|
||||
|
||||
emit this->CurrentInputDeviceChanged(this, deviceName, deviceNum);
|
||||
}
|
||||
|
||||
@@ -423,53 +561,61 @@ void ControllerWidget::on_inputDeviceRefreshButton_clicked()
|
||||
emit this->RefreshInputDevicesButtonClicked();
|
||||
}
|
||||
|
||||
void ControllerWidget::on_controllerPluggedCheckBox_toggled(bool value)
|
||||
void ControllerWidget::on_addProfileButton_clicked()
|
||||
{
|
||||
QWidget* widgetList[] =
|
||||
{
|
||||
// dpad
|
||||
this->groupBox,
|
||||
this->dpadUpButton, this->dpadUpAddButton, this->dpadUpRemoveButton,
|
||||
this->dpadDownButton, this->dpadDownAddButton, this->dpadDownRemoveButton,
|
||||
this->dpadLeftButton, this->dpadLeftAddButton, this->dpadLeftRemoveButton,
|
||||
this->dpadRightButton, this->dpadRightAddButton, this->dpadRightRemoveButton,
|
||||
// analog stick
|
||||
this->groupBox_2,
|
||||
this->analogStickUpButton, this->analogStickUpAddButton, this->analogStickUpRemoveButton,
|
||||
this->analogStickDownButton, this->analogStickDownAddButton, this->analogStickDownRemoveButton,
|
||||
this->analogStickLeftButton, this->analogStickLeftAddButton, this->analogStickLeftRemoveButton,
|
||||
this->analogStickRightButton, this->analogStickRightAddButton, this->analogStickRightRemoveButton,
|
||||
// cbuttons
|
||||
this->groupBox_3,
|
||||
this->cbuttonUpButton, this->cbuttonUpAddButton, this->cbuttonUpRemoveButton,
|
||||
this->cbuttonDownButton, this->cbuttonDownAddButton, this->cbuttonDownRemoveButton,
|
||||
this->cbuttonLeftButton, this->cbuttonLeftAddButton, this->cbuttonLeftRemoveButton,
|
||||
this->cbuttonRightButton, this->cbuttonRightAddButton, this->cbuttonRightRemoveButton,
|
||||
// triggers
|
||||
this->leftTriggerButton, this->leftTriggerAddButton, this->leftTriggerRemoveButton,
|
||||
this->rightTriggerButton, this->rightTriggerAddButton, this->rightTriggerRemoveButton,
|
||||
this->zTriggerButton, this->zTriggerAddButton, this->zTriggerRemoveButton,
|
||||
// buttons
|
||||
this->groupBox_6,
|
||||
this->aButton, this->aAddButton, this->aRemoveButton,
|
||||
this->bButton, this->bAddButton, this->bRemoveButton,
|
||||
this->startButton, this->startAddButton, this->startRemoveButton,
|
||||
// misc UI elements
|
||||
this->deadZoneSlider,
|
||||
this->optionsButton,
|
||||
this->profileComboBox,
|
||||
this->removeProfileButton,
|
||||
this->resetButton,
|
||||
this->inputDeviceComboBox,
|
||||
this->inputDeviceRefreshButton
|
||||
};
|
||||
std::vector<std::string> profiles;
|
||||
std::vector<std::string>::iterator profilesIter;
|
||||
QString section;
|
||||
|
||||
for (auto& widget : widgetList)
|
||||
{
|
||||
widget->setEnabled(value);
|
||||
// retrieve profiles from settings
|
||||
profiles = CoreSettingsGetStringListValue(SettingsID::Input_Profiles);
|
||||
|
||||
// ask user for a new profile name
|
||||
QString newProfile = QInputDialog::getText(this,
|
||||
"Create New Profile", "New profile name:",
|
||||
QLineEdit::Normal, "",
|
||||
nullptr,
|
||||
Qt::WindowCloseButtonHint | Qt::WindowTitleHint);
|
||||
if (newProfile.isEmpty())
|
||||
{ // do nothing when user has canceled
|
||||
return;
|
||||
}
|
||||
|
||||
this->ClearControllerImage();
|
||||
// ensure profile doesn't contain ';' or '[]'
|
||||
if (newProfile.contains(';') ||
|
||||
newProfile.contains('[') ||
|
||||
newProfile.contains(']'))
|
||||
{
|
||||
this->showErrorMessage("Profile name cannot contain ';','[' or ']'!");
|
||||
return;
|
||||
}
|
||||
|
||||
// ensure the name is unique
|
||||
profilesIter = std::find(profiles.begin(), profiles.end(), newProfile.toStdString());
|
||||
if (profilesIter != profiles.end())
|
||||
{
|
||||
this->showErrorMessage("Profile with the same name already exists!");
|
||||
return;
|
||||
}
|
||||
|
||||
section = this->getUserProfileSectionName(newProfile);
|
||||
|
||||
// add profile to UI
|
||||
this->profileComboBox->addItem(newProfile, section);
|
||||
this->profileComboBox->setCurrentText(newProfile);
|
||||
|
||||
// add profile to settings
|
||||
profiles.push_back(newProfile.toStdString());
|
||||
CoreSettingsSetValue(SettingsID::Input_Profiles, profiles);
|
||||
|
||||
// update added profiles
|
||||
if (!this->addedProfiles.contains(section))
|
||||
{
|
||||
this->addedProfiles.push_back(section);
|
||||
}
|
||||
|
||||
// emit signal
|
||||
emit this->UserProfileAdded(newProfile, section);
|
||||
}
|
||||
|
||||
void ControllerWidget::on_removeProfileButton_clicked()
|
||||
@@ -491,26 +637,60 @@ void ControllerWidget::on_removeProfileButton_clicked()
|
||||
return;
|
||||
}
|
||||
|
||||
// try to remove game settings section
|
||||
ret = CoreSettingsDeleteSection(this->getCurrentSettingsSection().toStdString());
|
||||
if (!ret)
|
||||
{ // show error when failed
|
||||
QMessageBox messageBox(this);
|
||||
messageBox.setIcon(QMessageBox::Icon::Critical);
|
||||
messageBox.setWindowTitle("Error");
|
||||
messageBox.setText("CoreSettingsDeleteSection() Failed!");
|
||||
messageBox.setDetailedText(QString::fromStdString(CoreGetError()));
|
||||
messageBox.addButton(QMessageBox::Ok);
|
||||
messageBox.exec();
|
||||
QString currentSection = this->getCurrentSettingsSection();
|
||||
|
||||
// add to removed profiles list (will be deleted later)
|
||||
if (!this->removedProfiles.contains(currentSection))
|
||||
{
|
||||
this->removedProfiles.push_back(currentSection);
|
||||
}
|
||||
this->addedProfiles.removeAll(currentSection);
|
||||
|
||||
// if section is a user profile,
|
||||
// also remove it from the profiles list
|
||||
if (this->isSectionUserProfile(currentSection))
|
||||
{
|
||||
std::string userProfile = this->profileComboBox->currentText().toStdString();
|
||||
std::vector<std::string> userProfiles = CoreSettingsGetStringListValue(SettingsID::Input_Profiles);
|
||||
std::vector<std::string>::iterator userProfilesIter;
|
||||
|
||||
userProfilesIter = std::find(userProfiles.begin(), userProfiles.end(), userProfile);
|
||||
if (userProfilesIter != userProfiles.end())
|
||||
{
|
||||
userProfiles.erase(userProfilesIter);
|
||||
}
|
||||
|
||||
CoreSettingsSetValue(SettingsID::Input_Profiles, userProfiles);
|
||||
|
||||
// remove item from UI
|
||||
this->profileComboBox->removeItem(this->profileComboBox->currentIndex());
|
||||
|
||||
// emit signal
|
||||
emit this->UserProfileRemoved(this->profileComboBox->currentText(), currentSection);
|
||||
}
|
||||
|
||||
// change profile back to main
|
||||
this->profileComboBox->setCurrentIndex(0);
|
||||
// switch back to main profile when deleting
|
||||
// a per game profile
|
||||
if (this->isSectionGameProfile(currentSection))
|
||||
{
|
||||
// ensure we don't save the game specific profile
|
||||
// upon switching to the main profile
|
||||
this->previousProfileComboBoxIndex = -1;
|
||||
this->profileComboBox->setCurrentIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerWidget::on_resetButton_clicked()
|
||||
{
|
||||
this->LoadSettings();
|
||||
QString section = this->getCurrentSettingsSection();
|
||||
|
||||
// revert settings in current section when it exists
|
||||
if (CoreSettingsSectionExists(section.toStdString()))
|
||||
{
|
||||
CoreSettingsRevertSection(section.toStdString());
|
||||
}
|
||||
|
||||
this->LoadSettings(section);
|
||||
}
|
||||
|
||||
void ControllerWidget::on_optionsButton_clicked()
|
||||
@@ -590,6 +770,12 @@ void ControllerWidget::on_MainDialog_SdlEvent(SDL_Event* event)
|
||||
if ((event->type == SDL_CONTROLLERBUTTONDOWN) ||
|
||||
(event->type == SDL_CONTROLLERBUTTONUP))
|
||||
{ // gamepad button
|
||||
if (!this->isCurrentJoystickGameController &&
|
||||
this->optionsDialogSettings.FilterEventsForButtons)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
joystickId = event->cbutton.which;
|
||||
inputType = InputType::GamepadButton;
|
||||
sdlButton = event->cbutton.button;
|
||||
@@ -598,6 +784,12 @@ void ControllerWidget::on_MainDialog_SdlEvent(SDL_Event* event)
|
||||
}
|
||||
else
|
||||
{ // joystick button
|
||||
if (this->isCurrentJoystickGameController &&
|
||||
this->optionsDialogSettings.FilterEventsForButtons)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
joystickId = event->jbutton.which;
|
||||
inputType = InputType::JoystickButton;
|
||||
sdlButton = event->jbutton.button;
|
||||
@@ -694,6 +886,12 @@ void ControllerWidget::on_MainDialog_SdlEvent(SDL_Event* event)
|
||||
|
||||
if (event->type == SDL_CONTROLLERAXISMOTION)
|
||||
{ // gamepad axis
|
||||
if (!this->isCurrentJoystickGameController &&
|
||||
this->optionsDialogSettings.FilterEventsForAxis)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
joystickId = event->caxis.which;
|
||||
inputType = InputType::GamepadAxis;
|
||||
sdlAxis = event->caxis.axis;
|
||||
@@ -703,6 +901,12 @@ void ControllerWidget::on_MainDialog_SdlEvent(SDL_Event* event)
|
||||
}
|
||||
else
|
||||
{ // joystick axis
|
||||
if (this->isCurrentJoystickGameController &&
|
||||
this->optionsDialogSettings.FilterEventsForAxis)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
joystickId = event->jaxis.which;
|
||||
inputType = InputType::JoystickAxis;
|
||||
sdlAxis = event->jaxis.axis;
|
||||
@@ -728,19 +932,6 @@ void ControllerWidget::on_MainDialog_SdlEvent(SDL_Event* event)
|
||||
{
|
||||
if (sdlAxisButtonPressed)
|
||||
{
|
||||
// invert axis for left, right & z trigger mappings
|
||||
if (this->optionsDialogSettings.InvertAxis)
|
||||
{
|
||||
if (this->currentButton == this->leftTriggerButton ||
|
||||
this->currentButton == this->rightTriggerButton ||
|
||||
this->currentButton == this->zTriggerButton)
|
||||
{
|
||||
sdlAxisDirection = (sdlAxisDirection == 1 ? 0 : 1);
|
||||
sdlAxisName = "axis " + QString::number(sdlAxis);
|
||||
sdlAxisName += sdlAxisDirection > 0 ? "+" : "-";
|
||||
}
|
||||
}
|
||||
|
||||
if (this->addMappingToButton)
|
||||
{
|
||||
this->currentButton->AddInputData(
|
||||
@@ -899,13 +1090,18 @@ void ControllerWidget::on_MainDialog_SdlEventPollFinished()
|
||||
|
||||
bool ControllerWidget::IsPluggedIn()
|
||||
{
|
||||
return this->controllerPluggedCheckBox->isChecked();
|
||||
return this->inputDeviceComboBox->currentData().toInt() != (int)InputDeviceType::None;
|
||||
}
|
||||
|
||||
void ControllerWidget::SetSettingsSection(QString section)
|
||||
void ControllerWidget::SetSettingsSection(QString profile, QString section)
|
||||
{
|
||||
this->settingsSection = section;
|
||||
this->profileComboBox->addItem("Main", section);
|
||||
this->profileComboBox->addItem(profile, section);
|
||||
}
|
||||
|
||||
void ControllerWidget::SetInitialized(bool value)
|
||||
{
|
||||
this->initialized = value;
|
||||
}
|
||||
|
||||
void ControllerWidget::LoadSettings()
|
||||
@@ -916,7 +1112,7 @@ void ControllerWidget::LoadSettings()
|
||||
}
|
||||
|
||||
QString section = this->settingsSection;
|
||||
QString gameSection;
|
||||
std::vector<std::string> userProfiles;
|
||||
|
||||
// if the main profile section doesn't exist,
|
||||
// save default settings
|
||||
@@ -933,45 +1129,128 @@ void ControllerWidget::LoadSettings()
|
||||
if (CoreGetCurrentRomSettings(romSettings) &&
|
||||
CoreGetCurrentRomHeader(romHeader))
|
||||
{
|
||||
gameSection = section + " Game " + QString::fromStdString(romSettings.MD5);
|
||||
this->gameSection = section + " Game " + QString::fromStdString(romSettings.MD5);
|
||||
|
||||
// use internal rom name
|
||||
QString internalName = QString::fromStdString(romHeader.Name);
|
||||
|
||||
// add game specific profile
|
||||
this->profileComboBox->addItem(internalName, gameSection);
|
||||
// add game specific profile when
|
||||
// it doesn't exist yet
|
||||
int index = this->profileComboBox->findData(this->gameSection);
|
||||
if (index == -1)
|
||||
{
|
||||
this->profileComboBox->addItem(internalName, this->gameSection);
|
||||
}
|
||||
|
||||
// if a game specific section exists,
|
||||
// select it in the profile combobox
|
||||
// and use it to load the settings
|
||||
if (CoreSettingsSectionExists(gameSection.toStdString()))
|
||||
if (CoreSettingsSectionExists(this->gameSection.toStdString()))
|
||||
{
|
||||
this->profileComboBox->setCurrentText(internalName);
|
||||
section = gameSection;
|
||||
// check if the key 'UseGameProfile' exists,
|
||||
// if it doesn't then use the profile,
|
||||
// else check if it's true and use it
|
||||
if (!CoreSettingsKeyExists(this->gameSection.toStdString(), "UseGameProfile") ||
|
||||
CoreSettingsGetBoolValue(SettingsID::Input_UseGameProfile, this->gameSection.toStdString()))
|
||||
{
|
||||
this->profileComboBox->setCurrentText(internalName);
|
||||
section = this->gameSection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->LoadSettings(section);
|
||||
// clear profiles list
|
||||
profiles.clear();
|
||||
|
||||
// try to add all the user's profiles
|
||||
userProfiles = CoreSettingsGetStringListValue(SettingsID::Input_Profiles);
|
||||
for (const std::string& profile : userProfiles)
|
||||
{
|
||||
QString profileSection = this->getUserProfileSectionName(QString::fromStdString(profile));
|
||||
|
||||
// ensure the profile section exists,
|
||||
// if it does, add it when it doesn't
|
||||
// exist yet
|
||||
if (CoreSettingsSectionExists(profileSection.toStdString()))
|
||||
{
|
||||
// add to profiles list
|
||||
this->profiles.push_back(profileSection);
|
||||
|
||||
int index = this->profileComboBox->findData(profileSection);
|
||||
if (index == -1)
|
||||
{
|
||||
this->profileComboBox->addItem(QString::fromStdString(profile), profileSection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->LoadSettings(section, true);
|
||||
}
|
||||
|
||||
void ControllerWidget::LoadSettings(QString sectionQString)
|
||||
void ControllerWidget::LoadSettings(QString sectionQString, bool loadUserProfile)
|
||||
{
|
||||
std::string section = sectionQString.toStdString();
|
||||
std::string useProfile;
|
||||
|
||||
// do nothing if the section doesn't exist
|
||||
if (!CoreSettingsSectionExists(section))
|
||||
// if the removed profiles contain
|
||||
// the section, don't load it
|
||||
if (this->removedProfiles.contains(sectionQString) &&
|
||||
!this->addedProfiles.contains(sectionQString))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->controllerPluggedCheckBox->setChecked(CoreSettingsGetBoolValue(SettingsID::Input_PluggedIn, section));
|
||||
// do nothing if the section doesn't exist
|
||||
if (!CoreSettingsSectionExists(section))
|
||||
{
|
||||
// fallback to main section for per game profiles
|
||||
if (this->isSectionGameProfile(sectionQString))
|
||||
{
|
||||
sectionQString = this->settingsSection;
|
||||
section = this->settingsSection.toStdString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// see if the profile contains the UseProfile setting,
|
||||
// if it does, see if that profile exists, then load
|
||||
// that instead
|
||||
useProfile = CoreSettingsGetStringValue(SettingsID::Input_UseProfile, section);
|
||||
if (loadUserProfile && !useProfile.empty())
|
||||
{
|
||||
QString profileSection = this->getUserProfileSectionName(QString::fromStdString(useProfile));
|
||||
|
||||
int index = this->profileComboBox->findData(profileSection);
|
||||
if (index != -1)
|
||||
{
|
||||
this->profileComboBox->setCurrentIndex(index);
|
||||
this->LoadSettings(profileSection);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->deadZoneSlider->setValue(CoreSettingsGetIntValue(SettingsID::Input_Deadzone, section));
|
||||
this->optionsDialogSettings.RemoveDuplicateMappings = CoreSettingsGetBoolValue(SettingsID::Input_RemoveDuplicateMappings, section);
|
||||
this->optionsDialogSettings.InvertAxis = CoreSettingsGetBoolValue(SettingsID::Input_InvertAxis, section);
|
||||
this->optionsDialogSettings.ControllerPak = CoreSettingsGetIntValue(SettingsID::Input_Pak, section);
|
||||
this->optionsDialogSettings.GameboyRom = CoreSettingsGetStringValue(SettingsID::Input_GameboyRom, section);
|
||||
this->optionsDialogSettings.GameboySave = CoreSettingsGetStringValue(SettingsID::Input_GameboySave, section);
|
||||
|
||||
// keep backwards compatibility with old profiles
|
||||
if (CoreSettingsKeyExists(section, "FilterEventsForButtons") &&
|
||||
CoreSettingsKeyExists(section, "FilterEventsForAxis"))
|
||||
{
|
||||
this->optionsDialogSettings.FilterEventsForButtons = CoreSettingsGetBoolValue(SettingsID::Input_FilterEventsForButtons, section);
|
||||
this->optionsDialogSettings.FilterEventsForAxis = CoreSettingsGetBoolValue(SettingsID::Input_FilterEventsForAxis, section);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->optionsDialogSettings.FilterEventsForButtons = true;
|
||||
this->optionsDialogSettings.FilterEventsForAxis = true;
|
||||
}
|
||||
|
||||
for (auto& buttonSetting : this->buttonSettingMappings)
|
||||
{
|
||||
buttonSetting.button->Clear();
|
||||
@@ -1000,8 +1279,21 @@ void ControllerWidget::LoadSettings(QString sectionQString)
|
||||
}
|
||||
|
||||
// force refresh some UI elements
|
||||
this->CheckInputDeviceSettings();
|
||||
this->on_deadZoneSlider_valueChanged(this->deadZoneSlider->value());
|
||||
this->on_controllerPluggedCheckBox_toggled(this->IsPluggedIn());
|
||||
this->setPluggedIn(this->IsPluggedIn());
|
||||
}
|
||||
|
||||
void ControllerWidget::LoadUserProfileSettings()
|
||||
{
|
||||
QString section = this->getCurrentSettingsSection();
|
||||
|
||||
if (!this->isSectionUserProfile(section))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->LoadSettings(section);
|
||||
}
|
||||
|
||||
void ControllerWidget::SaveDefaultSettings()
|
||||
@@ -1014,12 +1306,13 @@ void ControllerWidget::SaveDefaultSettings()
|
||||
std::string section = this->settingsSection.toStdString();
|
||||
|
||||
CoreSettingsSetValue(SettingsID::Input_PluggedIn, section, false);
|
||||
CoreSettingsSetValue(SettingsID::Input_DeviceName, section, std::string("Keyboard"));
|
||||
CoreSettingsSetValue(SettingsID::Input_DeviceNum, section, -1);
|
||||
CoreSettingsSetValue(SettingsID::Input_DeviceName, section, std::string("None"));
|
||||
CoreSettingsSetValue(SettingsID::Input_DeviceNum, section, (int)InputDeviceType::None);
|
||||
CoreSettingsSetValue(SettingsID::Input_Deadzone, section, 9);
|
||||
CoreSettingsSetValue(SettingsID::Input_Pak, section, 0);
|
||||
CoreSettingsSetValue(SettingsID::Input_RemoveDuplicateMappings, section, true);
|
||||
CoreSettingsSetValue(SettingsID::Input_InvertAxis, section, true);
|
||||
CoreSettingsSetValue(SettingsID::Input_FilterEventsForButtons, section, true);
|
||||
CoreSettingsSetValue(SettingsID::Input_FilterEventsForAxis, section, true);
|
||||
|
||||
for (auto& buttonSetting : this->buttonSettingMappings)
|
||||
{
|
||||
@@ -1028,8 +1321,6 @@ void ControllerWidget::SaveDefaultSettings()
|
||||
CoreSettingsSetValue(buttonSetting.dataSettingsId, section, std::vector<int>({ 0 }));
|
||||
CoreSettingsSetValue(buttonSetting.extraDataSettingsId, section, std::vector<int>({ 0 }));
|
||||
}
|
||||
|
||||
CoreSettingsSave();
|
||||
}
|
||||
|
||||
void ControllerWidget::SaveSettings()
|
||||
@@ -1038,32 +1329,115 @@ void ControllerWidget::SaveSettings()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this->SaveSettings(this->getCurrentSettingsSection());
|
||||
|
||||
// delete all removed profile sections
|
||||
for (QString section : this->removedProfiles)
|
||||
{
|
||||
if (!this->addedProfiles.contains(section))
|
||||
{
|
||||
CoreSettingsDeleteSection(section.toStdString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerWidget::SaveUserProfileSettings()
|
||||
{
|
||||
QString section = this->getCurrentSettingsSection();
|
||||
|
||||
// only save when we're a user profile
|
||||
if (!this->isSectionUserProfile(section))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->SaveSettings(section);
|
||||
}
|
||||
|
||||
void ControllerWidget::SaveSettings(QString section)
|
||||
{
|
||||
QString deviceName;
|
||||
int deviceNum;
|
||||
std::string section = this->getCurrentSettingsSection().toStdString();
|
||||
int deviceNum;
|
||||
|
||||
std::string mainSettingsSection = this->settingsSection.toStdString();
|
||||
std::string sectionStr = section.toStdString();
|
||||
|
||||
// when the section is a user profile,
|
||||
// make sure we inform the profile to use
|
||||
// that user profile instead
|
||||
if (this->isSectionUserProfile(section))
|
||||
{
|
||||
CoreSettingsSetValue(SettingsID::Input_UseProfile, mainSettingsSection, this->profileComboBox->currentText().toStdString());
|
||||
}
|
||||
else
|
||||
{
|
||||
CoreSettingsSetValue(SettingsID::Input_UseProfile, mainSettingsSection, std::string(""));
|
||||
}
|
||||
|
||||
// when section is a game specific profile,
|
||||
// make sure we tell it whether or not to use it
|
||||
if (this->isSectionGameProfile(section))
|
||||
{
|
||||
CoreSettingsSetValue(SettingsID::Input_UseGameProfile, sectionStr, true);
|
||||
}
|
||||
else if (!this->gameSection.isEmpty() &&
|
||||
CoreSettingsSectionExists(this->gameSection.toStdString()))
|
||||
{
|
||||
CoreSettingsSetValue(SettingsID::Input_UseGameProfile, this->gameSection.toStdString(), false);
|
||||
}
|
||||
|
||||
this->GetCurrentInputDevice(deviceName, deviceNum, true);
|
||||
|
||||
CoreSettingsSetValue(SettingsID::Input_PluggedIn, section, this->IsPluggedIn());
|
||||
CoreSettingsSetValue(SettingsID::Input_DeviceName, section, deviceName.toStdString());
|
||||
CoreSettingsSetValue(SettingsID::Input_DeviceNum, section, deviceNum);
|
||||
CoreSettingsSetValue(SettingsID::Input_Deadzone, section, this->deadZoneSlider->value());
|
||||
CoreSettingsSetValue(SettingsID::Input_Pak, section, this->optionsDialogSettings.ControllerPak);
|
||||
CoreSettingsSetValue(SettingsID::Input_GameboyRom, section, this->optionsDialogSettings.GameboyRom);
|
||||
CoreSettingsSetValue(SettingsID::Input_GameboySave, section, this->optionsDialogSettings.GameboySave);
|
||||
CoreSettingsSetValue(SettingsID::Input_RemoveDuplicateMappings, section, this->optionsDialogSettings.RemoveDuplicateMappings);
|
||||
CoreSettingsSetValue(SettingsID::Input_InvertAxis, section, this->optionsDialogSettings.InvertAxis);
|
||||
CoreSettingsSetValue(SettingsID::Input_PluggedIn, sectionStr, this->IsPluggedIn());
|
||||
CoreSettingsSetValue(SettingsID::Input_DeviceName, sectionStr, deviceName.toStdString());
|
||||
CoreSettingsSetValue(SettingsID::Input_DeviceNum, sectionStr, deviceNum);
|
||||
CoreSettingsSetValue(SettingsID::Input_Deadzone, sectionStr, this->deadZoneSlider->value());
|
||||
CoreSettingsSetValue(SettingsID::Input_Pak, sectionStr, this->optionsDialogSettings.ControllerPak);
|
||||
CoreSettingsSetValue(SettingsID::Input_GameboyRom, sectionStr, this->optionsDialogSettings.GameboyRom);
|
||||
CoreSettingsSetValue(SettingsID::Input_GameboySave, sectionStr, this->optionsDialogSettings.GameboySave);
|
||||
CoreSettingsSetValue(SettingsID::Input_RemoveDuplicateMappings, sectionStr, this->optionsDialogSettings.RemoveDuplicateMappings);
|
||||
CoreSettingsSetValue(SettingsID::Input_FilterEventsForButtons, sectionStr, this->optionsDialogSettings.FilterEventsForButtons);
|
||||
CoreSettingsSetValue(SettingsID::Input_FilterEventsForAxis, sectionStr, this->optionsDialogSettings.FilterEventsForAxis);
|
||||
|
||||
for (auto& buttonSetting : this->buttonSettingMappings)
|
||||
{
|
||||
CoreSettingsSetValue(buttonSetting.inputTypeSettingsId, section, buttonSetting.button->GetInputType());
|
||||
CoreSettingsSetValue(buttonSetting.nameSettingsId, section, buttonSetting.button->GetInputText());
|
||||
CoreSettingsSetValue(buttonSetting.dataSettingsId, section, buttonSetting.button->GetInputData());
|
||||
CoreSettingsSetValue(buttonSetting.extraDataSettingsId, section, buttonSetting.button->GetExtraInputData());
|
||||
CoreSettingsSetValue(buttonSetting.inputTypeSettingsId, sectionStr, buttonSetting.button->GetInputType());
|
||||
CoreSettingsSetValue(buttonSetting.nameSettingsId, sectionStr, buttonSetting.button->GetInputText());
|
||||
CoreSettingsSetValue(buttonSetting.dataSettingsId, sectionStr, buttonSetting.button->GetInputData());
|
||||
CoreSettingsSetValue(buttonSetting.extraDataSettingsId, sectionStr, buttonSetting.button->GetExtraInputData());
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerWidget::RevertSettings()
|
||||
{
|
||||
QList<QString> sections;
|
||||
|
||||
// add all sections from profiles
|
||||
sections.append(this->profiles);
|
||||
|
||||
// add section with profiles list
|
||||
sections.append("Rosalie's Mupen GUI - Input Plugin");
|
||||
|
||||
// revert each section
|
||||
for (QString section : sections)
|
||||
{
|
||||
CoreSettingsRevertSection(section.toStdString());
|
||||
}
|
||||
|
||||
CoreSettingsSave();
|
||||
// delete all sections for added & removed profiles,
|
||||
// which aren't in the base profile section list
|
||||
sections.clear();
|
||||
sections.append(this->addedProfiles);
|
||||
sections.append(this->removedProfiles);
|
||||
for (QString section : sections)
|
||||
{
|
||||
if (!this->profiles.contains(section) &&
|
||||
CoreSettingsSectionExists(section.toStdString()))
|
||||
{
|
||||
CoreSettingsDeleteSection(section.toStdString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerWidget::SetCurrentJoystickID(SDL_JoystickID joystickId)
|
||||
@@ -1071,3 +1445,26 @@ void ControllerWidget::SetCurrentJoystickID(SDL_JoystickID joystickId)
|
||||
this->currentJoystickId = joystickId;
|
||||
}
|
||||
|
||||
void ControllerWidget::SetIsCurrentJoystickGameController(bool isGameController)
|
||||
{
|
||||
this->isCurrentJoystickGameController = isGameController;
|
||||
}
|
||||
|
||||
void ControllerWidget::AddUserProfile(QString name, QString section)
|
||||
{
|
||||
int index = this->profileComboBox->findData(section);
|
||||
if (index == -1)
|
||||
{
|
||||
this->profileComboBox->addItem(name, section);
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerWidget::RemoveUserProfile(QString name, QString section)
|
||||
{
|
||||
int index = this->profileComboBox->findData(section);
|
||||
if (index != -1)
|
||||
{
|
||||
this->profileComboBox->removeItem(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,14 +34,20 @@ class ControllerWidget : public QWidget, Ui::ControllerWidget
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
bool initialized = false;
|
||||
QString settingsSection;
|
||||
|
||||
QString gameSection;
|
||||
|
||||
OptionsDialogSettings optionsDialogSettings;
|
||||
|
||||
QList<QString> inputDeviceNameList;
|
||||
MappingButton* currentButton = nullptr;
|
||||
bool addMappingToButton = false;
|
||||
|
||||
|
||||
QList<QString> profiles;
|
||||
QList<QString> removedProfiles;
|
||||
QList<QString> addedProfiles;
|
||||
|
||||
struct buttonWidgetMapping
|
||||
{
|
||||
enum N64ControllerButton button;
|
||||
@@ -68,7 +74,9 @@ private:
|
||||
QList<buttonSettingMapping> buttonSettingMappings;
|
||||
QList<MappingButton*> setupButtonWidgets;
|
||||
|
||||
void initializeButtons();
|
||||
void initializeMappingButtons();
|
||||
void initializeProfileButtons();
|
||||
void initializeMiscButtons();
|
||||
|
||||
bool isCurrentDeviceKeyboard();
|
||||
bool isCurrentDeviceNotFound();
|
||||
@@ -80,7 +88,19 @@ private:
|
||||
|
||||
QString getCurrentSettingsSection();
|
||||
|
||||
SDL_JoystickID currentJoystickId = -1;
|
||||
QString getUserProfileSectionName(QString profile);
|
||||
|
||||
bool isSectionUserProfile(QString section);
|
||||
bool isSectionGameProfile(QString section);
|
||||
|
||||
void setPluggedIn(bool value);
|
||||
|
||||
void showErrorMessage(QString text, QString details = "");
|
||||
|
||||
SDL_JoystickID currentJoystickId = -1;
|
||||
bool isCurrentJoystickGameController = false;
|
||||
|
||||
int previousProfileComboBoxIndex = -1;
|
||||
|
||||
public:
|
||||
ControllerWidget(QWidget* parent, EventFilter* eventFilter);
|
||||
@@ -96,14 +116,25 @@ public:
|
||||
void GetCurrentInputDevice(QString& deviceName, int& deviceNum, bool ignoreDeviceNotFound = false);
|
||||
bool IsPluggedIn();
|
||||
|
||||
void SetSettingsSection(QString section);
|
||||
void SetSettingsSection(QString profile, QString section);
|
||||
void SetInitialized(bool value);
|
||||
|
||||
void LoadSettings();
|
||||
void LoadSettings(QString section);
|
||||
void LoadSettings(QString section, bool loadUserProfile = false);
|
||||
void LoadUserProfileSettings();
|
||||
|
||||
void SaveDefaultSettings();
|
||||
void SaveSettings();
|
||||
void SaveUserProfileSettings();
|
||||
void SaveSettings(QString section);
|
||||
|
||||
void RevertSettings();
|
||||
|
||||
void SetCurrentJoystickID(SDL_JoystickID joystickId);
|
||||
void SetIsCurrentJoystickGameController(bool isGameController);
|
||||
|
||||
void AddUserProfile(QString name, QString section);
|
||||
void RemoveUserProfile(QString name, QString section);
|
||||
|
||||
private slots:
|
||||
void on_deadZoneSlider_valueChanged(int value);
|
||||
@@ -112,9 +143,8 @@ private slots:
|
||||
|
||||
void on_inputDeviceComboBox_currentIndexChanged(int value);
|
||||
void on_inputDeviceRefreshButton_clicked();
|
||||
|
||||
void on_controllerPluggedCheckBox_toggled(bool value);
|
||||
|
||||
void on_addProfileButton_clicked();
|
||||
void on_removeProfileButton_clicked();
|
||||
|
||||
void on_resetButton_clicked();
|
||||
@@ -130,9 +160,13 @@ public slots:
|
||||
|
||||
void on_MainDialog_SdlEvent(SDL_Event* event);
|
||||
void on_MainDialog_SdlEventPollFinished();
|
||||
|
||||
signals:
|
||||
void CurrentInputDeviceChanged(ControllerWidget* widget, QString deviceName, int deviceNum);
|
||||
void RefreshInputDevicesButtonClicked();
|
||||
|
||||
void UserProfileAdded(QString name, QString section);
|
||||
void UserProfileRemoved(QString name, QString section);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,20 +23,45 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_5">
|
||||
<widget class="QGroupBox" name="profileGroupBox">
|
||||
<property name="title">
|
||||
<string>Profile</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_11">
|
||||
<item>
|
||||
<widget class="QComboBox" name="profileComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToContents</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="addProfileButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="removeProfileButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
@@ -46,7 +71,20 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4">
|
||||
<spacer name="horizontalSpacer_8">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="inputDeviceGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -60,7 +98,7 @@
|
||||
<item>
|
||||
<widget class="QComboBox" name="inputDeviceComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@@ -73,7 +111,7 @@
|
||||
<item>
|
||||
<widget class="QPushButton" name="inputDeviceRefreshButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@@ -86,6 +124,19 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="deadZoneGroupBox">
|
||||
<property name="title">
|
||||
@@ -95,7 +146,7 @@
|
||||
<item>
|
||||
<widget class="QSlider" name="deadZoneSlider">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@@ -109,21 +160,17 @@
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="controllerPluggedCheckBox">
|
||||
<property name="text">
|
||||
<string>Controller is plugged in</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
@@ -157,7 +204,7 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<widget class="QGroupBox" name="dpadGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -368,7 +415,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<widget class="QGroupBox" name="analogStickGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -828,7 +875,7 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<widget class="QGroupBox" name="cButtonsGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -1033,7 +1080,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_6">
|
||||
<widget class="QGroupBox" name="buttonsGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
|
||||
@@ -14,8 +14,10 @@ using namespace UserInterface::Widget;
|
||||
|
||||
MappingButton::MappingButton(QWidget* parent) : QPushButton(parent)
|
||||
{
|
||||
this->countDownTimer = new QTimer(this);
|
||||
this->countDownTimer = new QTimer(this);
|
||||
connect(this->countDownTimer, &QTimer::timeout, this, &MappingButton::on_countDownTimer_triggered);
|
||||
|
||||
this->initialSize = this->size();
|
||||
}
|
||||
|
||||
MappingButton::~MappingButton()
|
||||
@@ -223,7 +225,7 @@ void MappingButton::reloadText()
|
||||
}
|
||||
}
|
||||
|
||||
QString elidedText = fontMetrics.elidedText(text, Qt::ElideRight, this->width());
|
||||
QString elidedText = fontMetrics.elidedText(text, Qt::ElideRight, this->initialSize.width());
|
||||
|
||||
// needed for spacing
|
||||
if (elidedText.isEmpty())
|
||||
|
||||
@@ -36,6 +36,7 @@ struct MappingButtonInputDataType
|
||||
class MappingButton : public QPushButton
|
||||
{
|
||||
private:
|
||||
QSize initialSize;
|
||||
QTimer* countDownTimer = nullptr;
|
||||
ControllerWidget* controllerWidget = nullptr;
|
||||
int currentCountDownNum = 0;
|
||||
|
||||
@@ -83,7 +83,7 @@ void InputDevice::OpenDevice(std::string name, int num)
|
||||
// wait until SDLThread is done first
|
||||
while (this->sdlThread->GetCurrentAction() != SDLThreadAction::None)
|
||||
{
|
||||
QThread::msleep(50);
|
||||
QThread::msleep(5);
|
||||
}
|
||||
|
||||
this->foundDevicesWithNameMatch.clear();
|
||||
|
||||
@@ -31,6 +31,13 @@ enum class N64ControllerButton
|
||||
Invalid
|
||||
};
|
||||
|
||||
enum class InputDeviceType
|
||||
{
|
||||
None = -3,
|
||||
Automatic = -2,
|
||||
Keyboard = -1
|
||||
};
|
||||
|
||||
enum class InputType
|
||||
{
|
||||
Keyboard = -1,
|
||||
|
||||
@@ -49,10 +49,11 @@
|
||||
|
||||
struct InputMapping
|
||||
{
|
||||
std::vector<int> Type;
|
||||
std::vector<int> Data;
|
||||
std::vector<int> ExtraData;
|
||||
int Count = 0;
|
||||
std::vector<std::string> Name;
|
||||
std::vector<int> Type;
|
||||
std::vector<int> Data;
|
||||
std::vector<int> ExtraData;
|
||||
int Count = 0;
|
||||
};
|
||||
|
||||
struct InputProfile
|
||||
@@ -121,15 +122,22 @@ static bool l_KeyboardState[SDL_NUM_SCANCODES];
|
||||
//
|
||||
|
||||
static void load_inputmapping_settings(InputMapping* mapping, std::string section,
|
||||
SettingsID inputTypeSettingsId, SettingsID dataSettingsId, SettingsID extraDataSettingsId)
|
||||
SettingsID inputNameSettingsId, SettingsID inputTypeSettingsId,
|
||||
SettingsID dataSettingsId, SettingsID extraDataSettingsId)
|
||||
{
|
||||
mapping->Name = CoreSettingsGetStringListValue(inputNameSettingsId, section);
|
||||
mapping->Type = CoreSettingsGetIntListValue(inputTypeSettingsId, section);
|
||||
mapping->Data = CoreSettingsGetIntListValue(dataSettingsId, section);
|
||||
mapping->ExtraData = CoreSettingsGetIntListValue(extraDataSettingsId, section);
|
||||
mapping->Count = std::min(mapping->Type.size(), std::min(mapping->Data.size(), mapping->ExtraData.size()));
|
||||
|
||||
if (mapping->Count == 0)
|
||||
{ // try to load old profile type
|
||||
// check if mapping is old profile type,
|
||||
// it should be true when the minimum of the lengths
|
||||
// of all int lists is 0, and the name isn't empty or whitespace
|
||||
if (mapping->Count == 0 &&
|
||||
!mapping->Name.empty() && !mapping->Name.at(0).empty() &&
|
||||
mapping->Name.at(0).find_first_not_of(' ') != std::string::npos)
|
||||
{
|
||||
mapping->Type.push_back(CoreSettingsGetIntValue(inputTypeSettingsId, section));
|
||||
mapping->Data.push_back(CoreSettingsGetIntValue(dataSettingsId, section));
|
||||
mapping->ExtraData.push_back(CoreSettingsGetIntValue(extraDataSettingsId, section));
|
||||
@@ -142,26 +150,60 @@ static void load_settings(void)
|
||||
std::string gameId;
|
||||
CoreRomSettings romSettings;
|
||||
|
||||
std::string userProfileSectionBase = "Rosalie's Mupen GUI - Input Plugin User Profile";
|
||||
std::vector<std::string> userProfiles;
|
||||
std::vector<std::string>::iterator userProfilesIter;
|
||||
|
||||
// try to retrieve current ROM settings
|
||||
if (CoreGetCurrentRomSettings(romSettings))
|
||||
{
|
||||
gameId = romSettings.MD5;
|
||||
}
|
||||
|
||||
// retrieve all user profiles
|
||||
userProfiles = CoreSettingsGetStringListValue(SettingsID::Input_Profiles);
|
||||
|
||||
for (int i = 0; i < NUM_CONTROLLERS; i++)
|
||||
{
|
||||
InputProfile* profile = &l_InputProfiles[i];
|
||||
std::string section = "Rosalie's Mupen GUI - Input Plugin Profile " + std::to_string(i);
|
||||
std::string section = "Rosalie's Mupen GUI - Input Plugin Profile " + std::to_string(i);
|
||||
std::string gameSection = section + " Game " + gameId;
|
||||
std::string userProfileSection;
|
||||
std::string userProfileName;
|
||||
|
||||
// when a user profile has been specified,
|
||||
// ensure it exists in the profile list and
|
||||
// in the settings, if it does,
|
||||
// use that user profile
|
||||
userProfileName = CoreSettingsGetStringValue(SettingsID::Input_UseProfile, section);
|
||||
if (!userProfileName.empty())
|
||||
{
|
||||
userProfilesIter = std::find(userProfiles.begin(), userProfiles.end(), userProfileName);
|
||||
if (userProfilesIter != userProfiles.end())
|
||||
{
|
||||
userProfileSection = userProfileSectionBase;
|
||||
userProfileSection += " \"" + userProfileName + "\"";
|
||||
if (CoreSettingsSectionExists(userProfileSection))
|
||||
{
|
||||
section = userProfileSection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if game ID was retrieved,
|
||||
// check if game section exists, if it does
|
||||
// use that section instead of the main section
|
||||
// check if game section exists,
|
||||
// if it does, check if the 'UseGameProfile' key exists,
|
||||
// if it doesn't then use the profile, else
|
||||
// check if the value is true
|
||||
if (!gameId.empty())
|
||||
{
|
||||
if (CoreSettingsSectionExists(gameSection))
|
||||
{
|
||||
section = gameSection;
|
||||
if (!CoreSettingsKeyExists(gameSection, "UseGameProfile") ||
|
||||
CoreSettingsGetBoolValue(SettingsID::Input_UseGameProfile, gameSection))
|
||||
{
|
||||
section = gameSection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,24 +224,24 @@ static void load_settings(void)
|
||||
profile->GameboySave = CoreSettingsGetStringValue(SettingsID::Input_GameboySave, section);
|
||||
|
||||
// load inputmapping settings
|
||||
load_inputmapping_settings(&profile->Button_A, section, SettingsID::Input_A_InputType, SettingsID::Input_A_Data, SettingsID::Input_A_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_B, section, SettingsID::Input_B_InputType, SettingsID::Input_B_Data, SettingsID::Input_B_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_Start, section, SettingsID::Input_Start_InputType, SettingsID::Input_Start_Data, SettingsID::Input_Start_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadUp, section, SettingsID::Input_DpadUp_InputType, SettingsID::Input_DpadUp_Data, SettingsID::Input_DpadUp_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadDown, section, SettingsID::Input_DpadDown_InputType, SettingsID::Input_DpadDown_Data, SettingsID::Input_DpadDown_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadLeft, section, SettingsID::Input_DpadLeft_InputType, SettingsID::Input_DpadLeft_Data, SettingsID::Input_DpadLeft_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadRight, section, SettingsID::Input_DpadRight_InputType, SettingsID::Input_DpadRight_Data, SettingsID::Input_DpadRight_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonUp, section, SettingsID::Input_CButtonUp_InputType, SettingsID::Input_CButtonUp_Data, SettingsID::Input_CButtonUp_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonDown, section, SettingsID::Input_CButtonDown_InputType, SettingsID::Input_CButtonDown_Data, SettingsID::Input_CButtonDown_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonLeft, section, SettingsID::Input_CButtonLeft_InputType, SettingsID::Input_CButtonLeft_Data, SettingsID::Input_CButtonLeft_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonRight, section, SettingsID::Input_CButtonRight_InputType, SettingsID::Input_CButtonRight_Data, SettingsID::Input_CButtonRight_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_LeftTrigger, section, SettingsID::Input_LeftTrigger_InputType, SettingsID::Input_LeftTrigger_Data, SettingsID::Input_LeftTrigger_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_RightTrigger, section, SettingsID::Input_RightTrigger_InputType, SettingsID::Input_RightTrigger_Data, SettingsID::Input_RightTrigger_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_ZTrigger, section, SettingsID::Input_ZTrigger_InputType, SettingsID::Input_ZTrigger_Data, SettingsID::Input_ZTrigger_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Up, section, SettingsID::Input_AnalogStickUp_InputType, SettingsID::Input_AnalogStickUp_Data, SettingsID::Input_AnalogStickUp_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Down, section, SettingsID::Input_AnalogStickDown_InputType, SettingsID::Input_AnalogStickDown_Data, SettingsID::Input_AnalogStickDown_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Left, section, SettingsID::Input_AnalogStickLeft_InputType, SettingsID::Input_AnalogStickLeft_Data, SettingsID::Input_AnalogStickLeft_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Right, section, SettingsID::Input_AnalogStickRight_InputType, SettingsID::Input_AnalogStickRight_Data, SettingsID::Input_AnalogStickRight_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_A, section, SettingsID::Input_A_Name, SettingsID::Input_A_InputType, SettingsID::Input_A_Data, SettingsID::Input_A_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_B, section, SettingsID::Input_B_Name, SettingsID::Input_B_InputType, SettingsID::Input_B_Data, SettingsID::Input_B_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_Start, section, SettingsID::Input_Start_Name, SettingsID::Input_Start_InputType, SettingsID::Input_Start_Data, SettingsID::Input_Start_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadUp, section, SettingsID::Input_DpadUp_Name, SettingsID::Input_DpadUp_InputType, SettingsID::Input_DpadUp_Data, SettingsID::Input_DpadUp_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadDown, section, SettingsID::Input_DpadDown_Name, SettingsID::Input_DpadDown_InputType, SettingsID::Input_DpadDown_Data, SettingsID::Input_DpadDown_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadLeft, section, SettingsID::Input_DpadLeft_Name, SettingsID::Input_DpadLeft_InputType, SettingsID::Input_DpadLeft_Data, SettingsID::Input_DpadLeft_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_DpadRight, section, SettingsID::Input_DpadRight_Name, SettingsID::Input_DpadRight_InputType, SettingsID::Input_DpadRight_Data, SettingsID::Input_DpadRight_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonUp, section, SettingsID::Input_CButtonUp_Name, SettingsID::Input_CButtonUp_InputType, SettingsID::Input_CButtonUp_Data, SettingsID::Input_CButtonUp_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonDown, section, SettingsID::Input_CButtonDown_Name, SettingsID::Input_CButtonDown_InputType, SettingsID::Input_CButtonDown_Data, SettingsID::Input_CButtonDown_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonLeft, section, SettingsID::Input_CButtonLeft_Name, SettingsID::Input_CButtonLeft_InputType, SettingsID::Input_CButtonLeft_Data, SettingsID::Input_CButtonLeft_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_CButtonRight, section, SettingsID::Input_CButtonRight_Name, SettingsID::Input_CButtonRight_InputType, SettingsID::Input_CButtonRight_Data, SettingsID::Input_CButtonRight_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_LeftTrigger, section, SettingsID::Input_LeftTrigger_Name, SettingsID::Input_LeftTrigger_InputType, SettingsID::Input_LeftTrigger_Data, SettingsID::Input_LeftTrigger_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_RightTrigger, section, SettingsID::Input_RightTrigger_Name, SettingsID::Input_RightTrigger_InputType, SettingsID::Input_RightTrigger_Data, SettingsID::Input_RightTrigger_ExtraData);
|
||||
load_inputmapping_settings(&profile->Button_ZTrigger, section, SettingsID::Input_ZTrigger_Name, SettingsID::Input_ZTrigger_InputType, SettingsID::Input_ZTrigger_Data, SettingsID::Input_ZTrigger_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Up, section, SettingsID::Input_AnalogStickUp_Name, SettingsID::Input_AnalogStickUp_InputType, SettingsID::Input_AnalogStickUp_Data, SettingsID::Input_AnalogStickUp_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Down, section, SettingsID::Input_AnalogStickDown_Name, SettingsID::Input_AnalogStickDown_InputType, SettingsID::Input_AnalogStickDown_Data, SettingsID::Input_AnalogStickDown_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Left, section, SettingsID::Input_AnalogStickLeft_Name, SettingsID::Input_AnalogStickLeft_InputType, SettingsID::Input_AnalogStickLeft_Data, SettingsID::Input_AnalogStickLeft_ExtraData);
|
||||
load_inputmapping_settings(&profile->AnalogStick_Right, section, SettingsID::Input_AnalogStickRight_Name, SettingsID::Input_AnalogStickRight_InputType, SettingsID::Input_AnalogStickRight_Data, SettingsID::Input_AnalogStickRight_ExtraData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,15 +311,90 @@ static void apply_gameboy_settings(void)
|
||||
CoreSettingsSave();
|
||||
}
|
||||
|
||||
static void setup_device_automatic(int num, InputProfile* profile)
|
||||
{
|
||||
static int previousSdlDeviceNum = -1;
|
||||
|
||||
// reset variable when needed
|
||||
if (num == 0)
|
||||
{
|
||||
previousSdlDeviceNum = -1;
|
||||
}
|
||||
|
||||
bool foundSdlDevice = false;
|
||||
SDL_GameController* sdlGameController = nullptr;
|
||||
SDL_Joystick* sdlJoystick = nullptr;
|
||||
std::string sdlDeviceName;
|
||||
|
||||
for (int i = (previousSdlDeviceNum + 1); i < SDL_NumJoysticks(); i++)
|
||||
{
|
||||
if (SDL_IsGameController(i))
|
||||
{
|
||||
sdlGameController = SDL_GameControllerOpen(i);
|
||||
if (sdlGameController != nullptr)
|
||||
{
|
||||
sdlDeviceName = SDL_GameControllerName(sdlGameController);
|
||||
SDL_GameControllerClose(sdlGameController);
|
||||
profile->DeviceNum = i;
|
||||
profile->DeviceName = sdlDeviceName;
|
||||
previousSdlDeviceNum = i;
|
||||
foundSdlDevice = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sdlJoystick = SDL_JoystickOpen(i);
|
||||
if (sdlJoystick != nullptr)
|
||||
{
|
||||
sdlDeviceName = SDL_JoystickName(sdlJoystick);
|
||||
SDL_JoystickClose(sdlJoystick);
|
||||
profile->DeviceNum = i;
|
||||
profile->DeviceName = sdlDeviceName;
|
||||
previousSdlDeviceNum = i;
|
||||
foundSdlDevice = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundSdlDevice)
|
||||
{ // fallback to keyboard
|
||||
if (num == 0)
|
||||
{
|
||||
profile->DeviceNum = (int)InputDeviceType::Keyboard;
|
||||
}
|
||||
else
|
||||
{
|
||||
profile->PluggedIn = false;
|
||||
|
||||
// only override present in core
|
||||
// when we have the control info
|
||||
if (l_HasControlInfo)
|
||||
{
|
||||
l_ControlInfo.Controls[num].Present = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void open_controllers(void)
|
||||
{
|
||||
// force re-fresh joystick list
|
||||
SDL_JoystickUpdate();
|
||||
|
||||
for (int i = 0; i < NUM_CONTROLLERS; i++)
|
||||
{
|
||||
InputProfile* profile = &l_InputProfiles[i];
|
||||
|
||||
profile->InputDevice.CloseDevice();
|
||||
|
||||
if (profile->DeviceNum != -1)
|
||||
if (profile->DeviceNum == (int)InputDeviceType::Automatic)
|
||||
{
|
||||
setup_device_automatic(i, profile);
|
||||
}
|
||||
|
||||
if (profile->DeviceNum != (int)InputDeviceType::Keyboard)
|
||||
{
|
||||
profile->InputDevice.OpenDevice(profile->DeviceName, profile->DeviceNum);
|
||||
}
|
||||
@@ -294,7 +411,7 @@ static void close_controllers(void)
|
||||
}
|
||||
}
|
||||
|
||||
static int get_button_state(InputProfile* profile, InputMapping* inputMapping)
|
||||
static int get_button_state(InputProfile* profile, const InputMapping* inputMapping)
|
||||
{
|
||||
int state = 0;
|
||||
|
||||
@@ -336,9 +453,10 @@ static int get_button_state(InputProfile* profile, InputMapping* inputMapping)
|
||||
}
|
||||
|
||||
// returns axis input scaled to the range [-1, 1]
|
||||
static double get_axis_state(InputProfile* profile, InputMapping* inputMapping, int direction, double value)
|
||||
static double get_axis_state(InputProfile* profile, const InputMapping* inputMapping, const int direction, const double value, bool& useButtonMapping)
|
||||
{
|
||||
int state = 0;
|
||||
double axis_state = value;
|
||||
bool button_state = false;
|
||||
|
||||
for (int i = 0; i < inputMapping->Count; i++)
|
||||
{
|
||||
@@ -349,8 +467,7 @@ static double get_axis_state(InputProfile* profile, InputMapping* inputMapping,
|
||||
{
|
||||
case InputType::GamepadButton:
|
||||
{
|
||||
int buttonState = SDL_GameControllerGetButton(profile->InputDevice.GetGameControllerHandle(), (SDL_GameControllerButton)data);
|
||||
state |= buttonState;
|
||||
button_state |= SDL_GameControllerGetButton(profile->InputDevice.GetGameControllerHandle(), (SDL_GameControllerButton)data);;
|
||||
} break;
|
||||
case InputType::GamepadAxis:
|
||||
{
|
||||
@@ -359,13 +476,12 @@ static double get_axis_state(InputProfile* profile, InputMapping* inputMapping,
|
||||
{
|
||||
axis_value = (axis_value / SDL_AXIS_PEAK);
|
||||
axis_value = abs(axis_value) * direction;
|
||||
return axis_value;
|
||||
axis_state = axis_value;
|
||||
}
|
||||
} break;
|
||||
case InputType::JoystickButton:
|
||||
{
|
||||
int buttonState = SDL_JoystickGetButton(profile->InputDevice.GetJoystickHandle(), data);
|
||||
state |= buttonState;
|
||||
button_state |= SDL_JoystickGetButton(profile->InputDevice.GetJoystickHandle(), data);;
|
||||
} break;
|
||||
case InputType::JoystickAxis:
|
||||
{
|
||||
@@ -374,22 +490,37 @@ static double get_axis_state(InputProfile* profile, InputMapping* inputMapping,
|
||||
{
|
||||
axis_value = (axis_value / SDL_AXIS_PEAK);
|
||||
axis_value = abs(axis_value) * direction;
|
||||
return axis_value;
|
||||
axis_state = axis_value;
|
||||
}
|
||||
} break;
|
||||
case InputType::Keyboard:
|
||||
{
|
||||
state |= l_KeyboardState[data];
|
||||
button_state |= l_KeyboardState[data];
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return state ? direction : value;
|
||||
// when a button has been mapped
|
||||
// to an axis, we should prioritize
|
||||
// the button when it's been pressed
|
||||
if (button_state)
|
||||
{
|
||||
useButtonMapping = true;
|
||||
return direction;
|
||||
}
|
||||
else if (!useButtonMapping)
|
||||
{
|
||||
return axis_state;
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
static double simulate_deadzone(double n64InputAxis, double maxAxis, int deadzone, double axisRange)
|
||||
static double simulate_deadzone(const double n64InputAxis, const double maxAxis, const int deadzone, const double axisRange)
|
||||
{
|
||||
double axisAbsolute = std::abs(n64InputAxis);
|
||||
|
||||
@@ -407,12 +538,12 @@ static double simulate_deadzone(double n64InputAxis, double maxAxis, int deadzon
|
||||
}
|
||||
|
||||
// Credit: MerryMage
|
||||
static void simulate_octagon(double inputX, double inputY, double deadzoneFactor, int& outputX, int& outputY)
|
||||
static void simulate_octagon(const double inputX, const double inputY, const double deadzoneFactor, int& outputX, int& outputY)
|
||||
{
|
||||
double maxAxis = N64_AXIS_PEAK;
|
||||
double maxDiagonal = MAX_DIAGONAL_VALUE;
|
||||
int deadzone = static_cast<int>(deadzoneFactor * N64_AXIS_PEAK);
|
||||
double axisRange = maxAxis - deadzone;
|
||||
const double maxAxis = N64_AXIS_PEAK;
|
||||
const double maxDiagonal = MAX_DIAGONAL_VALUE;
|
||||
const int deadzone = (int)(deadzoneFactor * N64_AXIS_PEAK);
|
||||
const double axisRange = maxAxis - deadzone;
|
||||
// scale to [-maxAxis, maxAxis]
|
||||
double ax = inputX * maxAxis;
|
||||
double ay = inputY * maxAxis;
|
||||
@@ -436,18 +567,18 @@ static void simulate_octagon(double inputX, double inputY, double deadzoneFactor
|
||||
// bound diagonals to an octagonal range [-69, 69]
|
||||
if (ax != 0.0 && ay != 0.0)
|
||||
{
|
||||
double slope = ay / ax;
|
||||
double edgex = copysign(maxAxis / (std::abs(slope) + (maxAxis - maxDiagonal) / maxDiagonal), ax);
|
||||
double edgey = copysign(std::min(std::abs(edgex * slope), maxAxis / (1.0 / std::abs(slope) + (maxAxis - maxDiagonal) / maxDiagonal)), ay);
|
||||
const double slope = ay / ax;
|
||||
double edgex = std::copysign(maxAxis / (std::abs(slope) + (maxAxis - maxDiagonal) / maxDiagonal), ax);
|
||||
const double edgey = std::copysign(std::min(std::abs(edgex * slope), maxAxis / (1.0 / std::abs(slope) + (maxAxis - maxDiagonal) / maxDiagonal)), ay);
|
||||
edgex = edgey / slope;
|
||||
|
||||
double scale = std::sqrt(edgex*edgex + edgey*edgey) / maxAxis;
|
||||
const double scale = std::sqrt(edgex*edgex + edgey*edgey) / maxAxis;
|
||||
ax *= scale;
|
||||
ay *= scale;
|
||||
}
|
||||
|
||||
outputX = static_cast<int>(ax);
|
||||
outputY = static_cast<int>(ay);
|
||||
outputX = (int)ax;
|
||||
outputY = (int)ay;
|
||||
}
|
||||
|
||||
static unsigned char data_crc(unsigned char *data, int length)
|
||||
@@ -476,18 +607,26 @@ static unsigned char data_crc(unsigned char *data, int length)
|
||||
|
||||
void sdl_init()
|
||||
{
|
||||
for (int subsystem : {SDL_INIT_GAMECONTROLLER, SDL_INIT_VIDEO, SDL_INIT_HAPTIC})
|
||||
std::filesystem::path gameControllerDbPath;
|
||||
|
||||
for (const int subsystem : {SDL_INIT_GAMECONTROLLER, SDL_INIT_VIDEO, SDL_INIT_HAPTIC})
|
||||
{
|
||||
if (!SDL_WasInit(subsystem))
|
||||
{
|
||||
SDL_InitSubSystem(subsystem);
|
||||
}
|
||||
}
|
||||
|
||||
gameControllerDbPath = CoreGetSharedDataDirectory();
|
||||
gameControllerDbPath += "/gamecontrollerdb.txt";
|
||||
|
||||
// load mappings from file
|
||||
SDL_GameControllerAddMappingsFromFile(gameControllerDbPath.string().c_str());
|
||||
}
|
||||
|
||||
void sdl_quit()
|
||||
{
|
||||
for (int subsystem : {SDL_INIT_GAMECONTROLLER, SDL_INIT_HAPTIC})
|
||||
for (const int subsystem : {SDL_INIT_GAMECONTROLLER, SDL_INIT_HAPTIC})
|
||||
{
|
||||
if (SDL_WasInit(subsystem))
|
||||
{
|
||||
@@ -596,6 +735,13 @@ EXPORT m64p_error CALL PluginConfig()
|
||||
dialog.exec();
|
||||
|
||||
l_SDLThread->SetAction(SDLThreadAction::None);
|
||||
|
||||
// wait until it's done pumping events
|
||||
while (l_SDLThread->GetCurrentAction() == SDLThreadAction::SDLPumpEvents)
|
||||
{
|
||||
QThread::msleep(5);
|
||||
}
|
||||
|
||||
|
||||
// reload settings
|
||||
load_settings();
|
||||
@@ -686,13 +832,13 @@ EXPORT void CALL GetKeys(int Control, BUTTONS* Keys)
|
||||
// check if device has been disconnected,
|
||||
// if it has, try to open it again,
|
||||
// only do this every 2 seconds to prevent lag
|
||||
const auto currentTime = std::chrono::high_resolution_clock::now();
|
||||
const auto currentTime = std::chrono::high_resolution_clock::now();
|
||||
const int secondsPassed = std::chrono::duration_cast<std::chrono::seconds>(currentTime - profile->LastDeviceCheckTime).count();
|
||||
if (secondsPassed >= 2)
|
||||
{
|
||||
profile->LastDeviceCheckTime = currentTime;
|
||||
|
||||
if (profile->DeviceNum != -1)
|
||||
if (profile->DeviceNum != (int)InputDeviceType::Keyboard)
|
||||
{
|
||||
if (profile->InputDevice.IsOpeningDevice())
|
||||
{
|
||||
@@ -702,7 +848,6 @@ EXPORT void CALL GetKeys(int Control, BUTTONS* Keys)
|
||||
if (!profile->InputDevice.HasOpenDevice() || !profile->InputDevice.IsAttached())
|
||||
{
|
||||
profile->InputDevice.OpenDevice(profile->DeviceName, profile->DeviceNum);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -723,10 +868,11 @@ EXPORT void CALL GetKeys(int Control, BUTTONS* Keys)
|
||||
Keys->Z_TRIG = get_button_state(profile, &profile->Button_ZTrigger);
|
||||
|
||||
double inputX = 0, inputY = 0;
|
||||
inputY = get_axis_state(profile, &profile->AnalogStick_Up, 1, inputY);
|
||||
inputY = get_axis_state(profile, &profile->AnalogStick_Down, -1, inputY);
|
||||
inputX = get_axis_state(profile, &profile->AnalogStick_Left, -1, inputX);
|
||||
inputX = get_axis_state(profile, &profile->AnalogStick_Right, 1, inputX);
|
||||
bool useButtonMapping = false;
|
||||
inputY = get_axis_state(profile, &profile->AnalogStick_Up, 1, inputY, useButtonMapping);
|
||||
inputY = get_axis_state(profile, &profile->AnalogStick_Down, -1, inputY, useButtonMapping);
|
||||
inputX = get_axis_state(profile, &profile->AnalogStick_Left, -1, inputX, useButtonMapping);
|
||||
inputX = get_axis_state(profile, &profile->AnalogStick_Right, 1, inputX, useButtonMapping);
|
||||
|
||||
int octagonX = 0, octagonY = 0;
|
||||
simulate_octagon(
|
||||
@@ -748,7 +894,7 @@ EXPORT void CALL InitiateControllers(CONTROL_INFO ControlInfo)
|
||||
l_KeyboardState[i] = 0;
|
||||
}
|
||||
|
||||
l_ControlInfo = ControlInfo;
|
||||
l_ControlInfo = ControlInfo;
|
||||
l_HasControlInfo = true;
|
||||
|
||||
// load settings
|
||||
@@ -772,6 +918,7 @@ EXPORT int CALL RomOpen(void)
|
||||
|
||||
EXPORT void CALL RomClosed(void)
|
||||
{
|
||||
l_HasControlInfo = false;
|
||||
close_controllers();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,18 @@ if (UPDATER)
|
||||
add_definitions(-DUPDATER)
|
||||
endif(UPDATER)
|
||||
|
||||
if (APPIMAGE_UPDATER)
|
||||
add_definitions(-DAPPIMAGE_UPDATER)
|
||||
endif(APPIMAGE_UPDATER)
|
||||
|
||||
if (DISCORD_RPC)
|
||||
add_definitions(-DDISCORD_RPC)
|
||||
endif(DISCORD_RPC)
|
||||
|
||||
if (FORCE_XCB)
|
||||
add_definitions(-DFORCE_XCB)
|
||||
endif(FORCE_XCB)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(SDL2 REQUIRED sdl2)
|
||||
|
||||
|
||||
@@ -36,11 +36,12 @@ bool CoreCallbacks::Init(void)
|
||||
{
|
||||
// needed for Qt
|
||||
qRegisterMetaType<CoreDebugMessageType>("CoreDebugMessageType");
|
||||
qRegisterMetaType<CoreDebugMessageType>("CoreStateCallbackType");
|
||||
|
||||
this->LoadSettings();
|
||||
|
||||
l_CoreCallbacks = this;
|
||||
return CoreSetupCallbacks(this->coreDebugCallback);
|
||||
return CoreSetupCallbacks(this->coreDebugCallback, this->coreStateCallback);
|
||||
}
|
||||
|
||||
void CoreCallbacks::Stop(void)
|
||||
@@ -66,5 +67,25 @@ void CoreCallbacks::coreDebugCallback(CoreDebugMessageType type, std::string con
|
||||
return;
|
||||
}
|
||||
|
||||
// skip non-fatal interpreter errors
|
||||
if (type == CoreDebugMessageType::Error)
|
||||
{
|
||||
if (message == "DIV_S by 0" ||
|
||||
message == "DIV_D by 0")
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
emit l_CoreCallbacks->OnCoreDebugCallback(type, QString::fromStdString(context), QString::fromStdString(message));
|
||||
}
|
||||
|
||||
void CoreCallbacks::coreStateCallback(CoreStateCallbackType type, int value)
|
||||
{
|
||||
if (l_CoreCallbacks == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
emit l_CoreCallbacks->OnCoreStateCallback(type, value);
|
||||
}
|
||||
|
||||
@@ -31,9 +31,11 @@ public:
|
||||
|
||||
private:
|
||||
static void coreDebugCallback(CoreDebugMessageType type, std::string context, std::string message);
|
||||
static void coreStateCallback(CoreStateCallbackType type, int value);
|
||||
|
||||
signals:
|
||||
void OnCoreDebugCallback(CoreDebugMessageType type, QString context, QString message);
|
||||
void OnCoreStateCallback(CoreStateCallbackType type, int value);
|
||||
};
|
||||
|
||||
#endif // RMG_CALLBACKS_HPP
|
||||
|
||||
@@ -22,25 +22,22 @@ EmulationThread::~EmulationThread(void)
|
||||
|
||||
void EmulationThread::SetRomFile(QString file)
|
||||
{
|
||||
this->rom_File = file;
|
||||
this->rom = file;
|
||||
}
|
||||
|
||||
void EmulationThread::SetDiskFile(QString file)
|
||||
{
|
||||
this->disk_File = file;
|
||||
this->disk = file;
|
||||
}
|
||||
|
||||
void EmulationThread::run(void)
|
||||
{
|
||||
emit this->on_Emulation_Started();
|
||||
|
||||
bool ret;
|
||||
|
||||
ret = CoreStartEmulation(this->rom_File.toStdU32String(), this->disk_File.toStdU32String());
|
||||
|
||||
bool ret = CoreStartEmulation(this->rom.toStdU32String(), this->disk.toStdU32String());
|
||||
if (!ret)
|
||||
{
|
||||
this->error_Message = QString::fromStdString(CoreGetError());
|
||||
this->errorMessage = QString::fromStdString(CoreGetError());
|
||||
}
|
||||
|
||||
emit this->on_Emulation_Finished(ret);
|
||||
@@ -48,5 +45,5 @@ void EmulationThread::run(void)
|
||||
|
||||
QString EmulationThread::GetLastError(void)
|
||||
{
|
||||
return this->error_Message;
|
||||
return this->errorMessage;
|
||||
}
|
||||
|
||||
@@ -32,9 +32,9 @@ class EmulationThread : public QThread
|
||||
QString GetLastError(void);
|
||||
|
||||
private:
|
||||
QString rom_File;
|
||||
QString disk_File;
|
||||
QString error_Message;
|
||||
QString rom;
|
||||
QString disk;
|
||||
QString errorMessage;
|
||||
|
||||
signals:
|
||||
void on_Emulation_Started(void);
|
||||
|
||||
@@ -95,14 +95,11 @@ void RomSearcherThread::searchDirectory(QString directory)
|
||||
}
|
||||
else
|
||||
{ // no cache entry
|
||||
// open rom, retrieve rom settings & header
|
||||
// open rom, retrieve rom settings, header & type
|
||||
ret = CoreOpenRom(file.toStdU32String()) &&
|
||||
CoreGetCurrentRomSettings(settings) &&
|
||||
CoreGetCurrentRomHeader(header);
|
||||
if (ret)
|
||||
{
|
||||
type = CoreGetRomType();
|
||||
}
|
||||
CoreGetCurrentRomHeader(header) &&
|
||||
CoreGetRomType(type);
|
||||
// always close the ROM,
|
||||
// even when retrieving rom info failed
|
||||
ret = CoreCloseRom() && ret;
|
||||
@@ -111,7 +108,7 @@ void RomSearcherThread::searchDirectory(QString directory)
|
||||
CoreAddCachedRomHeaderAndSettings(file.toStdU32String(), type, header, settings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (ret)
|
||||
{
|
||||
if (count++ >= this->maxItems)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
#ifndef ROMSEARCHERTHREAD_HPP
|
||||
#define ROMSEARCHERTHREAD_HPP
|
||||
|
||||
/*
|
||||
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
||||
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
|
||||
@@ -10,6 +7,8 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef ROMSEARCHERTHREAD_HPP
|
||||
#define ROMSEARCHERTHREAD_HPP
|
||||
|
||||
#include <QString>
|
||||
#include <QThread>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <QNetworkReply>
|
||||
#include <QTemporaryDir>
|
||||
#include <QFile>
|
||||
#include <QProcess>
|
||||
|
||||
using namespace UserInterface::Dialog;
|
||||
|
||||
@@ -79,6 +80,9 @@ void DownloadUpdateDialog::on_reply_finished(void)
|
||||
return;
|
||||
}
|
||||
|
||||
QString filePath;
|
||||
|
||||
#ifndef APPIMAGE_UPDATER
|
||||
QTemporaryDir temporaryDir;
|
||||
temporaryDir.setAutoRemove(false);
|
||||
if (!temporaryDir.isValid())
|
||||
@@ -90,12 +94,52 @@ void DownloadUpdateDialog::on_reply_finished(void)
|
||||
}
|
||||
|
||||
this->temporaryDirectory = temporaryDir.path();
|
||||
filePath = temporaryDir.filePath(this->filename);
|
||||
#else
|
||||
const char* appImageEnv = std::getenv("APPIMAGE");
|
||||
if (appImageEnv == nullptr ||
|
||||
!QFile(appImageEnv).exists())
|
||||
{
|
||||
this->showErrorMessage("APPIMAGE variable is empty or invalid!", "");
|
||||
this->reply->deleteLater();
|
||||
this->reject();
|
||||
return;
|
||||
}
|
||||
|
||||
filePath = appImageEnv;
|
||||
filePath += ".update";
|
||||
#endif // APPIMAGE_UPDATER
|
||||
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
|
||||
{
|
||||
this->showErrorMessage("QFile::open() Failed!", "");
|
||||
this->reply->deleteLater();
|
||||
this->reject();
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(temporaryDir.filePath(this->filename));
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(this->reply->readAll());
|
||||
#ifdef APPIMAGE_UPDATER
|
||||
file.setPermissions(QFile(appImageEnv).permissions());
|
||||
#endif // APPIMAGE_UPDATER
|
||||
file.close();
|
||||
|
||||
#ifdef APPIMAGE_UPDATER
|
||||
int ret = std::rename(filePath.toStdString().c_str(), appImageEnv);
|
||||
if (ret != 0)
|
||||
{
|
||||
this->showErrorMessage("std::rename() Failed!", QString(strerror(errno)));
|
||||
this->reply->deleteLater();
|
||||
this->reject();
|
||||
return;
|
||||
}
|
||||
|
||||
QProcess process;
|
||||
process.setProgram(appImageEnv);
|
||||
process.startDetached();
|
||||
#endif // APPIMAGE_UPDATER
|
||||
|
||||
this->reply->deleteLater();
|
||||
this->accept();
|
||||
}
|
||||
|
||||
@@ -39,23 +39,45 @@ void InstallUpdateDialog::install(void)
|
||||
|
||||
QString appPath = QCoreApplication::applicationDirPath();
|
||||
QString appPid = QString::number(QCoreApplication::applicationPid());
|
||||
QString logPath = appPath + "/Cache/updater.log";
|
||||
|
||||
// convert paths to use the right path seperator
|
||||
this->temporaryDirectory = QDir::toNativeSeparators(this->temporaryDirectory);
|
||||
fullFilePath = QDir::toNativeSeparators(fullFilePath);
|
||||
appPath = QDir::toNativeSeparators(appPath);
|
||||
logPath = QDir::toNativeSeparators(logPath);
|
||||
|
||||
// remove log file when it exists
|
||||
QFile qLogFile(logPath);
|
||||
if (qLogFile.exists())
|
||||
{
|
||||
qLogFile.remove();
|
||||
}
|
||||
|
||||
QString outputToLogLine = " >> \"" + logPath + "\" 2>&1";
|
||||
|
||||
#ifdef _WIN32
|
||||
if (this->filename.endsWith(".exe"))
|
||||
{
|
||||
this->label->setText("Executing " + this->filename + "...");
|
||||
QStringList scriptLines =
|
||||
QStringList scriptLines =
|
||||
{
|
||||
"@echo off",
|
||||
"taskkill /F /PID:" + appPid,
|
||||
"\"" + fullFilePath + "\" /CLOSEAPPLICATIONS /SILENT /DIR=\"" + appPath + "\"",
|
||||
"(",
|
||||
" echo == Attemping to kill PID " + appPid + outputToLogLine,
|
||||
" taskkill /F /PID:" + appPid + outputToLogLine,
|
||||
" echo == Attemping to start \'" + fullFilePath + "\'" + outputToLogLine,
|
||||
" \"" + fullFilePath + "\" /CLOSEAPPLICATIONS /NOCANCEL /SILENT /DIR=\"" + appPath + "\"" + outputToLogLine,
|
||||
")",
|
||||
"IF NOT ERRORLEVEL 0 (",
|
||||
" start \"\" cmd /c \"echo Rosalie's Mupen GUI failed to update, check the updater.log file in the Cache directory for more information && pause\"",
|
||||
")",
|
||||
// remove temporary directory at last
|
||||
"rmdir /S /Q \"" + this->temporaryDirectory + "\"",
|
||||
};
|
||||
this->writeAndRunScript(scriptLines);
|
||||
this->accept();
|
||||
return;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
this->label->setText("Extracting " + this->filename + "...");
|
||||
this->progressBar->setValue(50);
|
||||
@@ -77,47 +99,40 @@ void InstallUpdateDialog::install(void)
|
||||
this->showErrorMessage("CoreUnzip() Failed!", QString::fromStdString(CoreGetError()));
|
||||
this->reject();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->label->setText("Executing update script...");
|
||||
this->progressBar->setValue(100);
|
||||
|
||||
#ifdef _WIN32
|
||||
extractDirectory = QDir::toNativeSeparators(extractDirectory);
|
||||
|
||||
QStringList scriptLines =
|
||||
{
|
||||
"@echo off",
|
||||
"del /F /Q \"" + fullFilePath + "\"",
|
||||
"taskkill /F /PID:" + appPid,
|
||||
"xcopy /S /Y /I \"" + extractDirectory + "\\*\" \"" + appPath + "/\"",
|
||||
"start \"\" \"" + appPath + "\\RMG.exe\"",
|
||||
"rmdir /S /Q \"" + this->temporaryDirectory + "\"",
|
||||
"(",
|
||||
" echo == Attempting to remove \'" + fullFilePath + "\'" + outputToLogLine,
|
||||
" del /F /Q \"" + fullFilePath + "\"" + outputToLogLine,
|
||||
" echo == Attemping to kill PID " + appPid + outputToLogLine,
|
||||
" taskkill /F /PID:" + appPid + outputToLogLine,
|
||||
" echo == Attemping to copy \'" + extractDirectory + "\' to \'" + appPath + "\'" + outputToLogLine,
|
||||
" xcopy /S /Y /I \"" + extractDirectory + "\\*\" \"" + appPath + "\"" + outputToLogLine,
|
||||
" echo == Attemping to start \'" + appPath + "\\RMG.exe\'" + outputToLogLine,
|
||||
" start \"\" \"" + appPath + "\\RMG.exe\"" + outputToLogLine,
|
||||
")",
|
||||
"IF NOT ERRORLEVEL 0 (",
|
||||
" start \"\" cmd /c \"echo Rosalie's Mupen GUI failed to update, check the updater.log file in the Cache directory for more information && pause\"",
|
||||
")",
|
||||
// remove temporary directory at last
|
||||
"rmdir /S /Q \"" + this->temporaryDirectory + "\"",
|
||||
};
|
||||
this->writeAndRunScript(scriptLines);
|
||||
#else // Linux
|
||||
QStringList scriptLines =
|
||||
{
|
||||
"#!/usr/bin/env bash",
|
||||
"set -e",
|
||||
"rm -rf \"" + fullFilePath + "\"",
|
||||
"kill -1 " + appPid,
|
||||
"cp -R \"" + extractDirectory + "/\"* \"" + appPath + "/\"",
|
||||
"chmod +x \"" + appPath + "/RMG\"",
|
||||
"\"" + appPath + "/RMG\"&",
|
||||
"rm -rf \"" + this->temporaryDirectory + "\"" ,
|
||||
};
|
||||
this->writeAndRunScript(scriptLines);
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
void InstallUpdateDialog::writeAndRunScript(QStringList stringList)
|
||||
{
|
||||
QString scriptPath;
|
||||
scriptPath = this->temporaryDirectory;
|
||||
#ifdef _WIN32
|
||||
scriptPath += "/update.bat";
|
||||
#else
|
||||
scriptPath += "/update.sh";
|
||||
#endif // _WIN32
|
||||
|
||||
QFile scriptFile(scriptPath);
|
||||
if (!scriptFile.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||
|
||||
@@ -385,6 +385,7 @@ void SettingsDialog::loadInterfaceEmulationSettings(void)
|
||||
void SettingsDialog::loadInterfaceRomBrowserSettings(void)
|
||||
{
|
||||
this->searchSubDirectoriesCheckbox->setChecked(CoreSettingsGetBoolValue(SettingsID::RomBrowser_Recursive));
|
||||
this->sortRomBrowserResultsCheckBox->setChecked(CoreSettingsGetBoolValue(SettingsID::RomBrowser_SortAfterSearch));
|
||||
this->romSearchLimitSpinBox->setValue(CoreSettingsGetIntValue(SettingsID::RomBrowser_MaxItems));
|
||||
}
|
||||
|
||||
@@ -405,7 +406,24 @@ void SettingsDialog::loadInterfaceOSDSettings(void)
|
||||
|
||||
void SettingsDialog::loadInterfaceMiscSettings(void)
|
||||
{
|
||||
// find stylesheets and add them to the UI
|
||||
QString directory;
|
||||
directory = QString::fromStdString(CoreGetSharedDataDirectory().string());
|
||||
directory += "/Styles/";
|
||||
|
||||
QStringList filter;
|
||||
filter << "*.qss";
|
||||
|
||||
QDirIterator stylesDirectoryIter(directory, filter, QDir::Files, QDirIterator::NoIteratorFlags);
|
||||
while (stylesDirectoryIter.hasNext())
|
||||
{
|
||||
QFileInfo fileInfo(stylesDirectoryIter.next());
|
||||
this->themeComboBox->addItem(fileInfo.fileName());
|
||||
}
|
||||
|
||||
// select currently chosen theme in UI
|
||||
this->themeComboBox->setCurrentText(QString::fromStdString(CoreSettingsGetStringValue(SettingsID::GUI_Theme)));
|
||||
this->iconThemeComboBox->setCurrentText(QString::fromStdString(CoreSettingsGetStringValue(SettingsID::GUI_IconTheme)));
|
||||
#ifdef UPDATER
|
||||
this->checkForUpdatesCheckBox->setChecked(CoreSettingsGetBoolValue(SettingsID::GUI_CheckForUpdates));
|
||||
#endif // UPDATER
|
||||
@@ -527,6 +545,7 @@ void SettingsDialog::loadDefaultInterfaceEmulationSettings(void)
|
||||
void SettingsDialog::loadDefaultInterfaceRomBrowserSettings(void)
|
||||
{
|
||||
this->searchSubDirectoriesCheckbox->setChecked(CoreSettingsGetDefaultBoolValue(SettingsID::RomBrowser_Recursive));
|
||||
this->sortRomBrowserResultsCheckBox->setChecked(CoreSettingsGetDefaultBoolValue(SettingsID::RomBrowser_SortAfterSearch));
|
||||
this->romSearchLimitSpinBox->setValue(CoreSettingsGetDefaultIntValue(SettingsID::RomBrowser_MaxItems));
|
||||
}
|
||||
|
||||
@@ -548,6 +567,7 @@ void SettingsDialog::loadDefaultInterfaceOSDSettings(void)
|
||||
void SettingsDialog::loadDefaultInterfaceMiscSettings(void)
|
||||
{
|
||||
this->themeComboBox->setCurrentText(QString::fromStdString(CoreSettingsGetDefaultStringValue(SettingsID::GUI_Theme)));
|
||||
this->iconThemeComboBox->setCurrentText(QString::fromStdString(CoreSettingsGetDefaultStringValue(SettingsID::GUI_IconTheme)));
|
||||
#ifdef UPDATER
|
||||
this->checkForUpdatesCheckBox->setChecked(CoreSettingsGetDefaultBoolValue(SettingsID::GUI_CheckForUpdates));
|
||||
#endif // UPDATER
|
||||
@@ -730,6 +750,7 @@ void SettingsDialog::saveInterfaceEmulationSettings(void)
|
||||
void SettingsDialog::saveInterfaceRomBrowserSettings(void)
|
||||
{
|
||||
CoreSettingsSetValue(SettingsID::RomBrowser_Recursive, this->searchSubDirectoriesCheckbox->isChecked());
|
||||
CoreSettingsSetValue(SettingsID::RomBrowser_SortAfterSearch, this->sortRomBrowserResultsCheckBox->isChecked());
|
||||
CoreSettingsSetValue(SettingsID::RomBrowser_MaxItems, this->romSearchLimitSpinBox->value());
|
||||
}
|
||||
|
||||
@@ -751,6 +772,7 @@ void SettingsDialog::saveInterfaceOSDSettings(void)
|
||||
void SettingsDialog::saveInterfaceMiscSettings(void)
|
||||
{
|
||||
CoreSettingsSetValue(SettingsID::GUI_Theme, this->themeComboBox->currentText().toStdString());
|
||||
CoreSettingsSetValue(SettingsID::GUI_IconTheme, this->iconThemeComboBox->currentText().toStdString());
|
||||
#ifdef UPDATER
|
||||
CoreSettingsSetValue(SettingsID::GUI_CheckForUpdates, this->checkForUpdatesCheckBox->isChecked());
|
||||
#endif // UPDATER
|
||||
@@ -789,6 +811,22 @@ void SettingsDialog::commonHotkeySettings(SettingsDialogAction action)
|
||||
{ this->gsButtonKeyButton, SettingsID::KeyBinding_GSButton },
|
||||
};
|
||||
|
||||
std::vector<keybinding> keybindings_SpeedFactor =
|
||||
{
|
||||
{ this->speedFactor25KeyButton, SettingsID::KeyBinding_SpeedFactor25 },
|
||||
{ this->speedFactor50KeyButton, SettingsID::KeyBinding_SpeedFactor50 },
|
||||
{ this->speedFactor75KeyButton, SettingsID::KeyBinding_SpeedFactor75 },
|
||||
{ this->speedFactor100KeyButton, SettingsID::KeyBinding_SpeedFactor100 },
|
||||
{ this->speedFactor125KeyButton, SettingsID::KeyBinding_SpeedFactor125 },
|
||||
{ this->speedFactor150KeyButton, SettingsID::KeyBinding_SpeedFactor150 },
|
||||
{ this->speedFactor175KeyButton, SettingsID::KeyBinding_SpeedFactor175 },
|
||||
{ this->speedFactor200KeyButton, SettingsID::KeyBinding_SpeedFactor200 },
|
||||
{ this->speedFactor225KeyButton, SettingsID::KeyBinding_SpeedFactor225 },
|
||||
{ this->speedFactor250KeyButton, SettingsID::KeyBinding_SpeedFactor250 },
|
||||
{ this->speedFactor275KeyButton, SettingsID::KeyBinding_SpeedFactor275 },
|
||||
{ this->speedFactor300KeyButton, SettingsID::KeyBinding_SpeedFactor300 },
|
||||
};
|
||||
|
||||
std::vector<keybinding> keybindings_CurrentSaveState =
|
||||
{
|
||||
{ this->saveState0KeyButton, SettingsID::KeyBinding_SaveStateSlot0 },
|
||||
@@ -805,12 +843,17 @@ void SettingsDialog::commonHotkeySettings(SettingsDialogAction action)
|
||||
|
||||
std::vector<keybinding> keybindings_Settings =
|
||||
{
|
||||
{ this->graphicsSettingsKeyButton, SettingsID::KeyBinding_GraphicsSettings },
|
||||
{ this->audioSettingsKeyButton, SettingsID::KeyBinding_AudioSettings },
|
||||
{ this->rspSettingsKeyButton, SettingsID::KeyBinding_RspSettings },
|
||||
{ this->inputSettingsKeyButton, SettingsID::KeyBinding_InputSettings },
|
||||
{ this->settingsKeyButton, SettingsID::KeyBinding_Settings },
|
||||
};
|
||||
|
||||
std::vector<keybinding> keybindings_View =
|
||||
{
|
||||
{ this->fullscreenKeyButton, SettingsID::KeyBinding_Fullscreen },
|
||||
{ this->logKeyButton, SettingsID::Keybinding_ViewLog },
|
||||
{ this->refreshRomListKeyButton, SettingsID::KeyBinding_RefreshROMList }
|
||||
};
|
||||
|
||||
@@ -825,12 +868,15 @@ void SettingsDialog::commonHotkeySettings(SettingsDialogAction action)
|
||||
keybindings.insert(keybindings.end(), keybindings_System.begin(), keybindings_System.end());
|
||||
break;
|
||||
case 1:
|
||||
keybindings.insert(keybindings.end(), keybindings_CurrentSaveState.begin(), keybindings_CurrentSaveState.end());
|
||||
keybindings.insert(keybindings.end(), keybindings_SpeedFactor.begin(), keybindings_SpeedFactor.end());
|
||||
break;
|
||||
case 2:
|
||||
keybindings.insert(keybindings.end(), keybindings_Settings.begin(), keybindings_Settings.end());
|
||||
keybindings.insert(keybindings.end(), keybindings_CurrentSaveState.begin(), keybindings_CurrentSaveState.end());
|
||||
break;
|
||||
case 3:
|
||||
keybindings.insert(keybindings.end(), keybindings_Settings.begin(), keybindings_Settings.end());
|
||||
break;
|
||||
case 4:
|
||||
keybindings.insert(keybindings.end(), keybindings_View.begin(), keybindings_View.end());
|
||||
break;
|
||||
}
|
||||
@@ -838,6 +884,7 @@ void SettingsDialog::commonHotkeySettings(SettingsDialogAction action)
|
||||
else
|
||||
{
|
||||
keybindings.insert(keybindings.end(), keybindings_System.begin(), keybindings_System.end());
|
||||
keybindings.insert(keybindings.end(), keybindings_SpeedFactor.begin(), keybindings_SpeedFactor.end());
|
||||
keybindings.insert(keybindings.end(), keybindings_CurrentSaveState.begin(), keybindings_CurrentSaveState.end());
|
||||
keybindings.insert(keybindings.end(), keybindings_Settings.begin(), keybindings_Settings.end());
|
||||
keybindings.insert(keybindings.end(), keybindings_View.begin(), keybindings_View.end());
|
||||
@@ -888,7 +935,7 @@ void SettingsDialog::commonPluginSettings(SettingsDialogAction action)
|
||||
bool pluginFound[] = {false, false, false, false};
|
||||
|
||||
QComboBox *comboBox;
|
||||
std::string pluginFileName;
|
||||
QString pluginFileName;
|
||||
int index = 0;
|
||||
|
||||
// clear combobox items
|
||||
@@ -903,12 +950,15 @@ void SettingsDialog::commonPluginSettings(SettingsDialogAction action)
|
||||
comboBox = comboBoxArray[index];
|
||||
|
||||
pluginFileName = action == SettingsDialogAction::LoadSettings ?
|
||||
CoreSettingsGetStringValue(settingsIdArray[index]) :
|
||||
CoreSettingsGetDefaultStringValue(settingsIdArray[index]);
|
||||
QString::fromStdString(CoreSettingsGetStringValue(settingsIdArray[index])) :
|
||||
QString::fromStdString(CoreSettingsGetDefaultStringValue(settingsIdArray[index]));
|
||||
|
||||
// account for full path (<v0.3.5 we used the full path)
|
||||
pluginFileName = QFileInfo(pluginFileName).fileName();
|
||||
|
||||
comboBox->addItem(QString::fromStdString(p.Name), QString::fromStdString(p.File));
|
||||
|
||||
if (pluginFileName == p.File)
|
||||
if (pluginFileName == QString::fromStdString(p.File))
|
||||
{
|
||||
comboBox->setCurrentText(QString::fromStdString(p.Name));
|
||||
pluginFound[index] = true;
|
||||
@@ -931,7 +981,7 @@ void SettingsDialog::setIconsForEmulationInfoText(void)
|
||||
QLabel* labels[] = {
|
||||
this->infoIconLabel_0, this->infoIconLabel_1, this->infoIconLabel_2,
|
||||
this->infoIconLabel_3, this->infoIconLabel_4, this->infoIconLabel_5,
|
||||
this->infoIconLabel_7
|
||||
this->infoIconLabel_7, this->infoIconLabel_6, this->infoIconLabel_8
|
||||
};
|
||||
|
||||
QIcon infoIcon = QIcon::fromTheme("information-line");
|
||||
@@ -1145,7 +1195,12 @@ void SettingsDialog::on_KeybindButton_KeybindingChanged(KeybindButton* button)
|
||||
this->startComboKeyButton,
|
||||
this->shutdownKeyButton,
|
||||
this->exitKeyButton,
|
||||
this->graphicsSettingsKeyButton,
|
||||
this->audioSettingsKeyButton,
|
||||
this->rspSettingsKeyButton,
|
||||
this->inputSettingsKeyButton,
|
||||
this->settingsKeyButton,
|
||||
this->logKeyButton,
|
||||
this->refreshRomListKeyButton,
|
||||
};
|
||||
|
||||
@@ -1157,6 +1212,18 @@ void SettingsDialog::on_KeybindButton_KeybindingChanged(KeybindButton* button)
|
||||
this->pauseKeyButton,
|
||||
this->generateBitmapKeyButton,
|
||||
this->limitFPSKeyButton,
|
||||
this->speedFactor25KeyButton,
|
||||
this->speedFactor50KeyButton,
|
||||
this->speedFactor75KeyButton,
|
||||
this->speedFactor100KeyButton,
|
||||
this->speedFactor125KeyButton,
|
||||
this->speedFactor150KeyButton,
|
||||
this->speedFactor175KeyButton,
|
||||
this->speedFactor200KeyButton,
|
||||
this->speedFactor225KeyButton,
|
||||
this->speedFactor250KeyButton,
|
||||
this->speedFactor275KeyButton,
|
||||
this->speedFactor300KeyButton,
|
||||
this->saveStateKeyButton,
|
||||
this->saveAsKeyButton,
|
||||
this->loadStateKeyButton,
|
||||
@@ -1174,7 +1241,12 @@ void SettingsDialog::on_KeybindButton_KeybindingChanged(KeybindButton* button)
|
||||
this->saveState8KeyButton,
|
||||
this->saveState9KeyButton,
|
||||
this->exitKeyButton,
|
||||
this->graphicsSettingsKeyButton,
|
||||
this->audioSettingsKeyButton,
|
||||
this->rspSettingsKeyButton,
|
||||
this->inputSettingsKeyButton,
|
||||
this->settingsKeyButton,
|
||||
this->logKeyButton,
|
||||
this->fullscreenKeyButton,
|
||||
};
|
||||
|
||||
|
||||
@@ -184,6 +184,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="sortRomBrowserResultsCheckBox">
|
||||
<property name="text">
|
||||
<string>Sort Results After Search</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
@@ -400,7 +407,7 @@
|
||||
<attribute name="title">
|
||||
<string>Miscellaneous</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_13">
|
||||
<item>
|
||||
@@ -431,6 +438,36 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_14">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="text">
|
||||
<string>Icon Theme</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="iconThemeComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Automatic</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>White</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Black</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkForUpdatesCheckBox">
|
||||
<property name="text">
|
||||
@@ -824,6 +861,242 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="speedFactorTab">
|
||||
<attribute name="title">
|
||||
<string>Speed Factor</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_105">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_102">
|
||||
<property name="text">
|
||||
<string>25%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="speedFactor25KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_106">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_103">
|
||||
<property name="text">
|
||||
<string>50%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="speedFactor50KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_107">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_104">
|
||||
<property name="text">
|
||||
<string>75%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="speedFactor75KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_108">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_105">
|
||||
<property name="text">
|
||||
<string>100%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="speedFactor100KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_109">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_106">
|
||||
<property name="text">
|
||||
<string>125%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="speedFactor125KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_110">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_107">
|
||||
<property name="text">
|
||||
<string>150%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="speedFactor150KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_111">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_108">
|
||||
<property name="text">
|
||||
<string>175%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="speedFactor175KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_112">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_109">
|
||||
<property name="text">
|
||||
<string>200%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="speedFactor200KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_113">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_110">
|
||||
<property name="text">
|
||||
<string>225%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="speedFactor225KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_114">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_111">
|
||||
<property name="text">
|
||||
<string>250%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="speedFactor250KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_115">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_112">
|
||||
<property name="text">
|
||||
<string>275%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="speedFactor275KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_116">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_113">
|
||||
<property name="text">
|
||||
<string>300%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="speedFactor300KeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_9">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="currentSaveStateTab">
|
||||
<attribute name="title">
|
||||
<string>Current Save State</string>
|
||||
@@ -1028,7 +1301,79 @@
|
||||
<attribute name="title">
|
||||
<string>Settings</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_37">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_14">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_92">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_85">
|
||||
<property name="text">
|
||||
<string>Graphics</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="graphicsSettingsKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_93">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_87">
|
||||
<property name="text">
|
||||
<string>Audio</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="audioSettingsKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_94">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_89">
|
||||
<property name="text">
|
||||
<string>RSP</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="rspSettingsKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_117">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_90">
|
||||
<property name="text">
|
||||
<string>Input</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="inputSettingsKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_87">
|
||||
<item>
|
||||
@@ -1085,6 +1430,24 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_118">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_114">
|
||||
<property name="text">
|
||||
<string>Log</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeybindButton" name="logKeyButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_73">
|
||||
<item>
|
||||
@@ -1685,7 +2048,7 @@
|
||||
<attribute name="title">
|
||||
<string>Plugins</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_24">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_16">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_19">
|
||||
<property name="enabled">
|
||||
@@ -1817,6 +2180,51 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="emulationInfoLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="infoIconLabel_8">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>:/newPrefix/Resource/Info.ico</pixmap>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_68">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="midLineWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Make sure to use a LLE RSP plugin (i.e paraLLEl RSP) when using a LLE Video plugin (i.e paraLLEl)</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
@@ -1869,7 +2277,7 @@
|
||||
<attribute name="title">
|
||||
<string>Plugins</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_25">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_15">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_25">
|
||||
<property name="title">
|
||||
@@ -1968,6 +2376,51 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="emulationInfoLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="infoIconLabel_6">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>:/newPrefix/Resource/Info.ico</pixmap>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_64">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="midLineWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Make sure to use a LLE RSP plugin (i.e paraLLEl RSP) when using a LLE Video plugin (i.e paraLLEl)</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="emulationInfoLayout_1">
|
||||
<item>
|
||||
|
||||
@@ -48,7 +48,7 @@ MainWindow::~MainWindow()
|
||||
{
|
||||
}
|
||||
|
||||
bool MainWindow::Init(QApplication* app)
|
||||
bool MainWindow::Init(QApplication* app, bool showUI)
|
||||
{
|
||||
if (!CoreInit())
|
||||
{
|
||||
@@ -64,14 +64,16 @@ bool MainWindow::Init(QApplication* app)
|
||||
this->configureTheme(app);
|
||||
|
||||
this->initializeUI();
|
||||
this->configureUI(app);
|
||||
this->configureUI(app, showUI);
|
||||
|
||||
this->connectActionSignals();
|
||||
this->configureActions();
|
||||
this->updateActions(false, false);
|
||||
|
||||
#ifdef UPDATER
|
||||
this->checkForUpdates();
|
||||
this->checkForUpdates(true, false);
|
||||
#else
|
||||
this->action_Help_Update->setVisible(false);
|
||||
#endif // UPDATER
|
||||
|
||||
this->initializeEmulationThread();
|
||||
@@ -90,8 +92,15 @@ bool MainWindow::Init(QApplication* app)
|
||||
return false;
|
||||
}
|
||||
|
||||
// add actions when there's no UI
|
||||
if (!showUI)
|
||||
{
|
||||
this->addActions();
|
||||
}
|
||||
|
||||
connect(coreCallBacks, &CoreCallbacks::OnCoreDebugCallback, this, &MainWindow::on_Core_DebugCallback);
|
||||
connect(coreCallBacks, &CoreCallbacks::OnCoreDebugCallback, &this->logDialog, &Dialog::LogDialog::AddLogLine);
|
||||
connect(coreCallBacks, &CoreCallbacks::OnCoreStateCallback, this, &MainWindow::on_Core_StateCallback);
|
||||
connect(app, &QGuiApplication::applicationStateChanged, this, &MainWindow::on_QGuiApplication_applicationStateChanged);
|
||||
|
||||
return true;
|
||||
@@ -173,7 +182,7 @@ void MainWindow::initializeUI(void)
|
||||
&MainWindow::on_EventFilter_KeyReleased);
|
||||
}
|
||||
|
||||
void MainWindow::configureUI(QApplication* app)
|
||||
void MainWindow::configureUI(QApplication* app, bool showUI)
|
||||
{
|
||||
this->setCentralWidget(this->ui_Widgets);
|
||||
|
||||
@@ -191,9 +200,22 @@ void MainWindow::configureUI(QApplication* app)
|
||||
this->restoreGeometry(QByteArray::fromBase64(geometry.toLocal8Bit()));
|
||||
}
|
||||
|
||||
this->ui_ShowToolbar = CoreSettingsGetBoolValue(SettingsID::GUI_Toolbar);
|
||||
this->ui_ShowStatusbar = CoreSettingsGetBoolValue(SettingsID::GUI_StatusBar);
|
||||
this->ui_ShowUI = showUI;
|
||||
|
||||
if (this->ui_ShowUI)
|
||||
{
|
||||
this->ui_ShowMenubar = true;
|
||||
this->ui_ShowToolbar = CoreSettingsGetBoolValue(SettingsID::GUI_Toolbar);
|
||||
this->ui_ShowStatusbar = CoreSettingsGetBoolValue(SettingsID::GUI_StatusBar);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->ui_ShowMenubar = false;
|
||||
this->ui_ShowToolbar = false;
|
||||
this->ui_ShowStatusbar = false;
|
||||
}
|
||||
|
||||
this->menuBar()->setVisible(this->ui_ShowMenubar);
|
||||
this->toolBar->setVisible(this->ui_ShowToolbar);
|
||||
this->statusBar()->setVisible(this->ui_ShowStatusbar);
|
||||
this->statusBar()->addPermanentWidget(this->ui_StatusBar_Label, 1);
|
||||
@@ -228,7 +250,11 @@ void MainWindow::configureTheme(QApplication* app)
|
||||
|
||||
// set application theme
|
||||
QString theme = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::GUI_Theme));
|
||||
if (theme == "Fusion")
|
||||
if (theme == "Native")
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (theme == "Fusion")
|
||||
{
|
||||
app->setPalette(QApplication::style()->standardPalette());
|
||||
app->setStyleSheet(QString());
|
||||
@@ -270,21 +296,46 @@ void MainWindow::configureTheme(QApplication* app)
|
||||
|
||||
app->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
|
||||
}
|
||||
else if (theme == "Native")
|
||||
else if (theme.endsWith(".qss"))
|
||||
{
|
||||
// do nothing
|
||||
QString themePath;
|
||||
themePath = QString::fromStdString(CoreGetSharedDataDirectory().string());
|
||||
themePath += "/Styles/";
|
||||
themePath += theme;
|
||||
|
||||
// use Fusion as a base for the stylesheet
|
||||
app->setPalette(QApplication::style()->standardPalette());
|
||||
app->setStyleSheet(QString());
|
||||
app->setStyle(QStyleFactory::create("Fusion"));
|
||||
|
||||
// set the stylesheet theme,
|
||||
// if the file exists and can be opened
|
||||
QFile themeFile(themePath);
|
||||
if (themeFile.exists() &&
|
||||
themeFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
app->setStyleSheet(themeFile.readAll());
|
||||
}
|
||||
}
|
||||
|
||||
// set icon theme
|
||||
QPalette palette = app->palette();
|
||||
bool dark = palette.windowText().color().value() > palette.window().color().value();
|
||||
QIcon::setThemeName(dark ? "white" : "black");
|
||||
// set application icon theme
|
||||
QString iconTheme = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::GUI_IconTheme));
|
||||
if (iconTheme == "White" || iconTheme == "Black")
|
||||
{
|
||||
QIcon::setThemeName(iconTheme.toLower());
|
||||
}
|
||||
else
|
||||
{ // fallback to automatic
|
||||
QPalette palette = app->palette();
|
||||
bool dark = palette.windowText().color().value() > palette.window().color().value();
|
||||
QIcon::setThemeName(dark ? "white" : "black");
|
||||
}
|
||||
|
||||
// fallback for icons we don't provide (i.e standard system icons)
|
||||
QIcon::setFallbackThemeName(fallbackThemeName);
|
||||
}
|
||||
|
||||
void MainWindow::showErrorMessage(QString text, QString details = "")
|
||||
void MainWindow::showErrorMessage(QString text, QString details)
|
||||
{
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setIcon(QMessageBox::Icon::Critical);
|
||||
@@ -367,20 +418,32 @@ void MainWindow::loadGeometry(void)
|
||||
this->showNormal();
|
||||
}
|
||||
|
||||
if (this->menuBar()->isHidden())
|
||||
if (this->ui_ShowMenubar && this->menuBar()->isHidden())
|
||||
{
|
||||
this->menuBar()->show();
|
||||
}
|
||||
else if (!this->ui_ShowMenubar && !this->menuBar()->isHidden())
|
||||
{
|
||||
this->menuBar()->hide();
|
||||
}
|
||||
|
||||
if (this->ui_ShowToolbar && this->toolBar->isHidden())
|
||||
{
|
||||
this->toolBar->show();
|
||||
}
|
||||
else if (!this->ui_ShowToolbar && !this->toolBar->isHidden())
|
||||
{
|
||||
this->toolBar->hide();
|
||||
}
|
||||
|
||||
if (this->ui_ShowStatusbar && this->statusBar()->isHidden())
|
||||
{
|
||||
this->statusBar()->show();
|
||||
}
|
||||
else if (!this->ui_ShowStatusbar && !this->statusBar()->isHidden())
|
||||
{
|
||||
this->statusBar()->hide();
|
||||
}
|
||||
|
||||
this->ui_Geometry_Saved = false;
|
||||
}
|
||||
@@ -439,14 +502,22 @@ void MainWindow::launchEmulationThread(QString cartRom, QString diskRom)
|
||||
|
||||
if (!CoreArePluginsReady())
|
||||
{
|
||||
// always go back to ROM Browser
|
||||
this->ui_NoSwitchToRomBrowser = false;
|
||||
this->updateUI(false, false);
|
||||
|
||||
this->showErrorMessage("CoreArePluginsReady() Failed", QString::fromStdString(CoreGetError()));
|
||||
return;
|
||||
}
|
||||
|
||||
this->ui_HideCursorInEmulation = CoreSettingsGetBoolValue(SettingsID::GUI_HideCursorInEmulation);
|
||||
this->ui_HideCursorInFullscreenEmulation = CoreSettingsGetBoolValue(SettingsID::GUI_HideCursorInFullscreenEmulation);
|
||||
this->ui_ShowToolbar = CoreSettingsGetBoolValue(SettingsID::GUI_Toolbar);
|
||||
this->ui_ShowStatusbar = CoreSettingsGetBoolValue(SettingsID::GUI_StatusBar);
|
||||
|
||||
if (this->ui_ShowUI)
|
||||
{
|
||||
this->ui_ShowToolbar = CoreSettingsGetBoolValue(SettingsID::GUI_Toolbar);
|
||||
this->ui_ShowStatusbar = CoreSettingsGetBoolValue(SettingsID::GUI_StatusBar);
|
||||
}
|
||||
|
||||
this->ui_Widget_OpenGL->SetHideCursor(this->ui_HideCursorInEmulation);
|
||||
|
||||
@@ -455,11 +526,6 @@ void MainWindow::launchEmulationThread(QString cartRom, QString diskRom)
|
||||
this->emulationThread->start();
|
||||
}
|
||||
|
||||
void MainWindow::launchEmulationThread(QString file)
|
||||
{
|
||||
this->launchEmulationThread(file, "");
|
||||
}
|
||||
|
||||
void MainWindow::updateActions(bool inEmulation, bool isPaused)
|
||||
{
|
||||
QString keyBinding;
|
||||
@@ -491,6 +557,7 @@ void MainWindow::updateActions(bool inEmulation, bool isPaused)
|
||||
this->action_System_LimitFPS->setEnabled(inEmulation);
|
||||
this->action_System_LimitFPS->setShortcut(QKeySequence(keyBinding));
|
||||
this->action_System_LimitFPS->setChecked(CoreIsSpeedLimiterEnabled());
|
||||
this->menuSpeedFactor->setEnabled(inEmulation);
|
||||
keyBinding = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::KeyBinding_SaveState));
|
||||
this->action_System_SaveState->setEnabled(inEmulation);
|
||||
this->action_System_SaveState->setShortcut(QKeySequence(keyBinding));
|
||||
@@ -513,14 +580,30 @@ void MainWindow::updateActions(bool inEmulation, bool isPaused)
|
||||
keyBinding = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::KeyBinding_Exit));
|
||||
this->action_System_Exit->setShortcut(QKeySequence(keyBinding));
|
||||
|
||||
// configure keybindings for save slots
|
||||
QAction* slotActions[] =
|
||||
// configure keybindings for speed factor
|
||||
QAction* speedActions[] =
|
||||
{
|
||||
this->actionSlot_0, this->actionSlot_1, this->actionSlot_2,
|
||||
this->actionSlot_3, this->actionSlot_4, this->actionSlot_5,
|
||||
this->actionSlot_6, this->actionSlot_7, this->actionSlot_8,
|
||||
this->actionSlot_9
|
||||
this->actionSpeed25, this->actionSpeed50, this->actionSpeed75,
|
||||
this->actionSpeed100, this->actionSpeed125, this->actionSpeed150,
|
||||
this->actionSpeed175, this->actionSpeed200, this->actionSpeed225,
|
||||
this->actionSpeed250, this->actionSpeed275, this->actionSpeed300
|
||||
};
|
||||
SettingsID speedKeybindSettingsId[] =
|
||||
{
|
||||
SettingsID::KeyBinding_SpeedFactor25, SettingsID::KeyBinding_SpeedFactor50,
|
||||
SettingsID::KeyBinding_SpeedFactor75, SettingsID::KeyBinding_SpeedFactor100,
|
||||
SettingsID::KeyBinding_SpeedFactor125, SettingsID::KeyBinding_SpeedFactor150,
|
||||
SettingsID::KeyBinding_SpeedFactor175, SettingsID::KeyBinding_SpeedFactor200,
|
||||
SettingsID::KeyBinding_SpeedFactor225, SettingsID::KeyBinding_SpeedFactor250,
|
||||
SettingsID::KeyBinding_SpeedFactor275, SettingsID::KeyBinding_SpeedFactor300
|
||||
};
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
keyBinding = QString::fromStdString(CoreSettingsGetStringValue(speedKeybindSettingsId[i]));
|
||||
speedActions[i]->setShortcut(QKeySequence(keyBinding));
|
||||
}
|
||||
|
||||
// configure keybindings for save slots
|
||||
SettingsID slotKeybindSettingsId[] =
|
||||
{
|
||||
SettingsID::KeyBinding_SaveStateSlot0, SettingsID::KeyBinding_SaveStateSlot1,
|
||||
@@ -529,16 +612,29 @@ void MainWindow::updateActions(bool inEmulation, bool isPaused)
|
||||
SettingsID::KeyBinding_SaveStateSlot6, SettingsID::KeyBinding_SaveStateSlot7,
|
||||
SettingsID::KeyBinding_SaveStateSlot8, SettingsID::KeyBinding_SaveStateSlot9
|
||||
};
|
||||
int currentSlot = CoreGetSaveStateSlot();
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
keyBinding = QString::fromStdString(CoreSettingsGetStringValue(slotKeybindSettingsId[i]));
|
||||
slotActions[i]->setShortcut(QKeySequence(keyBinding));
|
||||
this->ui_SlotActions[i]->setShortcut(QKeySequence(keyBinding));
|
||||
this->ui_SlotActions[i]->setChecked(i == currentSlot);
|
||||
}
|
||||
|
||||
// configure text and filename data for save slots
|
||||
this->updateSaveStateSlotActions(inEmulation, isPaused);
|
||||
|
||||
keyBinding = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::KeyBinding_GraphicsSettings));
|
||||
this->action_Settings_Graphics->setEnabled(CorePluginsHasConfig(CorePluginType::Gfx));
|
||||
this->action_Settings_Graphics->setShortcut(QKeySequence(keyBinding));
|
||||
keyBinding = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::KeyBinding_AudioSettings));
|
||||
this->action_Settings_Audio->setEnabled(CorePluginsHasConfig(CorePluginType::Audio));
|
||||
this->action_Settings_Audio->setShortcut(QKeySequence(keyBinding));
|
||||
keyBinding = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::KeyBinding_RspSettings));
|
||||
this->action_Settings_Rsp->setEnabled(CorePluginsHasConfig(CorePluginType::Rsp));
|
||||
this->action_Settings_Rsp->setShortcut(QKeySequence(keyBinding));
|
||||
keyBinding = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::KeyBinding_InputSettings));
|
||||
this->action_Settings_Input->setEnabled(CorePluginsHasConfig(CorePluginType::Input));
|
||||
this->action_Settings_Input->setShortcut(QKeySequence(keyBinding));
|
||||
keyBinding = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::KeyBinding_Settings));
|
||||
this->action_Settings_Settings->setShortcut(QKeySequence(keyBinding));
|
||||
|
||||
@@ -549,101 +645,191 @@ void MainWindow::updateActions(bool inEmulation, bool isPaused)
|
||||
keyBinding = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::KeyBinding_RefreshROMList));
|
||||
this->action_View_RefreshRoms->setEnabled(!inEmulation);
|
||||
this->action_View_RefreshRoms->setShortcut(QKeySequence(keyBinding));
|
||||
keyBinding = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::Keybinding_ViewLog));
|
||||
this->action_View_Log->setShortcut(QKeySequence(keyBinding));
|
||||
this->action_View_ClearRomCache->setEnabled(!inEmulation);
|
||||
}
|
||||
|
||||
void MainWindow::addFullscreenActions(void)
|
||||
void MainWindow::updateSaveStateSlotActions(bool inEmulation, bool isPaused)
|
||||
{
|
||||
this->addAction(this->action_System_StartRom);
|
||||
this->addAction(this->action_System_OpenCombo);
|
||||
this->addAction(this->action_System_Shutdown);
|
||||
this->addAction(this->action_System_SoftReset);
|
||||
this->addAction(this->action_System_HardReset);
|
||||
this->addAction(this->action_System_Pause);
|
||||
this->addAction(this->action_System_Screenshot);
|
||||
this->addAction(this->action_System_LimitFPS);
|
||||
this->addAction(this->action_System_SaveState);
|
||||
this->addAction(this->action_System_SaveAs);
|
||||
this->addAction(this->action_System_LoadState);
|
||||
this->addAction(this->action_System_Load);
|
||||
this->addAction(this->actionSlot_0);
|
||||
this->addAction(this->actionSlot_1);
|
||||
this->addAction(this->actionSlot_2);
|
||||
this->addAction(this->actionSlot_3);
|
||||
this->addAction(this->actionSlot_4);
|
||||
this->addAction(this->actionSlot_5);
|
||||
this->addAction(this->actionSlot_6);
|
||||
this->addAction(this->actionSlot_7);
|
||||
this->addAction(this->actionSlot_8);
|
||||
this->addAction(this->actionSlot_9);
|
||||
this->addAction(this->action_System_Cheats);
|
||||
this->addAction(this->action_System_GSButton);
|
||||
this->addAction(this->action_System_Exit);
|
||||
this->addAction(this->action_Settings_Graphics);
|
||||
this->addAction(this->action_Settings_Audio);
|
||||
this->addAction(this->action_Settings_Rsp);
|
||||
this->addAction(this->action_Settings_Input);
|
||||
this->addAction(this->action_Settings_Settings);
|
||||
this->addAction(this->action_View_Fullscreen);
|
||||
this->addAction(this->action_Help_Github);
|
||||
this->addAction(this->action_Help_About);
|
||||
std::filesystem::path saveStatePath;
|
||||
|
||||
// do nothing when paused
|
||||
if (isPaused)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
if (inEmulation &&
|
||||
CoreGetSaveStatePath(i, saveStatePath))
|
||||
{
|
||||
this->ui_SlotActions[i]->setData(QString::fromStdString(saveStatePath.string()));
|
||||
this->ui_SlotActions[i]->setText(this->getSaveStateSlotText(this->ui_SlotActions[i], i));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->ui_SlotActions[i]->setData("");
|
||||
this->ui_SlotActions[i]->setText(this->getSaveStateSlotText(this->ui_SlotActions[i], i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::removeFullscreenActions(void)
|
||||
void MainWindow::addActions(void)
|
||||
{
|
||||
this->removeAction(this->action_System_StartRom);
|
||||
this->removeAction(this->action_System_OpenCombo);
|
||||
this->removeAction(this->action_System_Shutdown);
|
||||
this->removeAction(this->action_System_SoftReset);
|
||||
this->removeAction(this->action_System_HardReset);
|
||||
this->removeAction(this->action_System_Pause);
|
||||
this->removeAction(this->action_System_Screenshot);
|
||||
this->removeAction(this->action_System_LimitFPS);
|
||||
this->removeAction(this->action_System_SaveState);
|
||||
this->removeAction(this->action_System_SaveAs);
|
||||
this->removeAction(this->action_System_LoadState);
|
||||
this->removeAction(this->action_System_Load);
|
||||
this->removeAction(this->actionSlot_0);
|
||||
this->removeAction(this->actionSlot_1);
|
||||
this->removeAction(this->actionSlot_2);
|
||||
this->removeAction(this->actionSlot_3);
|
||||
this->removeAction(this->actionSlot_4);
|
||||
this->removeAction(this->actionSlot_5);
|
||||
this->removeAction(this->actionSlot_6);
|
||||
this->removeAction(this->actionSlot_7);
|
||||
this->removeAction(this->actionSlot_8);
|
||||
this->removeAction(this->actionSlot_9);
|
||||
this->removeAction(this->action_System_Cheats);
|
||||
this->removeAction(this->action_System_GSButton);
|
||||
this->removeAction(this->action_System_Exit);
|
||||
this->removeAction(this->action_Settings_Graphics);
|
||||
this->removeAction(this->action_Settings_Audio);
|
||||
this->removeAction(this->action_Settings_Rsp);
|
||||
this->removeAction(this->action_Settings_Input);
|
||||
this->removeAction(this->action_Settings_Settings);
|
||||
this->removeAction(this->action_View_Fullscreen);
|
||||
this->removeAction(this->action_Help_Github);
|
||||
this->removeAction(this->action_Help_About);
|
||||
if (this->ui_AddedActions)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (QAction* action : this->ui_Actions)
|
||||
{
|
||||
this->addAction(action);
|
||||
}
|
||||
|
||||
this->ui_AddedActions = true;
|
||||
}
|
||||
|
||||
void MainWindow::removeActions(void)
|
||||
{
|
||||
if (!this->ui_AddedActions)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (QAction* action : this->ui_Actions)
|
||||
{
|
||||
this->removeAction(action);
|
||||
}
|
||||
|
||||
this->ui_AddedActions = false;
|
||||
}
|
||||
|
||||
QString MainWindow::getSaveStateSlotDateTimeText(QAction* action)
|
||||
{
|
||||
QString saveStateSlotText;
|
||||
QString filePath;
|
||||
|
||||
// retrieve file name
|
||||
filePath = action->data().toString();
|
||||
|
||||
// check if file exists, if it does,
|
||||
// return a string with the datetime
|
||||
QFileInfo saveStateFileInfo(filePath);
|
||||
if (!filePath.isEmpty() && saveStateFileInfo.exists())
|
||||
{
|
||||
saveStateSlotText = saveStateFileInfo.lastModified().toString("dd/MM/yyyy hh:mm");
|
||||
}
|
||||
|
||||
return saveStateSlotText;
|
||||
}
|
||||
|
||||
QString MainWindow::getSaveStateSlotText(QAction* action, int slot)
|
||||
{
|
||||
QString saveStateSlotText;
|
||||
QString dateTimeText;
|
||||
QString filePath;
|
||||
|
||||
// base text
|
||||
saveStateSlotText = "Slot " + QString::number(slot);
|
||||
|
||||
// add date and time text when it isnt empty
|
||||
dateTimeText = this->getSaveStateSlotDateTimeText(action);
|
||||
if (!dateTimeText.isEmpty())
|
||||
{
|
||||
saveStateSlotText += " - ";
|
||||
saveStateSlotText += dateTimeText;
|
||||
}
|
||||
|
||||
return saveStateSlotText;
|
||||
}
|
||||
|
||||
void MainWindow::configureActions(void)
|
||||
{
|
||||
// configure actions list
|
||||
this->ui_Actions.append(
|
||||
{
|
||||
// System actions
|
||||
this->action_System_StartRom, this->action_System_OpenCombo,
|
||||
this->action_System_Shutdown, this->action_System_SoftReset,
|
||||
this->action_System_HardReset, this->action_System_Pause,
|
||||
this->action_System_Screenshot, this->action_System_LimitFPS,
|
||||
this->actionSpeed25, this->actionSpeed50, this->actionSpeed75,
|
||||
this->actionSpeed100, this->actionSpeed125, this->actionSpeed150,
|
||||
this->actionSpeed175, this->actionSpeed200, this->actionSpeed225,
|
||||
this->actionSpeed250, this->actionSpeed275, this->actionSpeed300,
|
||||
this->action_System_SaveState, this->action_System_SaveAs,
|
||||
this->action_System_LoadState, this->action_System_Load,
|
||||
this->actionSlot_0, this->actionSlot_1, this->actionSlot_2,
|
||||
this->actionSlot_3, this->actionSlot_4, this->actionSlot_5,
|
||||
this->actionSlot_6, this->actionSlot_7, this->actionSlot_8,
|
||||
this->actionSlot_9, this->action_System_Cheats,
|
||||
this->action_System_GSButton, this->action_System_Exit,
|
||||
// Settings actions
|
||||
this->action_Settings_Graphics, this->action_Settings_Audio,
|
||||
this->action_Settings_Rsp, this->action_Settings_Input,
|
||||
this->action_Settings_Settings,
|
||||
// View actions
|
||||
this->action_View_Fullscreen, this->action_View_RefreshRoms,
|
||||
this->action_View_Log,
|
||||
// Help actions
|
||||
this->action_Help_Github, this->action_Help_About,
|
||||
});
|
||||
|
||||
// configure save slot actions
|
||||
QActionGroup* slotActionGroup = new QActionGroup(this);
|
||||
QAction* slotActions[] =
|
||||
this->ui_SlotActions.append(
|
||||
{
|
||||
this->actionSlot_0, this->actionSlot_1, this->actionSlot_2,
|
||||
this->actionSlot_3, this->actionSlot_4, this->actionSlot_5,
|
||||
this->actionSlot_6, this->actionSlot_7, this->actionSlot_8,
|
||||
this->actionSlot_9
|
||||
});
|
||||
|
||||
// configure emulation speed actions
|
||||
QActionGroup* speedActionGroup = new QActionGroup(this);
|
||||
QAction* speedActions[] =
|
||||
{
|
||||
this->actionSpeed25, this->actionSpeed50, this->actionSpeed75,
|
||||
this->actionSpeed100, this->actionSpeed125, this->actionSpeed150,
|
||||
this->actionSpeed175, this->actionSpeed200, this->actionSpeed225,
|
||||
this->actionSpeed250, this->actionSpeed275, this->actionSpeed300
|
||||
};
|
||||
int currentSlot = CoreGetSaveStateSlot();
|
||||
int speedActionNumbers[] =
|
||||
{
|
||||
25, 50, 75, 100,
|
||||
125, 150, 175, 200,
|
||||
225, 250, 275, 300
|
||||
};
|
||||
int currentSpeedFactor = CoreGetSpeedFactor();
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
QAction* speedAction = speedActions[i];
|
||||
|
||||
speedAction->setCheckable(true);
|
||||
speedAction->setChecked(currentSpeedFactor == speedActionNumbers[i]);
|
||||
speedAction->setActionGroup(speedActionGroup);
|
||||
|
||||
// connect emulation speed action here because we need to do
|
||||
// something special for them
|
||||
connect(speedAction, &QAction::triggered, [=](bool checked)
|
||||
{
|
||||
if (checked)
|
||||
{
|
||||
int factor = speedAction->text().split("%").first().toInt();
|
||||
this->on_Action_System_SpeedFactor(factor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// configure save slot actions
|
||||
QActionGroup* slotActionGroup = new QActionGroup(this);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
QAction* slotAction = slotActions[i];
|
||||
QAction* slotAction = this->ui_SlotActions[i];
|
||||
|
||||
slotAction->setCheckable(true);
|
||||
slotAction->setChecked(i == currentSlot);
|
||||
slotAction->setChecked(i == 0);
|
||||
slotAction->setActionGroup(slotActionGroup);
|
||||
|
||||
// connect slot action here because we need to do
|
||||
@@ -652,7 +838,7 @@ void MainWindow::configureActions(void)
|
||||
{
|
||||
if (checked)
|
||||
{
|
||||
int slot = slotAction->text().split(" ").last().toInt();
|
||||
int slot = slotAction->text().split(" ").at(1).toInt();
|
||||
this->on_Action_System_CurrentSaveState(slot);
|
||||
}
|
||||
});
|
||||
@@ -722,16 +908,44 @@ void MainWindow::connectActionSignals(void)
|
||||
|
||||
connect(this->action_Help_Github, &QAction::triggered, this, &MainWindow::on_Action_Help_Github);
|
||||
connect(this->action_Help_About, &QAction::triggered, this, &MainWindow::on_Action_Help_About);
|
||||
connect(this->action_Help_Update, &QAction::triggered, this, &MainWindow::on_Action_Help_Update);
|
||||
}
|
||||
|
||||
#ifdef UPDATER
|
||||
void MainWindow::checkForUpdates(void)
|
||||
void MainWindow::checkForUpdates(bool silent, bool force)
|
||||
{
|
||||
if (!CoreSettingsGetBoolValue(SettingsID::GUI_CheckForUpdates))
|
||||
if (!force && !CoreSettingsGetBoolValue(SettingsID::GUI_CheckForUpdates))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// only check for updates on the stable versions unless forced
|
||||
QString currentVersion = QString::fromStdString(CoreGetVersion());
|
||||
if (!force && currentVersion.size() != 6)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QString dateTimeFormat = "dd-MM-yyyy_hh:mm";
|
||||
QString lastUpdateCheckDateTimeString = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::GUI_LastUpdateCheck));
|
||||
QDateTime lastUpdateCheckDateTime = QDateTime::fromString(lastUpdateCheckDateTimeString, dateTimeFormat);
|
||||
QDateTime currentDateTime = QDateTime::currentDateTime();
|
||||
|
||||
// only check for updates once every hour unless forced
|
||||
if (!force &&
|
||||
lastUpdateCheckDateTime.isValid() &&
|
||||
lastUpdateCheckDateTime.addSecs(3600) > currentDateTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// update last update check date & time
|
||||
CoreSettingsSetValue(SettingsID::GUI_LastUpdateCheck, currentDateTime.toString(dateTimeFormat).toStdString());
|
||||
|
||||
// whether or not the update check is silent
|
||||
this->ui_SilentUpdateCheck = silent;
|
||||
|
||||
// execute update check
|
||||
QNetworkAccessManager* networkAccessManager = new QNetworkAccessManager(this);
|
||||
connect(networkAccessManager, &QNetworkAccessManager::finished, this, &MainWindow::on_networkAccessManager_Finished);
|
||||
networkAccessManager->get(QNetworkRequest(QUrl("https://api.github.com/repos/Rosalie241/RMG/releases/latest")));
|
||||
@@ -776,6 +990,13 @@ void MainWindow::timerEvent(QTimerEvent *event)
|
||||
this->ui_GamesharkButtonTimerId = 0;
|
||||
}
|
||||
}
|
||||
else if (timerId == this->ui_UpdateSaveStateSlotTimerId)
|
||||
{
|
||||
this->updateSaveStateSlotActions(CoreIsEmulationRunning(), false);
|
||||
this->killTimer(timerId);
|
||||
this->ui_UpdateSaveStateSlotTimerId = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::on_EventFilter_KeyPressed(QKeyEvent *event)
|
||||
@@ -843,6 +1064,10 @@ void MainWindow::on_networkAccessManager_Finished(QNetworkReply* reply)
|
||||
{
|
||||
if (reply->error())
|
||||
{
|
||||
if (!this->ui_SilentUpdateCheck)
|
||||
{
|
||||
this->showErrorMessage("Failed to check for updates!", reply->errorString());
|
||||
}
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
@@ -855,16 +1080,13 @@ void MainWindow::on_networkAccessManager_Finished(QNetworkReply* reply)
|
||||
|
||||
reply->deleteLater();
|
||||
|
||||
// make sure the current version is valid
|
||||
// and not a dev version
|
||||
if (currentVersion.size() != 6)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// do nothing when versions match
|
||||
if (currentVersion == latestVersion)
|
||||
{
|
||||
if (!this->ui_SilentUpdateCheck)
|
||||
{
|
||||
this->showErrorMessage("You're already on the latest version!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -884,12 +1106,16 @@ void MainWindow::on_networkAccessManager_Finished(QNetworkReply* reply)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef APPIMAGE_UPDATER
|
||||
this->on_Action_System_Exit();
|
||||
#else // normal updater
|
||||
Dialog::InstallUpdateDialog installUpdateDialog(this, QCoreApplication::applicationDirPath(), downloadUpdateDialog.GetTempDirectory(), downloadUpdateDialog.GetFileName());
|
||||
ret = installUpdateDialog.exec();
|
||||
if (ret != QDialog::Accepted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif // APPIMAGE_UPDATER
|
||||
}
|
||||
#endif // UPDATER
|
||||
|
||||
@@ -1067,6 +1293,18 @@ void MainWindow::on_Action_System_LimitFPS(void)
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_Action_System_SpeedFactor(int factor)
|
||||
{
|
||||
if (!CoreSetSpeedFactor(factor))
|
||||
{
|
||||
this->showErrorMessage("CoreSetSpeedFactor() Failed!", QString::fromStdString(CoreGetError()));
|
||||
}
|
||||
else
|
||||
{
|
||||
OnScreenDisplaySetMessage("Playback speed: " + std::to_string(CoreGetSpeedFactor()) + "%");
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_Action_System_SaveState(void)
|
||||
{
|
||||
if (!CoreSaveState())
|
||||
@@ -1076,6 +1314,15 @@ void MainWindow::on_Action_System_SaveState(void)
|
||||
else
|
||||
{
|
||||
OnScreenDisplaySetMessage("Saved state to slot: " + std::to_string(CoreGetSaveStateSlot()));
|
||||
|
||||
// refresh savestate slot times in 1 second,
|
||||
// kill any existing timers
|
||||
if (this->ui_UpdateSaveStateSlotTimerId != 0)
|
||||
{
|
||||
this->killTimer(this->ui_UpdateSaveStateSlotTimerId);
|
||||
this->ui_UpdateSaveStateSlotTimerId = 0;
|
||||
}
|
||||
this->ui_UpdateSaveStateSlotTimerId = this->startTimer(1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1154,13 +1401,36 @@ void MainWindow::on_Action_System_Load(void)
|
||||
|
||||
void MainWindow::on_Action_System_CurrentSaveState(int slot)
|
||||
{
|
||||
QAction* slotAction;
|
||||
QString dateTimeText;
|
||||
std::string message;
|
||||
|
||||
if (!CoreSetSaveStateSlot(slot))
|
||||
{
|
||||
this->showErrorMessage("CoreSetSaveStateSlot() Failed", QString::fromStdString(CoreGetError()));
|
||||
}
|
||||
else
|
||||
{
|
||||
OnScreenDisplaySetMessage("Selected save slot: " + std::to_string(slot));
|
||||
slotAction = this->ui_SlotActions[slot];
|
||||
dateTimeText = this->getSaveStateSlotDateTimeText(slotAction);
|
||||
|
||||
// construct OSD message
|
||||
message = "Selected save slot: " + std::to_string(slot);
|
||||
|
||||
// add date and time when available
|
||||
if (!dateTimeText.isEmpty())
|
||||
{
|
||||
message += " [";
|
||||
message += dateTimeText.toStdString();
|
||||
message += "]";
|
||||
}
|
||||
else
|
||||
{
|
||||
message += " [N/A]";
|
||||
}
|
||||
|
||||
// display message
|
||||
OnScreenDisplaySetMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1248,6 +1518,11 @@ void MainWindow::on_Action_Settings_Settings(void)
|
||||
|
||||
void MainWindow::on_Action_View_Toolbar(bool checked)
|
||||
{
|
||||
if (!this->ui_ShowUI)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CoreSettingsSetValue(SettingsID::GUI_Toolbar, checked);
|
||||
this->toolBar->setVisible(checked);
|
||||
this->ui_ShowToolbar = checked;
|
||||
@@ -1255,6 +1530,11 @@ void MainWindow::on_Action_View_Toolbar(bool checked)
|
||||
|
||||
void MainWindow::on_Action_View_StatusBar(bool checked)
|
||||
{
|
||||
if (!this->ui_ShowUI)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CoreSettingsSetValue(SettingsID::GUI_StatusBar, checked);
|
||||
this->statusBar()->setVisible(checked);
|
||||
this->ui_ShowStatusbar = checked;
|
||||
@@ -1324,6 +1604,13 @@ void MainWindow::on_Action_Help_About(void)
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
void MainWindow::on_Action_Help_Update(void)
|
||||
{
|
||||
#ifdef UPDATER
|
||||
this->checkForUpdates(false, true);
|
||||
#endif // UPDATER
|
||||
}
|
||||
|
||||
void MainWindow::on_Emulation_Started(void)
|
||||
{
|
||||
this->logDialog.Clear();
|
||||
@@ -1541,7 +1828,7 @@ void MainWindow::on_VidExt_SetWindowedMode(int width, int height, int bps, int f
|
||||
this->showNormal();
|
||||
}
|
||||
|
||||
if (this->menuBar()->isHidden())
|
||||
if (this->ui_ShowMenubar && this->menuBar()->isHidden())
|
||||
{
|
||||
this->menuBar()->show();
|
||||
}
|
||||
@@ -1561,7 +1848,10 @@ void MainWindow::on_VidExt_SetWindowedMode(int width, int height, int bps, int f
|
||||
this->ui_Widget_OpenGL->SetHideCursor(false);
|
||||
}
|
||||
|
||||
this->removeFullscreenActions();
|
||||
if (this->ui_ShowUI)
|
||||
{
|
||||
this->removeActions();
|
||||
}
|
||||
|
||||
// only resize window when we're
|
||||
// not returning from fullscreen
|
||||
@@ -1598,7 +1888,10 @@ void MainWindow::on_VidExt_SetFullscreenMode(int width, int height, int bps, int
|
||||
this->ui_Widget_OpenGL->SetHideCursor(true);
|
||||
}
|
||||
|
||||
this->addFullscreenActions();
|
||||
if (this->ui_ShowUI)
|
||||
{
|
||||
this->addActions();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_VidExt_ResizeWindow(int width, int height)
|
||||
@@ -1654,7 +1947,7 @@ void MainWindow::on_VidExt_ToggleFS(bool fullscreen)
|
||||
this->showFullScreen();
|
||||
}
|
||||
|
||||
if (!this->menuBar()->isHidden())
|
||||
if (this->ui_ShowMenubar && !this->menuBar()->isHidden())
|
||||
{
|
||||
this->menuBar()->hide();
|
||||
}
|
||||
@@ -1674,7 +1967,10 @@ void MainWindow::on_VidExt_ToggleFS(bool fullscreen)
|
||||
this->ui_Widget_OpenGL->SetHideCursor(true);
|
||||
}
|
||||
|
||||
this->addFullscreenActions();
|
||||
if (this->ui_ShowUI)
|
||||
{
|
||||
this->addActions();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1683,11 +1979,11 @@ void MainWindow::on_VidExt_ToggleFS(bool fullscreen)
|
||||
this->showNormal();
|
||||
}
|
||||
|
||||
if (this->menuBar()->isHidden())
|
||||
if (this->ui_ShowMenubar && this->menuBar()->isHidden())
|
||||
{
|
||||
this->menuBar()->show();
|
||||
}
|
||||
|
||||
|
||||
if (this->ui_ShowToolbar && this->toolBar->isHidden())
|
||||
{
|
||||
this->toolBar->show();
|
||||
@@ -1703,7 +1999,10 @@ void MainWindow::on_VidExt_ToggleFS(bool fullscreen)
|
||||
this->ui_Widget_OpenGL->SetHideCursor(false);
|
||||
}
|
||||
|
||||
this->removeFullscreenActions();
|
||||
if (this->ui_ShowUI)
|
||||
{
|
||||
this->removeActions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1752,3 +2051,21 @@ void MainWindow::on_Core_DebugCallback(CoreDebugMessageType type, QString contex
|
||||
}
|
||||
this->ui_TimerId = this->startTimer(this->ui_TimerTimeout * 1000);
|
||||
}
|
||||
|
||||
void MainWindow::on_Core_StateCallback(CoreStateCallbackType type, int value)
|
||||
{
|
||||
if (type == CoreStateCallbackType::SaveStateLoaded)
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
OnScreenDisplaySetMessage("Failed to load save state.");
|
||||
}
|
||||
}
|
||||
else if (type == CoreStateCallbackType::SaveStateSaved)
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
OnScreenDisplaySetMessage("Failed to save state.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,19 +47,19 @@ class MainWindow : public QMainWindow, private Ui::MainWindow
|
||||
MainWindow(void);
|
||||
~MainWindow(void);
|
||||
|
||||
bool Init(QApplication*);
|
||||
void OpenROM(QString, QString, bool, bool);
|
||||
bool Init(QApplication* app, bool showUI);
|
||||
void OpenROM(QString file, QString disk, bool fullscreen, bool quitAfterEmulation);
|
||||
|
||||
private:
|
||||
Thread::EmulationThread *emulationThread;
|
||||
Thread::EmulationThread *emulationThread = nullptr;
|
||||
|
||||
CoreCallbacks* coreCallBacks;
|
||||
CoreCallbacks* coreCallBacks = nullptr;
|
||||
|
||||
QStackedWidget *ui_Widgets;
|
||||
Widget::OGLWidget *ui_Widget_OpenGL;
|
||||
Widget::RomBrowserWidget *ui_Widget_RomBrowser;
|
||||
EventFilter *ui_EventFilter;
|
||||
QLabel *ui_StatusBar_Label;
|
||||
QStackedWidget *ui_Widgets = nullptr;
|
||||
Widget::OGLWidget *ui_Widget_OpenGL = nullptr;
|
||||
Widget::RomBrowserWidget *ui_Widget_RomBrowser = nullptr;
|
||||
EventFilter *ui_EventFilter = nullptr;
|
||||
QLabel *ui_StatusBar_Label = nullptr;
|
||||
|
||||
QByteArray ui_Geometry;
|
||||
bool ui_Geometry_Maximized = false;
|
||||
@@ -73,16 +73,26 @@ class MainWindow : public QMainWindow, private Ui::MainWindow
|
||||
bool ui_QuitAfterEmulation = false;
|
||||
bool ui_RefreshRomListAfterEmulation = false;
|
||||
|
||||
bool ui_ShowUI = false;
|
||||
bool ui_ShowMenubar = false;
|
||||
bool ui_ShowToolbar = false;
|
||||
bool ui_ShowStatusbar = false;
|
||||
|
||||
bool ui_ManuallyPaused = true;
|
||||
|
||||
QList<QAction*> ui_Actions;
|
||||
bool ui_AddedActions = false;
|
||||
|
||||
QList<QAction*> ui_SlotActions;
|
||||
|
||||
bool ui_SilentUpdateCheck = false;
|
||||
|
||||
int ui_TimerId = 0;
|
||||
int ui_TimerTimeout = 0;
|
||||
|
||||
int ui_FullscreenTimerId = 0;
|
||||
int ui_GamesharkButtonTimerId = 0;
|
||||
int ui_UpdateSaveStateSlotTimerId = 0;
|
||||
|
||||
QString ui_WindowTitle;
|
||||
|
||||
@@ -91,43 +101,46 @@ class MainWindow : public QMainWindow, private Ui::MainWindow
|
||||
void closeEvent(QCloseEvent *) Q_DECL_OVERRIDE;
|
||||
|
||||
void initializeUI();
|
||||
|
||||
void configureUI(QApplication*);
|
||||
void configureTheme(QApplication*);
|
||||
|
||||
void showErrorMessage(QString, QString);
|
||||
|
||||
void updateUI(bool, bool);
|
||||
void configureUI(QApplication* app, bool showUI);
|
||||
void configureTheme(QApplication* app);
|
||||
|
||||
void showErrorMessage(QString text, QString details = "");
|
||||
|
||||
void updateUI(bool inEmulation, bool isPaused);
|
||||
|
||||
void storeGeometry(void);
|
||||
void loadGeometry(void);
|
||||
|
||||
void initializeEmulationThread(void);
|
||||
void connectEmulationThreadSignals(void);
|
||||
void launchEmulationThread(QString, QString);
|
||||
void launchEmulationThread(QString);
|
||||
void launchEmulationThread(QString cartRom, QString diskRom = "");
|
||||
|
||||
QString getSaveStateSlotDateTimeText(QAction* action);
|
||||
QString getSaveStateSlotText(QAction* action, int slot);
|
||||
|
||||
void configureActions(void);
|
||||
void connectActionSignals(void);
|
||||
void updateActions(bool, bool);
|
||||
void updateActions(bool inEmulation, bool isPaused);
|
||||
void updateSaveStateSlotActions(bool inEmulation, bool isPaused);
|
||||
|
||||
void addFullscreenActions(void);
|
||||
void removeFullscreenActions(void);
|
||||
void addActions(void);
|
||||
void removeActions(void);
|
||||
|
||||
#ifdef UPDATER
|
||||
void checkForUpdates(void);
|
||||
void checkForUpdates(bool silent, bool force);
|
||||
#endif // UPDATER
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *) Q_DECL_OVERRIDE;
|
||||
void timerEvent(QTimerEvent *event) Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void on_EventFilter_KeyPressed(QKeyEvent *);
|
||||
void on_EventFilter_KeyReleased(QKeyEvent *);
|
||||
void on_EventFilter_KeyPressed(QKeyEvent *event);
|
||||
void on_EventFilter_KeyReleased(QKeyEvent *event);
|
||||
|
||||
void on_QGuiApplication_applicationStateChanged(Qt::ApplicationState);
|
||||
void on_QGuiApplication_applicationStateChanged(Qt::ApplicationState state);
|
||||
|
||||
#ifdef UPDATER
|
||||
void on_networkAccessManager_Finished(QNetworkReply *);
|
||||
void on_networkAccessManager_Finished(QNetworkReply *reply);
|
||||
#endif // UPDATER
|
||||
|
||||
void on_Action_System_OpenRom(void);
|
||||
@@ -138,11 +151,12 @@ class MainWindow : public QMainWindow, private Ui::MainWindow
|
||||
void on_Action_System_Pause(void);
|
||||
void on_Action_System_Screenshot(void);
|
||||
void on_Action_System_LimitFPS(void);
|
||||
void on_Action_System_SpeedFactor(int factor);
|
||||
void on_Action_System_SaveState(void);
|
||||
void on_Action_System_SaveAs(void);
|
||||
void on_Action_System_LoadState(void);
|
||||
void on_Action_System_Load(void);
|
||||
void on_Action_System_CurrentSaveState(int);
|
||||
void on_Action_System_CurrentSaveState(int slot);
|
||||
void on_Action_System_Cheats(void);
|
||||
void on_Action_System_GSButton(void);
|
||||
void on_Action_System_Exit(void);
|
||||
@@ -153,11 +167,11 @@ class MainWindow : public QMainWindow, private Ui::MainWindow
|
||||
void on_Action_Settings_Input(void);
|
||||
void on_Action_Settings_Settings(void);
|
||||
|
||||
void on_Action_View_Toolbar(bool);
|
||||
void on_Action_View_StatusBar(bool);
|
||||
void on_Action_View_GameList(bool);
|
||||
void on_Action_View_GameGrid(bool);
|
||||
void on_Action_View_UniformSize(bool);
|
||||
void on_Action_View_Toolbar(bool checked);
|
||||
void on_Action_View_StatusBar(bool checked);
|
||||
void on_Action_View_GameList(bool checked);
|
||||
void on_Action_View_GameGrid(bool checked);
|
||||
void on_Action_View_UniformSize(bool checked);
|
||||
void on_Action_View_Fullscreen(void);
|
||||
void on_Action_View_RefreshRoms(void);
|
||||
void on_Action_View_ClearRomCache(void);
|
||||
@@ -165,27 +179,29 @@ class MainWindow : public QMainWindow, private Ui::MainWindow
|
||||
|
||||
void on_Action_Help_Github(void);
|
||||
void on_Action_Help_About(void);
|
||||
void on_Action_Help_Update(void);
|
||||
|
||||
void on_Emulation_Started(void);
|
||||
void on_Emulation_Finished(bool);
|
||||
void on_Emulation_Finished(bool ret);
|
||||
|
||||
void on_RomBrowser_PlayGame(QString);
|
||||
void on_RomBrowser_PlayGameWith(CoreRomType, QString);
|
||||
void on_RomBrowser_PlayGame(QString file);
|
||||
void on_RomBrowser_PlayGameWith(CoreRomType type, QString file);
|
||||
void on_RomBrowser_ChangeRomDirectory(void);
|
||||
void on_RomBrowser_RomInformation(QString);
|
||||
void on_RomBrowser_EditGameSettings(QString);
|
||||
void on_RomBrowser_Cheats(QString);
|
||||
void on_RomBrowser_RomInformation(QString file);
|
||||
void on_RomBrowser_EditGameSettings(QString file);
|
||||
void on_RomBrowser_Cheats(QString file);
|
||||
|
||||
public slots:
|
||||
|
||||
void on_VidExt_Init(void);
|
||||
void on_VidExt_SetupOGL(QSurfaceFormat, QThread *);
|
||||
void on_VidExt_SetWindowedMode(int, int, int, int);
|
||||
void on_VidExt_SetFullscreenMode(int, int, int, int);
|
||||
void on_VidExt_ResizeWindow(int, int);
|
||||
void on_VidExt_ToggleFS(bool);
|
||||
void on_VidExt_SetupOGL(QSurfaceFormat format, QThread *thread);
|
||||
void on_VidExt_SetWindowedMode(int width, int height, int bps, int flags);
|
||||
void on_VidExt_SetFullscreenMode(int width, int height, int bps, int flags);
|
||||
void on_VidExt_ResizeWindow(int width, int height);
|
||||
void on_VidExt_ToggleFS(bool fullscreen);
|
||||
|
||||
void on_Core_DebugCallback(CoreDebugMessageType, QString, QString);
|
||||
void on_Core_DebugCallback(CoreDebugMessageType type, QString context, QString message);
|
||||
void on_Core_StateCallback(CoreStateCallbackType type, int value);
|
||||
};
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
@@ -63,6 +63,30 @@
|
||||
<addaction name="actionSlot_8"/>
|
||||
<addaction name="actionSlot_9"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuSpeedFactor">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Speed Factor</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="speed-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<addaction name="actionSpeed25"/>
|
||||
<addaction name="actionSpeed50"/>
|
||||
<addaction name="actionSpeed75"/>
|
||||
<addaction name="actionSpeed100"/>
|
||||
<addaction name="actionSpeed125"/>
|
||||
<addaction name="actionSpeed150"/>
|
||||
<addaction name="actionSpeed175"/>
|
||||
<addaction name="actionSpeed200"/>
|
||||
<addaction name="actionSpeed225"/>
|
||||
<addaction name="actionSpeed250"/>
|
||||
<addaction name="actionSpeed275"/>
|
||||
<addaction name="actionSpeed300"/>
|
||||
</widget>
|
||||
<addaction name="action_System_StartRom"/>
|
||||
<addaction name="action_System_OpenCombo"/>
|
||||
<addaction name="separator"/>
|
||||
@@ -73,6 +97,7 @@
|
||||
<addaction name="action_System_Screenshot"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_System_LimitFPS"/>
|
||||
<addaction name="menuSpeedFactor"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_System_SaveState"/>
|
||||
<addaction name="action_System_SaveAs"/>
|
||||
@@ -122,6 +147,8 @@
|
||||
</property>
|
||||
<addaction name="action_Help_Github"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Help_Update"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Help_About"/>
|
||||
</widget>
|
||||
<addaction name="menuSystem"/>
|
||||
@@ -532,6 +559,78 @@
|
||||
<string>Uniform Size (Grid View)</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSpeed25">
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>25%</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSpeed50">
|
||||
<property name="text">
|
||||
<string>50%</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSpeed100">
|
||||
<property name="text">
|
||||
<string>100%</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSpeed125">
|
||||
<property name="text">
|
||||
<string>125%</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSpeed150">
|
||||
<property name="text">
|
||||
<string>150%</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSpeed200">
|
||||
<property name="text">
|
||||
<string>200%</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSpeed75">
|
||||
<property name="text">
|
||||
<string>75%</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSpeed175">
|
||||
<property name="text">
|
||||
<string>175%</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSpeed225">
|
||||
<property name="text">
|
||||
<string>225%</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSpeed250">
|
||||
<property name="text">
|
||||
<string>250%</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSpeed275">
|
||||
<property name="text">
|
||||
<string>275%</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSpeed300">
|
||||
<property name="text">
|
||||
<string>300%</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Help_Update">
|
||||
<property name="icon">
|
||||
<iconset theme="download-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Check For Updates</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="UIResources.qrc"/>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 19h18v2H3v-2zm10-5.828L19.071 7.1l1.414 1.414L12 17 3.515 8.515 4.929 7.1 11 13.17V2h2v11.172z"/></svg>
|
||||
|
After Width: | Height: | Size: 235 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 19h18v2H3v-2zm10-5.828L19.071 7.1l1.414 1.414L12 17 3.515 8.515 4.929 7.1 11 13.17V2h2v11.172z" fill="#ffffff"/></svg>
|
||||
|
After Width: | Height: | Size: 250 B |
@@ -11,6 +11,7 @@
|
||||
<file alias="icons/black/svg/delete-back-line.svg">Resource/icons/black/svg/delete-back-line.svg</file>
|
||||
<file alias="icons/black/svg/delete-bin-line.svg">Resource/icons/black/svg/delete-bin-line.svg</file>
|
||||
<file alias="icons/black/svg/door-open-line.svg">Resource/icons/black/svg/door-open-line.svg</file>
|
||||
<file alias="icons/black/svg/download-line.svg">Resource/icons/black/svg/download-line.svg</file>
|
||||
<file alias="icons/black/svg/file-line.svg">Resource/icons/black/svg/file-line.svg</file>
|
||||
<file alias="icons/black/svg/file-list-line.svg">Resource/icons/black/svg/file-list-line.svg</file>
|
||||
<file alias="icons/black/svg/folder-open-line.svg">Resource/icons/black/svg/folder-open-line.svg</file>
|
||||
@@ -40,6 +41,7 @@
|
||||
<file alias="icons/white/svg/delete-back-line.svg">Resource/icons/white/svg/delete-back-line.svg</file>
|
||||
<file alias="icons/white/svg/delete-bin-line.svg">Resource/icons/white/svg/delete-bin-line.svg</file>
|
||||
<file alias="icons/white/svg/door-open-line.svg">Resource/icons/white/svg/door-open-line.svg</file>
|
||||
<file alias="icons/white/svg/download-line.svg">Resource/icons/white/svg/download-line.svg</file>
|
||||
<file alias="icons/white/svg/file-line.svg">Resource/icons/white/svg/file-line.svg</file>
|
||||
<file alias="icons/white/svg/file-list-line.svg">Resource/icons/white/svg/file-list-line.svg</file>
|
||||
<file alias="icons/white/svg/folder-open-line.svg">Resource/icons/white/svg/folder-open-line.svg</file>
|
||||
|
||||
@@ -65,9 +65,11 @@ void OGLWidget::resizeEvent(QResizeEvent *event)
|
||||
|
||||
void OGLWidget::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
CoreSetVideoSize(this->width, this->height);
|
||||
|
||||
// remove current timer
|
||||
this->killTimer(this->timerId);
|
||||
this->timerId = 0;
|
||||
// only remove current timer
|
||||
// when setting the video size succeeds
|
||||
if (CoreSetVideoSize(this->width, this->height))
|
||||
{
|
||||
this->killTimer(this->timerId);
|
||||
this->timerId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,17 +89,19 @@ RomBrowserWidget::RomBrowserWidget(QWidget *parent) : QStackedWidget(parent)
|
||||
this->listViewWidget->setVerticalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel);
|
||||
this->listViewWidget->verticalHeader()->hide();
|
||||
this->listViewWidget->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
this->listViewWidget->horizontalHeader()->setSectionsMovable(true);
|
||||
this->listViewWidget->horizontalHeader()->setFirstSectionMovable(true);
|
||||
this->listViewWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
|
||||
this->listViewWidget->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
|
||||
this->listViewWidget->horizontalHeader()->setStretchLastSection(true);
|
||||
this->listViewWidget->horizontalHeader()->setSortIndicatorShown(false);
|
||||
this->listViewWidget->horizontalHeader()->setHighlightSections(false);
|
||||
this->listViewWidget->horizontalHeader()->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
|
||||
this->addWidget(this->listViewWidget);
|
||||
connect(this->listViewWidget, &QTableView::doubleClicked, this, &RomBrowserWidget::on_DoubleClicked);
|
||||
connect(this->listViewWidget, &QTableView::customContextMenuRequested, this, &RomBrowserListViewWidget::customContextMenuRequested);
|
||||
connect(this->listViewWidget->horizontalHeader(), &QHeaderView::sortIndicatorChanged, this, &RomBrowserWidget::on_listViewWidget_sortIndicatorChanged);
|
||||
connect(this->listViewWidget->horizontalHeader(), &QHeaderView::sectionResized, this, &RomBrowserWidget::on_listViewWidget_sectionResized);
|
||||
connect(this->listViewWidget->horizontalHeader(), &QHeaderView::sectionMoved, this, &RomBrowserWidget::on_listViewWidget_sectionMoved);
|
||||
connect(this->listViewWidget->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, &RomBrowserWidget::on_listViewWidget_headerContextMenuRequested);
|
||||
connect(this->listViewWidget, &Widget::RomBrowserListViewWidget::ZoomIn, this, &RomBrowserWidget::on_ZoomIn);
|
||||
connect(this->listViewWidget, &Widget::RomBrowserListViewWidget::ZoomOut, this, &RomBrowserWidget::on_ZoomOut);
|
||||
|
||||
@@ -126,14 +128,12 @@ RomBrowserWidget::RomBrowserWidget(QWidget *parent) : QStackedWidget(parent)
|
||||
this->gridViewWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
this->gridViewWidget->verticalScrollBar()->setSingleStep(15);
|
||||
this->gridViewWidget->setWordWrap(true);
|
||||
this->gridViewWidget->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
|
||||
this->gridViewWidget->setFrameStyle(QFrame::NoFrame);
|
||||
int iconWidth = CoreSettingsGetIntValue(SettingsID::RomBrowser_GridViewIconWidth);
|
||||
int iconHeight = CoreSettingsGetIntValue(SettingsID::RomBrowser_GridViewIconHeight);
|
||||
this->gridViewWidget->setIconSize(QSize(iconWidth, iconHeight));
|
||||
this->addWidget(this->gridViewWidget);
|
||||
connect(this->gridViewWidget, &QListView::doubleClicked, this, &RomBrowserWidget::on_DoubleClicked);
|
||||
connect(this->gridViewWidget, &QListView::customContextMenuRequested, this, &RomBrowserListViewWidget::customContextMenuRequested);
|
||||
connect(this->gridViewWidget, &QListView::iconSizeChanged, this, &RomBrowserWidget::on_gridViewWidget_iconSizeChanged);
|
||||
connect(this->gridViewWidget, &Widget::RomBrowserGridViewWidget::ZoomIn, this, &RomBrowserWidget::on_ZoomIn);
|
||||
connect(this->gridViewWidget, &Widget::RomBrowserGridViewWidget::ZoomOut, this, &RomBrowserWidget::on_ZoomOut);
|
||||
@@ -142,7 +142,8 @@ RomBrowserWidget::RomBrowserWidget(QWidget *parent) : QStackedWidget(parent)
|
||||
this->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
|
||||
connect(this, &QStackedWidget::customContextMenuRequested, this, &RomBrowserWidget::customContextMenuRequested);
|
||||
|
||||
// configure context menu actions
|
||||
// define context menu and its contents
|
||||
this->contextMenu = new QMenu(this);
|
||||
this->action_PlayGame = new QAction(this);
|
||||
this->action_PlayGameWith = new QAction(this);
|
||||
this->action_RefreshRomList = new QAction(this);
|
||||
@@ -153,6 +154,12 @@ RomBrowserWidget::RomBrowserWidget(QWidget *parent) : QStackedWidget(parent)
|
||||
this->action_EditCheats = new QAction(this);
|
||||
this->action_SetCoverImage = new QAction(this);
|
||||
this->action_RemoveCoverImage = new QAction(this);
|
||||
|
||||
// define columns menu and its contents
|
||||
this->menu_Columns = new QMenu(this);
|
||||
this->action_ColumnsMenuEntry = new QAction(this);
|
||||
|
||||
// configure context menu contents
|
||||
this->action_PlayGame->setText("Play Game");
|
||||
this->action_PlayGameWith->setText("Play Game with Disk");
|
||||
this->action_RefreshRomList->setText("Refresh ROM List");
|
||||
@@ -163,6 +170,7 @@ RomBrowserWidget::RomBrowserWidget(QWidget *parent) : QStackedWidget(parent)
|
||||
this->action_EditCheats->setText("Edit Cheats");
|
||||
this->action_SetCoverImage->setText("Set Cover Image...");
|
||||
this->action_RemoveCoverImage->setText("Remove Cover Image");
|
||||
this->menu_Columns->menuAction()->setText("Show/Hide Columns");
|
||||
connect(this->action_PlayGame, &QAction::triggered, this, &RomBrowserWidget::on_Action_PlayGame);
|
||||
connect(this->action_PlayGameWith, &QAction::triggered, this, &RomBrowserWidget::on_Action_PlayGameWith);
|
||||
connect(this->action_RefreshRomList, &QAction::triggered, this, &RomBrowserWidget::on_Action_RefreshRomList);
|
||||
@@ -175,7 +183,6 @@ RomBrowserWidget::RomBrowserWidget(QWidget *parent) : QStackedWidget(parent)
|
||||
connect(this->action_RemoveCoverImage, &QAction::triggered, this, &RomBrowserWidget::on_Action_RemoveCoverImage);
|
||||
|
||||
// configure context menu
|
||||
this->contextMenu = new QMenu(this);
|
||||
this->contextMenu->addAction(this->action_PlayGame);
|
||||
this->contextMenu->addAction(this->action_PlayGameWith);
|
||||
this->contextMenu->addSeparator();
|
||||
@@ -191,6 +198,7 @@ RomBrowserWidget::RomBrowserWidget(QWidget *parent) : QStackedWidget(parent)
|
||||
this->contextMenu->addSeparator();
|
||||
this->contextMenu->addAction(this->action_SetCoverImage);
|
||||
this->contextMenu->addAction(this->action_RemoveCoverImage);
|
||||
this->contextMenu->addMenu(this->menu_Columns);
|
||||
|
||||
// configure current view widget
|
||||
int currentView = CoreSettingsGetIntValue(SettingsID::RomBrowser_ViewMode);
|
||||
@@ -216,6 +224,7 @@ void RomBrowserWidget::RefreshRomList(void)
|
||||
this->coversDirectory = QString::fromStdString(CoreGetUserDataDirectory().string());
|
||||
this->coversDirectory += "/Covers";
|
||||
|
||||
this->sortRomResults = CoreSettingsGetBoolValue(SettingsID::RomBrowser_SortAfterSearch);
|
||||
this->listViewSortSection = CoreSettingsGetIntValue(SettingsID::RomBrowser_ListViewSortSection);
|
||||
this->listViewSortOrder = CoreSettingsGetIntValue(SettingsID::RomBrowser_ListViewSortOrder);
|
||||
|
||||
@@ -381,14 +390,8 @@ QIcon RomBrowserWidget::getCurrentCover(QString file, CoreRomHeader header, Core
|
||||
{
|
||||
// fixup file name
|
||||
QString fixedName = name;
|
||||
for (const char c : ":<>\"/\\|?*")
|
||||
for (const QChar c : QString(":<>\"/\\|?*"))
|
||||
{
|
||||
// skip empty characters
|
||||
if (c == '\0')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
fixedName.replace(c, "_");
|
||||
}
|
||||
|
||||
@@ -466,6 +469,7 @@ void RomBrowserWidget::customContextMenuRequested(QPoint position)
|
||||
this->action_SetCoverImage->setVisible(view == this->gridViewWidget);
|
||||
this->action_RemoveCoverImage->setEnabled(hasSelection && !data.coverFile.isEmpty());
|
||||
this->action_RemoveCoverImage->setVisible(view == this->gridViewWidget);
|
||||
this->menu_Columns->menuAction()->setVisible(view == this->listViewWidget);
|
||||
|
||||
if (hasSelection && data.type == CoreRomType::Disk)
|
||||
{ // disk selected
|
||||
@@ -488,9 +492,32 @@ void RomBrowserWidget::customContextMenuRequested(QPoint position)
|
||||
}
|
||||
}
|
||||
|
||||
if (view == this->listViewWidget)
|
||||
{ // list view
|
||||
this->generateColumnsMenu();
|
||||
}
|
||||
|
||||
this->contextMenu->popup(this->mapToGlobal(position));
|
||||
}
|
||||
|
||||
void RomBrowserWidget::generateColumnsMenu(void)
|
||||
{
|
||||
this->menu_Columns->clear();
|
||||
|
||||
for (int i = 0; i < this->listViewModel->columnCount(); i++)
|
||||
{
|
||||
int column = this->listViewWidget->horizontalHeader()->logicalIndex(i);
|
||||
|
||||
this->action_ColumnsMenuEntry = menu_Columns->addAction(this->listViewModel->horizontalHeaderItem(column)->text());
|
||||
this->action_ColumnsMenuEntry->setCheckable(true);
|
||||
this->action_ColumnsMenuEntry->setChecked(!this->listViewWidget->horizontalHeader()->isSectionHidden(column));
|
||||
connect(this->action_ColumnsMenuEntry, &QAction::toggled, [this, column](bool checked)
|
||||
{
|
||||
this->listViewWidget->horizontalHeader()->setSectionHidden(column, !checked);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void RomBrowserWidget::on_listViewWidget_sortIndicatorChanged(int logicalIndex, Qt::SortOrder sortOrder)
|
||||
{
|
||||
CoreSettingsSetValue(SettingsID::RomBrowser_ListViewSortSection, logicalIndex);
|
||||
@@ -499,18 +526,53 @@ void RomBrowserWidget::on_listViewWidget_sortIndicatorChanged(int logicalIndex,
|
||||
|
||||
void RomBrowserWidget::on_listViewWidget_sectionResized(int logicalIndex, int oldWidth, int newWidth)
|
||||
{
|
||||
std::vector<int> columnVisibility = CoreSettingsGetIntListValue(SettingsID::RomBrowser_ColumnVisibility);
|
||||
std::vector<int> columnSizes = CoreSettingsGetIntListValue(SettingsID::RomBrowser_ColumnSizes);
|
||||
|
||||
if (columnSizes.size() <= logicalIndex)
|
||||
if (newWidth == 0)
|
||||
{
|
||||
columnSizes.push_back(newWidth);
|
||||
columnVisibility.at(logicalIndex) = 0;
|
||||
CoreSettingsSetValue(SettingsID::RomBrowser_ColumnVisibility, columnVisibility);
|
||||
}
|
||||
else
|
||||
{
|
||||
columnSizes.at(logicalIndex) = newWidth;
|
||||
columnVisibility.at(logicalIndex) = 1;
|
||||
CoreSettingsSetValue(SettingsID::RomBrowser_ColumnVisibility, columnVisibility);
|
||||
|
||||
int lastVisibleColumn = -1;
|
||||
for (int i = 0; i < this->listViewModel->columnCount(); i++)
|
||||
{
|
||||
int column = this->listViewWidget->horizontalHeader()->logicalIndex(i);
|
||||
if (!this->listViewWidget->horizontalHeader()->isSectionHidden(column))
|
||||
{
|
||||
lastVisibleColumn = column;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->listViewWidget->horizontalHeader()->stretchLastSection() || logicalIndex != lastVisibleColumn)
|
||||
{
|
||||
columnSizes.at(logicalIndex) = newWidth;
|
||||
CoreSettingsSetValue(SettingsID::RomBrowser_ColumnSizes, columnSizes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RomBrowserWidget::on_listViewWidget_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
|
||||
{
|
||||
std::vector<int> columnOrder = CoreSettingsGetIntListValue(SettingsID::RomBrowser_ColumnOrder);
|
||||
|
||||
for (int i = 0; i < columnOrder.size(); i++)
|
||||
{
|
||||
columnOrder.at(i) = this->listViewWidget->horizontalHeader()->visualIndex(i);
|
||||
}
|
||||
|
||||
CoreSettingsSetValue(SettingsID::RomBrowser_ColumnSizes, columnSizes);
|
||||
CoreSettingsSetValue(SettingsID::RomBrowser_ColumnOrder, columnOrder);
|
||||
}
|
||||
|
||||
void RomBrowserWidget::on_listViewWidget_headerContextMenuRequested(QPoint position)
|
||||
{
|
||||
this->generateColumnsMenu();
|
||||
this->menu_Columns->popup(this->mapToGlobal(position));
|
||||
}
|
||||
|
||||
void RomBrowserWidget::on_gridViewWidget_iconSizeChanged(const QSize& size)
|
||||
@@ -595,29 +657,82 @@ void RomBrowserWidget::on_RomBrowserThread_RomFound(QString file, CoreRomType ty
|
||||
void RomBrowserWidget::on_RomBrowserThread_Finished(bool canceled)
|
||||
{
|
||||
// sort data
|
||||
this->listViewModel->sort(this->listViewSortSection, (Qt::SortOrder)this->listViewSortOrder);
|
||||
this->gridViewModel->sort(0, Qt::SortOrder::AscendingOrder);
|
||||
|
||||
// resize list view's columns
|
||||
std::vector<int> columnSizes = CoreSettingsGetIntListValue(SettingsID::RomBrowser_ColumnSizes);
|
||||
if (columnSizes.size() != this->listViewModel->columnCount() ||
|
||||
columnSizes.at(0) == -1)
|
||||
if (this->sortRomResults)
|
||||
{
|
||||
// reset column sizes setting
|
||||
columnSizes.clear();
|
||||
CoreSettingsSetValue(SettingsID::RomBrowser_ColumnSizes, columnSizes);
|
||||
|
||||
this->listViewWidget->resizeColumnsToContents();
|
||||
this->listViewModel->sort(this->listViewSortSection, (Qt::SortOrder)this->listViewSortOrder);
|
||||
this->gridViewModel->sort(0, Qt::SortOrder::AscendingOrder);
|
||||
}
|
||||
else
|
||||
|
||||
// retrieve column settings
|
||||
std::vector<int> columnVisibility = CoreSettingsGetIntListValue(SettingsID::RomBrowser_ColumnVisibility);
|
||||
std::vector<int> columnOrder = CoreSettingsGetIntListValue(SettingsID::RomBrowser_ColumnOrder);
|
||||
std::vector<int> columnSizes = CoreSettingsGetIntListValue(SettingsID::RomBrowser_ColumnSizes);
|
||||
|
||||
// temporarily disable stretching last column in list view
|
||||
this->listViewWidget->horizontalHeader()->setStretchLastSection(false);
|
||||
|
||||
// reset column visibility setting in config file if number of values is incorrect
|
||||
if (!columnVisibility.empty() &&
|
||||
columnVisibility.size() != this->listViewModel->columnCount())
|
||||
{
|
||||
// use settings' width
|
||||
for (int i = 0; i < (columnSizes.size() - 1); i++)
|
||||
columnVisibility.clear();
|
||||
columnVisibility.resize(this->listViewModel->columnCount(), 1);
|
||||
CoreSettingsSetValue(SettingsID::RomBrowser_ColumnVisibility, columnVisibility);
|
||||
}
|
||||
|
||||
// reset column order setting in config file if number of values is incorrect
|
||||
if (!columnOrder.empty() &&
|
||||
columnOrder.size() != this->listViewModel->columnCount())
|
||||
{
|
||||
columnOrder.clear();
|
||||
for (int i = 0; i < this->listViewModel->columnCount(); i++)
|
||||
{
|
||||
columnOrder.push_back(i);
|
||||
}
|
||||
CoreSettingsSetValue(SettingsID::RomBrowser_ColumnOrder, columnOrder);
|
||||
}
|
||||
|
||||
// reset column sizes setting in config file if number of values is incorrect
|
||||
if (!columnSizes.empty() &&
|
||||
columnSizes.size() != this->listViewModel->columnCount())
|
||||
{
|
||||
columnSizes.clear();
|
||||
columnSizes.resize(this->listViewModel->columnCount(), -1);
|
||||
CoreSettingsSetValue(SettingsID::RomBrowser_ColumnSizes, columnSizes);
|
||||
}
|
||||
|
||||
// update list view's column visibilities
|
||||
for (int i = 0; i < columnVisibility.size(); i++)
|
||||
{
|
||||
if (columnVisibility.at(i) == 0)
|
||||
{
|
||||
this->listViewWidget->horizontalHeader()->setSectionHidden(i, true);
|
||||
}
|
||||
}
|
||||
|
||||
// update list view's column order
|
||||
for (int i = 0; i < columnOrder.size(); i++)
|
||||
{
|
||||
this->listViewWidget->horizontalHeader()->moveSection(this->listViewWidget->horizontalHeader()->visualIndex(i), columnOrder.at(i));
|
||||
}
|
||||
|
||||
// update list view's column sizes
|
||||
for (int i = 0; i < columnSizes.size(); i++)
|
||||
{
|
||||
// set column widths to values specified in config file (or resize to content if not already specified)
|
||||
if (columnSizes.at(i) == -1)
|
||||
{
|
||||
this->listViewWidget->resizeColumnToContents(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->listViewWidget->setColumnWidth(i, columnSizes.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
// enable stretching last column in list view
|
||||
this->listViewWidget->horizontalHeader()->setStretchLastSection(true);
|
||||
|
||||
if (!canceled)
|
||||
{
|
||||
if (this->listViewModel->rowCount() == 0)
|
||||
|
||||
@@ -67,6 +67,8 @@ class RomBrowserWidget : public QStackedWidget
|
||||
QElapsedTimer romSearcherTimer;
|
||||
Thread::RomSearcherThread* romSearcherThread = nullptr;
|
||||
|
||||
bool sortRomResults = false;
|
||||
|
||||
int listViewSortSection = 0;
|
||||
int listViewSortOrder = 0;
|
||||
|
||||
@@ -82,6 +84,9 @@ class RomBrowserWidget : public QStackedWidget
|
||||
QAction* action_SetCoverImage;
|
||||
QAction* action_RemoveCoverImage;
|
||||
|
||||
QMenu* menu_Columns;
|
||||
QAction* action_ColumnsMenuEntry;
|
||||
|
||||
QString coversDirectory;
|
||||
|
||||
QStandardItemModel* getCurrentModel(void);
|
||||
@@ -97,10 +102,13 @@ class RomBrowserWidget : public QStackedWidget
|
||||
|
||||
private slots:
|
||||
void on_DoubleClicked(const QModelIndex& index);
|
||||
void customContextMenuRequested(QPoint point);
|
||||
void customContextMenuRequested(QPoint position);
|
||||
void generateColumnsMenu(void);
|
||||
|
||||
void on_listViewWidget_sortIndicatorChanged(int logicalIndex, Qt::SortOrder sortOrder);
|
||||
void on_listViewWidget_sectionResized(int logicalIndex, int oldWidth, int newWidth);
|
||||
void on_listViewWidget_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex);
|
||||
void on_listViewWidget_headerContextMenuRequested(QPoint position);
|
||||
|
||||
void on_gridViewWidget_iconSizeChanged(const QSize& size);
|
||||
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
|
||||
#include <RMG-Core/Core.hpp>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
//
|
||||
// Local Functions
|
||||
@@ -65,6 +70,11 @@ void message_handler(QtMsgType type, const QMessageLogContext &context, const QS
|
||||
std::cerr << typeString << localMsg.constData() << std::endl;
|
||||
}
|
||||
|
||||
void signal_handler(int sig)
|
||||
{
|
||||
QGuiApplication::quit();
|
||||
}
|
||||
|
||||
//
|
||||
// Exported Functions
|
||||
//
|
||||
@@ -74,6 +84,16 @@ int main(int argc, char **argv)
|
||||
// install message handler
|
||||
qInstallMessageHandler(message_handler);
|
||||
|
||||
#ifndef _WIN32
|
||||
// install signal handler
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
#endif
|
||||
|
||||
#ifdef FORCE_XCB
|
||||
setenv("QT_QPA_PLATFORM", "xcb", 1);
|
||||
#endif // FORCE_XCB
|
||||
|
||||
QApplication app(argc, argv);
|
||||
|
||||
UserInterface::MainWindow window;
|
||||
@@ -91,19 +111,59 @@ int main(int argc, char **argv)
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
// custom options
|
||||
#ifndef PORTABLE_INSTALL
|
||||
QCommandLineOption corePathOption("core-path", "Changes the path where the core library is stored", "path");
|
||||
QCommandLineOption pluginPathOption("plugin-path", "Changes the path where the plugins are stored", "path");
|
||||
QCommandLineOption sharedDataPathOption("shared-data-path", "Changes the path where the shared data is stored", "path");
|
||||
corePathOption.setFlags(QCommandLineOption::HiddenFromHelp);
|
||||
pluginPathOption.setFlags(QCommandLineOption::HiddenFromHelp);
|
||||
sharedDataPathOption.setFlags(QCommandLineOption::HiddenFromHelp);
|
||||
#endif // PORTABLE_INSTALL
|
||||
QCommandLineOption debugMessagesOption({"d", "debug-messages"}, "Prints debug callback messages to stdout");
|
||||
QCommandLineOption fullscreenOption({"f", "fullscreen"}, "Launches ROM in fullscreen mode");
|
||||
QCommandLineOption quitAfterEmulation({"q", "quit-after-emulation"}, "Quits RMG when emulation has finished");
|
||||
QCommandLineOption noGuiOption({"n", "nogui"}, "Hides GUI elements (menubar, toolbar, statusbar)");
|
||||
QCommandLineOption quitAfterEmulationOption({"q", "quit-after-emulation"}, "Quits RMG when emulation has finished");
|
||||
QCommandLineOption diskOption("disk", "64DD Disk to open ROM in combination with", "64DD Disk");
|
||||
|
||||
#ifndef PORTABLE_INSTALL
|
||||
parser.addOption(corePathOption);
|
||||
parser.addOption(pluginPathOption);
|
||||
parser.addOption(sharedDataPathOption);
|
||||
#endif // PORTABLE_INSTALL
|
||||
parser.addOption(debugMessagesOption);
|
||||
parser.addOption(fullscreenOption);
|
||||
parser.addOption(quitAfterEmulation);
|
||||
parser.addOption(noGuiOption);
|
||||
parser.addOption(quitAfterEmulationOption);
|
||||
parser.addOption(diskOption);
|
||||
parser.addPositionalArgument("ROM", "ROM to open");
|
||||
|
||||
// parse arguments
|
||||
parser.process(app);
|
||||
|
||||
#ifndef PORTABLE_INSTALL
|
||||
// set path overrides before initializing
|
||||
QString corePathOveride = parser.value(corePathOption);
|
||||
QString pluginPathOverride = parser.value(pluginPathOption);
|
||||
QString sharedDataPathOverride = parser.value(sharedDataPathOption);
|
||||
if (!corePathOveride.isEmpty())
|
||||
{
|
||||
CoreSetCorePathOverride(corePathOveride.toStdString());
|
||||
}
|
||||
if (!pluginPathOverride.isEmpty())
|
||||
{
|
||||
CoreSetPluginPathOverride(pluginPathOverride.toStdString());
|
||||
}
|
||||
if (!sharedDataPathOverride.isEmpty())
|
||||
{
|
||||
CoreSetSharedDataPathOverride(sharedDataPathOverride.toStdString());
|
||||
}
|
||||
#endif // PORTABLE_INSTALL
|
||||
|
||||
// print debug callbacks to stdout if needed
|
||||
CoreSetPrintDebugCallback(parser.isSet(debugMessagesOption));
|
||||
|
||||
// initialize window
|
||||
if (!window.Init(&app))
|
||||
if (!window.Init(&app, !parser.isSet(noGuiOption)))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -112,8 +172,8 @@ int main(int argc, char **argv)
|
||||
QStringList args = parser.positionalArguments();
|
||||
if (!args.empty())
|
||||
{
|
||||
window.OpenROM(args.at(0), parser.value(diskOption), parser.isSet(fullscreenOption), parser.isSet(quitAfterEmulation));
|
||||
window.OpenROM(args.at(0), parser.value(diskOption), parser.isSet(fullscreenOption), parser.isSet(quitAfterEmulationOption));
|
||||
}
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
}
|
||||
|
||||