new branch
This commit is contained in:
parent
31de0bf8c6
commit
2f8fbb21c9
215
.github/workflows/create_sdl3_pr.yml
vendored
Normal file
215
.github/workflows/create_sdl3_pr.yml
vendored
Normal file
@ -0,0 +1,215 @@
|
||||
name: Create SDL3 pr
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# schedule:
|
||||
# - cron: '0 0 * * *'
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.platform.name }}
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- { name: win-x64, os: windows-latest, flags: -A x64 }
|
||||
# - { name: win-x86, os: windows-latest, flags: -A Win32 }
|
||||
# - { name: win-arm64, os: windows-latest, flags: -A ARM64 }
|
||||
- { name: linux-x64, os: ubuntu-20.04, flags: -GNinja, target_apt_arch: ":amd64" }
|
||||
# - { name: linux-x86, os: ubuntu-20.04, flags: -GNinja -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32", target_apt_arch: ":i386" }
|
||||
- { name: linux-arm64, os: ubuntu-22.04-arm, flags: -GNinja, target_apt_arch: ":arm64", container: "arm64v8/ubuntu:20.04" }
|
||||
# - { name: linux-arm, os: ubuntu-22.04-arm, flags: -GNinja, target_apt_arch: ":armhf", container: "arm32v7/ubuntu:20.04" }
|
||||
# - { name: osx-x64, os: macos-latest, flags: -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 }
|
||||
# NOTE: macOS 11.0 is the first released supported by Apple Silicon.
|
||||
- { name: osx-arm64, os: macos-latest, flags: -DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 }
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Build (Linux ARM)
|
||||
if: contains(matrix.platform.container, 'arm')
|
||||
uses: addnab/docker-run-action@v3
|
||||
with:
|
||||
image: ${{ matrix.platform.container }}
|
||||
options: >
|
||||
-v ${{ github.workspace }}:/workspace
|
||||
-e NAME=${{ matrix.platform.name }}
|
||||
-e TARGET_APT_ARCH=${{ matrix.platform.target_apt_arch }}
|
||||
-e RUNNER_OS=${{ runner.os }}
|
||||
-e FLAGS=${{ matrix.platform.flags }}
|
||||
-e BUILD_TYPE=${{ env.BUILD_TYPE }}
|
||||
run: |
|
||||
cd /workspace
|
||||
./src/Ryujinx.SDL3-CS/external/build.sh
|
||||
|
||||
- name: Build
|
||||
if: ${{ !contains(matrix.platform.container, 'arm') }}
|
||||
shell: bash
|
||||
env:
|
||||
NAME: ${{ matrix.platform.name }}
|
||||
TARGET_APT_ARCH: ${{ matrix.platform.target_apt_arch }}
|
||||
RUNNER_OS: ${{ runner.os }}
|
||||
FLAGS: ${{ matrix.platform.flags }}
|
||||
run: ./src/Ryujinx.SDL3-CS/external/build.sh
|
||||
|
||||
- name: Get Actions user id
|
||||
if: runner.os == 'Linux'
|
||||
id: get_uid
|
||||
run: echo "uid=$(id -u $USER)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Correct Ownership in GITHUB_WORKSPACE directory
|
||||
if: runner.os == 'Linux'
|
||||
uses: peter-murray/reset-workspace-ownership-action@v1
|
||||
with:
|
||||
user_id: ${{ steps.get_uid.outputs.uid }}
|
||||
|
||||
- name: Compress native directory
|
||||
run: tar -cf native-${{ matrix.platform.name }}.tar src/Ryujinx.SDL3-CS/runtimes/${{ matrix.platform.name }}
|
||||
|
||||
- name: Upload native artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: native-${{ matrix.platform.name }}
|
||||
path: native-${{ matrix.platform.name }}.tar
|
||||
if-no-files-found: error
|
||||
|
||||
build-ios:
|
||||
name: ios
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Build (iOS)
|
||||
run: xcodebuild -project src/Ryujinx.SDL3-CS/external/SDL/Xcode/SDL/SDL.xcodeproj -target SDL3.xcframework -configuration Release
|
||||
|
||||
- name: Prepare release directory (iOS)
|
||||
run: mkdir -p src/Ryujinx.SDL3-CS/runtimes/ios
|
||||
|
||||
- name: Prepare release (iOS)
|
||||
run: |
|
||||
mkdir -p src/Ryujinx.SDL3-CS/runtimes/ios/native/SDL3.xcframework/ios-arm64/SDL3.framework;
|
||||
mkdir -p src/Ryujinx.SDL3-CS/runtimes/ios/native/SDL3.xcframework/ios-arm64_x86_64-simulator/SDL3.framework;
|
||||
cp src/Ryujinx.SDL3-CS/external/SDL/Xcode/SDL/build/SDL3.xcframework/Info.plist src/Ryujinx.SDL3-CS/runtimes/ios/native/SDL3.xcframework/Info.plist;
|
||||
cp src/Ryujinx.SDL3-CS/external/SDL/Xcode/SDL/build/SDL3.xcframework/ios-arm64/SDL3.framework/SDL3 src/Ryujinx.SDL3-CS/runtimes/ios/native/SDL3.xcframework/ios-arm64/SDL3.framework/SDL3;
|
||||
cp src/Ryujinx.SDL3-CS/external/SDL/Xcode/SDL/build/SDL3.xcframework/ios-arm64/SDL3.framework/Info.plist src/Ryujinx.SDL3-CS/runtimes/ios/native/SDL3.xcframework/ios-arm64/SDL3.framework/Info.plist;
|
||||
cp src/Ryujinx.SDL3-CS/external/SDL/Xcode/SDL/build/SDL3.xcframework/ios-arm64_x86_64-simulator/SDL3.framework/SDL3 src/Ryujinx.SDL3-CS/runtimes/ios/native/SDL3.xcframework/ios-arm64_x86_64-simulator/SDL3.framework/SDL3;
|
||||
cp src/Ryujinx.SDL3-CS/external/SDL/Xcode/SDL/build/SDL3.xcframework/ios-arm64_x86_64-simulator/SDL3.framework/Info.plist src/Ryujinx.SDL3-CS/runtimes/ios/native/SDL3.xcframework/ios-arm64_x86_64-simulator/SDL3.framework/Info.plist;
|
||||
|
||||
- name: Compress native directory
|
||||
run: tar -cf native-ios.tar src/Ryujinx.SDL3-CS/runtimes/ios
|
||||
|
||||
- name: Upload native artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: native-ios
|
||||
path: native-ios.tar
|
||||
if-no-files-found: error
|
||||
|
||||
build-android:
|
||||
name: android
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
NDK_VER: 23.1.7779620
|
||||
PLATFORM_VER: android-34
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: microsoft
|
||||
java-version: |
|
||||
11
|
||||
17
|
||||
|
||||
- name: Install Android SDK Manager
|
||||
uses: android-actions/setup-android@v3
|
||||
with:
|
||||
packages: ''
|
||||
|
||||
- name: Install Android SDK
|
||||
run: |
|
||||
sdkmanager --install "platform-tools" "platforms;$PLATFORM_VER"
|
||||
sdkmanager --install "ndk;$NDK_VER" --channel=3
|
||||
|
||||
- name: Build (Android)
|
||||
run: |
|
||||
export PATH=$ANDROID_HOME/ndk/$NDK_VER:$PATH
|
||||
export OUTPUT=$PWD/src/Ryujinx.SDL3-CS/runtimes/android
|
||||
rm -rf $OUTPUT && mkdir -p $OUTPUT
|
||||
|
||||
# Build SDL3
|
||||
./src/Ryujinx.SDL3-CS/external/SDL/build-scripts/androidbuildlibs.sh APP_ABI="armeabi-v7a arm64-v8a x86 x86_64" NDK_LIBS_OUT="$OUTPUT"
|
||||
|
||||
- name: Build SDL3 Android Java
|
||||
run: |
|
||||
export JAVA_HOME=$JAVA_HOME_11_X64
|
||||
export PATH=$JAVA_HOME_11_X64/bin:$PATH
|
||||
export OUTPUT=$PWD/src/Ryujinx.SDL3-CS/runtimes/android/Jars/
|
||||
rm -rf $OUTPUT && mkdir -p $OUTPUT
|
||||
|
||||
# Build SDL3 Android Java part
|
||||
cd ./src/Ryujinx.SDL3-CS/external/SDL/android-project/app/src/main/java
|
||||
javac -cp $ANDROID_HOME/platforms/$PLATFORM_VER/android.jar -encoding utf8 org/libsdl/app/*.java
|
||||
jar cvf $OUTPUT/SDL3AndroidBridge.jar org/libsdl/app/*.class
|
||||
|
||||
- name: Compress native directory
|
||||
run: tar -cf native-android.tar src/Ryujinx.SDL3-CS/runtimes/android
|
||||
|
||||
- name: Upload native artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: native-android
|
||||
path: native-android.tar
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload JAR artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: android-jar
|
||||
path: src/Ryujinx.SDL3-CS/runtimes/android/Jars/SDL3AndroidBridge.jar
|
||||
if-no-files-found: error
|
||||
|
||||
make-pr:
|
||||
name: Submit pull request
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ build, build-ios, build-android ]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download native artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: native-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Decompress native artifacts
|
||||
run: |
|
||||
for file in native-*.tar
|
||||
do
|
||||
tar -xf "$file"
|
||||
done
|
||||
rm native-*.tar
|
||||
|
||||
- name: Download JAR artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: android-jar
|
||||
path: src/Ryujinx.SDL3-CS/runtimes/android/Jars/
|
||||
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
commit-message: Update native binaries
|
||||
title: Update native binaries
|
||||
body: This PR has been auto-generated to update the native SDL binaries
|
||||
branch: update-native-binaries
|
||||
env:
|
||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
|
245
.github/workflows/sdl3_release.yml
vendored
Normal file
245
.github/workflows/sdl3_release.yml
vendored
Normal file
@ -0,0 +1,245 @@
|
||||
name: SDL3 Release job
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs: {}
|
||||
|
||||
concurrency: release
|
||||
|
||||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
RYUJINX_BASE_VERSION: "1.2"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "madwind"
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx"
|
||||
RELEASE: 1
|
||||
|
||||
jobs:
|
||||
tag:
|
||||
name: Create tag
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
run: |
|
||||
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Create tag
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
github.rest.git.createRef({
|
||||
owner: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}",
|
||||
repo: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}",
|
||||
ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}-SDL3',
|
||||
sha: context.sha
|
||||
})
|
||||
|
||||
- name: Create release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: ${{ steps.version_info.outputs.build_version }}-SDL3
|
||||
tag: ${{ steps.version_info.outputs.build_version }}-SDL3
|
||||
body: |
|
||||
# Stable builds:
|
||||
| Platform | Artifact |
|
||||
|--|--|
|
||||
| Windows 64-bit | [Stable Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
|
||||
| Linux 64-bit | [Stable Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
|
||||
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
|
||||
| macOS | [Stable macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
|
||||
|
||||
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}
|
||||
omitBodyDuringUpdate: true
|
||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
|
||||
release:
|
||||
name: Release for ${{ matrix.platform.name }}
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
|
||||
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
||||
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
global-json-file: global.json
|
||||
|
||||
- name: Overwrite csc problem matcher
|
||||
run: echo "::add-matcher::.github/csc.json"
|
||||
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
run: |
|
||||
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Configure for release
|
||||
run: |
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
shell: bash
|
||||
|
||||
- name: Create output dir
|
||||
run: "mkdir release_output"
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
|
||||
|
||||
- name: Packing Windows builds
|
||||
if: matrix.platform.os == 'windows-latest'
|
||||
run: |
|
||||
pushd publish
|
||||
rm libarmeilleure-jitsupport.dylib
|
||||
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
|
||||
popd
|
||||
shell: bash
|
||||
|
||||
- name: Packing Linux builds
|
||||
if: matrix.platform.os == 'ubuntu-latest'
|
||||
run: |
|
||||
pushd publish
|
||||
chmod +x Ryujinx.sh Ryujinx
|
||||
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
|
||||
popd
|
||||
shell: bash
|
||||
|
||||
- name: Build AppImage (Linux)
|
||||
if: matrix.platform.os == 'ubuntu-latest'
|
||||
run: |
|
||||
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
|
||||
PLATFORM_NAME="${{ matrix.platform.name }}"
|
||||
|
||||
sudo apt install -y zsync desktop-file-utils appstream
|
||||
|
||||
mkdir -p tools
|
||||
export PATH="$PATH:$(readlink -f tools)"
|
||||
|
||||
# Setup appimagetool
|
||||
wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
|
||||
chmod +x tools/appimagetool
|
||||
chmod +x distribution/linux/appimage/build-appimage.sh
|
||||
|
||||
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
|
||||
if [ "$PLATFORM_NAME" = "linux-x64" ]; then
|
||||
ARCH_NAME=x64
|
||||
export ARCH=x86_64
|
||||
elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
|
||||
ARCH_NAME=arm64
|
||||
export ARCH=aarch64
|
||||
else
|
||||
echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync"
|
||||
BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
|
||||
|
||||
pushd publish_appimage
|
||||
mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
|
||||
mv Ryujinx.AppImage.zsync ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
|
||||
popd
|
||||
shell: bash
|
||||
|
||||
- name: Pushing new release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: ${{ steps.version_info.outputs.build_version }}-SDL3
|
||||
artifacts: "release_output/*.tar.gz,release_output/*.zip,release_output/*AppImage*"
|
||||
tag: ${{ steps.version_info.outputs.build_version }}-SDL3
|
||||
body: |
|
||||
# Stable builds:
|
||||
| Platform | Artifact |
|
||||
|--|--|
|
||||
| Windows 64-bit | [Stable Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
|
||||
| Linux 64-bit | [Stable Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
|
||||
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
|
||||
| macOS | [Stable macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
|
||||
|
||||
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}
|
||||
omitBodyDuringUpdate: true
|
||||
allowUpdates: true
|
||||
replacesArtifacts: true
|
||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
|
||||
macos_release:
|
||||
name: Release MacOS universal
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
global-json-file: global.json
|
||||
|
||||
- name: Setup LLVM 17
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 17
|
||||
|
||||
- name: Install rcodesign
|
||||
run: |
|
||||
mkdir -p $HOME/.bin
|
||||
gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
|
||||
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
|
||||
rm apple-codesign.tar.gz
|
||||
mv rcodesign $HOME/.bin/
|
||||
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
run: |
|
||||
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Configure for release
|
||||
run: |
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||
shell: bash
|
||||
|
||||
- name: Publish macOS Ryujinx
|
||||
run: |
|
||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0
|
||||
|
||||
- name: Pushing new release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: ${{ steps.version_info.outputs.build_version }}-SDL3
|
||||
artifacts: "publish/*.tar.gz"
|
||||
tag: ${{ steps.version_info.outputs.build_version }}-SDL3
|
||||
body: ""
|
||||
omitBodyDuringUpdate: true
|
||||
allowUpdates: true
|
||||
replacesArtifacts: true
|
||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "src/Ryujinx.SDL3-CS/external/SDL"]
|
||||
path = src/Ryujinx.SDL3-CS/external/SDL
|
||||
url = https://github.com/libsdl-org/SDL.git
|
@ -41,7 +41,6 @@
|
||||
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
||||
<PackageVersion Include="Gommon" Version="2.7.1" />
|
||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||
<PackageVersion Include="Sep" Version="0.6.0" />
|
||||
|
42
Ryujinx.sln
42
Ryujinx.sln
@ -51,12 +51,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.Soun
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input", "src\Ryujinx.Input\Ryujinx.Input.csproj", "{C16F112F-38C3-40BC-9F5F-4791112063D6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input.SDL2", "src\Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj", "{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.SDL2.Common", "src\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj", "{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL2", "src\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj", "{D99A395A-8569-4DB0-B336-900647890052}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmpeg", "src\Ryujinx.Graphics.Nvdec.FFmpeg\Ryujinx.Graphics.Nvdec.FFmpeg.csproj", "{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "src\Ryujinx\Ryujinx.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
||||
@ -95,6 +89,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
.github\workflows\release.yml = .github\workflows\release.yml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.SDL3.Common", "src\Ryujinx.SDL3.Common\Ryujinx.SDL3.Common.csproj", "{7C70B441-F3D1-41FE-A648-19014BFB88D9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Input.SDL3", "src\Ryujinx.Input.SDL3\Ryujinx.Input.SDL3.csproj", "{7420A718-7E3C-42B5-82EA-74F6BEE0F1FB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.SDL3-CS", "src\Ryujinx.SDL3-CS\Ryujinx.SDL3-CS.csproj", "{ED2A7EA4-4098-47ED-BA87-EDB3537CFC10}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Audio.Backends.SDL3", "src\Ryujinx.Audio.Backends.SDL3\Ryujinx.Audio.Backends.SDL3.csproj", "{027A38DC-774D-4CF7-A1C0-C510BFC4BD29}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -197,18 +199,6 @@ Global
|
||||
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C16F112F-38C3-40BC-9F5F-4791112063D6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BEE1C184-C9A4-410B-8DFC-FB74D5C93AEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@ -259,6 +249,22 @@ Global
|
||||
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7C70B441-F3D1-41FE-A648-19014BFB88D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7C70B441-F3D1-41FE-A648-19014BFB88D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7C70B441-F3D1-41FE-A648-19014BFB88D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7C70B441-F3D1-41FE-A648-19014BFB88D9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7420A718-7E3C-42B5-82EA-74F6BEE0F1FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7420A718-7E3C-42B5-82EA-74F6BEE0F1FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7420A718-7E3C-42B5-82EA-74F6BEE0F1FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7420A718-7E3C-42B5-82EA-74F6BEE0F1FB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ED2A7EA4-4098-47ED-BA87-EDB3537CFC10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ED2A7EA4-4098-47ED-BA87-EDB3537CFC10}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ED2A7EA4-4098-47ED-BA87-EDB3537CFC10}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ED2A7EA4-4098-47ED-BA87-EDB3537CFC10}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{027A38DC-774D-4CF7-A1C0-C510BFC4BD29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{027A38DC-774D-4CF7-A1C0-C510BFC4BD29}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{027A38DC-774D-4CF7-A1C0-C510BFC4BD29}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{027A38DC-774D-4CF7-A1C0-C510BFC4BD29}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -1,13 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.SDL3.Common\Ryujinx.SDL3.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,12 +1,12 @@
|
||||
namespace Ryujinx.Audio.Backends.SDL2
|
||||
namespace Ryujinx.Audio.Backends.SDL3
|
||||
{
|
||||
class SDL2AudioBuffer
|
||||
class SDL3AudioBuffer
|
||||
{
|
||||
public readonly ulong DriverIdentifier;
|
||||
public readonly ulong SampleCount;
|
||||
public ulong SamplePlayed;
|
||||
|
||||
public SDL2AudioBuffer(ulong driverIdentifier, ulong sampleCount)
|
||||
public SDL3AudioBuffer(ulong driverIdentifier, ulong sampleCount)
|
||||
{
|
||||
DriverIdentifier = driverIdentifier;
|
||||
SampleCount = sampleCount;
|
@ -2,44 +2,34 @@ using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Memory;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
|
||||
using static SDL2.SDL;
|
||||
using static SDL3.SDL;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SDL2
|
||||
namespace Ryujinx.Audio.Backends.SDL3
|
||||
{
|
||||
public class SDL2HardwareDeviceDriver : IHardwareDeviceDriver
|
||||
public class SDL3HardwareDeviceDriver : IHardwareDeviceDriver
|
||||
{
|
||||
private readonly ManualResetEvent _updateRequiredEvent;
|
||||
private readonly ManualResetEvent _pauseEvent;
|
||||
private readonly ConcurrentDictionary<SDL2HardwareDeviceSession, byte> _sessions;
|
||||
private readonly ConcurrentDictionary<SDL3HardwareDeviceSession, byte> _sessions;
|
||||
|
||||
private readonly bool _supportSurroundConfiguration;
|
||||
|
||||
public float Volume { get; set; }
|
||||
|
||||
// TODO: Add this to SDL2-CS
|
||||
// NOTE: We use a DllImport here because of marshaling issue for spec.
|
||||
#pragma warning disable SYSLIB1054
|
||||
[DllImport("SDL2")]
|
||||
private static extern int SDL_GetDefaultAudioInfo(nint name, out SDL_AudioSpec spec, int isCapture);
|
||||
#pragma warning restore SYSLIB1054
|
||||
|
||||
public SDL2HardwareDeviceDriver()
|
||||
public SDL3HardwareDeviceDriver()
|
||||
{
|
||||
_updateRequiredEvent = new ManualResetEvent(false);
|
||||
_pauseEvent = new ManualResetEvent(true);
|
||||
_sessions = new ConcurrentDictionary<SDL2HardwareDeviceSession, byte>();
|
||||
_sessions = new ConcurrentDictionary<SDL3HardwareDeviceSession, byte>();
|
||||
|
||||
SDL2Driver.Instance.Initialize();
|
||||
SDL3Driver.Instance.Initialize();
|
||||
|
||||
int res = SDL_GetDefaultAudioInfo(nint.Zero, out SDL_AudioSpec spec, 0);
|
||||
|
||||
if (res != 0)
|
||||
if (!SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, out var spec, out int _))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application,
|
||||
$"SDL_GetDefaultAudioInfo failed with error \"{SDL_GetError()}\"");
|
||||
@ -58,11 +48,11 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
|
||||
private static bool IsSupportedInternal()
|
||||
{
|
||||
uint device = OpenStream(SampleFormat.PcmInt16, Constants.TargetSampleRate, Constants.ChannelCountMax, Constants.TargetSampleCount, null);
|
||||
nint device = OpenStream(SampleFormat.PcmInt16, Constants.TargetSampleRate, Constants.ChannelCountMax, null);
|
||||
|
||||
if (device != 0)
|
||||
{
|
||||
SDL_CloseAudioDevice(device);
|
||||
SDL_DestroyAudioStream(device);
|
||||
}
|
||||
|
||||
return device != 0;
|
||||
@ -78,7 +68,8 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
return _pauseEvent;
|
||||
}
|
||||
|
||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager,
|
||||
SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
||||
{
|
||||
if (channelCount == 0)
|
||||
{
|
||||
@ -92,70 +83,61 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
|
||||
if (direction != Direction.Output)
|
||||
{
|
||||
throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!");
|
||||
throw new NotImplementedException("Input direction is currently not implemented on SDL3 backend!");
|
||||
}
|
||||
|
||||
SDL2HardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
||||
SDL3HardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
||||
|
||||
_sessions.TryAdd(session, 0);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
internal bool Unregister(SDL2HardwareDeviceSession session)
|
||||
internal bool Unregister(SDL3HardwareDeviceSession session)
|
||||
{
|
||||
return _sessions.TryRemove(session, out _);
|
||||
}
|
||||
|
||||
private static SDL_AudioSpec GetSDL2Spec(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount)
|
||||
private static SDL_AudioSpec GetSDL3Spec(SampleFormat requestedSampleFormat, uint requestedSampleRate,
|
||||
uint requestedChannelCount)
|
||||
{
|
||||
return new SDL_AudioSpec
|
||||
{
|
||||
channels = (byte)requestedChannelCount,
|
||||
format = GetSDL2Format(requestedSampleFormat),
|
||||
format = GetSDL3Format(requestedSampleFormat),
|
||||
freq = (int)requestedSampleRate,
|
||||
samples = (ushort)sampleCount,
|
||||
};
|
||||
}
|
||||
|
||||
internal static ushort GetSDL2Format(SampleFormat format)
|
||||
internal static SDL_AudioFormat GetSDL3Format(SampleFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
SampleFormat.PcmInt8 => AUDIO_S8,
|
||||
SampleFormat.PcmInt16 => AUDIO_S16,
|
||||
SampleFormat.PcmInt32 => AUDIO_S32,
|
||||
SampleFormat.PcmFloat => AUDIO_F32,
|
||||
SampleFormat.PcmInt8 => SDL_AudioFormat.SDL_AUDIO_S8,
|
||||
SampleFormat.PcmInt16 => SDL_AudioFormat.SDL_AUDIO_S16,
|
||||
SampleFormat.PcmInt32 => SDL_AudioFormat.SDL_AUDIO_S32,
|
||||
SampleFormat.PcmFloat => SDL_AudioFormat.SDL_AUDIO_F32,
|
||||
_ => throw new ArgumentException($"Unsupported sample format {format}"),
|
||||
};
|
||||
}
|
||||
|
||||
internal static uint OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount, SDL_AudioCallback callback)
|
||||
internal static nint OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate,
|
||||
uint requestedChannelCount, SDL_AudioStreamCallback callback)
|
||||
{
|
||||
SDL_AudioSpec desired = GetSDL2Spec(requestedSampleFormat, requestedSampleRate, requestedChannelCount, sampleCount);
|
||||
SDL_AudioSpec desired = GetSDL3Spec(requestedSampleFormat, requestedSampleRate, requestedChannelCount);
|
||||
|
||||
desired.callback = callback;
|
||||
nint stream =
|
||||
SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, ref desired, callback, nint.Zero);
|
||||
|
||||
uint device = SDL_OpenAudioDevice(nint.Zero, 0, ref desired, out SDL_AudioSpec got, 0);
|
||||
|
||||
if (device == 0)
|
||||
if (stream == 0)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"SDL2 open audio device initialization failed with error \"{SDL_GetError()}\"");
|
||||
Logger.Error?.Print(LogClass.Application,
|
||||
$"SDL3 open audio device initialization failed with error \"{SDL_GetError()}\"");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isValid = got.format == desired.format && got.freq == desired.freq && got.channels == desired.channels;
|
||||
|
||||
if (!isValid)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, "SDL2 open audio device is not valid");
|
||||
SDL_CloseAudioDevice(device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return device;
|
||||
return stream;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@ -168,12 +150,12 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
foreach (SDL2HardwareDeviceSession session in _sessions.Keys)
|
||||
foreach (SDL3HardwareDeviceSession session in _sessions.Keys)
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
|
||||
SDL2Driver.Instance.Dispose();
|
||||
SDL3Driver.Instance.Dispose();
|
||||
|
||||
_pauseEvent.Dispose();
|
||||
}
|
@ -4,40 +4,38 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using static SDL3.SDL;
|
||||
|
||||
using static SDL2.SDL;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SDL2
|
||||
namespace Ryujinx.Audio.Backends.SDL3
|
||||
{
|
||||
class SDL2HardwareDeviceSession : HardwareDeviceSessionOutputBase
|
||||
class SDL3HardwareDeviceSession : HardwareDeviceSessionOutputBase
|
||||
{
|
||||
private readonly SDL2HardwareDeviceDriver _driver;
|
||||
private readonly ConcurrentQueue<SDL2AudioBuffer> _queuedBuffers;
|
||||
private readonly SDL3HardwareDeviceDriver _driver;
|
||||
private readonly ConcurrentQueue<SDL3AudioBuffer> _queuedBuffers;
|
||||
private readonly DynamicRingBuffer _ringBuffer;
|
||||
private ulong _playedSampleCount;
|
||||
private readonly ManualResetEvent _updateRequiredEvent;
|
||||
private uint _outputStream;
|
||||
private nint _outputStream;
|
||||
private bool _hasSetupError;
|
||||
private readonly SDL_AudioCallback _callbackDelegate;
|
||||
private readonly SDL_AudioStreamCallback _callbackDelegate;
|
||||
private readonly int _bytesPerFrame;
|
||||
private uint _sampleCount;
|
||||
private bool _started;
|
||||
private float _volume;
|
||||
private readonly ushort _nativeSampleFormat;
|
||||
private readonly SDL_AudioFormat _nativeSampleFormat;
|
||||
|
||||
public SDL2HardwareDeviceSession(SDL2HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||
public SDL3HardwareDeviceSession(SDL3HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager,
|
||||
SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(
|
||||
memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||
{
|
||||
_driver = driver;
|
||||
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
|
||||
_queuedBuffers = new ConcurrentQueue<SDL2AudioBuffer>();
|
||||
_queuedBuffers = new ConcurrentQueue<SDL3AudioBuffer>();
|
||||
_ringBuffer = new DynamicRingBuffer();
|
||||
_callbackDelegate = Update;
|
||||
_bytesPerFrame = BackendHelper.GetSampleSize(RequestedSampleFormat) * (int)RequestedChannelCount;
|
||||
_nativeSampleFormat = SDL2HardwareDeviceDriver.GetSDL2Format(RequestedSampleFormat);
|
||||
_sampleCount = uint.MaxValue;
|
||||
_nativeSampleFormat = SDL3HardwareDeviceDriver.GetSDL3Format(RequestedSampleFormat);
|
||||
_started = false;
|
||||
_volume = 1f;
|
||||
}
|
||||
@ -46,13 +44,12 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
{
|
||||
uint bufferSampleCount = (uint)GetSampleCount(buffer);
|
||||
bool needAudioSetup = (_outputStream == 0 && !_hasSetupError) ||
|
||||
(bufferSampleCount >= Constants.TargetSampleCount && bufferSampleCount < _sampleCount);
|
||||
(bufferSampleCount >= Constants.TargetSampleCount);
|
||||
|
||||
if (needAudioSetup)
|
||||
{
|
||||
_sampleCount = Math.Max(Constants.TargetSampleCount, bufferSampleCount);
|
||||
|
||||
uint newOutputStream = SDL2HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount, _sampleCount, _callbackDelegate);
|
||||
nint newOutputStream = SDL3HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate,
|
||||
RequestedChannelCount, _callbackDelegate);
|
||||
|
||||
_hasSetupError = newOutputStream == 0;
|
||||
|
||||
@ -60,64 +57,64 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
{
|
||||
if (_outputStream != 0)
|
||||
{
|
||||
SDL_CloseAudioDevice(_outputStream);
|
||||
SDL_DestroyAudioStream(_outputStream);
|
||||
}
|
||||
|
||||
_outputStream = newOutputStream;
|
||||
|
||||
SDL_PauseAudioDevice(_outputStream, _started ? 0 : 1);
|
||||
|
||||
Logger.Info?.Print(LogClass.Audio, $"New audio stream setup with a target sample count of {_sampleCount}");
|
||||
if (_started)
|
||||
{
|
||||
SDL_ResumeAudioStreamDevice(_outputStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_PauseAudioStreamDevice(_outputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void Update(nint userdata, nint stream, int streamLength)
|
||||
private unsafe void Update(nint userdata, nint stream, int additionalAmount, int totalAmount)
|
||||
{
|
||||
Span<byte> streamSpan = new((void*)stream, streamLength);
|
||||
|
||||
int maxFrameCount = (int)GetSampleCount(streamLength);
|
||||
int maxFrameCount = (int)GetSampleCount(additionalAmount);
|
||||
int bufferedFrames = _ringBuffer.Length / _bytesPerFrame;
|
||||
|
||||
int frameCount = Math.Min(bufferedFrames, maxFrameCount);
|
||||
|
||||
if (frameCount == 0)
|
||||
{
|
||||
// SDL2 left the responsibility to the user to clear the buffer.
|
||||
streamSpan.Clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
using SpanOwner<byte> samplesOwner = SpanOwner<byte>.Rent(frameCount * _bytesPerFrame);
|
||||
|
||||
Span<byte> samples = samplesOwner.Span;
|
||||
|
||||
_ringBuffer.Read(samples, 0, samples.Length);
|
||||
int samplesLength = samples.Length;
|
||||
_ringBuffer.Read(samples, 0, samplesLength);
|
||||
|
||||
fixed (byte* p = samples)
|
||||
{
|
||||
nint pStreamSrc = (nint)p;
|
||||
|
||||
// Zero the dest buffer
|
||||
streamSpan.Clear();
|
||||
|
||||
nint pStreamDst = SDL_calloc(1,samplesLength);
|
||||
// Apply volume to written data
|
||||
SDL_MixAudioFormat(stream, pStreamSrc, _nativeSampleFormat, (uint)samples.Length, (int)(_driver.Volume * _volume * SDL_MIX_MAXVOLUME));
|
||||
SDL_MixAudio(pStreamDst, pStreamSrc, _nativeSampleFormat, (uint)samplesLength, _driver.Volume);
|
||||
SDL_PutAudioStreamData(stream, pStreamDst, samplesLength);
|
||||
SDL_free(pStreamDst);
|
||||
}
|
||||
|
||||
ulong sampleCount = GetSampleCount(samples.Length);
|
||||
ulong sampleCount = GetSampleCount(samplesLength);
|
||||
|
||||
ulong availaibleSampleCount = sampleCount;
|
||||
|
||||
bool needUpdate = false;
|
||||
|
||||
while (availaibleSampleCount > 0 && _queuedBuffers.TryPeek(out SDL2AudioBuffer driverBuffer))
|
||||
while (availaibleSampleCount > 0 && _queuedBuffers.TryPeek(out SDL3AudioBuffer driverBuffer))
|
||||
{
|
||||
ulong sampleStillNeeded = driverBuffer.SampleCount - Interlocked.Read(ref driverBuffer.SamplePlayed);
|
||||
ulong playedAudioBufferSampleCount = Math.Min(sampleStillNeeded, availaibleSampleCount);
|
||||
|
||||
ulong currentSamplePlayed = Interlocked.Add(ref driverBuffer.SamplePlayed, playedAudioBufferSampleCount);
|
||||
ulong currentSamplePlayed =
|
||||
Interlocked.Add(ref driverBuffer.SamplePlayed, playedAudioBufferSampleCount);
|
||||
availaibleSampleCount -= playedAudioBufferSampleCount;
|
||||
|
||||
if (currentSamplePlayed == driverBuffer.SampleCount)
|
||||
@ -155,7 +152,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
|
||||
if (_outputStream != 0)
|
||||
{
|
||||
SDL2AudioBuffer driverBuffer = new(buffer.DataPointer, GetSampleCount(buffer));
|
||||
SDL3AudioBuffer driverBuffer = new(buffer.DataPointer, GetSampleCount(buffer));
|
||||
|
||||
_ringBuffer.Write(buffer.Data, 0, buffer.Data.Length);
|
||||
|
||||
@ -180,7 +177,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
{
|
||||
if (_outputStream != 0)
|
||||
{
|
||||
SDL_PauseAudioDevice(_outputStream, 0);
|
||||
SDL_ResumeAudioStreamDevice(_outputStream);
|
||||
}
|
||||
|
||||
_started = true;
|
||||
@ -193,7 +190,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
{
|
||||
if (_outputStream != 0)
|
||||
{
|
||||
SDL_PauseAudioDevice(_outputStream, 1);
|
||||
SDL_PauseAudioStreamDevice(_outputStream);
|
||||
}
|
||||
|
||||
_started = false;
|
||||
@ -204,7 +201,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
|
||||
public override bool WasBufferFullyConsumed(AudioBuffer buffer)
|
||||
{
|
||||
if (!_queuedBuffers.TryPeek(out SDL2AudioBuffer driverBuffer))
|
||||
if (!_queuedBuffers.TryPeek(out SDL3AudioBuffer driverBuffer))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -221,7 +218,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
|
||||
if (_outputStream != 0)
|
||||
{
|
||||
SDL_CloseAudioDevice(_outputStream);
|
||||
SDL_DestroyAudioStream(_outputStream);
|
||||
}
|
||||
}
|
||||
}
|
@ -78,10 +78,5 @@ namespace Ryujinx.Common.Configuration.Hid.Controller
|
||||
/// Controller Rumble Settings
|
||||
/// </summary>
|
||||
public RumbleConfigController Rumble { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controller LED Settings
|
||||
/// </summary>
|
||||
public LedConfigController Led { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
namespace Ryujinx.Common.Configuration.Hid.Controller
|
||||
{
|
||||
public class LedConfigController
|
||||
{
|
||||
/// <summary>
|
||||
/// Enable LED color changing by the emulator
|
||||
/// </summary>
|
||||
public bool EnableLed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ignores the color and disables the LED entirely.
|
||||
/// </summary>
|
||||
public bool TurnOffLed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ignores the color and uses the rainbow color functionality for the LED.
|
||||
/// </summary>
|
||||
public bool UseRainbow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Packed RGB int of the color
|
||||
/// </summary>
|
||||
public uint LedColor { get; set; }
|
||||
}
|
||||
}
|
@ -8,6 +8,6 @@ namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
Invalid,
|
||||
WindowKeyboard,
|
||||
GamepadSDL2,
|
||||
GamepadSDL3
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
||||
return backendType switch
|
||||
{
|
||||
InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardKeyboardInputConfig),
|
||||
InputBackendType.GamepadSDL2 => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardControllerInputConfig),
|
||||
InputBackendType.GamepadSDL3 => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardControllerInputConfig),
|
||||
_ => throw new InvalidOperationException($"Unknown backend type {backendType}"),
|
||||
};
|
||||
}
|
||||
@ -70,7 +70,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
||||
case InputBackendType.WindowKeyboard:
|
||||
JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, _serializerContext.StandardKeyboardInputConfig);
|
||||
break;
|
||||
case InputBackendType.GamepadSDL2:
|
||||
case InputBackendType.GamepadSDL3:
|
||||
JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, _serializerContext.StandardControllerInputConfig);
|
||||
break;
|
||||
default:
|
||||
|
@ -1,129 +0,0 @@
|
||||
using Gommon;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Common.Utilities
|
||||
{
|
||||
public static class Rainbow
|
||||
{
|
||||
public static bool CyclingEnabled { get; set; }
|
||||
|
||||
public static void Enable()
|
||||
{
|
||||
if (!CyclingEnabled)
|
||||
{
|
||||
CyclingEnabled = true;
|
||||
Executor.ExecuteBackgroundAsync(async () =>
|
||||
{
|
||||
while (CyclingEnabled)
|
||||
{
|
||||
await Task.Delay(20);
|
||||
Tick();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void Disable()
|
||||
{
|
||||
CyclingEnabled = false;
|
||||
}
|
||||
|
||||
|
||||
public static float Speed { get; set; } = 1;
|
||||
|
||||
private static readonly Lock _lock = new();
|
||||
|
||||
private static Color _color = Color.Blue;
|
||||
|
||||
public static ref Color Color
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return ref _color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Tick()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_color = HsbToRgb((_color.GetHue() + Speed) / 360);
|
||||
|
||||
_updatedHandler.Call(_color.ToArgb());
|
||||
}
|
||||
}
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
_updatedHandler.Clear();
|
||||
|
||||
lock (_lock)
|
||||
_color = Color.Blue;
|
||||
}
|
||||
|
||||
public static event Action<int> Updated
|
||||
{
|
||||
add => _updatedHandler.Add(value);
|
||||
remove => _updatedHandler.Remove(value);
|
||||
}
|
||||
|
||||
private static readonly Event<int> _updatedHandler = new();
|
||||
|
||||
private static Color HsbToRgb(float hue, float saturation = 1, float brightness = 1)
|
||||
{
|
||||
int r = 0, g = 0, b = 0;
|
||||
if (saturation == 0)
|
||||
{
|
||||
r = g = b = (int)(brightness * 255.0f + 0.5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
float h = (hue - (float)Math.Floor(hue)) * 6.0f;
|
||||
float f = h - (float)Math.Floor(h);
|
||||
float p = brightness * (1.0f - saturation);
|
||||
float q = brightness * (1.0f - saturation * f);
|
||||
float t = brightness * (1.0f - (saturation * (1.0f - f)));
|
||||
switch ((int)h)
|
||||
{
|
||||
case 0:
|
||||
r = (int)(brightness * 255.0f + 0.5f);
|
||||
g = (int)(t * 255.0f + 0.5f);
|
||||
b = (int)(p * 255.0f + 0.5f);
|
||||
break;
|
||||
case 1:
|
||||
r = (int)(q * 255.0f + 0.5f);
|
||||
g = (int)(brightness * 255.0f + 0.5f);
|
||||
b = (int)(p * 255.0f + 0.5f);
|
||||
break;
|
||||
case 2:
|
||||
r = (int)(p * 255.0f + 0.5f);
|
||||
g = (int)(brightness * 255.0f + 0.5f);
|
||||
b = (int)(t * 255.0f + 0.5f);
|
||||
break;
|
||||
case 3:
|
||||
r = (int)(p * 255.0f + 0.5f);
|
||||
g = (int)(q * 255.0f + 0.5f);
|
||||
b = (int)(brightness * 255.0f + 0.5f);
|
||||
break;
|
||||
case 4:
|
||||
r = (int)(t * 255.0f + 0.5f);
|
||||
g = (int)(p * 255.0f + 0.5f);
|
||||
b = (int)(brightness * 255.0f + 0.5f);
|
||||
break;
|
||||
case 5:
|
||||
r = (int)(brightness * 255.0f + 0.5f);
|
||||
g = (int)(p * 255.0f + 0.5f);
|
||||
b = (int)(q * 255.0f + 0.5f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Color.FromArgb(Convert.ToByte(255), Convert.ToByte(r), Convert.ToByte(g), Convert.ToByte(b));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,188 +0,0 @@
|
||||
using Ryujinx.SDL2.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using static SDL2.SDL;
|
||||
|
||||
namespace Ryujinx.Input.SDL2
|
||||
{
|
||||
public class SDL2GamepadDriver : IGamepadDriver
|
||||
{
|
||||
private readonly Dictionary<int, string> _gamepadsInstanceIdsMapping;
|
||||
private readonly List<string> _gamepadsIds;
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
public ReadOnlySpan<string> GamepadsIds
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _gamepadsIds.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string DriverName => "SDL2";
|
||||
|
||||
public event Action<string> OnGamepadConnected;
|
||||
public event Action<string> OnGamepadDisconnected;
|
||||
|
||||
public SDL2GamepadDriver()
|
||||
{
|
||||
_gamepadsInstanceIdsMapping = new Dictionary<int, string>();
|
||||
_gamepadsIds = [];
|
||||
|
||||
SDL2Driver.Instance.Initialize();
|
||||
SDL2Driver.Instance.OnJoyStickConnected += HandleJoyStickConnected;
|
||||
SDL2Driver.Instance.OnJoystickDisconnected += HandleJoyStickDisconnected;
|
||||
|
||||
// Add already connected gamepads
|
||||
int numJoysticks = SDL_NumJoysticks();
|
||||
|
||||
for (int joystickIndex = 0; joystickIndex < numJoysticks; joystickIndex++)
|
||||
{
|
||||
HandleJoyStickConnected(joystickIndex, SDL_JoystickGetDeviceInstanceID(joystickIndex));
|
||||
}
|
||||
}
|
||||
|
||||
private string GenerateGamepadId(int joystickIndex)
|
||||
{
|
||||
Guid guid = SDL_JoystickGetDeviceGUID(joystickIndex);
|
||||
|
||||
// Add a unique identifier to the start of the GUID in case of duplicates.
|
||||
|
||||
if (guid == Guid.Empty)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string id;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
int guidIndex = 0;
|
||||
id = guidIndex + "-" + guid;
|
||||
|
||||
while (_gamepadsIds.Contains(id))
|
||||
{
|
||||
id = (++guidIndex) + "-" + guid;
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private int GetJoystickIndexByGamepadId(string id)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _gamepadsIds.IndexOf(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleJoyStickDisconnected(int joystickInstanceId)
|
||||
{
|
||||
if (!_gamepadsInstanceIdsMapping.Remove(joystickInstanceId, out string id))
|
||||
return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_gamepadsIds.Remove(id);
|
||||
}
|
||||
|
||||
OnGamepadDisconnected?.Invoke(id);
|
||||
}
|
||||
|
||||
private void HandleJoyStickConnected(int joystickDeviceId, int joystickInstanceId)
|
||||
{
|
||||
if (SDL_IsGameController(joystickDeviceId) == SDL_bool.SDL_TRUE)
|
||||
{
|
||||
if (_gamepadsInstanceIdsMapping.ContainsKey(joystickInstanceId))
|
||||
{
|
||||
// Sometimes a JoyStick connected event fires after the app starts even though it was connected before
|
||||
// so it is rejected to avoid doubling the entries.
|
||||
return;
|
||||
}
|
||||
|
||||
string id = GenerateGamepadId(joystickDeviceId);
|
||||
|
||||
if (id == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_gamepadsInstanceIdsMapping.TryAdd(joystickInstanceId, id))
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (joystickDeviceId <= _gamepadsIds.FindLastIndex(_ => true))
|
||||
_gamepadsIds.Insert(joystickDeviceId, id);
|
||||
else
|
||||
_gamepadsIds.Add(id);
|
||||
}
|
||||
|
||||
OnGamepadConnected?.Invoke(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
SDL2Driver.Instance.OnJoyStickConnected -= HandleJoyStickConnected;
|
||||
SDL2Driver.Instance.OnJoystickDisconnected -= HandleJoyStickDisconnected;
|
||||
|
||||
// Simulate a full disconnect when disposing
|
||||
foreach (string id in _gamepadsIds)
|
||||
{
|
||||
OnGamepadDisconnected?.Invoke(id);
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_gamepadsIds.Clear();
|
||||
}
|
||||
|
||||
SDL2Driver.Instance.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
public IGamepad GetGamepad(string id)
|
||||
{
|
||||
int joystickIndex = GetJoystickIndexByGamepadId(id);
|
||||
|
||||
if (joystickIndex == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
nint gamepadHandle = SDL_GameControllerOpen(joystickIndex);
|
||||
|
||||
if (gamepadHandle == nint.Zero)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SDL2Gamepad(gamepadHandle, id);
|
||||
}
|
||||
|
||||
public IEnumerable<IGamepad> GetGamepads()
|
||||
{
|
||||
lock (_gamepadsIds)
|
||||
{
|
||||
foreach (string gamepadId in _gamepadsIds)
|
||||
{
|
||||
yield return GetGamepad(gamepadId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.SDL3.Common\Ryujinx.SDL3.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,18 +1,17 @@
|
||||
using Humanizer;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.HLE.HOS.Services.Hid;
|
||||
using SDL2;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using static SDL2.SDL;
|
||||
using static SDL3.SDL;
|
||||
|
||||
namespace Ryujinx.Input.SDL2
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
class SDL2Gamepad : IGamepad
|
||||
class SDL3Gamepad : IGamepad
|
||||
{
|
||||
private bool HasConfiguration => _configuration != null;
|
||||
|
||||
@ -20,58 +19,38 @@ namespace Ryujinx.Input.SDL2
|
||||
{
|
||||
public bool IsValid => To is not GamepadButtonInputId.Unbound && From is not GamepadButtonInputId.Unbound;
|
||||
}
|
||||
private static readonly Dictionary<GamepadButtonInputId, SDL_GamepadButton> _buttonsDriverDict = new()
|
||||
{
|
||||
{ GamepadButtonInputId.LeftStick, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_STICK },
|
||||
{ GamepadButtonInputId.DpadUp, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_DPAD_UP },
|
||||
{ GamepadButtonInputId.DpadDown, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_DPAD_DOWN },
|
||||
{ GamepadButtonInputId.DpadLeft, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_DPAD_LEFT },
|
||||
{ GamepadButtonInputId.DpadRight, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_DPAD_RIGHT },
|
||||
{ GamepadButtonInputId.Minus, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_BACK },
|
||||
{ GamepadButtonInputId.LeftShoulder, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_SHOULDER },
|
||||
{ GamepadButtonInputId.LeftTrigger, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_PADDLE2 },
|
||||
{ GamepadButtonInputId.RightStick, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_STICK },
|
||||
{ GamepadButtonInputId.A, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_EAST },
|
||||
{ GamepadButtonInputId.B, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_SOUTH },
|
||||
{ GamepadButtonInputId.X, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_NORTH },
|
||||
{ GamepadButtonInputId.Y, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_WEST },
|
||||
{ GamepadButtonInputId.Plus, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_START },
|
||||
{ GamepadButtonInputId.RightShoulder, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER },
|
||||
{ GamepadButtonInputId.RightTrigger, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2 },
|
||||
};
|
||||
|
||||
private StandardControllerInputConfig _configuration;
|
||||
|
||||
private static readonly SDL_GameControllerButton[] _buttonsDriverMapping =
|
||||
[
|
||||
// Unbound, ignored.
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID,
|
||||
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_A,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_B,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_X,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_Y,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSTICK,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSTICK,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
|
||||
|
||||
// NOTE: The left and right trigger are axis, we handle those differently
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID,
|
||||
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_UP,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_DOWN,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_LEFT,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_BACK,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_START,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_GUIDE,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_MISC1,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE1,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE2,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE3,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE4,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_TOUCHPAD,
|
||||
|
||||
// Virtual buttons are invalid, ignored.
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID,
|
||||
SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID
|
||||
];
|
||||
private static readonly SDL_GamepadButton[] _buttonsDriverMapping = ToSDLButtonMapping(_buttonsDriverDict);
|
||||
|
||||
private readonly Lock _userMappingLock = new();
|
||||
|
||||
private readonly List<ButtonMappingEntry> _buttonsUserMapping;
|
||||
|
||||
private readonly StickInputId[] _stickUserMapping =
|
||||
[
|
||||
StickInputId.Unbound,
|
||||
StickInputId.Left,
|
||||
StickInputId.Right
|
||||
];
|
||||
private readonly StickInputId[] _stickUserMapping = new StickInputId[(int)StickInputId.Count]
|
||||
{
|
||||
StickInputId.Unbound, StickInputId.Left, StickInputId.Right,
|
||||
};
|
||||
|
||||
public GamepadFeaturesFlag Features { get; }
|
||||
|
||||
@ -79,76 +58,69 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
private float _triggerThreshold;
|
||||
|
||||
public SDL2Gamepad(nint gamepadHandle, string driverId)
|
||||
public SDL3Gamepad(uint joystickId, string driverId)
|
||||
{
|
||||
_gamepadHandle = gamepadHandle;
|
||||
_gamepadHandle = SDL_OpenGamepad(joystickId);
|
||||
_buttonsUserMapping = new List<ButtonMappingEntry>(20);
|
||||
|
||||
Name = SDL_GameControllerName(_gamepadHandle);
|
||||
Name = SDL_GetGamepadName(_gamepadHandle);
|
||||
Id = driverId;
|
||||
Features = GetFeaturesFlag();
|
||||
_triggerThreshold = 0.0f;
|
||||
|
||||
|
||||
// Enable motion tracking
|
||||
if (Features.HasFlag(GamepadFeaturesFlag.Motion))
|
||||
{
|
||||
if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL, SDL_bool.SDL_TRUE) != 0)
|
||||
if (!SDL_SetGamepadSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL, true))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Hid, $"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_ACCEL}.");
|
||||
Logger.Error?.Print(LogClass.Hid,
|
||||
$"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_ACCEL}.");
|
||||
}
|
||||
|
||||
if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO, SDL_bool.SDL_TRUE) != 0)
|
||||
if (!SDL_SetGamepadSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO, true))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Hid, $"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_GYRO}.");
|
||||
Logger.Error?.Print(LogClass.Hid,
|
||||
$"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_GYRO}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLed(uint packedRgb)
|
||||
private static SDL_GamepadButton[] ToSDLButtonMapping(
|
||||
Dictionary<GamepadButtonInputId, SDL_GamepadButton> buttonsDriverMapping)
|
||||
{
|
||||
if (!Features.HasFlag(GamepadFeaturesFlag.Led)) return;
|
||||
|
||||
byte red = packedRgb > 0 ? (byte)(packedRgb >> 16) : (byte)0;
|
||||
byte green = packedRgb > 0 ? (byte)(packedRgb >> 8) : (byte)0;
|
||||
byte blue = packedRgb > 0 ? (byte)(packedRgb % 256) : (byte)0;
|
||||
|
||||
if (SDL_GameControllerSetLED(_gamepadHandle, red, green, blue) != 0)
|
||||
Logger.Error?.Print(LogClass.Hid, "LED is not supported on this game controller.");
|
||||
return Enumerable.Range(0, (int)GamepadButtonInputId.Count)
|
||||
.Select(i =>
|
||||
buttonsDriverMapping.GetValueOrDefault((GamepadButtonInputId)i,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_INVALID))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private GamepadFeaturesFlag GetFeaturesFlag()
|
||||
{
|
||||
GamepadFeaturesFlag result = GamepadFeaturesFlag.None;
|
||||
|
||||
if (SDL_GameControllerHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL) == SDL_bool.SDL_TRUE &&
|
||||
SDL_GameControllerHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO) == SDL_bool.SDL_TRUE)
|
||||
if (SDL_GamepadHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL) &&
|
||||
SDL_GamepadHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO))
|
||||
{
|
||||
result |= GamepadFeaturesFlag.Motion;
|
||||
}
|
||||
|
||||
if (SDL_GameControllerHasRumble(_gamepadHandle) == SDL_bool.SDL_TRUE)
|
||||
if (SDL_RumbleGamepad(_gamepadHandle, 0, 0, 100))
|
||||
{
|
||||
result |= GamepadFeaturesFlag.Rumble;
|
||||
}
|
||||
|
||||
if (SDL_GameControllerHasLED(_gamepadHandle) == SDL_bool.SDL_TRUE)
|
||||
{
|
||||
result |= GamepadFeaturesFlag.Led;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
public string Name { get; }
|
||||
|
||||
public bool IsConnected => SDL_GameControllerGetAttached(_gamepadHandle) == SDL_bool.SDL_TRUE;
|
||||
public bool IsConnected => SDL_GamepadConnected(_gamepadHandle);
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _gamepadHandle != nint.Zero)
|
||||
{
|
||||
SDL_GameControllerClose(_gamepadHandle);
|
||||
SDL_CloseGamepad(_gamepadHandle);
|
||||
|
||||
_gamepadHandle = nint.Zero;
|
||||
}
|
||||
@ -172,19 +144,10 @@ namespace Ryujinx.Input.SDL2
|
||||
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
|
||||
ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue);
|
||||
|
||||
if (durationMs == uint.MaxValue)
|
||||
|
||||
if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs))
|
||||
{
|
||||
if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, SDL_HAPTIC_INFINITY) != 0)
|
||||
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
|
||||
}
|
||||
else if (durationMs > SDL_HAPTIC_INFINITY)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Hid, $"Unsupported rumble duration {durationMs}");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs) != 0)
|
||||
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
|
||||
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,14 +165,14 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
const int ElementCount = 3;
|
||||
|
||||
|
||||
unsafe
|
||||
{
|
||||
float* values = stackalloc float[ElementCount];
|
||||
|
||||
int result = SDL_GameControllerGetSensorData(_gamepadHandle, sensorType, (nint)values, ElementCount);
|
||||
|
||||
if (result != 0)
|
||||
if (!SDL_GetGamepadSensorData(_gamepadHandle, sensorType, values, ElementCount))
|
||||
{
|
||||
return Vector3.Zero;
|
||||
}
|
||||
|
||||
Vector3 value = new(values[0], values[1], values[2]);
|
||||
|
||||
@ -225,24 +188,13 @@ namespace Ryujinx.Input.SDL2
|
||||
private static Vector3 RadToDegree(Vector3 rad) => rad * (180 / MathF.PI);
|
||||
|
||||
private static Vector3 GsToMs2(Vector3 gs) => gs / SDL_STANDARD_GRAVITY;
|
||||
|
||||
|
||||
public void SetConfiguration(InputConfig configuration)
|
||||
{
|
||||
lock (_userMappingLock)
|
||||
{
|
||||
_configuration = (StandardControllerInputConfig)configuration;
|
||||
|
||||
if (Features.HasFlag(GamepadFeaturesFlag.Led) && _configuration.Led.EnableLed)
|
||||
{
|
||||
if (_configuration.Led.TurnOffLed)
|
||||
(this as IGamepad).ClearLed();
|
||||
else if (_configuration.Led.UseRainbow)
|
||||
SetLed((uint)Rainbow.Color.ToArgb());
|
||||
|
||||
if (!_configuration.Led.TurnOffLed && !_configuration.Led.UseRainbow)
|
||||
SetLed(_configuration.Led.LedColor);
|
||||
}
|
||||
|
||||
_buttonsUserMapping.Clear();
|
||||
|
||||
// First update sticks
|
||||
@ -250,28 +202,48 @@ namespace Ryujinx.Input.SDL2
|
||||
_stickUserMapping[(int)StickInputId.Right] = (StickInputId)_configuration.RightJoyconStick.Joystick;
|
||||
|
||||
// Then left joycon
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (GamepadButtonInputId)_configuration.LeftJoyconStick.StickButton));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (GamepadButtonInputId)_configuration.LeftJoycon.DpadUp));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown, (GamepadButtonInputId)_configuration.LeftJoycon.DpadDown));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (GamepadButtonInputId)_configuration.LeftJoycon.DpadLeft));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (GamepadButtonInputId)_configuration.LeftJoycon.DpadRight));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonMinus));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonL));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonZl));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSr));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSl));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick,
|
||||
(GamepadButtonInputId)_configuration.LeftJoyconStick.StickButton));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadUp));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadDown));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadLeft));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadRight));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonMinus));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonL));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonZl));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonSr));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonSl));
|
||||
|
||||
// Finally right joycon
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (GamepadButtonInputId)_configuration.RightJoyconStick.StickButton));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A, (GamepadButtonInputId)_configuration.RightJoycon.ButtonA));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B, (GamepadButtonInputId)_configuration.RightJoycon.ButtonB));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X, (GamepadButtonInputId)_configuration.RightJoycon.ButtonX));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y, (GamepadButtonInputId)_configuration.RightJoycon.ButtonY));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus, (GamepadButtonInputId)_configuration.RightJoycon.ButtonPlus));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder, (GamepadButtonInputId)_configuration.RightJoycon.ButtonR));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (GamepadButtonInputId)_configuration.RightJoycon.ButtonZr));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (GamepadButtonInputId)_configuration.RightJoycon.ButtonSr));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (GamepadButtonInputId)_configuration.RightJoycon.ButtonSl));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick,
|
||||
(GamepadButtonInputId)_configuration.RightJoyconStick.StickButton));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonA));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonB));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonX));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonY));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonPlus));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonR));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonZr));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonSr));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonSl));
|
||||
|
||||
SetTriggerThreshold(_configuration.TriggerThreshold);
|
||||
}
|
||||
@ -323,21 +295,25 @@ namespace Ryujinx.Input.SDL2
|
||||
return value * ConvertRate;
|
||||
}
|
||||
|
||||
private JoyconConfigControllerStick<GamepadInputId, Common.Configuration.Hid.Controller.StickInputId> GetLogicalJoyStickConfig(StickInputId inputId)
|
||||
private JoyconConfigControllerStick<GamepadInputId, Common.Configuration.Hid.Controller.StickInputId>
|
||||
GetLogicalJoyStickConfig(StickInputId inputId)
|
||||
{
|
||||
switch (inputId)
|
||||
{
|
||||
case StickInputId.Left:
|
||||
if (_configuration.RightJoyconStick.Joystick == Common.Configuration.Hid.Controller.StickInputId.Left)
|
||||
if (_configuration.RightJoyconStick.Joystick ==
|
||||
Common.Configuration.Hid.Controller.StickInputId.Left)
|
||||
return _configuration.RightJoyconStick;
|
||||
else
|
||||
return _configuration.LeftJoyconStick;
|
||||
case StickInputId.Right:
|
||||
if (_configuration.LeftJoyconStick.Joystick == Common.Configuration.Hid.Controller.StickInputId.Right)
|
||||
if (_configuration.LeftJoyconStick.Joystick ==
|
||||
Common.Configuration.Hid.Controller.StickInputId.Right)
|
||||
return _configuration.LeftJoyconStick;
|
||||
else
|
||||
return _configuration.RightJoyconStick;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -353,7 +329,7 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
if (HasConfiguration)
|
||||
{
|
||||
JoyconConfigControllerStick<GamepadInputId, Common.Configuration.Hid.Controller.StickInputId> joyconStickConfig = GetLogicalJoyStickConfig(inputId);
|
||||
var joyconStickConfig = GetLogicalJoyStickConfig(inputId);
|
||||
|
||||
if (joyconStickConfig != null)
|
||||
{
|
||||
@ -380,11 +356,11 @@ namespace Ryujinx.Input.SDL2
|
||||
inputId switch
|
||||
{
|
||||
StickInputId.Left => (
|
||||
SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTX),
|
||||
SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTY)),
|
||||
SDL_GetGamepadAxis(_gamepadHandle, SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFTX),
|
||||
SDL_GetGamepadAxis(_gamepadHandle, SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFTY)),
|
||||
StickInputId.Right => (
|
||||
SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTX),
|
||||
SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTY)),
|
||||
SDL_GetGamepadAxis(_gamepadHandle, SDL_GamepadAxis.SDL_GAMEPAD_AXIS_RIGHTX),
|
||||
SDL_GetGamepadAxis(_gamepadHandle, SDL_GamepadAxis.SDL_GAMEPAD_AXIS_RIGHTY)),
|
||||
_ => throw new NotSupportedException($"Unsupported stick {inputId}")
|
||||
};
|
||||
|
||||
@ -393,17 +369,20 @@ namespace Ryujinx.Input.SDL2
|
||||
switch (inputId)
|
||||
{
|
||||
case GamepadButtonInputId.LeftTrigger:
|
||||
return ConvertRawStickValue(SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERLEFT)) > _triggerThreshold;
|
||||
return ConvertRawStickValue(SDL_GetGamepadAxis(_gamepadHandle,
|
||||
SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFT_TRIGGER)) > _triggerThreshold;
|
||||
case GamepadButtonInputId.RightTrigger:
|
||||
return ConvertRawStickValue(SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) > _triggerThreshold;
|
||||
return ConvertRawStickValue(SDL_GetGamepadAxis(_gamepadHandle,
|
||||
SDL_GamepadAxis.SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)) > _triggerThreshold;
|
||||
}
|
||||
|
||||
if (_buttonsDriverMapping[(int)inputId] == SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID)
|
||||
var button = _buttonsDriverMapping[(int)inputId];
|
||||
if (button == SDL_GamepadButton.SDL_GAMEPAD_BUTTON_INVALID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return SDL_GameControllerGetButton(_gamepadHandle, _buttonsDriverMapping[(int)inputId]) == 1;
|
||||
return SDL_GetGamepadButton(_gamepadHandle, button);
|
||||
}
|
||||
}
|
||||
}
|
192
src/Ryujinx.Input.SDL3/SDL3GamepadDriver.cs
Normal file
192
src/Ryujinx.Input.SDL3/SDL3GamepadDriver.cs
Normal file
@ -0,0 +1,192 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Input.SDL3;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using static SDL3.SDL;
|
||||
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
public class SDL3GamepadDriver : IGamepadDriver
|
||||
{
|
||||
private readonly Dictionary<uint, string> _gamepadsInstanceIdsMapping;
|
||||
private readonly List<string> _gamepadsIds;
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
public ReadOnlySpan<string> GamepadsIds
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _gamepadsIds.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string DriverName => "SDL3";
|
||||
|
||||
public event Action<string> OnGamepadConnected;
|
||||
public event Action<string> OnGamepadDisconnected;
|
||||
|
||||
public SDL3GamepadDriver()
|
||||
{
|
||||
_gamepadsInstanceIdsMapping = new Dictionary<uint, string>();
|
||||
_gamepadsIds = new List<string>();
|
||||
|
||||
SDL3Driver.Instance.Initialize();
|
||||
SDL3Driver.Instance.OnJoyStickConnected += HandleJoyStickConnected;
|
||||
SDL3Driver.Instance.OnJoystickDisconnected += HandleJoyStickDisconnected;
|
||||
SDL3Driver.Instance.OnJoyBatteryUpdated += HandleJoyBatteryUpdated;
|
||||
}
|
||||
|
||||
private string GenerateGamepadId(uint joystickId)
|
||||
{
|
||||
int bufferSize = 33;
|
||||
Span<byte> pszGuid = stackalloc byte[bufferSize];
|
||||
SDL_GUIDToString(SDL_GetJoystickGUIDForID(joystickId), pszGuid, bufferSize);
|
||||
var guid = Encoding.UTF8.GetString(pszGuid);
|
||||
|
||||
string id;
|
||||
lock (_lock)
|
||||
{
|
||||
int guidIndex = 0;
|
||||
id = guidIndex + guid;
|
||||
|
||||
while (_gamepadsIds.Contains(id))
|
||||
{
|
||||
id = (++guidIndex) + "-" + guid;
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private KeyValuePair<uint,string> GetGamepadInfoByGamepadId(string id)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _gamepadsInstanceIdsMapping.FirstOrDefault(gamepadId => gamepadId.Value == id);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleJoyStickDisconnected(uint joystickId)
|
||||
{
|
||||
bool joyConPairDisconnected = false;
|
||||
if (!_gamepadsInstanceIdsMapping.Remove(joystickId, out string id))
|
||||
return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_gamepadsIds.Remove(id);
|
||||
if (!SDL3JoyConPair.IsCombinable(_gamepadsInstanceIdsMapping))
|
||||
{
|
||||
_gamepadsIds.Remove(SDL3JoyConPair.Id);
|
||||
joyConPairDisconnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
OnGamepadDisconnected?.Invoke(id);
|
||||
if (joyConPairDisconnected)
|
||||
{
|
||||
OnGamepadDisconnected?.Invoke(SDL3JoyConPair.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleJoyStickConnected(uint joystickId)
|
||||
{
|
||||
bool joyConPairConnected = false;
|
||||
|
||||
if (_gamepadsInstanceIdsMapping.ContainsKey(joystickId))
|
||||
{
|
||||
// Sometimes a JoyStick connected event fires after the app starts even though it was connected before
|
||||
// so it is rejected to avoid doubling the entries.
|
||||
return;
|
||||
}
|
||||
|
||||
string id = GenerateGamepadId(joystickId);
|
||||
if (id == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_gamepadsInstanceIdsMapping.TryAdd(joystickId, id))
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_gamepadsIds.Add(id);
|
||||
|
||||
if (SDL3JoyConPair.IsCombinable(_gamepadsInstanceIdsMapping))
|
||||
{
|
||||
_gamepadsIds.Remove(SDL3JoyConPair.Id);
|
||||
_gamepadsIds.Add(SDL3JoyConPair.Id);
|
||||
joyConPairConnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
OnGamepadConnected?.Invoke(id);
|
||||
if (joyConPairConnected)
|
||||
{
|
||||
OnGamepadConnected?.Invoke(SDL3JoyConPair.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleJoyBatteryUpdated(uint joystickId, SDL_JoyBatteryEvent joyBatteryEvent)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Hid,
|
||||
$"{SDL_GetGamepadNameForID(joystickId)}, Battery percent: {joyBatteryEvent.percent}");
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
SDL3Driver.Instance.OnJoyStickConnected -= HandleJoyStickConnected;
|
||||
SDL3Driver.Instance.OnJoystickDisconnected -= HandleJoyStickDisconnected;
|
||||
|
||||
// Simulate a full disconnect when disposing
|
||||
foreach (string id in _gamepadsIds)
|
||||
{
|
||||
OnGamepadDisconnected?.Invoke(id);
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_gamepadsIds.Clear();
|
||||
}
|
||||
|
||||
SDL3Driver.Instance.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
public IGamepad GetGamepad(string id)
|
||||
{
|
||||
if (id == SDL3JoyConPair.Id)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return SDL3JoyConPair.GetGamepad(_gamepadsInstanceIdsMapping);
|
||||
}
|
||||
}
|
||||
|
||||
var gamepadInfo = GetGamepadInfoByGamepadId(id);
|
||||
|
||||
if (SDL3JoyCon.IsJoyCon(gamepadInfo.Key))
|
||||
{
|
||||
return new SDL3JoyCon(gamepadInfo.Key, gamepadInfo.Value);
|
||||
}
|
||||
|
||||
return new SDL3Gamepad(gamepadInfo.Key, gamepadInfo.Value);
|
||||
}
|
||||
}
|
||||
}
|
418
src/Ryujinx.Input.SDL3/SDL3JoyCon.cs
Normal file
418
src/Ryujinx.Input.SDL3/SDL3JoyCon.cs
Normal file
@ -0,0 +1,418 @@
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using static SDL3.SDL;
|
||||
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
class SDL3JoyCon : IGamepad
|
||||
{
|
||||
private bool HasConfiguration => _configuration != null;
|
||||
|
||||
private readonly record struct ButtonMappingEntry(GamepadButtonInputId To, GamepadButtonInputId From)
|
||||
{
|
||||
public bool IsValid => To is not GamepadButtonInputId.Unbound && From is not GamepadButtonInputId.Unbound;
|
||||
}
|
||||
|
||||
private StandardControllerInputConfig _configuration;
|
||||
|
||||
private static readonly Dictionary<GamepadButtonInputId, SDL_GamepadButton> _leftButtonsDriverDict = new()
|
||||
{
|
||||
{ GamepadButtonInputId.LeftStick, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_STICK },
|
||||
{ GamepadButtonInputId.DpadUp, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_WEST },
|
||||
{ GamepadButtonInputId.DpadDown, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_EAST },
|
||||
{ GamepadButtonInputId.DpadLeft, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_SOUTH },
|
||||
{ GamepadButtonInputId.DpadRight, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_NORTH },
|
||||
{ GamepadButtonInputId.Minus, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_START },
|
||||
{ GamepadButtonInputId.LeftShoulder, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_PADDLE1 },
|
||||
{ GamepadButtonInputId.LeftTrigger, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_PADDLE2 },
|
||||
{ GamepadButtonInputId.SingleRightTrigger0, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER },
|
||||
{ GamepadButtonInputId.SingleLeftTrigger0, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_SHOULDER },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<GamepadButtonInputId, SDL_GamepadButton> _rightButtonsDriverDict = new()
|
||||
{
|
||||
{ GamepadButtonInputId.RightStick, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_STICK },
|
||||
{ GamepadButtonInputId.A, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_SOUTH },
|
||||
{ GamepadButtonInputId.B, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_WEST },
|
||||
{ GamepadButtonInputId.X, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_EAST },
|
||||
{ GamepadButtonInputId.Y, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_NORTH },
|
||||
{ GamepadButtonInputId.Plus, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_START },
|
||||
{ GamepadButtonInputId.RightShoulder, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1 },
|
||||
{ GamepadButtonInputId.RightTrigger, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2 },
|
||||
{ GamepadButtonInputId.SingleRightTrigger1, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER },
|
||||
{ GamepadButtonInputId.SingleLeftTrigger1, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_SHOULDER }
|
||||
};
|
||||
|
||||
private readonly SDL_GamepadButton[] _buttonsDriverMapping;
|
||||
private readonly Lock _userMappingLock = new();
|
||||
|
||||
private readonly List<ButtonMappingEntry> _buttonsUserMapping;
|
||||
|
||||
private readonly StickInputId[] _stickUserMapping = new StickInputId[(int)StickInputId.Count]
|
||||
{
|
||||
StickInputId.Unbound, StickInputId.Left, StickInputId.Right,
|
||||
};
|
||||
|
||||
public GamepadFeaturesFlag Features { get; }
|
||||
|
||||
private nint _gamepadHandle;
|
||||
|
||||
private readonly SDL_GamepadType _gamepadType;
|
||||
|
||||
public SDL3JoyCon(uint joystickId, string driverId)
|
||||
{
|
||||
_gamepadHandle = SDL_OpenGamepad(joystickId);
|
||||
_buttonsUserMapping = new List<ButtonMappingEntry>(10);
|
||||
|
||||
Name = SDL_GetGamepadName(_gamepadHandle);
|
||||
Id = driverId;
|
||||
Features = GetFeaturesFlag();
|
||||
|
||||
// Enable motion tracking
|
||||
if (Features.HasFlag(GamepadFeaturesFlag.Motion))
|
||||
{
|
||||
if (!SDL_SetGamepadSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL, true))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Hid,
|
||||
$"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_ACCEL}.");
|
||||
}
|
||||
|
||||
if (!SDL_SetGamepadSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO, true))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Hid,
|
||||
$"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_GYRO}.");
|
||||
}
|
||||
}
|
||||
|
||||
_gamepadType = SDL_GetGamepadType(_gamepadHandle);
|
||||
|
||||
_buttonsDriverMapping = _gamepadType switch
|
||||
{
|
||||
SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT => ToSDLButtonMapping(
|
||||
_leftButtonsDriverDict),
|
||||
SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT => ToSDLButtonMapping(
|
||||
_rightButtonsDriverDict),
|
||||
_ => throw new InvalidOperationException($"Unexpected JoyConType value: {_gamepadType}")
|
||||
};
|
||||
}
|
||||
|
||||
private static SDL_GamepadButton[] ToSDLButtonMapping(
|
||||
Dictionary<GamepadButtonInputId, SDL_GamepadButton> buttonsDriverDict)
|
||||
{
|
||||
return Enumerable.Range(0, (int)GamepadButtonInputId.Count)
|
||||
.Select(i =>
|
||||
buttonsDriverDict.GetValueOrDefault((GamepadButtonInputId)i,
|
||||
SDL_GamepadButton.SDL_GAMEPAD_BUTTON_INVALID))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private GamepadFeaturesFlag GetFeaturesFlag()
|
||||
{
|
||||
GamepadFeaturesFlag result = GamepadFeaturesFlag.None;
|
||||
if (SDL_GamepadHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL) &&
|
||||
SDL_GamepadHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO))
|
||||
{
|
||||
result |= GamepadFeaturesFlag.Motion;
|
||||
}
|
||||
|
||||
if (SDL_RumbleGamepad(_gamepadHandle, 0, 0, 100))
|
||||
{
|
||||
result |= GamepadFeaturesFlag.Rumble;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
public string Name { get; }
|
||||
public bool IsConnected => SDL_GamepadConnected(_gamepadHandle);
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _gamepadHandle != nint.Zero)
|
||||
{
|
||||
SDL_CloseGamepad(_gamepadHandle);
|
||||
_gamepadHandle = nint.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
|
||||
public void SetTriggerThreshold(float triggerThreshold)
|
||||
{
|
||||
}
|
||||
|
||||
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
||||
{
|
||||
if (!Features.HasFlag(GamepadFeaturesFlag.Rumble))
|
||||
return;
|
||||
|
||||
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
|
||||
ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue);
|
||||
|
||||
if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 GetMotionData(MotionInputId inputId)
|
||||
{
|
||||
SDL_SensorType sensorType = inputId switch
|
||||
{
|
||||
MotionInputId.Accelerometer => SDL_SensorType.SDL_SENSOR_ACCEL,
|
||||
MotionInputId.Gyroscope => SDL_SensorType.SDL_SENSOR_GYRO,
|
||||
_ => SDL_SensorType.SDL_SENSOR_INVALID
|
||||
};
|
||||
|
||||
if (!Features.HasFlag(GamepadFeaturesFlag.Motion) || sensorType is SDL_SensorType.SDL_SENSOR_INVALID)
|
||||
return Vector3.Zero;
|
||||
|
||||
const int ElementCount = 3;
|
||||
|
||||
unsafe
|
||||
{
|
||||
float* values = stackalloc float[ElementCount];
|
||||
|
||||
if (!SDL_GetGamepadSensorData(_gamepadHandle, sensorType, values, ElementCount))
|
||||
{
|
||||
return Vector3.Zero;
|
||||
}
|
||||
|
||||
Vector3 value = _gamepadType switch
|
||||
{
|
||||
SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT => new Vector3(-values[2], values[1], values[0]),
|
||||
SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT => new Vector3(values[2], values[1], -values[0]),
|
||||
_ => throw new ArgumentOutOfRangeException($"Unexpected JoyConType value: {_gamepadType}")
|
||||
};
|
||||
|
||||
return inputId switch
|
||||
{
|
||||
MotionInputId.Gyroscope => RadToDegree(value),
|
||||
MotionInputId.Accelerometer => GsToMs2(value),
|
||||
_ => value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static Vector3 RadToDegree(Vector3 rad) => rad * (180 / MathF.PI);
|
||||
private static Vector3 GsToMs2(Vector3 gs) => gs / SDL_STANDARD_GRAVITY;
|
||||
|
||||
public void SetConfiguration(InputConfig configuration)
|
||||
{
|
||||
lock (_userMappingLock)
|
||||
{
|
||||
_configuration = (StandardControllerInputConfig)configuration;
|
||||
|
||||
_buttonsUserMapping.Clear();
|
||||
|
||||
// First update sticks
|
||||
_stickUserMapping[(int)StickInputId.Left] = (StickInputId)_configuration.LeftJoyconStick.Joystick;
|
||||
_stickUserMapping[(int)StickInputId.Right] = (StickInputId)_configuration.RightJoyconStick.Joystick;
|
||||
|
||||
|
||||
switch (_gamepadType)
|
||||
{
|
||||
case SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick,
|
||||
(GamepadButtonInputId)_configuration.LeftJoyconStick.StickButton));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadUp));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadDown));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadLeft));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadRight));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonMinus));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonL));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonZl));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonSr));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonSl));
|
||||
break;
|
||||
case SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick,
|
||||
(GamepadButtonInputId)_configuration.RightJoyconStick.StickButton));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonA));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonB));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonX));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonY));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonPlus));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonR));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonZr));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonSr));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonSl));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
SetTriggerThreshold(_configuration.TriggerThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
public GamepadStateSnapshot GetStateSnapshot()
|
||||
{
|
||||
return IGamepad.GetStateSnapshot(this);
|
||||
}
|
||||
|
||||
public GamepadStateSnapshot GetMappedStateSnapshot()
|
||||
{
|
||||
GamepadStateSnapshot rawState = GetStateSnapshot();
|
||||
GamepadStateSnapshot result = default;
|
||||
|
||||
lock (_userMappingLock)
|
||||
{
|
||||
if (_buttonsUserMapping.Count == 0)
|
||||
return rawState;
|
||||
|
||||
|
||||
// ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
|
||||
foreach (ButtonMappingEntry entry in _buttonsUserMapping)
|
||||
{
|
||||
if (!entry.IsValid)
|
||||
continue;
|
||||
|
||||
// Do not touch state of button already pressed
|
||||
if (!result.IsPressed(entry.To))
|
||||
{
|
||||
result.SetPressed(entry.To, rawState.IsPressed(entry.From));
|
||||
}
|
||||
}
|
||||
|
||||
(float leftStickX, float leftStickY) = rawState.GetStick(_stickUserMapping[(int)StickInputId.Left]);
|
||||
(float rightStickX, float rightStickY) = rawState.GetStick(_stickUserMapping[(int)StickInputId.Right]);
|
||||
|
||||
result.SetStick(StickInputId.Left, leftStickX, leftStickY);
|
||||
result.SetStick(StickInputId.Right, rightStickX, rightStickY);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static float ConvertRawStickValue(short value)
|
||||
{
|
||||
const float ConvertRate = 1.0f / (short.MaxValue + 0.5f);
|
||||
|
||||
return value * ConvertRate;
|
||||
}
|
||||
|
||||
private JoyconConfigControllerStick<GamepadInputId, Common.Configuration.Hid.Controller.StickInputId>
|
||||
GetLogicalJoyStickConfig(StickInputId inputId)
|
||||
{
|
||||
switch (inputId)
|
||||
{
|
||||
case StickInputId.Left:
|
||||
if (_configuration.RightJoyconStick.Joystick ==
|
||||
Common.Configuration.Hid.Controller.StickInputId.Left)
|
||||
return _configuration.RightJoyconStick;
|
||||
else
|
||||
return _configuration.LeftJoyconStick;
|
||||
case StickInputId.Right:
|
||||
if (_configuration.LeftJoyconStick.Joystick ==
|
||||
Common.Configuration.Hid.Controller.StickInputId.Right)
|
||||
return _configuration.LeftJoyconStick;
|
||||
else
|
||||
return _configuration.RightJoyconStick;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public (float, float) GetStick(StickInputId inputId)
|
||||
{
|
||||
if (inputId == StickInputId.Unbound)
|
||||
return (0.0f, 0.0f);
|
||||
|
||||
if (inputId == StickInputId.Left && _gamepadType == SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT ||
|
||||
inputId == StickInputId.Right && _gamepadType == SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT)
|
||||
{
|
||||
return (0.0f, 0.0f);
|
||||
}
|
||||
|
||||
(short stickX, short stickY) = GetStickXY();
|
||||
|
||||
float resultX = ConvertRawStickValue(stickX);
|
||||
float resultY = -ConvertRawStickValue(stickY);
|
||||
|
||||
if (HasConfiguration)
|
||||
{
|
||||
var joyconStickConfig = GetLogicalJoyStickConfig(inputId);
|
||||
|
||||
if (joyconStickConfig != null)
|
||||
{
|
||||
if (joyconStickConfig.InvertStickX)
|
||||
resultX = -resultX;
|
||||
|
||||
if (joyconStickConfig.InvertStickY)
|
||||
resultY = -resultY;
|
||||
|
||||
if (joyconStickConfig.Rotate90CW)
|
||||
{
|
||||
float temp = resultX;
|
||||
resultX = resultY;
|
||||
resultY = -temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inputId switch
|
||||
{
|
||||
StickInputId.Left when _gamepadType == SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT => (resultY, -resultX),
|
||||
StickInputId.Right when _gamepadType == SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT => (-resultY, resultX),
|
||||
_ => (0.0f, 0.0f)
|
||||
};
|
||||
}
|
||||
|
||||
private (short, short) GetStickXY()
|
||||
{
|
||||
return (
|
||||
SDL_GetGamepadAxis(_gamepadHandle, SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFTX),
|
||||
SDL_GetGamepadAxis(_gamepadHandle, SDL_GamepadAxis.SDL_GAMEPAD_AXIS_LEFTY));
|
||||
}
|
||||
|
||||
public bool IsPressed(GamepadButtonInputId inputId)
|
||||
{
|
||||
var button = _buttonsDriverMapping[(int)inputId];
|
||||
if (button == SDL_GamepadButton.SDL_GAMEPAD_BUTTON_INVALID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return SDL_GetGamepadButton(_gamepadHandle, button);
|
||||
}
|
||||
|
||||
public static bool IsJoyCon(uint joystickId)
|
||||
{
|
||||
var gamepadName = SDL_GetGamepadTypeForID(joystickId);
|
||||
return gamepadName is SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT
|
||||
or SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
|
||||
}
|
||||
}
|
||||
}
|
215
src/Ryujinx.Input.SDL3/SDL3JoyConPair.cs
Normal file
215
src/Ryujinx.Input.SDL3/SDL3JoyConPair.cs
Normal file
@ -0,0 +1,215 @@
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using static SDL3.SDL;
|
||||
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
class SDL3JoyConPair(SDL3JoyCon left, SDL3JoyCon right) : IGamepad
|
||||
{
|
||||
private StandardControllerInputConfig _configuration;
|
||||
|
||||
private readonly record struct ButtonMappingEntry(GamepadButtonInputId To, GamepadButtonInputId From)
|
||||
{
|
||||
public bool IsValid => To is not GamepadButtonInputId.Unbound && From is not GamepadButtonInputId.Unbound;
|
||||
}
|
||||
|
||||
private readonly StickInputId[] _stickUserMapping = new StickInputId[(int)StickInputId.Count]
|
||||
{
|
||||
StickInputId.Unbound, StickInputId.Left, StickInputId.Right,
|
||||
};
|
||||
|
||||
public GamepadFeaturesFlag Features => (left?.Features ?? GamepadFeaturesFlag.None) |
|
||||
(right?.Features ?? GamepadFeaturesFlag.None);
|
||||
|
||||
public const string Id = "JoyConPair";
|
||||
private readonly Lock _userMappingLock = new();
|
||||
|
||||
private readonly List<ButtonMappingEntry> _buttonsUserMapping = new List<ButtonMappingEntry>(20);
|
||||
string IGamepad.Id => Id;
|
||||
|
||||
public string Name => "* Nintendo Switch Joy-Con (L/R)";
|
||||
public bool IsConnected => left is { IsConnected: true } && right is { IsConnected: true };
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
left?.Dispose();
|
||||
right?.Dispose();
|
||||
}
|
||||
|
||||
public GamepadStateSnapshot GetMappedStateSnapshot()
|
||||
{
|
||||
GamepadStateSnapshot rawState = GetStateSnapshot();
|
||||
GamepadStateSnapshot result = default;
|
||||
|
||||
lock (_userMappingLock)
|
||||
{
|
||||
if (_buttonsUserMapping.Count == 0)
|
||||
return rawState;
|
||||
|
||||
|
||||
// ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
|
||||
foreach (ButtonMappingEntry entry in _buttonsUserMapping)
|
||||
{
|
||||
if (!entry.IsValid)
|
||||
continue;
|
||||
|
||||
// Do not touch state of button already pressed
|
||||
if (!result.IsPressed(entry.To))
|
||||
{
|
||||
result.SetPressed(entry.To, rawState.IsPressed(entry.From));
|
||||
}
|
||||
}
|
||||
|
||||
(float leftStickX, float leftStickY) = rawState.GetStick(_stickUserMapping[(int)StickInputId.Left]);
|
||||
(float rightStickX, float rightStickY) = rawState.GetStick(_stickUserMapping[(int)StickInputId.Right]);
|
||||
|
||||
result.SetStick(StickInputId.Left, leftStickX, leftStickY);
|
||||
result.SetStick(StickInputId.Right, rightStickX, rightStickY);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Vector3 GetMotionData(MotionInputId inputId)
|
||||
{
|
||||
return inputId switch
|
||||
{
|
||||
MotionInputId.Accelerometer or
|
||||
MotionInputId.Gyroscope => left.GetMotionData(inputId),
|
||||
MotionInputId.RightAccelerometer => right.GetMotionData(MotionInputId.Accelerometer),
|
||||
MotionInputId.RightGyroscope => right.GetMotionData(MotionInputId.Gyroscope),
|
||||
_ => Vector3.Zero
|
||||
};
|
||||
}
|
||||
|
||||
public GamepadStateSnapshot GetStateSnapshot()
|
||||
{
|
||||
return IGamepad.GetStateSnapshot(this);
|
||||
}
|
||||
|
||||
public (float, float) GetStick(StickInputId inputId)
|
||||
{
|
||||
return inputId switch
|
||||
{
|
||||
StickInputId.Left => left.GetStick(StickInputId.Left),
|
||||
StickInputId.Right => right.GetStick(StickInputId.Right),
|
||||
_ => (0, 0)
|
||||
};
|
||||
}
|
||||
|
||||
public bool IsPressed(GamepadButtonInputId inputId)
|
||||
{
|
||||
return left.IsPressed(inputId) || right.IsPressed(inputId);
|
||||
}
|
||||
|
||||
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
||||
{
|
||||
if (lowFrequency != 0)
|
||||
{
|
||||
right.Rumble(lowFrequency, lowFrequency, durationMs);
|
||||
}
|
||||
|
||||
if (highFrequency != 0)
|
||||
{
|
||||
left.Rumble(highFrequency, highFrequency, durationMs);
|
||||
}
|
||||
|
||||
if (lowFrequency == 0 && highFrequency == 0)
|
||||
{
|
||||
left.Rumble(0, 0, durationMs);
|
||||
right.Rumble(0, 0, durationMs);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetConfiguration(InputConfig configuration)
|
||||
{
|
||||
lock (_userMappingLock)
|
||||
{
|
||||
_configuration = (StandardControllerInputConfig)configuration;
|
||||
_buttonsUserMapping.Clear();
|
||||
|
||||
// First update sticks
|
||||
_stickUserMapping[(int)StickInputId.Left] = (StickInputId)_configuration.LeftJoyconStick.Joystick;
|
||||
_stickUserMapping[(int)StickInputId.Right] = (StickInputId)_configuration.RightJoyconStick.Joystick;
|
||||
|
||||
// Then left joycon
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick,
|
||||
(GamepadButtonInputId)_configuration.LeftJoyconStick.StickButton));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadUp));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadDown));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadLeft));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadRight));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonMinus));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonL));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonZl));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonSr));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonSl));
|
||||
|
||||
// Finally right joycon
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick,
|
||||
(GamepadButtonInputId)_configuration.RightJoyconStick.StickButton));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonA));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonB));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonX));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonY));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonPlus));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonR));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonZr));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonSr));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonSl));
|
||||
left.SetConfiguration(configuration);
|
||||
right.SetConfiguration(configuration);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTriggerThreshold(float triggerThreshold)
|
||||
{
|
||||
}
|
||||
|
||||
public static bool IsCombinable(Dictionary<uint, string> gamepadsInstanceIdsMapping)
|
||||
{
|
||||
var gamepadTypes = gamepadsInstanceIdsMapping.Keys.Select(SDL_GetGamepadTypeForID).ToArray();
|
||||
return gamepadTypes.Contains(SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT) &&
|
||||
gamepadTypes.Contains(SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT);
|
||||
}
|
||||
|
||||
public static IGamepad GetGamepad(Dictionary<uint, string> gamepadsInstanceIdsMapping)
|
||||
{
|
||||
var leftPair =
|
||||
gamepadsInstanceIdsMapping.FirstOrDefault(pair =>
|
||||
SDL_GetGamepadTypeForID(pair.Key) == SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT);
|
||||
var rightPair =
|
||||
gamepadsInstanceIdsMapping.FirstOrDefault(pair =>
|
||||
SDL_GetGamepadTypeForID(pair.Key) == SDL_GamepadType.SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT);
|
||||
if (leftPair.Key == 0 || rightPair.Key == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SDL3JoyConPair(new SDL3JoyCon(leftPair.Key, leftPair.Value),
|
||||
new SDL3JoyCon(rightPair.Key, rightPair.Value));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +1,18 @@
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using static SDL2.SDL;
|
||||
using static SDL3.SDL;
|
||||
|
||||
using ConfigKey = Ryujinx.Common.Configuration.Hid.Key;
|
||||
|
||||
namespace Ryujinx.Input.SDL2
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
class SDL2Keyboard : IKeyboard
|
||||
class SDL3Keyboard : IKeyboard
|
||||
{
|
||||
private readonly record struct ButtonMappingEntry(GamepadButtonInputId To, Key From)
|
||||
{
|
||||
@ -22,7 +22,7 @@ namespace Ryujinx.Input.SDL2
|
||||
private readonly Lock _userMappingLock = new();
|
||||
|
||||
#pragma warning disable IDE0052 // Remove unread private member
|
||||
private readonly SDL2KeyboardDriver _driver;
|
||||
private readonly SDL3KeyboardDriver _driver;
|
||||
#pragma warning restore IDE0052
|
||||
private StandardKeyboardInputConfig _configuration;
|
||||
private readonly List<ButtonMappingEntry> _buttonsUserMapping;
|
||||
@ -116,32 +116,32 @@ namespace Ryujinx.Input.SDL2
|
||||
SDL_Keycode.SDLK_KP_PLUS,
|
||||
SDL_Keycode.SDLK_KP_DECIMAL,
|
||||
SDL_Keycode.SDLK_KP_ENTER,
|
||||
SDL_Keycode.SDLK_a,
|
||||
SDL_Keycode.SDLK_b,
|
||||
SDL_Keycode.SDLK_c,
|
||||
SDL_Keycode.SDLK_d,
|
||||
SDL_Keycode.SDLK_e,
|
||||
SDL_Keycode.SDLK_f,
|
||||
SDL_Keycode.SDLK_g,
|
||||
SDL_Keycode.SDLK_h,
|
||||
SDL_Keycode.SDLK_i,
|
||||
SDL_Keycode.SDLK_j,
|
||||
SDL_Keycode.SDLK_k,
|
||||
SDL_Keycode.SDLK_l,
|
||||
SDL_Keycode.SDLK_m,
|
||||
SDL_Keycode.SDLK_n,
|
||||
SDL_Keycode.SDLK_o,
|
||||
SDL_Keycode.SDLK_p,
|
||||
SDL_Keycode.SDLK_q,
|
||||
SDL_Keycode.SDLK_r,
|
||||
SDL_Keycode.SDLK_s,
|
||||
SDL_Keycode.SDLK_t,
|
||||
SDL_Keycode.SDLK_u,
|
||||
SDL_Keycode.SDLK_v,
|
||||
SDL_Keycode.SDLK_w,
|
||||
SDL_Keycode.SDLK_x,
|
||||
SDL_Keycode.SDLK_y,
|
||||
SDL_Keycode.SDLK_z,
|
||||
SDL_Keycode.SDLK_A,
|
||||
SDL_Keycode.SDLK_B,
|
||||
SDL_Keycode.SDLK_C,
|
||||
SDL_Keycode.SDLK_D,
|
||||
SDL_Keycode.SDLK_E,
|
||||
SDL_Keycode.SDLK_F,
|
||||
SDL_Keycode.SDLK_G,
|
||||
SDL_Keycode.SDLK_H,
|
||||
SDL_Keycode.SDLK_I,
|
||||
SDL_Keycode.SDLK_J,
|
||||
SDL_Keycode.SDLK_K,
|
||||
SDL_Keycode.SDLK_L,
|
||||
SDL_Keycode.SDLK_M,
|
||||
SDL_Keycode.SDLK_N,
|
||||
SDL_Keycode.SDLK_O,
|
||||
SDL_Keycode.SDLK_P,
|
||||
SDL_Keycode.SDLK_Q,
|
||||
SDL_Keycode.SDLK_R,
|
||||
SDL_Keycode.SDLK_S,
|
||||
SDL_Keycode.SDLK_T,
|
||||
SDL_Keycode.SDLK_U,
|
||||
SDL_Keycode.SDLK_V,
|
||||
SDL_Keycode.SDLK_W,
|
||||
SDL_Keycode.SDLK_X,
|
||||
SDL_Keycode.SDLK_Y,
|
||||
SDL_Keycode.SDLK_Z,
|
||||
SDL_Keycode.SDLK_0,
|
||||
SDL_Keycode.SDLK_1,
|
||||
SDL_Keycode.SDLK_2,
|
||||
@ -152,14 +152,14 @@ namespace Ryujinx.Input.SDL2
|
||||
SDL_Keycode.SDLK_7,
|
||||
SDL_Keycode.SDLK_8,
|
||||
SDL_Keycode.SDLK_9,
|
||||
SDL_Keycode.SDLK_BACKQUOTE,
|
||||
SDL_Keycode.SDLK_BACKQUOTE,
|
||||
SDL_Keycode.SDLK_GRAVE,
|
||||
SDL_Keycode.SDLK_GRAVE,
|
||||
SDL_Keycode.SDLK_MINUS,
|
||||
SDL_Keycode.SDLK_PLUS,
|
||||
SDL_Keycode.SDLK_LEFTBRACKET,
|
||||
SDL_Keycode.SDLK_RIGHTBRACKET,
|
||||
SDL_Keycode.SDLK_SEMICOLON,
|
||||
SDL_Keycode.SDLK_QUOTE,
|
||||
SDL_Keycode.SDLK_APOSTROPHE,
|
||||
SDL_Keycode.SDLK_COMMA,
|
||||
SDL_Keycode.SDLK_PERIOD,
|
||||
SDL_Keycode.SDLK_SLASH,
|
||||
@ -169,7 +169,7 @@ namespace Ryujinx.Input.SDL2
|
||||
SDL_Keycode.SDLK_0
|
||||
];
|
||||
|
||||
public SDL2Keyboard(SDL2KeyboardDriver driver, string id, string name)
|
||||
public SDL3Keyboard(SDL3KeyboardDriver driver, string id, string name)
|
||||
{
|
||||
_driver = driver;
|
||||
Id = id;
|
||||
@ -193,55 +193,54 @@ namespace Ryujinx.Input.SDL2
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int ToSDL2Scancode(Key key)
|
||||
private static int ToSDL3Scancode(Key key)
|
||||
{
|
||||
if (key >= Key.Unknown && key <= Key.Menu)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)SDL_GetScancodeFromKey(_keysDriverMapping[(int)key]);
|
||||
IntPtr modstate = (int)SDL_Keymod.SDL_KMOD_NONE;
|
||||
return (int)SDL_GetScancodeFromKey((uint)_keysDriverMapping[(int)key], modstate);
|
||||
}
|
||||
|
||||
private static SDL_Keymod GetKeyboardModifierMask(Key key)
|
||||
{
|
||||
return key switch
|
||||
{
|
||||
Key.ShiftLeft => SDL_Keymod.KMOD_LSHIFT,
|
||||
Key.ShiftRight => SDL_Keymod.KMOD_RSHIFT,
|
||||
Key.ControlLeft => SDL_Keymod.KMOD_LCTRL,
|
||||
Key.ControlRight => SDL_Keymod.KMOD_RCTRL,
|
||||
Key.AltLeft => SDL_Keymod.KMOD_LALT,
|
||||
Key.AltRight => SDL_Keymod.KMOD_RALT,
|
||||
Key.WinLeft => SDL_Keymod.KMOD_LGUI,
|
||||
Key.WinRight => SDL_Keymod.KMOD_RGUI,
|
||||
// NOTE: Menu key isn't supported by SDL2.
|
||||
_ => SDL_Keymod.KMOD_NONE,
|
||||
Key.ShiftLeft => SDL_Keymod.SDL_KMOD_LSHIFT,
|
||||
Key.ShiftRight => SDL_Keymod.SDL_KMOD_RSHIFT,
|
||||
Key.ControlLeft => SDL_Keymod.SDL_KMOD_LCTRL,
|
||||
Key.ControlRight => SDL_Keymod.SDL_KMOD_RCTRL,
|
||||
Key.AltLeft => SDL_Keymod.SDL_KMOD_LALT,
|
||||
Key.AltRight => SDL_Keymod.SDL_KMOD_RALT,
|
||||
Key.WinLeft => SDL_Keymod.SDL_KMOD_LGUI,
|
||||
Key.WinRight => SDL_Keymod.SDL_KMOD_RGUI,
|
||||
// NOTE: Menu key isn't supported by SDL3.
|
||||
_ => SDL_Keymod.SDL_KMOD_NONE,
|
||||
};
|
||||
}
|
||||
|
||||
public KeyboardStateSnapshot GetKeyboardStateSnapshot()
|
||||
{
|
||||
ReadOnlySpan<byte> rawKeyboardState;
|
||||
Span<SDLBool> rawKeyboardState;
|
||||
SDL_Keymod rawKeyboardModifierState = SDL_GetModState();
|
||||
|
||||
unsafe
|
||||
{
|
||||
nint statePtr = SDL_GetKeyboardState(out int numKeys);
|
||||
|
||||
rawKeyboardState = new ReadOnlySpan<byte>((byte*)statePtr, numKeys);
|
||||
rawKeyboardState = SDL_GetKeyboardState(out int numKeys);
|
||||
}
|
||||
|
||||
bool[] keysState = new bool[(int)Key.Count];
|
||||
|
||||
for (Key key = 0; key < Key.Count; key++)
|
||||
{
|
||||
int index = ToSDL2Scancode(key);
|
||||
int index = ToSDL3Scancode(key);
|
||||
if (index == -1)
|
||||
{
|
||||
SDL_Keymod modifierMask = GetKeyboardModifierMask(key);
|
||||
|
||||
if (modifierMask == SDL_Keymod.KMOD_NONE)
|
||||
if (modifierMask == SDL_Keymod.SDL_KMOD_NONE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -250,7 +249,7 @@ namespace Ryujinx.Input.SDL2
|
||||
}
|
||||
else
|
||||
{
|
||||
keysState[(int)key] = rawKeyboardState[index] == 1;
|
||||
keysState[(int)key] = rawKeyboardState[index];
|
||||
}
|
||||
}
|
||||
|
||||
@ -386,11 +385,6 @@ namespace Ryujinx.Input.SDL2
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLed(uint packedRgb)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.UI, "SetLed called on an SDL2Keyboard");
|
||||
}
|
||||
|
||||
public void SetTriggerThreshold(float triggerThreshold)
|
||||
{
|
||||
// No operations
|
@ -1,19 +1,18 @@
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Input.SDL2
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
public class SDL2KeyboardDriver : IGamepadDriver
|
||||
public class SDL3KeyboardDriver : IGamepadDriver
|
||||
{
|
||||
public SDL2KeyboardDriver()
|
||||
public SDL3KeyboardDriver()
|
||||
{
|
||||
SDL2Driver.Instance.Initialize();
|
||||
SDL3Driver.Instance.Initialize();
|
||||
}
|
||||
|
||||
public string DriverName => "SDL2";
|
||||
public string DriverName => "SDL3";
|
||||
|
||||
private static readonly string[] _keyboardIdentifers = ["0"];
|
||||
private static readonly string[] _keyboardIdentifers = new string[1] { "0" };
|
||||
|
||||
public ReadOnlySpan<string> GamepadsIds => _keyboardIdentifers;
|
||||
|
||||
@ -33,7 +32,7 @@ namespace Ryujinx.Input.SDL2
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
SDL2Driver.Instance.Dispose();
|
||||
SDL3Driver.Instance.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,15 +49,7 @@ namespace Ryujinx.Input.SDL2
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SDL2Keyboard(this, _keyboardIdentifers[0], "All keyboards");
|
||||
}
|
||||
|
||||
public IEnumerable<IGamepad> GetGamepads()
|
||||
{
|
||||
foreach (string keyboardId in _keyboardIdentifers)
|
||||
{
|
||||
yield return GetGamepad(keyboardId);
|
||||
}
|
||||
return new SDL3Keyboard(this, _keyboardIdentifers[0], "All keyboards");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +1,19 @@
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Input.SDL2
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
public class SDL2Mouse : IMouse
|
||||
public class SDL3Mouse : IMouse
|
||||
{
|
||||
private SDL2MouseDriver _driver;
|
||||
private SDL3MouseDriver _driver;
|
||||
|
||||
public GamepadFeaturesFlag Features => throw new NotImplementedException();
|
||||
|
||||
public string Id => "0";
|
||||
|
||||
public string Name => "SDL2Mouse";
|
||||
public string Name => "SDL3Mouse";
|
||||
|
||||
public bool IsConnected => true;
|
||||
|
||||
@ -22,7 +21,7 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
Size IMouse.ClientSize => _driver.GetClientSize();
|
||||
|
||||
public SDL2Mouse(SDL2MouseDriver driver)
|
||||
public SDL3Mouse(SDL3MouseDriver driver)
|
||||
{
|
||||
_driver = driver;
|
||||
}
|
||||
@ -77,11 +76,6 @@ namespace Ryujinx.Input.SDL2
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetLed(uint packedRgb)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.UI, "SetLed called on an SDL2Mouse");
|
||||
}
|
||||
|
||||
public void SetTriggerThreshold(float triggerThreshold)
|
||||
{
|
||||
throw new NotImplementedException();
|
@ -1,16 +1,15 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using static SDL2.SDL;
|
||||
using static SDL3.SDL;
|
||||
|
||||
namespace Ryujinx.Input.SDL2
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
public class SDL2MouseDriver : IGamepadDriver
|
||||
public class SDL3MouseDriver : IGamepadDriver
|
||||
{
|
||||
private const int CursorHideIdleTime = 5; // seconds
|
||||
|
||||
@ -25,14 +24,14 @@ namespace Ryujinx.Input.SDL2
|
||||
public Vector2 Scroll { get; private set; }
|
||||
public Size ClientSize;
|
||||
|
||||
public SDL2MouseDriver(HideCursorMode hideCursorMode)
|
||||
public SDL3MouseDriver(HideCursorMode hideCursorMode)
|
||||
{
|
||||
PressedButtons = new bool[(int)MouseButton.Count];
|
||||
_hideCursorMode = hideCursorMode;
|
||||
|
||||
if (_hideCursorMode == HideCursorMode.Always)
|
||||
{
|
||||
if (SDL_ShowCursor(SDL_DISABLE) != SDL_DISABLE)
|
||||
if (!SDL_HideCursor())
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Failed to disable the cursor.");
|
||||
}
|
||||
@ -51,7 +50,7 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
public void UpdatePosition()
|
||||
{
|
||||
_ = SDL_GetMouseState(out int posX, out int posY);
|
||||
_ = SDL_GetMouseState(out float posX, out float posY);
|
||||
Vector2 position = new(posX, posY);
|
||||
|
||||
if (CurrentPosition != position)
|
||||
@ -76,7 +75,7 @@ namespace Ryujinx.Input.SDL2
|
||||
{
|
||||
if (!_isHidden)
|
||||
{
|
||||
if (SDL_ShowCursor(SDL_DISABLE) != SDL_DISABLE)
|
||||
if (!SDL_HideCursor())
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Failed to disable the cursor.");
|
||||
}
|
||||
@ -88,7 +87,7 @@ namespace Ryujinx.Input.SDL2
|
||||
{
|
||||
if (_isHidden)
|
||||
{
|
||||
if (SDL_ShowCursor(SDL_ENABLE) != SDL_ENABLE)
|
||||
if (!SDL_HideCursor())
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Failed to enable the cursor.");
|
||||
}
|
||||
@ -100,15 +99,16 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
public void Update(SDL_Event evnt)
|
||||
{
|
||||
switch (evnt.type)
|
||||
var type = (SDL_EventType)evnt.type;
|
||||
switch (type)
|
||||
{
|
||||
case SDL_EventType.SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_EventType.SDL_MOUSEBUTTONUP:
|
||||
case SDL_EventType.SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EventType.SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
uint rawButton = evnt.button.button;
|
||||
|
||||
if (rawButton > 0 && rawButton <= (int)MouseButton.Count)
|
||||
{
|
||||
PressedButtons[(int)DriverButtonToMouseButton(rawButton)] = evnt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN;
|
||||
PressedButtons[(int)DriverButtonToMouseButton(rawButton)] = type == SDL_EventType.SDL_EVENT_MOUSE_BUTTON_DOWN;
|
||||
|
||||
CurrentPosition = new Vector2(evnt.button.x, evnt.button.y);
|
||||
}
|
||||
@ -116,13 +116,13 @@ namespace Ryujinx.Input.SDL2
|
||||
break;
|
||||
|
||||
// NOTE: On Linux using Wayland mouse motion events won't be received at all.
|
||||
case SDL_EventType.SDL_MOUSEMOTION:
|
||||
case SDL_EventType.SDL_EVENT_MOUSE_MOTION:
|
||||
CurrentPosition = new Vector2(evnt.motion.x, evnt.motion.y);
|
||||
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
||||
|
||||
break;
|
||||
|
||||
case SDL_EventType.SDL_MOUSEWHEEL:
|
||||
case SDL_EventType.SDL_EVENT_MOUSE_WHEEL:
|
||||
Scroll = new Vector2(evnt.wheel.x, evnt.wheel.y);
|
||||
|
||||
break;
|
||||
@ -144,7 +144,7 @@ namespace Ryujinx.Input.SDL2
|
||||
return ClientSize;
|
||||
}
|
||||
|
||||
public string DriverName => "SDL2";
|
||||
public string DriverName => "SDL3";
|
||||
|
||||
public event Action<string> OnGamepadConnected
|
||||
{
|
||||
@ -162,11 +162,9 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
public IGamepad GetGamepad(string id)
|
||||
{
|
||||
return new SDL2Mouse(this);
|
||||
return new SDL3Mouse(this);
|
||||
}
|
||||
|
||||
public IEnumerable<IGamepad> GetGamepads() => [GetGamepad("0")];
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
@ -24,10 +24,5 @@ namespace Ryujinx.Input
|
||||
/// <remarks>Also named sixaxis</remarks>
|
||||
/// </summary>
|
||||
Motion,
|
||||
|
||||
/// <summary>
|
||||
/// The LED on the back of modern PlayStation controllers (DualSense & DualShock 4).
|
||||
/// </summary>
|
||||
Led,
|
||||
}
|
||||
}
|
||||
|
@ -269,6 +269,7 @@ namespace Ryujinx.Input.HLE
|
||||
if (motionConfig.MotionBackend != MotionInputBackendType.CemuHook)
|
||||
{
|
||||
_leftMotionInput = new MotionInput();
|
||||
_rightMotionInput = new MotionInput();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -301,7 +302,20 @@ namespace Ryujinx.Input.HLE
|
||||
|
||||
if (controllerConfig.ControllerType == ConfigControllerType.JoyconPair)
|
||||
{
|
||||
_rightMotionInput = _leftMotionInput;
|
||||
if (gamepad.Id== "JoyConPair")
|
||||
{
|
||||
Vector3 rightAccelerometer = gamepad.GetMotionData(MotionInputId.RightAccelerometer);
|
||||
Vector3 rightGyroscope = gamepad.GetMotionData(MotionInputId.RightGyroscope);
|
||||
|
||||
rightAccelerometer = new Vector3(rightAccelerometer.X, -rightAccelerometer.Z, rightAccelerometer.Y);
|
||||
rightGyroscope = new Vector3(rightGyroscope.X, -rightGyroscope.Z, rightGyroscope.Y);
|
||||
|
||||
_rightMotionInput.Update(rightAccelerometer, rightGyroscope, (ulong)PerformanceCounter.ElapsedNanoseconds / 1000, controllerConfig.Motion.Sensitivity, (float)controllerConfig.Motion.GyroDeadzone);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rightMotionInput = _leftMotionInput;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -336,6 +350,7 @@ namespace Ryujinx.Input.HLE
|
||||
// Reset states
|
||||
State = default;
|
||||
_leftMotionInput = null;
|
||||
_rightMotionInput = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -380,6 +395,11 @@ namespace Ryujinx.Input.HLE
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public static JoystickPosition GetJoystickPosition(float x, float y, float deadzone, float range)
|
||||
{
|
||||
return ClampToCircle(ApplyDeadzone(x, y,deadzone), range);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static JoystickPosition ApplyDeadzone(float x, float y, float deadzone)
|
||||
|
@ -65,15 +65,6 @@ namespace Ryujinx.Input
|
||||
/// <param name="configuration">The configuration of the gamepad</param>
|
||||
void SetConfiguration(InputConfig configuration);
|
||||
|
||||
/// <summary>
|
||||
/// Set the LED on the gamepad to a given color.
|
||||
/// </summary>
|
||||
/// <remarks>Does nothing on a controller without LED functionality.</remarks>
|
||||
/// <param name="packedRgb">The packed RGB integer.</param>
|
||||
void SetLed(uint packedRgb);
|
||||
|
||||
public void ClearLed() => SetLed(0);
|
||||
|
||||
/// <summary>
|
||||
/// Starts a rumble effect on the gamepad.
|
||||
/// </summary>
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Input
|
||||
{
|
||||
@ -34,11 +33,6 @@ namespace Ryujinx.Input
|
||||
/// <param name="id">The unique id of the gamepad</param>
|
||||
/// <returns>An instance of <see cref="IGamepad"/> associated to the gamepad id given or null if not found</returns>
|
||||
IGamepad GetGamepad(string id);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an <see cref="IEnumerable{T}"/> of the connected gamepads.
|
||||
/// </summary>
|
||||
IEnumerable<IGamepad> GetGamepads();
|
||||
|
||||
/// <summary>
|
||||
/// Clear the internal state of the driver.
|
||||
|
@ -21,5 +21,17 @@ namespace Ryujinx.Input
|
||||
/// </summary>
|
||||
/// <remarks>Values are in degrees</remarks>
|
||||
Gyroscope,
|
||||
|
||||
/// <summary>
|
||||
/// Right accelerometer.
|
||||
/// </summary>
|
||||
/// <remarks>Values are in m/s^2</remarks>
|
||||
RightAccelerometer,
|
||||
|
||||
/// <summary>
|
||||
/// Right gyroscope.
|
||||
/// </summary>
|
||||
/// <remarks>Values are in degrees</remarks>
|
||||
RightGyroscope
|
||||
}
|
||||
}
|
||||
|
37
src/Ryujinx.SDL3-CS/Ryujinx.SDL3-CS.csproj
Normal file
37
src/Ryujinx.SDL3-CS/Ryujinx.SDL3-CS.csproj
Normal file
@ -0,0 +1,37 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<RootNamespace>Ryujinx.SDL3_CS</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
|
||||
<UseCurrentRuntimeIdentifier>true</UseCurrentRuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="runtimes/win-x64/native/libSDL3.dll" Condition="'$(RuntimeIdentifier)' == 'win-x64'">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>libSDL3.dll</Link>
|
||||
</None>
|
||||
<None Include="runtimes/linux-x64/native/libSDL3.so" Condition="'$(RuntimeIdentifier)' == 'linux-x64'">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>libSDL3.so</Link>
|
||||
</None>
|
||||
<None Include="runtimes/linux-arm64/native/libSDL3.so" Condition="'$(RuntimeIdentifier)' == 'linux-arm64'">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>libSDL3.so</Link>
|
||||
</None>
|
||||
<None Include="runtimes/osx-arm64/native/libSDL3.dylib" Condition="'$(RuntimeIdentifier)' == 'osx-arm64'">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>libSDL3.dylib</Link>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="runtimes\win-x64\native\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
1
src/Ryujinx.SDL3-CS/external/SDL
vendored
Submodule
1
src/Ryujinx.SDL3-CS/external/SDL
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit fa8c0f0552cc232ab454b4b5ce787e63617ef7ab
|
118
src/Ryujinx.SDL3-CS/external/build.sh
vendored
Executable file
118
src/Ryujinx.SDL3-CS/external/build.sh
vendored
Executable file
@ -0,0 +1,118 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
pushd "$(dirname "$0")"
|
||||
|
||||
# Check if environment variables are defined
|
||||
if [[ -z $NAME || -z $RUNNER_OS || -z $FLAGS ]]; then
|
||||
echo "One or more required environment variables are not defined."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SUDO=$(which sudo || exit 0)
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
if [[ $RUNNER_OS == 'Linux' ]]; then
|
||||
# Setup Linux dependencies
|
||||
if [[ $TARGET_APT_ARCH == :i386 ]]; then
|
||||
$SUDO dpkg --add-architecture i386
|
||||
fi
|
||||
|
||||
$SUDO apt-get update -y -qq
|
||||
|
||||
if [[ $TARGET_APT_ARCH == :i386 ]]; then
|
||||
# Workaround GitHub's ubuntu-20.04 image issue <https://github.com/actions/virtual-environments/issues/4589>
|
||||
$SUDO apt-get install -y --allow-downgrades libpcre2-8-0=10.34-7
|
||||
fi
|
||||
|
||||
if [[ $NAME != 'linux-x86' && $NAME != 'linux-x64' ]]; then
|
||||
GCC="gcc"
|
||||
GPP="g++"
|
||||
else
|
||||
GCC="gcc-multilib"
|
||||
GPP="g++-multilib"
|
||||
fi
|
||||
|
||||
$SUDO apt-get install -y \
|
||||
$GCC \
|
||||
$GPP \
|
||||
git \
|
||||
cmake \
|
||||
ninja-build \
|
||||
wayland-scanner++ \
|
||||
wayland-protocols \
|
||||
meson \
|
||||
pkg-config$TARGET_APT_ARCH \
|
||||
libasound2-dev$TARGET_APT_ARCH \
|
||||
libdbus-1-dev$TARGET_APT_ARCH \
|
||||
libegl1-mesa-dev$TARGET_APT_ARCH \
|
||||
libgl1-mesa-dev$TARGET_APT_ARCH \
|
||||
libgles2-mesa-dev$TARGET_APT_ARCH \
|
||||
libglu1-mesa-dev$TARGET_APT_ARCH \
|
||||
libgtk-3-dev$TARGET_APT_ARCH \
|
||||
libibus-1.0-dev$TARGET_APT_ARCH \
|
||||
libpango1.0-dev$TARGET_APT_ARCH \
|
||||
libpulse-dev$TARGET_APT_ARCH \
|
||||
libsndio-dev$TARGET_APT_ARCH \
|
||||
libudev-dev$TARGET_APT_ARCH \
|
||||
libwayland-dev$TARGET_APT_ARCH \
|
||||
libx11-dev$TARGET_APT_ARCH \
|
||||
libxcursor-dev$TARGET_APT_ARCH \
|
||||
libxext-dev$TARGET_APT_ARCH \
|
||||
libxi-dev$TARGET_APT_ARCH \
|
||||
libxinerama-dev$TARGET_APT_ARCH \
|
||||
libxkbcommon-dev$TARGET_APT_ARCH \
|
||||
libxrandr-dev$TARGET_APT_ARCH \
|
||||
libxss-dev$TARGET_APT_ARCH \
|
||||
libxt-dev$TARGET_APT_ARCH \
|
||||
libxv-dev$TARGET_APT_ARCH \
|
||||
libxxf86vm-dev$TARGET_APT_ARCH \
|
||||
libdrm-dev$TARGET_APT_ARCH \
|
||||
libgbm-dev$TARGET_APT_ARCH \
|
||||
libpulse-dev$TARGET_APT_ARCH
|
||||
|
||||
if [[ $TARGET_APT_ARCH != :i386 ]]; then
|
||||
# Build libdecor.
|
||||
# This is required so that window decorations can work on wayland.
|
||||
# The support will only be enabled in SDL, but we're not shipping the libdecor binaries
|
||||
# because making them work from a c# app as everything else does (via runtimes) is too difficult.
|
||||
# Also skip i386 because attempting to support this for i386 is a pain.
|
||||
# Special shoutouts to gnome for refusing to support server-side decorations.
|
||||
git clone https://gitlab.freedesktop.org/libdecor/libdecor.git
|
||||
cd libdecor
|
||||
git checkout 0.2.2
|
||||
meson build --buildtype release
|
||||
$SUDO meson install -C build
|
||||
cd ..
|
||||
fi
|
||||
fi
|
||||
|
||||
# Build SDL
|
||||
pushd SDL
|
||||
git reset --hard HEAD || echo "Failed to clean up the repository"
|
||||
|
||||
if [[ $RUNNER_OS == 'Windows' ]]; then
|
||||
echo "Patching SDL to not include gameinput.h"
|
||||
sed -i 's/#include <gameinput.h>/#_include <gameinput.h>/g' CMakeLists.txt
|
||||
fi
|
||||
|
||||
cmake -B build $FLAGS -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSDL_SHARED_ENABLED_BY_DEFAULT=ON -DSDL_STATIC_ENABLED_BY_DEFAULT=ON
|
||||
cmake --build build/ --config Release
|
||||
$SUDO cmake --install build/ --prefix install_output --config Release
|
||||
popd
|
||||
|
||||
# Ensure the directory exists
|
||||
mkdir -p ../runtimes/$NAME/native
|
||||
|
||||
# Move build lib into correct folders
|
||||
if [[ $RUNNER_OS == 'Windows' ]]; then
|
||||
cp SDL/install_output/bin/SDL3.dll ../runtimes/$NAME/native/libSDL3.dll
|
||||
elif [[ $RUNNER_OS == 'Linux' ]]; then
|
||||
cp SDL/install_output/lib/libSDL3.so ../runtimes/$NAME/native/libSDL3.so
|
||||
elif [[ $RUNNER_OS == 'macOS' ]]; then
|
||||
cp SDL/install_output/lib/libSDL3.dylib ../runtimes/$NAME/native/libSDL3.dylib
|
||||
fi
|
||||
|
||||
popd
|
8078
src/Ryujinx.SDL3-CS/libSDL3.cs
Normal file
8078
src/Ryujinx.SDL3-CS/libSDL3.cs
Normal file
File diff suppressed because it is too large
Load Diff
BIN
src/Ryujinx.SDL3-CS/runtimes/android/Jars/SDL3AndroidBridge.jar
Normal file
BIN
src/Ryujinx.SDL3-CS/runtimes/android/Jars/SDL3AndroidBridge.jar
Normal file
Binary file not shown.
BIN
src/Ryujinx.SDL3-CS/runtimes/android/arm64-v8a/libSDL3.so
Executable file
BIN
src/Ryujinx.SDL3-CS/runtimes/android/arm64-v8a/libSDL3.so
Executable file
Binary file not shown.
BIN
src/Ryujinx.SDL3-CS/runtimes/android/armeabi-v7a/libSDL3.so
Executable file
BIN
src/Ryujinx.SDL3-CS/runtimes/android/armeabi-v7a/libSDL3.so
Executable file
Binary file not shown.
BIN
src/Ryujinx.SDL3-CS/runtimes/android/x86/libSDL3.so
Executable file
BIN
src/Ryujinx.SDL3-CS/runtimes/android/x86/libSDL3.so
Executable file
Binary file not shown.
BIN
src/Ryujinx.SDL3-CS/runtimes/android/x86_64/libSDL3.so
Executable file
BIN
src/Ryujinx.SDL3-CS/runtimes/android/x86_64/libSDL3.so
Executable file
Binary file not shown.
@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>AvailableLibraries</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>SDL3.framework/SDL3</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>tvos-arm64_x86_64-simulator</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>SDL3.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
<string>x86_64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>tvos</string>
|
||||
<key>SupportedPlatformVariant</key>
|
||||
<string>simulator</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>SDL3.framework/Versions/A/SDL3</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>macos-arm64_x86_64</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>SDL3.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
<string>x86_64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>macos</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>SDL3.framework/SDL3</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>tvos-arm64</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>SDL3.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>tvos</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>SDL3.framework/SDL3</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64_x86_64-simulator</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>SDL3.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
<string>x86_64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
<key>SupportedPlatformVariant</key>
|
||||
<string>simulator</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>SDL3.framework/SDL3</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>SDL3.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XFWK</string>
|
||||
<key>XCFrameworkFormatVersion</key>
|
||||
<string>1.0</string>
|
||||
</dict>
|
||||
</plist>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
src/Ryujinx.SDL3-CS/runtimes/linux-arm64/native/libSDL3.so
Normal file
BIN
src/Ryujinx.SDL3-CS/runtimes/linux-arm64/native/libSDL3.so
Normal file
Binary file not shown.
BIN
src/Ryujinx.SDL3-CS/runtimes/linux-x64/native/libSDL3.so
Normal file
BIN
src/Ryujinx.SDL3-CS/runtimes/linux-x64/native/libSDL3.so
Normal file
Binary file not shown.
BIN
src/Ryujinx.SDL3-CS/runtimes/osx-arm64/native/libSDL3.dylib
Executable file
BIN
src/Ryujinx.SDL3-CS/runtimes/osx-arm64/native/libSDL3.dylib
Executable file
Binary file not shown.
BIN
src/Ryujinx.SDL3-CS/runtimes/win-x64/native/libSDL3.dll
Normal file
BIN
src/Ryujinx.SDL3-CS/runtimes/win-x64/native/libSDL3.dll
Normal file
Binary file not shown.
@ -1,15 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Ryujinx.SDL2-CS" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.SDL3-CS\Ryujinx.SDL3-CS.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,24 +1,24 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using static SDL2.SDL;
|
||||
using static SDL3.SDL;
|
||||
|
||||
namespace Ryujinx.SDL2.Common
|
||||
|
||||
namespace Ryujinx.SDL3.Common
|
||||
{
|
||||
public class SDL2Driver : IDisposable
|
||||
public class SDL3Driver : IDisposable
|
||||
{
|
||||
private static SDL2Driver _instance;
|
||||
private static SDL3Driver _instance;
|
||||
|
||||
public static SDL2Driver Instance
|
||||
public static SDL3Driver Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
_instance ??= new SDL2Driver();
|
||||
_instance ??= new SDL3Driver();
|
||||
|
||||
return _instance;
|
||||
}
|
||||
@ -26,22 +26,22 @@ namespace Ryujinx.SDL2.Common
|
||||
|
||||
public static Action<Action> MainThreadDispatcher { get; set; }
|
||||
|
||||
private const uint SdlInitFlags = SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_VIDEO;
|
||||
private const SDL_InitFlags SdlInitFlags = SDL_InitFlags.SDL_INIT_GAMEPAD | SDL_InitFlags.SDL_INIT_AUDIO |
|
||||
SDL_InitFlags.SDL_INIT_VIDEO;
|
||||
|
||||
private bool _isRunning;
|
||||
private uint _refereceCount;
|
||||
private Thread _worker;
|
||||
|
||||
public event Action<int, int> OnJoyStickConnected;
|
||||
public event Action<int> OnJoystickDisconnected;
|
||||
public event Action<uint> OnJoyStickConnected;
|
||||
public event Action<uint> OnJoystickDisconnected;
|
||||
public event Action<uint, SDL_JoyBatteryEvent> OnJoyBatteryUpdated;
|
||||
|
||||
private ConcurrentDictionary<uint, Action<SDL_Event>> _registeredWindowHandlers;
|
||||
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
private SDL2Driver() { }
|
||||
|
||||
private const string SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS = "SDL_JOYSTICK_HIDAPI_COMBINE_JOY_CONS";
|
||||
private SDL3Driver() { }
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
@ -55,21 +55,20 @@ namespace Ryujinx.SDL2.Common
|
||||
}
|
||||
|
||||
SDL_SetHint(SDL_HINT_APP_NAME, "Ryujinx");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
|
||||
// SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
||||
// SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
|
||||
SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1");
|
||||
|
||||
|
||||
// NOTE: As of SDL2 2.24.0, joycons are combined by default but the motion source only come from one of them.
|
||||
// We disable this behavior for now.
|
||||
//
|
||||
//
|
||||
// // NOTE: As of SDL2 2.24.0, joycons are combined by default but the motion source only come from one of them.
|
||||
// // We disable this behavior for now.
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "0");
|
||||
|
||||
if (SDL_Init(SdlInitFlags) != 0)
|
||||
if (!SDL_Init(SdlInitFlags))
|
||||
{
|
||||
string errorMessage = $"SDL2 initialization failed with error \"{SDL_GetError()}\"";
|
||||
string errorMessage = $"SDL3 initialization failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
Logger.Error?.Print(LogClass.Application, errorMessage);
|
||||
|
||||
@ -77,30 +76,32 @@ namespace Ryujinx.SDL2.Common
|
||||
}
|
||||
|
||||
// First ensure that we only enable joystick events (for connected/disconnected).
|
||||
if (SDL_GameControllerEventState(SDL_IGNORE) != SDL_IGNORE)
|
||||
if (!SDL_GamepadEventsEnabled())
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Couldn't change the state of game controller events.");
|
||||
Logger.Error?.PrintMsg(LogClass.Application,
|
||||
"Couldn't change the state of game controller events.");
|
||||
}
|
||||
|
||||
if (SDL_JoystickEventState(SDL_ENABLE) < 0)
|
||||
if (!SDL_JoystickEventsEnabled())
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, $"Failed to enable joystick event polling: {SDL_GetError()}");
|
||||
Logger.Error?.PrintMsg(LogClass.Application,
|
||||
$"Failed to enable joystick event polling: {SDL_GetError()}");
|
||||
}
|
||||
|
||||
// Disable all joysticks information, we don't need them no need to flood the event queue for that.
|
||||
SDL_EventState(SDL_EventType.SDL_JOYAXISMOTION, SDL_DISABLE);
|
||||
SDL_EventState(SDL_EventType.SDL_JOYBALLMOTION, SDL_DISABLE);
|
||||
SDL_EventState(SDL_EventType.SDL_JOYHATMOTION, SDL_DISABLE);
|
||||
SDL_EventState(SDL_EventType.SDL_JOYBUTTONDOWN, SDL_DISABLE);
|
||||
SDL_EventState(SDL_EventType.SDL_JOYBUTTONUP, SDL_DISABLE);
|
||||
SDL_SetEventEnabled((uint)SDL_EventType.SDL_EVENT_JOYSTICK_AXIS_MOTION, false);
|
||||
SDL_SetEventEnabled((uint)SDL_EventType.SDL_EVENT_JOYSTICK_BALL_MOTION, false);
|
||||
SDL_SetEventEnabled((uint)SDL_EventType.SDL_EVENT_JOYSTICK_HAT_MOTION, false);
|
||||
SDL_SetEventEnabled((uint)SDL_EventType.SDL_EVENT_JOYSTICK_BUTTON_DOWN, false);
|
||||
SDL_SetEventEnabled((uint)SDL_EventType.SDL_EVENT_JOYSTICK_BUTTON_UP, false);
|
||||
|
||||
SDL_EventState(SDL_EventType.SDL_CONTROLLERSENSORUPDATE, SDL_DISABLE);
|
||||
SDL_SetEventEnabled((uint)SDL_EventType.SDL_EVENT_GAMEPAD_SENSOR_UPDATE, false);
|
||||
|
||||
string gamepadDbPath = Path.Combine(AppDataManager.BaseDirPath, "SDL_GameControllerDB.txt");
|
||||
|
||||
if (File.Exists(gamepadDbPath))
|
||||
{
|
||||
SDL_GameControllerAddMappingsFromFile(gamepadDbPath);
|
||||
SDL_AddGamepadMappingsFromFile(gamepadDbPath);
|
||||
}
|
||||
|
||||
_registeredWindowHandlers = new ConcurrentDictionary<uint, Action<SDL_Event>>();
|
||||
@ -122,29 +123,29 @@ namespace Ryujinx.SDL2.Common
|
||||
|
||||
private void HandleSDLEvent(ref SDL_Event evnt)
|
||||
{
|
||||
if (evnt.type == SDL_EventType.SDL_JOYDEVICEADDED)
|
||||
if (evnt.type == (uint)SDL_EventType.SDL_EVENT_GAMEPAD_ADDED)
|
||||
{
|
||||
int deviceId = evnt.cbutton.which;
|
||||
|
||||
// SDL2 loves to be inconsistent here by providing the device id instead of the instance id (like on removed event), as such we just grab it and send it inside our system.
|
||||
int instanceId = SDL_JoystickGetDeviceInstanceID(deviceId);
|
||||
|
||||
if (instanceId == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var instanceId = evnt.jdevice.which;
|
||||
|
||||
Logger.Debug?.Print(LogClass.Application, $"Added joystick instance id {instanceId}");
|
||||
|
||||
OnJoyStickConnected?.Invoke(deviceId, instanceId);
|
||||
OnJoyStickConnected?.Invoke(instanceId);
|
||||
}
|
||||
else if (evnt.type == SDL_EventType.SDL_JOYDEVICEREMOVED)
|
||||
else if (evnt.type == (uint)SDL_EventType.SDL_EVENT_GAMEPAD_REMOVED)
|
||||
{
|
||||
Logger.Debug?.Print(LogClass.Application, $"Removed joystick instance id {evnt.cbutton.which}");
|
||||
var instanceId = evnt.jdevice.which;
|
||||
|
||||
OnJoystickDisconnected?.Invoke(evnt.cbutton.which);
|
||||
Logger.Debug?.Print(LogClass.Application, $"Removed joystick instance id {instanceId}");
|
||||
|
||||
OnJoystickDisconnected?.Invoke(instanceId);
|
||||
}
|
||||
else if (evnt.type is SDL_EventType.SDL_WINDOWEVENT or SDL_EventType.SDL_MOUSEBUTTONDOWN or SDL_EventType.SDL_MOUSEBUTTONUP)
|
||||
else if (evnt.type == (uint)SDL_EventType.SDL_EVENT_JOYSTICK_BATTERY_UPDATED)
|
||||
{
|
||||
OnJoyBatteryUpdated?.Invoke(evnt.jbattery.which, evnt.jbattery);
|
||||
}
|
||||
else if (evnt.type is >= (uint)SDL_EventType.SDL_EVENT_WINDOW_FIRST and <= (uint)SDL_EventType.SDL_EVENT_WINDOW_LAST
|
||||
or (uint)SDL_EventType.SDL_EVENT_MOUSE_BUTTON_DOWN
|
||||
or (uint)SDL_EventType.SDL_EVENT_MOUSE_BUTTON_UP)
|
||||
{
|
||||
if (_registeredWindowHandlers.TryGetValue(evnt.window.windowID, out Action<SDL_Event> handler))
|
||||
{
|
||||
@ -158,12 +159,11 @@ namespace Ryujinx.SDL2.Common
|
||||
const int WaitTimeMs = 10;
|
||||
|
||||
using ManualResetEventSlim waitHandle = new(false);
|
||||
|
||||
while (_isRunning)
|
||||
{
|
||||
MainThreadDispatcher?.Invoke(() =>
|
||||
{
|
||||
while (SDL_PollEvent(out SDL_Event evnt) != 0)
|
||||
while (SDL_PollEvent(out SDL_Event evnt))
|
||||
{
|
||||
HandleSDLEvent(ref evnt);
|
||||
}
|
@ -9,7 +9,7 @@ using LibHac.Ns;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.Dummy;
|
||||
using Ryujinx.Audio.Backends.OpenAL;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
using Ryujinx.Audio.Backends.SDL3;
|
||||
using Ryujinx.Audio.Backends.SoundIo;
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Ava.Common;
|
||||
@ -502,8 +502,6 @@ namespace Ryujinx.Ava
|
||||
_renderingThread.Start();
|
||||
|
||||
_viewModel.Volume = ConfigurationState.Instance.System.AudioVolume.Value;
|
||||
|
||||
Rainbow.Enable();
|
||||
|
||||
MainLoop();
|
||||
|
||||
@ -590,17 +588,6 @@ namespace Ryujinx.Ava
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (IGamepad gamepad in RyujinxApp.MainWindow.InputManager.GamepadDriver.GetGamepads())
|
||||
{
|
||||
gamepad?.ClearLed();
|
||||
gamepad?.Dispose();
|
||||
}
|
||||
|
||||
DiscordIntegrationModule.GuestAppStartedAt = null;
|
||||
|
||||
Rainbow.Disable();
|
||||
Rainbow.Reset();
|
||||
|
||||
_isStopped = true;
|
||||
Stop();
|
||||
}
|
||||
@ -977,7 +964,7 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
List<AudioBackend> availableBackends =
|
||||
[
|
||||
AudioBackend.SDL2,
|
||||
AudioBackend.SDL3,
|
||||
AudioBackend.SoundIo,
|
||||
AudioBackend.OpenAl,
|
||||
AudioBackend.Dummy
|
||||
@ -1016,7 +1003,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
deviceDriver = currentBackend switch
|
||||
{
|
||||
AudioBackend.SDL2 => InitializeAudioBackend<SDL2HardwareDeviceDriver>(AudioBackend.SDL2, nextBackend),
|
||||
AudioBackend.SDL3 => InitializeAudioBackend<SDL3HardwareDeviceDriver>(AudioBackend.SDL3, nextBackend),
|
||||
AudioBackend.SoundIo => InitializeAudioBackend<SoundIoHardwareDeviceDriver>(AudioBackend.SoundIo, nextBackend),
|
||||
AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend),
|
||||
_ => new DummyHardwareDeviceDriver(),
|
||||
|
346
src/Ryujinx/Assets/Icons/Controller_JoyConLeft_Settings.svg
Normal file
346
src/Ryujinx/Assets/Icons/Controller_JoyConLeft_Settings.svg
Normal file
@ -0,0 +1,346 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="Layer_1"
|
||||
width="1000.8"
|
||||
height="1300"
|
||||
x="0"
|
||||
y="0"
|
||||
version="1.1"
|
||||
viewBox="0 0 1000.8 1300"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1"><filter
|
||||
id="filter3994"
|
||||
color-interpolation-filters="sRGB"
|
||||
y="-0.059999999"
|
||||
height="1.12"
|
||||
x="-inf"
|
||||
width="inf"><feGaussianBlur
|
||||
stdDeviation="0.95"
|
||||
id="feGaussianBlur3996" /></filter><filter
|
||||
id="filter3994-1"
|
||||
color-interpolation-filters="sRGB"
|
||||
y="-0.059999999"
|
||||
height="1.12"
|
||||
x="-inf"
|
||||
width="inf"><feGaussianBlur
|
||||
stdDeviation="0.95"
|
||||
id="feGaussianBlur3996-5" /></filter></defs><metadata
|
||||
id="metadata85" /><g
|
||||
id="left"
|
||||
transform="matrix(8.7009658,0,0,8.7009658,119.872,-3.6543895e-8)"><path
|
||||
id="path4182"
|
||||
d="m 33.122745,22.256318 v 1.601973 c 0,0.13798 -0.164307,0.292312 -0.292312,0.292312 h -5.133234 c -0.161131,0 -0.356659,-0.160814 -0.356659,-0.356632 V 22.24465 Z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path4186"
|
||||
d="m 23.902465,22.949553 c 0,0.04675 0,1.145911 0,1.145911 0,0.478393 0.291942,0.666512 0.666512,0.666512 2.017951,0.177932 4.469606,0.167507 6.536399,0 0.347319,0 0.687917,-0.3302 0.684054,-0.684054 v -1.397318 h -7.886965 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path4190"
|
||||
d="m 7.3451308,22.949553 c 0,0.04675 0,1.145911 0,1.145911 0,0.478393 0.291941,0.666512 0.666485,0.666512 2.0179782,0.177932 4.4696072,0.167507 6.5364262,0 0.347319,0 0.687917,-0.3302 0.684027,-0.684054 V 22.680604 H 7.3451308 Z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path3828"
|
||||
d="m 39.503431,63.613187 h -1.110837 v 9.132265 h 1.08745 c 0.379812,0 0.572961,-0.264917 0.572961,-0.572958 v -8.079893 c 0,-0.292343 -0.213908,-0.479414 -0.549574,-0.479414 z"
|
||||
style="fill:#00bbdb;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
style="fill:#00bbdb;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 39.503431,117.52619 h -1.110837 v -9.13226 h 1.08745 c 0.379812,0 0.572961,0.26491 0.572961,0.57295 v 8.0799 c 0,0.29234 -0.213908,0.47941 -0.549574,0.47941 z"
|
||||
id="path3830" /><path
|
||||
id="path3826"
|
||||
d="m 39.643747,44.647073 h -3.706693 v 88.793887 h 1.734386 v 3.68763 c 0,0.41206 0.346123,1.10727 0.639284,1.10727 h 1.291346 c 0.23689,0 0.450287,-0.18294 0.450287,-0.45028 v -17.26235 c 0,-2.28046 -1.425903,-2.30845 -1.425903,-3.0285 v -9.15565 c 0,-0.64968 1.391472,-0.80953 1.391472,-2.66601 V 75.364695 c 0,-1.840243 -1.391472,-1.964013 -1.391472,-2.666016 l 0.03307,-9.003641 c 0,-0.620152 1.391473,-1.097836 1.391473,-2.747865 l -7.93e-4,-15.823609 c 0.0024,-0.259744 -0.125479,-0.476491 -0.406334,-0.476491 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path3832"
|
||||
d="m 30.154591,38.984701 v -1.107942 c 0,-0.332553 -0.339662,-0.7028 -0.7028,-0.7028 h -6.945312 c -10.713919,0 -20.3040802,8.641797 -21.9475795,14.775418 -0.07498,0.279813 -0.101862,0.841899 0.385869,0.841899 h 1.2336173 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path3925"
|
||||
d="m 36.251186,39.39185 v 109.19024 c 0,0.2708 -0.133853,0.47543 -0.475422,0.47543 H 22.641681 c -13.6735372,0 -22.5093895,-12.09084 -22.5093895,-22.50939 V 60.947123 c 0,-15.315427 15.0189105,-22.415219 22.4152215,-22.415219 h 12.909171 c 0.657781,0 0.794502,0.361942 0.794502,0.859946 z"
|
||||
style="fill:#00bbdb;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><circle
|
||||
id="path3871"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="19.236444"
|
||||
cy="90.101624"
|
||||
r="4.0085444" /><circle
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3873"
|
||||
cx="19.236444"
|
||||
cy="106.67232"
|
||||
r="4.0085444" /><circle
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3875"
|
||||
transform="rotate(90)"
|
||||
cx="98.386986"
|
||||
cy="-10.951083"
|
||||
r="4.0085444" /><circle
|
||||
transform="rotate(90)"
|
||||
id="path3877"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="98.386986"
|
||||
cy="-27.521791"
|
||||
r="4.0085444" /><rect
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="Back"
|
||||
width="5.8704429"
|
||||
height="1.6867187"
|
||||
x="27.454165"
|
||||
y="48.675072"
|
||||
class="button" /><circle
|
||||
id="path3907-1"
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="19.268621"
|
||||
cy="67.195892"
|
||||
r="8.4832029" /><g
|
||||
id="LeftStick"
|
||||
class="button"
|
||||
style="display:inline"><circle
|
||||
id="path3907"
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="19.268621"
|
||||
cy="67.195892"
|
||||
r="8.4832029" /><circle
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3909"
|
||||
cx="19.268621"
|
||||
cy="67.254738"
|
||||
r="5.9489326" /><path
|
||||
id="path3911"
|
||||
d="m 18.961234,60.286636 c 5.3e-5,-0.567529 0.0016,-0.902846 0.0042,-0.920872 0.0079,-0.0547 0.03481,-0.106976 0.07652,-0.148682 0.12573,-0.12573 0.339551,-0.08842 0.417084,0.07277 0.02906,0.06043 0.02704,-0.01283 0.02711,0.983676 l 6.1e-5,0.900091 -0.185587,-2.17e-4 c -0.102071,-1.19e-4 -0.220205,0.0011 -0.262517,0.0028 l -0.07693,0.003 7.9e-5,-0.892521 z"
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
|
||||
id="path3913"
|
||||
d="m 19.194179,75.214639 c -0.103042,-0.01154 -0.191744,-0.08817 -0.224182,-0.193662 -0.0065,-0.02098 -0.0071,-0.08735 -0.0082,-0.857049 l -0.0013,-0.83431 0.06106,0.0034 c 0.03359,0.0019 0.151787,0.0034 0.262666,0.0034 h 0.201596 l -0.0011,0.835091 -0.0011,0.835091 -0.0092,0.02687 c -0.02764,0.08078 -0.09145,0.144222 -0.172333,0.171315 -0.02641,0.0088 -0.07656,0.01347 -0.107987,0.0099 v 0 z"
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
|
||||
id="path3915"
|
||||
d="m 25.349998,67.27271 c 0,-0.09385 -0.0016,-0.212042 -0.0034,-0.262662 l -0.0034,-0.09203 0.854977,0.0011 0.854977,0.0011 0.03032,0.01109 c 0.114072,0.04167 0.18497,0.146846 0.177263,0.262977 -0.0043,0.06436 -0.02954,0.121094 -0.07432,0.166859 -0.03514,0.03591 -0.06816,0.05496 -0.129127,0.07451 -0.01318,0.0042 -0.188249,0.0056 -0.861963,0.0065 l -0.845428,0.0013 v -0.170629 0 z"
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
|
||||
id="path3917"
|
||||
d="m 11.413899,67.438823 c -0.0569,-0.0075 -0.112456,-0.03622 -0.152974,-0.07906 -0.140957,-0.149016 -0.06985,-0.386953 0.130236,-0.435806 0.01865,-0.0046 0.182329,-0.0056 0.912762,-0.0057 l 0.890024,-1.49e-4 -0.0033,0.08785 c -0.0019,0.04832 -0.0033,0.166452 -0.0033,0.26252 v 0.174667 l -0.874363,-5.29e-4 c -0.480901,-2.65e-4 -0.885529,-0.0021 -0.899171,-0.0038 z"
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /></g><rect
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect3919"
|
||||
width="6.8130207"
|
||||
height="6.647656"
|
||||
x="21.930986"
|
||||
y="116.11076"
|
||||
ry="0.89296871" /><circle
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3921"
|
||||
cx="25.312696"
|
||||
cy="119.49245"
|
||||
r="2.3399088" /><g
|
||||
id="dpad"><path
|
||||
style="stroke:#000000;stroke-width:0.904694;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="DpadRight"
|
||||
d="m 134.97306,862.5577 0,-11.46794 9.93153,5.73397 z"
|
||||
transform="matrix(0.29245602,0,0,0.29245602,-13.019911,-152.18812)" /><path
|
||||
transform="matrix(-0.29245602,0,0,0.29245602,51.534858,-152.18812)"
|
||||
d="m 134.97306,862.5577 0,-11.46794 9.93153,5.73397 z"
|
||||
id="DpadLeft"
|
||||
style="stroke:#000000;stroke-width:0.904694;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
|
||||
transform="matrix(0,-0.29245602,0.29245602,0,-231.35192,130.71823)"
|
||||
d="m 134.97306,862.5577 0,-11.46794 9.93153,5.73397 z"
|
||||
id="DpadUp"
|
||||
style="stroke:#000000;stroke-width:0.904694;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
|
||||
style="stroke:#000000;stroke-width:0.904694;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="DpadDown"
|
||||
d="m 134.97306,862.5577 0,-11.46794 9.93153,5.73397 z"
|
||||
transform="matrix(0,0.29245602,0.29245602,0,-231.35192,66.16345)" /></g><path
|
||||
id="path4046"
|
||||
d="m 63.771731,94.018495 c -0.04677,0 -1.145916,0 -1.145916,0 -0.478388,0 -0.666504,-0.29194 -0.666504,-0.66651 -0.177953,-2.01795 -0.167505,-4.469602 0,-6.536416 0,-0.34731 0.330195,-0.687918 0.684043,-0.684039 h 1.397318 v 7.886965 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 63.771731,102.29717 c -0.04677,0 -1.145916,0 -1.145916,0 -0.478388,0 -0.666504,-0.29194 -0.666504,-0.6665 -0.177953,-2.017963 -0.167505,-4.469615 0,-6.536415 0,-0.34732 0.330195,-0.68793 0.684043,-0.68405 h 1.397318 v 7.886965 z"
|
||||
id="path4048" /><path
|
||||
id="path4000"
|
||||
d="m 75.761211,40.180332 c 2.66361,0 6.929524,2.444663 10.289879,2.759557 0.71167,0 1.19269,0.71711 1.19269,1.262848 v 2.38538 c 0,0.791848 -1.14592,1.389081 -1.14592,2.081363 h -8.956871 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path4002"
|
||||
d="m 78.916187,62.68351 c 0,-5.087943 6.223703,-8.710414 8.301303,-13.411065 0.12093,-0.451335 -0.23601,-0.975651 -0.678,-0.975651 h -2.629293 c -4.062013,0 -3.225233,-4.668785 -5.936588,-6.746875 v 21.139517 z"
|
||||
style="fill:#00bbdb;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path4004"
|
||||
d="m 80.619442,54.481429 h -1.852083 v -4.960938 h 1.885156 c 0.243705,0 0.353693,0.213125 0.405143,0.405144 0.371227,1.385437 0.352412,2.876767 0,4.191992 -0.05556,0.207356 -0.194738,0.363802 -0.438216,0.363802 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path4006"
|
||||
d="m 87.20095,46.246273 c -4.955503,0 -5.858031,-1.554427 -7.17682,-1.554427"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path4008"
|
||||
d="m 72.381923,39.022719 c 0,0 -5.575442,-0.03565 -5.565888,0 0.0096,0.03565 0,-1.110837 0,-1.110837 0,-0.29836 0.221101,-0.491109 0.491106,-0.491109 h 4.548595 c 0.321982,0 0.526187,0.287943 0.526187,0.526187 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path4036"
|
||||
d="m 63.776512,62.176195 c -0.740701,0.206184 -2.124943,1.629344 -2.447311,3.050632 -0.421434,0 -1.005602,-0.247509 -1.005602,-0.760047 v -4.887693 c 0,-1.029377 -3.348204,-0.770353 -3.348204,0.897149 v 1.687013 c -1.440344,3.504932 -1.469731,6.635117 0,10.056016 v 1.777344 c 0,1.5485 3.317637,1.867318 3.317637,0.888957 v -4.794435 c 0,-0.695261 0.486277,-1.020768 0.979958,-0.979958 0.516763,1.247886 1.193901,2.362125 2.453283,2.991162 h 0.467722 V 62.17202 Z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><rect
|
||||
ry="0.26458332"
|
||||
y="116.11076"
|
||||
x="62.155056"
|
||||
height="6.647656"
|
||||
width="6.8130207"
|
||||
id="rect4044"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
rx="0.26458332" /><path
|
||||
id="path4050"
|
||||
d="m 63.771731,110.57584 c -0.04677,0 -1.145916,0 -1.145916,0 -0.478388,0 -0.666504,-0.29194 -0.666504,-0.6665 -0.177953,-2.01796 -0.167505,-4.46961 0,-6.53641 0,-0.34731 0.330195,-0.68792 0.684043,-0.68404 h 1.397318 v 7.88695 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><rect
|
||||
y="48.675072"
|
||||
x="62.34621"
|
||||
height="1.6867187"
|
||||
width="5.8704429"
|
||||
id="rect4052"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
rx="0.25724691"
|
||||
ry="0.25724691" /><path
|
||||
id="path4010"
|
||||
d="m 73.258901,38.730394 h -5.713257 c -2.190945,0 -3.68795,2.151269 -3.68795,4.115951 V 145.23063 c 0,2.30569 1.843881,4.04578 4.045794,4.04578 h 5.168323 c 3.238691,0 6.06869,-3.32049 6.06869,-6.06868 V 44.623686 c 0,-2.970212 -3.405764,-5.893292 -5.8816,-5.893292 z"
|
||||
style="fill:#00bbdb;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><rect
|
||||
ry="1.1287988"
|
||||
rx="1.1287988"
|
||||
y="44.664803"
|
||||
x="-76.537956"
|
||||
height="93.491112"
|
||||
width="10.22559"
|
||||
id="rect4012"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="scale(-1,1)" /><rect
|
||||
ry="1.1287988"
|
||||
rx="1.1287988"
|
||||
y="120.92493"
|
||||
x="-75.426994"
|
||||
height="16.172655"
|
||||
width="7.9375"
|
||||
id="rect4014"
|
||||
style="fill:#2e2f31;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="scale(-1,1)" /><path
|
||||
transform="matrix(-0.29141287,0,0,0.29141287,93.622165,249.09704)"
|
||||
d="m 76,-387.61176 -8.184578,-14.17611 16.369155,0 z"
|
||||
id="path4016"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4018"
|
||||
d="m 76,-387.61176 -8.184578,-14.17611 16.369155,0 z"
|
||||
transform="matrix(-0.29141287,0,0,0.29141287,93.622165,244.15264)" /><path
|
||||
transform="matrix(-0.29141287,0,0,0.29141287,93.622165,239.20824)"
|
||||
d="m 76,-387.61176 -8.184578,-14.17611 16.369155,0 z"
|
||||
id="path4020"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><rect
|
||||
ry="0.53348637"
|
||||
rx="0.53348637"
|
||||
y="79.881424"
|
||||
x="-72.450432"
|
||||
height="1.9513021"
|
||||
width="1.9513021"
|
||||
id="rect4022"
|
||||
style="fill:#aaeeff;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="scale(-1,1)" /><rect
|
||||
style="fill:#aaeeff;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect4024"
|
||||
width="1.9513021"
|
||||
height="1.9513021"
|
||||
x="-72.450432"
|
||||
y="84.213974"
|
||||
rx="0.53348637"
|
||||
ry="0.53348637"
|
||||
transform="scale(-1,1)" /><rect
|
||||
style="fill:#aaeeff;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect4026"
|
||||
width="1.9513021"
|
||||
height="1.9513021"
|
||||
x="-72.450432"
|
||||
y="88.645744"
|
||||
rx="0.53348637"
|
||||
ry="0.53348637"
|
||||
transform="scale(-1,1)" /><rect
|
||||
ry="0.53348637"
|
||||
rx="0.53348637"
|
||||
y="92.978294"
|
||||
x="-72.450432"
|
||||
height="1.9513021"
|
||||
width="1.9513021"
|
||||
id="rect4028"
|
||||
style="fill:#aaeeff;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="scale(-1,1)" /><circle
|
||||
transform="scale(-1,1)"
|
||||
id="path4030"
|
||||
style="fill:#2e2f31;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="-71.498466"
|
||||
cy="100.48306"
|
||||
r="1.5743958" /><rect
|
||||
ry="1.3933822"
|
||||
rx="1.4264551"
|
||||
y="108.258"
|
||||
x="-73.442619"
|
||||
height="9.2934895"
|
||||
width="4.0018229"
|
||||
id="rect4032"
|
||||
style="fill:#999595;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="scale(-1,1)" /><rect
|
||||
style="fill:#999595;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect4034"
|
||||
width="4.0018229"
|
||||
height="9.2934895"
|
||||
x="-73.442619"
|
||||
y="63.58707"
|
||||
rx="1.4264551"
|
||||
ry="1.3933822"
|
||||
transform="scale(-1,1)" /><path
|
||||
id="path4038"
|
||||
d="m 60.336929,64.524372 v 5.126302"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path4040"
|
||||
d="m 61.350497,65.186661 v 4.070318"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path4042"
|
||||
d="m 381.46875,790.93248 v 38"
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3994-1)"
|
||||
transform="matrix(-0.26458333,0,0,0.26458333,157.90199,-147.07481)" /><g
|
||||
id="SingleRightTrigger0"><path
|
||||
id="path4064"
|
||||
d="m 71.505099,112.83374 h 1.144559 l -7.9e-5,0.49975 c -4e-5,0.27487 -0.0019,0.54181 -0.0035,0.59321 -0.01228,0.33592 -0.05805,0.48248 -0.190294,0.60941 -0.147082,0.14117 -0.373115,0.19975 -0.597612,0.15487 -0.125074,-0.025 -0.235143,-0.0877 -0.319156,-0.18196 -0.07718,-0.0865 -0.137531,-0.22569 -0.161803,-0.37297 -0.0054,-0.0327 -0.0066,-0.0361 -0.01082,-0.0301 -0.0026,0.004 -0.0205,0.0307 -0.03981,0.0598 -0.081,0.12257 -0.150067,0.19243 -0.287899,0.2912 -0.07999,0.0573 -0.119782,0.083 -0.421375,0.27174 l -0.255003,0.1596 -7.94e-4,-0.27267 c -5.29e-4,-0.21526 2.7e-5,-0.27353 0.0035,-0.27675 0.0024,-0.002 0.137388,-0.0932 0.299871,-0.20215 0.469347,-0.31467 0.522494,-0.35391 0.578141,-0.42691 0.0552,-0.0724 0.06888,-0.13057 0.07187,-0.30563 l 0.0019,-0.11056 H 70.838662 70.36057 v -0.22998 -0.22997 h 1.144559 z m 0.178769,1.03666 c 0.0042,0.0751 0.01164,0.1415 0.01913,0.17021 0.02265,0.0867 0.09008,0.15452 0.179001,0.18013 0.03318,0.01 0.117309,0.0135 0.154126,0.007 0.08929,-0.0151 0.15145,-0.06 0.189063,-0.13655 0.03607,-0.0734 0.0386,-0.10715 0.03873,-0.51921 l 7.7e-5,-0.27862 H 71.97211 71.680214 l 8e-6,0.25563 c 0,0.14059 0.0019,0.28507 0.0036,0.32108 z"
|
||||
style="stroke:#000000;stroke-width:0.00353806;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
|
||||
id="path4066"
|
||||
d="m 70.31892,111.61238 c 0.02758,-0.34989 0.150275,-0.57472 0.391093,-0.71667 0.09661,-0.0569 0.252651,-0.10728 0.374687,-0.12089 l 0.02191,-0.002 0.0021,0.012 c 0.0038,0.0221 0.04287,0.43626 0.04133,0.4378 -7.93e-4,8e-4 -0.01757,0.005 -0.03718,0.009 -0.01963,0.004 -0.055,0.0141 -0.07862,0.0219 -0.182986,0.0608 -0.284086,0.17537 -0.317138,0.35939 -0.01011,0.0563 -0.01098,0.1795 -0.0016,0.23891 0.01243,0.0793 0.0329,0.13956 0.06354,0.18693 0.04204,0.065 0.100952,0.11379 0.164135,0.13598 0.03574,0.0125 0.107465,0.0162 0.143017,0.007 0.06979,-0.0176 0.11543,-0.0597 0.157126,-0.14499 0.02925,-0.0598 0.04967,-0.12802 0.114951,-0.38399 0.05945,-0.23312 0.09148,-0.32753 0.15009,-0.4425 0.05596,-0.10976 0.132363,-0.19601 0.23001,-0.25965 0.103841,-0.0677 0.22174,-0.0992 0.351624,-0.0939 0.111916,0.005 0.215693,0.0387 0.310094,0.10225 0.06944,0.0467 0.118321,0.0959 0.165862,0.16675 0.09689,0.14446 0.144976,0.33554 0.144928,0.57598 -5.6e-5,0.26803 -0.05399,0.47124 -0.164872,0.62123 -0.02867,0.0388 -0.08962,0.0997 -0.128405,0.12841 -0.104757,0.0774 -0.22251,0.12035 -0.363342,0.13239 -0.02162,0.002 -0.03976,0.003 -0.04031,0.002 -0.0016,-0.002 -0.02178,-0.46169 -0.02048,-0.46312 7.94e-4,-7.9e-4 0.01529,-0.004 0.0327,-0.008 0.09819,-0.0214 0.177176,-0.0644 0.221345,-0.12058 0.03419,-0.0434 0.05729,-0.0998 0.07029,-0.1715 0.01037,-0.0573 0.01138,-0.18658 0.0019,-0.24714 -0.01484,-0.0949 -0.04761,-0.1723 -0.09421,-0.22263 -0.04939,-0.0534 -0.134353,-0.0688 -0.202705,-0.0368 -0.03108,0.0145 -0.06774,0.0497 -0.08965,0.0861 -0.04056,0.0673 -0.07932,0.18338 -0.131625,0.39436 -0.06314,0.25467 -0.111067,0.39943 -0.168209,0.50814 -0.09247,0.17592 -0.245912,0.29388 -0.430609,0.33102 -0.06693,0.0135 -0.116637,0.0173 -0.193069,0.0151 -0.0537,-0.002 -0.0776,-0.004 -0.111324,-0.0111 -0.186269,-0.0393 -0.3468,-0.14926 -0.442304,-0.30291 -0.06723,-0.10815 -0.110238,-0.246 -0.130572,-0.41855 -0.0054,-0.0461 -0.0099,-0.26111 -0.0064,-0.30598 v 0 z"
|
||||
style="stroke:#000000;stroke-width:0.00357862;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /></g><g
|
||||
id="SingleLeftTrigger0"><path
|
||||
id="path4062"
|
||||
d="m 70.291107,67.031148 c 0.01625,-0.267039 0.09544,-0.468148 0.240077,-0.60956 0.107686,-0.105286 0.255148,-0.17938 0.427839,-0.214958 0.03948,-0.0081 0.111088,-0.01982 0.112144,-0.01826 0,5.3e-4 0.0095,0.09546 0.02061,0.211246 0.01106,0.115779 0.02098,0.216483 0.02204,0.223785 0.0019,0.01167 0.0011,0.01326 -0.0053,0.01326 -0.01241,0 -0.06767,0.01376 -0.108361,0.02695 -0.193627,0.06286 -0.297113,0.190103 -0.320421,0.393944 -0.0054,0.04684 -0.0024,0.167018 0.0052,0.21064 0.02,0.115199 0.06517,0.200318 0.136514,0.257399 0.05573,0.04458 0.101222,0.06073 0.171641,0.06091 0.05019,0 0.07817,-0.0063 0.111447,-0.02553 0.02289,-0.01326 0.05459,-0.04465 0.07293,-0.07224 0.04377,-0.06585 0.06522,-0.131302 0.142975,-0.43634 0.04856,-0.190569 0.07764,-0.281419 0.12169,-0.380288 0.05968,-0.133898 0.138597,-0.231024 0.246674,-0.303562 0.05956,-0.03997 0.136289,-0.07169 0.209214,-0.08647 0.03466,-0.0071 0.05009,-0.008 0.121102,-0.0079 0.07039,8.2e-5 0.08604,0.0011 0.115898,0.0079 0.174774,0.03905 0.32139,0.148201 0.407297,0.303199 0.03861,0.06965 0.07079,0.159824 0.08892,0.249145 0.03979,0.196067 0.0318,0.45579 -0.01966,0.639056 -0.02373,0.08456 -0.06444,0.172673 -0.108688,0.235291 -0.03041,0.04306 -0.106032,0.118632 -0.149458,0.149384 -0.06951,0.04921 -0.160809,0.0887 -0.24679,0.106752 -0.04347,0.0091 -0.099,0.01627 -0.126307,0.01627 h -0.01606 l -2.1e-5,-0.02035 c -1.6e-5,-0.01117 -0.004,-0.108704 -0.0088,-0.216704 -0.0049,-0.108 -0.0088,-0.20197 -0.0088,-0.208833 l -5e-6,-0.01246 0.03095,-0.0063 c 0.0694,-0.01413 0.138766,-0.04496 0.180888,-0.08032 0.08203,-0.06887 0.117224,-0.170627 0.117173,-0.338828 -2.3e-5,-0.110773 -0.01492,-0.18678 -0.05113,-0.260747 -0.04201,-0.08582 -0.08739,-0.120843 -0.161595,-0.124727 -0.05522,-0.0029 -0.09344,0.01029 -0.131992,0.04553 -0.06168,0.05637 -0.102473,0.159848 -0.17213,0.436541 -0.05422,0.21541 -0.07865,0.297317 -0.120269,0.403336 -0.07394,0.188336 -0.176062,0.308941 -0.323448,0.381971 -0.08816,0.04368 -0.16936,0.06288 -0.286578,0.06778 -0.139166,0.0058 -0.266994,-0.02745 -0.389152,-0.10123 -0.136559,-0.08248 -0.234262,-0.210222 -0.290007,-0.37915 -0.0486,-0.147275 -0.06991,-0.343268 -0.05823,-0.535456 v 0 z"
|
||||
style="stroke:#000000;stroke-width:0.00353806;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
|
||||
id="path4068"
|
||||
d="m 71.473953,68.654557 h 1.148736 v 0.232609 0.232608 h -0.955495 -0.955493 v 0.581531 0.581522 h -0.193246 -0.193246 v -0.814131 -0.814139 z"
|
||||
style="stroke:#000000;stroke-width:0.00357862;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /></g><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 35.904558,11.76891 h 1.501193 l 0.0042,-0.958829 h 1.605518 c 0.420423,0 0.553932,0.382418 0.553932,0.959443 v 8.792554 c 0,0.41027 -0.387932,0.732996 -0.733002,0.732996 h -1.083786 c -0.295275,0 -0.312341,-0.319455 -0.342768,-0.593693 V 20.117742 H 35.92972 Z"
|
||||
id="path4172" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 37.406703,20.100131 V 11.831902"
|
||||
id="path4174" /><path
|
||||
id="path4176"
|
||||
d="m 29.267115,6.5371897 v 1.852083 h 4.960937 v -1.885156 c 0,-0.2437079 -0.213122,-0.3536949 -0.40513,-0.4051559 -1.385438,-0.371211 -2.876788,-0.352399 -4.192005,0 -0.207354,0.05556 -0.363802,0.1947589 -0.363802,0.4382289 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path4180"
|
||||
d="m 24.419382,23.304492 c -0.20619,0.740701 -1.629331,2.124948 -3.05062,2.447316 0,0.421429 0.247492,1.005602 0.760043,1.005602 h 4.8877 c 1.029362,0 0.770335,3.348196 -0.897149,3.348196 h -1.68701 c -3.504935,1.440339 -6.635141,1.469734 -10.056019,0 h -1.777365 c -1.5485,0 -1.867296,-3.317637 -0.888947,-3.317637 h 4.794436 c 0.695272,0 1.020762,-0.486277 0.979963,-0.979963 -1.247881,-0.516758 -2.36212,-1.193906 -2.991167,-2.45327 v -0.46773 h 9.930315 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
style="fill:#00bbdb;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="m 3.1349058,23.187797 c -0.970783,0 -2.8442713,-1.929288 -2.8442713,-2.968281 v -6.81302 c 0.520621,-1.975089 1.5200053,-4.1207013 4.7625003,-4.7625003 0.566261,0 0.859896,-0.533533 0.859896,-0.859896 3.06951,-4.0709057 6.4024682,-6.1659029 9.8226572,-7.6067708 h 7.242969 c 3.115389,1.3968147 4.852326,3.4828691 6.217708,5.7546869 v 1.2567709 c 0,0.518822 0.42971,1.058334 0.992187,1.058334 h 6.019271 V 23.196079 Z"
|
||||
id="path4178" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 1.9949328,14.65448 H 30.215662 v 3.251237 c 0,1.098243 -1.401771,2.731037 -2.73104,2.731037 H 3.8156258 c -1.444993,0 -2.5142903,-1.396424 -2.5142903,-2.514288 0,-1.34669 -0.1161,-3.467986 0.6935973,-3.467986 z"
|
||||
id="path4164" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 4.1190738,11.099795 H 29.998912 C 29.985313,5.7111738 27.335498,0.1322915 22.239297,0.1322915 l -7.239421,0.086701 C 7.9123758,1.5120668 6.4557758,6.6026927 4.1190738,11.099795 Z"
|
||||
id="path4162" /><path
|
||||
id="path4184"
|
||||
d="M 22.071205,26.744075 H 16.944902"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path4188"
|
||||
d="M 21.408926,25.730509 H 17.338602"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path4192"
|
||||
d="m 1036.7923,1045.6825 v 38"
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3994-1)"
|
||||
transform="matrix(0,0.26458333,-0.26458333,0,301.07298,-244.20874)" /><g
|
||||
id="LeftTrigger"><path
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.00334412;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 17.550756,7.0784747 v -0.211688 l 0.613645,-0.7579259 c 0.337506,-0.416859 0.613646,-0.7587585 0.613646,-0.7597745 0,-0.00106 -0.245293,-0.00185 -0.545092,-0.00185 H 17.687866 V 5.1516039 4.9559737 h 0.854421 0.854419 v 0.1817132 0.1817105 l -0.64054,0.7889164 -0.640541,0.7889159 0.665621,7.93e-4 0.665623,7.94e-4 v 0.195623 0.195622 h -0.948055 -0.948058 v -0.211688 z"
|
||||
id="path4216" /><path
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.00334412;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 19.761215,6.1330998 V 4.976037 h 0.234087 0.234088 v 0.9614328 0.9614319 h 0.586891 0.586893 v 0.19563 0.19563 h -0.820981 -0.820978 z"
|
||||
id="path4218" /></g><path
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.00334412;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 18.76467,17.759127 v -1.157063 h 0.234087 0.234088 v 0.961433 0.961429 h 0.586893 0.586891 v 0.195633 0.19563 H 19.585648 18.76467 Z"
|
||||
id="LeftShoulder" /></g><style
|
||||
id="style1">.button{fill:#44484c;}</style></svg>
|
After Width: | Height: | Size: 32 KiB |
442
src/Ryujinx/Assets/Icons/Controller_JoyConPair_Settings.svg
Normal file
442
src/Ryujinx/Assets/Icons/Controller_JoyConPair_Settings.svg
Normal file
@ -0,0 +1,442 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="1000"
|
||||
height="1300"
|
||||
viewBox="0 0 264.58333 343.95833"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1">
|
||||
<filter
|
||||
id="filter3994"
|
||||
color-interpolation-filters="sRGB"
|
||||
y="-0.059999999"
|
||||
height="1.12"
|
||||
x="-inf"
|
||||
width="inf">
|
||||
<feGaussianBlur
|
||||
stdDeviation="0.95"
|
||||
id="feGaussianBlur3996" />
|
||||
</filter>
|
||||
</defs>
|
||||
<style
|
||||
id="style2">#buttons{fill:#ffffff;}
|
||||
.button{fill:#44484c;}
|
||||
</style>
|
||||
<g
|
||||
id="joycon"
|
||||
transform="scale(2.3029882)">
|
||||
<g
|
||||
id="right"
|
||||
transform="translate(27.045207)">
|
||||
<path
|
||||
style="fill:#ff5f53;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 48.375673,63.613147 h 1.11085 v 9.13226 h -1.08746 c -0.37981,0 -0.57295,-0.264917 -0.57295,-0.572958 v -8.079888 c 0,-0.292343 0.21391,-0.479414 0.54956,-0.479414 z"
|
||||
id="path3822" />
|
||||
<path
|
||||
id="path3824"
|
||||
d="m 48.375673,117.52611 h 1.11085 v -9.13226 h -1.08746 c -0.37981,0 -0.57295,0.26491 -0.57295,0.57295 v 8.0799 c 0,0.29234 0.21391,0.47941 0.54956,0.47941 z"
|
||||
style="fill:#ff5f53;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 48.235363,44.647045 h 3.70671 v 88.793825 h -1.7344 v 3.68762 c 0,0.41206 -0.34613,1.10727 -0.63928,1.10727 h -1.29136 c -0.23688,0 -0.45026,-0.18294 -0.45026,-0.45028 v -17.26234 c 0,-2.28045 1.42589,-2.30844 1.42589,-3.02849 V 108.339 c 0,-0.64968 -1.39147,-0.80953 -1.39147,-2.66601 V 75.364648 c 0,-1.840242 1.39147,-1.964011 1.39147,-2.666014 l -0.0331,-9.003636 c 0,-0.620151 -1.39147,-1.097835 -1.39147,-2.747863 V 45.123536 c -0.003,-0.259744 0.12546,-0.476491 0.40632,-0.476491 z"
|
||||
id="path3820" />
|
||||
<path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 57.624263,38.984676 v -1.107941 c 0,-0.332553 0.33967,-0.7028 0.70281,-0.7028 h 6.94532 c 10.7139,0 20.30407,8.641792 21.94758,14.775409 0.075,0.279813 0.10186,0.841899 -0.38587,0.841899 h -1.23362 z"
|
||||
id="path3818" />
|
||||
<path
|
||||
style="fill:#ff5f53;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 51.580203,39.555184 V 148.74535 c 0,0.2708 0.13385,0.47542 0.47543,0.47542 h 13.13407 c 13.67354,0 22.5094,-12.09083 22.5094,-22.50937 V 61.110444 c 0,-15.315418 -15.01891,-22.415206 -22.41523,-22.415206 h -12.90918 c -0.65778,0 -0.79449,0.361942 -0.79449,0.859946 z"
|
||||
id="path3923" />
|
||||
<path
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="m 54.564653,48.674461 h 2.01287 v -1.987816 h 1.67042 v 1.996166 h 1.97948 v 1.670435 h -1.97112 v 2.062984 h -1.69548 v -2.062984 h -1.98784 z"
|
||||
id="Start"
|
||||
class="button" />
|
||||
<ellipse
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3095"
|
||||
cx="68.447418"
|
||||
cy="58.209846"
|
||||
rx="4.0085444"
|
||||
ry="4.0085421" />
|
||||
<ellipse
|
||||
id="path3865"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="68.447418"
|
||||
cy="74.78054"
|
||||
rx="4.0085444"
|
||||
ry="4.0085421" />
|
||||
<ellipse
|
||||
transform="rotate(90)"
|
||||
id="path3867"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="66.495186"
|
||||
cy="-60.162022"
|
||||
rx="4.0085421"
|
||||
ry="4.0085444" />
|
||||
<ellipse
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3869"
|
||||
transform="rotate(90)"
|
||||
cx="66.495186"
|
||||
cy="-76.732758"
|
||||
rx="4.0085421"
|
||||
ry="4.0085444" />
|
||||
<ellipse
|
||||
id="path3879"
|
||||
style="fill:#999595;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="62.425785"
|
||||
cy="117.94157"
|
||||
rx="4.4528017"
|
||||
ry="4.4527988" />
|
||||
<ellipse
|
||||
style="fill:#3a3d40;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3881"
|
||||
cx="62.425755"
|
||||
cy="117.98209"
|
||||
rx="3.3256173"
|
||||
ry="3.3256154" />
|
||||
<path
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 60.086503,117.80491 h 0.6572 v 2.06837 c 1.10172,0 2.33288,0 3.3343,0 v -2.06837 h 0.65718 c -0.72404,-0.67331 -1.69847,-1.59279 -2.32434,-2.16524 -0.75748,0.69632 -1.55583,1.44766 -2.32434,2.16524 z m 1.54956,0.0346 c 0.51263,0 1.08492,0 1.54956,0 v 1.32128 c -0.5121,0 -1.08427,0 -1.54956,0 z"
|
||||
id="path3883" />
|
||||
<circle
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rightStickBackground"
|
||||
cx="68.49704"
|
||||
cy="97.259186"
|
||||
r="8.4832029" />
|
||||
<g
|
||||
id="RightStick"
|
||||
class="button"
|
||||
style="display:inline">
|
||||
<circle
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3888"
|
||||
cx="68.49704"
|
||||
cy="97.259186"
|
||||
r="8.4832029" />
|
||||
<circle
|
||||
id="path3892"
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="68.49704"
|
||||
cy="97.318031"
|
||||
r="5.9489326" />
|
||||
<path
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 68.1896,90.349919 c 6e-5,-0.56753 0.003,-0.90285 0.004,-0.92087 0.008,-0.0547 0.0348,-0.10698 0.0765,-0.14869 0.12573,-0.12573 0.33957,-0.0884 0.41709,0.0728 0.029,0.0604 0.027,-0.0128 0.0271,0.98367 l 6e-5,0.900086 -0.18558,-2.1e-4 c -0.10208,-1.2e-4 -0.22022,0.001 -0.26252,0.003 l -0.0769,0.003 8e-5,-0.892526 z"
|
||||
id="path3899" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 68.42254,105.27792 c -0.10303,-0.0115 -0.19174,-0.0882 -0.22418,-0.19366 -0.006,-0.021 -0.007,-0.0874 -0.008,-0.85705 l -0.003,-0.83431 0.0611,0.003 c 0.0336,0.002 0.15179,0.003 0.26268,0.003 h 0.20158 v 0.83509 0.83509 l -0.009,0.0269 c -0.0276,0.0808 -0.0914,0.14422 -0.17232,0.17131 -0.0265,0.009 -0.0766,0.0135 -0.10798,0.01 v 0 z"
|
||||
id="path3901" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 74.57836,97.335985 c 0,-0.0938 -0.003,-0.21204 -0.003,-0.26266 l -0.003,-0.092 0.85498,0.001 0.855,10e-4 0.0303,0.0111 c 0.11406,0.0417 0.18497,0.14685 0.17725,0.26298 -0.004,0.0644 -0.0295,0.121092 -0.0743,0.166862 -0.0351,0.0359 -0.0682,0.055 -0.12911,0.0745 -0.0132,0.004 -0.18825,0.006 -0.86196,0.007 l -0.84543,10e-4 v -0.170632 0 z"
|
||||
id="path3903" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 60.64228,97.502107 c -0.0569,-0.008 -0.11247,-0.0362 -0.15298,-0.0791 -0.14097,-0.149022 -0.0698,-0.386962 0.13023,-0.435812 0.0188,-0.005 0.18232,-0.006 0.91276,-0.006 l 0.89003,-1.4e-4 -0.003,0.0878 c -0.003,0.0483 -0.003,0.16646 -0.003,0.26252 v 0.174672 l -0.87437,-5.3e-4 c -0.4809,-2.6e-4 -0.88553,-0.002 -0.89916,-0.004 z"
|
||||
id="path3905" />
|
||||
</g>
|
||||
<g
|
||||
id="buttons">
|
||||
<path
|
||||
style="stroke:none;stroke-width:0.264583"
|
||||
d="m 66.51122,60.355075 c 0,-0.0029 0.32536,-0.501412 0.72302,-1.108578 l 0.72303,-1.104053 -0.64079,-0.989807 c -0.35243,-0.54438 -0.64887,-1.001289 -0.65874,-1.015312 l -0.018,-0.0254 h 0.49586 0.49585 l 0.42516,0.681726 c 0.23384,0.374967 0.42924,0.680376 0.43421,0.678709 0.005,-0.0029 0.19529,-0.307393 0.42291,-0.679397 l 0.41381,-0.676407 0.4885,-0.0029 c 0.26866,-0.0029 0.48847,0 0.48847,0.0029 0,0.0029 -0.2945,0.464634 -0.6545,1.026662 -0.37467,0.58502 -0.65283,1.028224 -0.65061,1.036744 0.003,0.0082 0.32589,0.499559 0.71943,1.091988 0.39354,0.592402 0.71554,1.0795 0.71554,1.082463 0,0.0029 -0.22849,0.005 -0.50779,0.005 H 69.41879 L 68.9468,59.621781 c -0.38118,-0.595762 -0.47418,-0.73533 -0.48339,-0.725408 -0.006,0.0066 -0.22109,0.338852 -0.47734,0.738029 l -0.46593,0.725752 h -0.50448 c -0.27747,0 -0.5045,-0.0029 -0.5045,-0.0045 z"
|
||||
id="X" />
|
||||
<path
|
||||
style="stroke:none;stroke-width:0.264583"
|
||||
d="m 74.70499,68.578086 c 0.0159,-0.04217 1.60489,-4.121546 1.62507,-4.172241 l 0.023,-0.05794 h 0.44749 0.44749 l 0.84585,2.111613 c 0.46522,1.161388 0.84796,2.116852 0.85055,2.123281 0.004,0.0093 -0.0887,0.01111 -0.45704,0.009 l -0.4618,-0.0026 -0.18592,-0.484399 -0.18593,-0.484399 -0.85288,0.0026 -0.85288,0.0026 -0.17585,0.484452 -0.17584,0.484426 h -0.44876 c -0.42357,0 -0.44844,-0.0026 -0.44265,-0.01614 z m 2.6729,-1.673542 c -0.003,-0.0064 -0.13496,-0.363246 -0.29456,-0.793062 -0.15957,-0.429816 -0.29292,-0.778193 -0.2963,-0.774197 -0.005,0.0053 -0.3456,0.938741 -0.57272,1.567259 -0.003,0.009 0.11697,0.01138 0.5819,0.01138 0.46506,0 0.5852,-0.0026 0.58168,-0.01138 z"
|
||||
id="A" />
|
||||
<path
|
||||
style="stroke:none;stroke-width:0.264583"
|
||||
d="m 66.87468,74.871836 v -2.119339 l 1.04767,0.0045 c 1.04455,0.0045 1.15731,0.0079 1.36678,0.03969 0.31785,0.04826 0.57875,0.210662 0.75094,0.467466 0.26178,0.390393 0.21953,0.899689 -0.103,1.241584 -0.0752,0.07972 -0.14721,0.13552 -0.24757,0.19177 l -0.0779,0.04366 0.0631,0.0209 c 0.15386,0.05075 0.31663,0.143987 0.42588,0.243999 0.12821,0.117316 0.23862,0.303504 0.28662,0.483314 0.022,0.08186 0.0243,0.108189 0.0251,0.26215 0,0.133508 -0.003,0.187933 -0.0161,0.247623 -0.0534,0.243126 -0.17126,0.469159 -0.32763,0.628544 -0.20013,0.203968 -0.44561,0.30742 -0.79624,0.335571 -0.17465,0.01402 -1.02536,0.02733 -1.76633,0.02762 h -0.63137 v -2.119339 z m 2.11238,1.397661 c 0.19571,-0.01614 0.30006,-0.05593 0.40346,-0.153458 0.10692,-0.100859 0.15121,-0.217382 0.15087,-0.396848 0,-0.276649 -0.15034,-0.470483 -0.41069,-0.531337 -0.15079,-0.03524 -0.23929,-0.03998 -0.82907,-0.04434 l -0.57587,-0.0042 v 0.56978 0.569807 h 0.57587 c 0.3167,-8e-5 0.62515,-0.0042 0.68543,-0.0093 v 0 z m -0.34084,-1.831075 c 0.30885,-0.0087 0.37068,-0.01773 0.48202,-0.07046 0.11647,-0.05524 0.19852,-0.147002 0.23728,-0.265377 0.0265,-0.08091 0.0265,-0.246115 -2.7e-4,-0.328348 -0.0499,-0.154278 -0.16449,-0.253629 -0.3406,-0.295354 -0.0549,-0.01296 -0.15099,-0.01561 -0.68143,-0.01905 l -0.61749,-0.0037 v 0.495591 0.495617 h 0.32145 c 0.17682,0 0.44635,-0.004 0.59899,-0.0082 v 0 z"
|
||||
id="B" />
|
||||
<path
|
||||
style="display:inline;stroke:none;stroke-width:0.264583"
|
||||
d="m 59.75794,67.784945 v -0.891461 l -0.77073,-1.218485 c -0.42392,-0.670137 -0.77076,-1.220179 -0.77076,-1.222269 0,-0.0026 0.22119,-0.0037 0.49152,-0.0037 l 0.49154,1.06e-4 0.49842,0.839153 c 0.37071,0.624072 0.50107,0.836374 0.50864,0.828331 0.006,-0.0058 0.2272,-0.38362 0.49241,-0.839232 l 0.48218,-0.828437 h 0.4848 0.48477 l -0.0132,0.0209 c -0.007,0.01138 -0.35669,0.563192 -0.77642,1.226185 L 60.598,66.901451 v 0.887572 0.887571 h -0.42 -0.41998 v -0.891487 z"
|
||||
id="Y" />
|
||||
</g>
|
||||
<path
|
||||
id="path4136"
|
||||
d="m 52.095503,11.768902 h -1.50119 l -0.004,-0.958829 h -1.60551 c -0.42043,0 -0.55393,0.382418 -0.55393,0.959443 v 8.792548 c 0,0.41027 0.38793,0.732996 0.733,0.732996 h 1.08378 c 0.29528,0 0.31234,-0.319455 0.34277,-0.593693 v -0.583638 h 1.48016 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
id="path4138"
|
||||
d="M 50.593363,20.100118 V 11.831894"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 58.732943,6.5371856 v 1.8520819 h -4.96093 V 6.5041126 c 0,-0.2437079 0.21312,-0.3536948 0.40513,-0.4051558 1.38543,-0.3712107 2.87678,-0.3523988 4.192,0 0.20736,0.05556 0.3638,0.1947589 0.3638,0.4382288 z"
|
||||
id="path4158" />
|
||||
<path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 63.516853,23.377163 c 0.20619,0.7407 1.62933,2.124946 3.05062,2.447315 0,0.421428 -0.24749,1.005601 -0.76004,1.005601 h -4.8877 c -1.02937,0 -0.77034,3.348195 0.89714,3.348195 h 1.68701 c 3.50494,1.440337 6.63515,1.469733 10.05602,0 h 1.77737 c 1.5485,0 1.86729,-3.317635 0.88895,-3.317635 h -4.79444 c -0.69527,0 -1.02076,-0.486278 -0.97996,-0.979964 1.24788,-0.516758 2.36212,-1.193905 2.99116,-2.453268 v -0.46773 h -9.93031 z"
|
||||
id="path4146" />
|
||||
<path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 64.033773,23.022225 c 0,0.04675 0,1.145909 0,1.145909 0,0.478393 -0.29195,0.666512 -0.66652,0.666512 -2.01795,0.177932 -4.4696,0.167508 -6.5364,0 -0.34731,0 -0.68791,-0.3302 -0.68405,-0.684054 v -1.397316 h 7.88697 z"
|
||||
id="path4154" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 65.865033,26.816744 h 5.1263"
|
||||
id="path4148" />
|
||||
<path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 54.813493,22.32899 v 1.601971 c 0,0.137981 0.1643,0.292312 0.29231,0.292312 h 5.13323 c 0.16113,0 0.35666,-0.160814 0.35666,-0.356632 v -1.549319 z"
|
||||
id="path4142" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 66.527303,25.803179 h 4.07033"
|
||||
id="path4150" />
|
||||
<path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 80.591103,23.022225 c 0,0.04675 0,1.145909 0,1.145909 0,0.478393 -0.29194,0.666512 -0.66649,0.666512 -2.01797,0.177932 -4.4696,0.167508 -6.53642,0 -0.34732,0 -0.68792,-0.3302 -0.68403,-0.684054 v -1.397316 h 7.88694 z"
|
||||
id="path4144" />
|
||||
<path
|
||||
transform="matrix(0,0.26458317,0.26458333,0,-213.13674,-244.1359)"
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3994)"
|
||||
d="m 1036.7923,1045.6825 v 38"
|
||||
id="path4152" />
|
||||
<path
|
||||
id="path4160"
|
||||
d="m 72.311283,23.022225 c 0,0.04675 0,1.145909 0,1.145909 0,0.478393 -0.29194,0.666512 -0.66648,0.666512 -2.01798,0.177932 -4.46961,0.167508 -6.53643,0 -0.34732,0 -0.68792,-0.3302 -0.68403,-0.684054 v -1.397316 h 7.88694 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
id="path4140"
|
||||
d="m 84.865153,23.187782 c 0.97078,0 2.84427,-1.929287 2.84427,-2.968279 v -6.813016 c -0.52062,-1.975088 -1.52,-4.1206981 -4.7625,-4.7624967 -0.56626,0 -0.85989,-0.5335327 -0.85989,-0.8598955 -3.06951,-4.070903 -6.40247,-6.165899 -9.82266,-7.60676597 h -7.24297 c -3.11539,1.39681387 -4.85232,3.48286697 -6.21771,5.75468307 v 1.2567703 c 0,0.5188217 -0.42971,1.0583333 -0.99218,1.0583333 h -6.01927 V 23.196064 Z"
|
||||
style="fill:#ff5f53;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
|
||||
<path
|
||||
id="path4166"
|
||||
d="m 83.838993,11.099787 h -25.87985 c 0.0135,-5.3886169 2.66343,-10.96749554 7.75962,-10.96749554 l 7.23943,0.0867009 c 7.0875,1.29307354 8.54411,6.38369614 10.8808,10.88079464 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
id="path4168"
|
||||
d="m 85.963123,14.65447 h -28.22072 v 3.251235 c 0,1.098242 1.40179,2.731035 2.73105,2.731035 h 23.66899 c 1.445,0 2.51429,-1.396423 2.51429,-2.514286 0,-1.346689 0.11609,-3.467984 -0.69361,-3.467984 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.00490037;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 67.399983,17.660894 v -1.210391 l 0.55007,6.6e-5 c 0.30252,3.7e-5 0.59417,0.0024 0.64807,0.0049 0.23492,0.01161 0.38338,0.0444 0.48519,0.107175 0.15637,0.09641 0.26062,0.266599 0.28702,0.468541 0.008,0.06163 0.003,0.197848 -0.009,0.254818 -0.0416,0.191362 -0.15044,0.334386 -0.32104,0.421952 -0.0654,0.03355 -0.16529,0.06695 -0.24334,0.08133 -0.0324,0.006 -0.0588,0.01204 -0.0587,0.01349 8e-5,0.0016 0.0175,0.01312 0.0385,0.0259 0.0679,0.04108 0.1387,0.09714 0.19921,0.157761 0.0982,0.09839 0.16031,0.1876 0.42134,0.605501 0.0884,0.141498 0.16391,0.26223 0.16783,0.268295 l 0.007,0.01103 h -0.28882 -0.28884 l -0.19288,-0.287895 c -0.36298,-0.541718 -0.41074,-0.605218 -0.49914,-0.663437 -0.0681,-0.04484 -0.12798,-0.05709 -0.3008,-0.06151 l -0.11637,-0.003 v 0.50791 0.507904 h -0.24257 -0.24257 v -1.210391 z m 1.19824,-0.197742 c 0.10322,-0.01349 0.15547,-0.03687 0.20572,-0.09207 0.0507,-0.0557 0.0711,-0.115819 0.0711,-0.210029 0,-0.130434 -0.0482,-0.216285 -0.14822,-0.264016 -0.0777,-0.03708 -0.0652,-0.03607 -0.47538,-0.03847 l -0.36629,-0.0021 v 0.309612 0.309613 l 0.3271,-0.0024 c 0.25813,-0.0019 0.33952,-0.004 0.386,-0.01011 v 0 z"
|
||||
id="RightShoulder" />
|
||||
<g
|
||||
id="RightTrigger">
|
||||
<path
|
||||
style="fill-opacity:1;stroke-width:0.00490037;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 68.43397,6.1446855 v -1.208246 l 0.62601,0.00212 c 0.66757,0.00212 0.68236,0.00273 0.82087,0.027509 0.0828,0.014843 0.13729,0.031819 0.19719,0.061523 0.0639,0.03167 0.10575,0.06235 0.15301,0.112101 0.0781,0.08226 0.1333,0.187278 0.16198,0.308377 0.0119,0.0507 0.013,0.06345 0.0132,0.162814 8e-5,0.08425 -0.003,0.117107 -0.008,0.150355 -0.0561,0.275579 -0.24551,0.44872 -0.56002,0.512053 -0.0592,0.01193 -0.066,0.01426 -0.0579,0.02011 0.005,0.0036 0.0317,0.02135 0.0591,0.03937 0.14401,0.09456 0.23318,0.189458 0.36915,0.392957 0.0495,0.07413 0.38581,0.60892 0.3906,0.621162 0.003,0.0049 -0.0559,0.0059 -0.2858,0.0049 l -0.28824,-0.0013 -0.21582,-0.321297 c -0.30739,-0.457573 -0.34192,-0.505973 -0.40719,-0.570571 -0.09,-0.08909 -0.15473,-0.109487 -0.36116,-0.113864 l -0.12173,-0.0027 v 0.505378 0.505375 h -0.24257 -0.24257 v -1.208244 z m 1.17118,-0.193157 c 0.0797,-0.0088 0.11668,-0.01786 0.15986,-0.03914 0.0694,-0.03421 0.11798,-0.09792 0.13692,-0.179525 0.0103,-0.04405 0.0101,-0.136223 -2.6e-4,-0.175921 -0.0313,-0.12047 -0.12489,-0.194789 -0.26226,-0.208325 -0.0257,-0.0026 -0.19817,-0.0046 -0.38346,-0.0047 l -0.33689,-1.7e-4 v 0.309306 0.309304 l 0.31729,-0.0026 c 0.18902,-0.0016 0.33811,-0.0049 0.36875,-0.0083 v 0 z"
|
||||
id="path4234" />
|
||||
<path
|
||||
style="fill-opacity:1;stroke-width:0.00490037;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 66.15527,7.1346265 v -0.218302 l 0.63461,-0.783873 c 0.34904,-0.431131 0.6346,-0.784953 0.6346,-0.786276 0,-0.0013 -0.2536,-0.0024 -0.56354,-0.0024 H 66.29741 V 5.1404305 4.9370639 h 0.8845 0.88453 v 0.1874836 0.187481 l -0.66442,0.818322 -0.6644,0.818319 0.6889,0.0013 0.68892,0.0013 v 0.200898 0.200898 h -0.98007 -0.9801 v -0.2183 z"
|
||||
id="path4236" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="left">
|
||||
<path
|
||||
id="path4182"
|
||||
d="m 33.122745,22.256318 v 1.601973 c 0,0.13798 -0.164307,0.292312 -0.292312,0.292312 h -5.133234 c -0.161131,0 -0.356659,-0.160814 -0.356659,-0.356632 V 22.24465 Z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
id="path4186"
|
||||
d="m 23.902465,22.949553 c 0,0.04675 0,1.145911 0,1.145911 0,0.478393 0.291942,0.666512 0.666512,0.666512 2.017951,0.177932 4.469606,0.167507 6.536399,0 0.347319,0 0.687917,-0.3302 0.684054,-0.684054 v -1.397318 h -7.886965 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
id="path4190"
|
||||
d="m 7.3451308,22.949553 c 0,0.04675 0,1.145911 0,1.145911 0,0.478393 0.291941,0.666512 0.666485,0.666512 2.0179782,0.177932 4.4696072,0.167507 6.5364262,0 0.347319,0 0.687917,-0.3302 0.684027,-0.684054 V 22.680604 H 7.3451308 Z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
id="path3828"
|
||||
d="m 39.503431,63.613187 h -1.110837 v 9.132265 h 1.08745 c 0.379812,0 0.572961,-0.264917 0.572961,-0.572958 v -8.079893 c 0,-0.292343 -0.213908,-0.479414 -0.549574,-0.479414 z"
|
||||
style="fill:#00bbdb;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:#00bbdb;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 39.503431,117.52619 h -1.110837 v -9.13226 h 1.08745 c 0.379812,0 0.572961,0.26491 0.572961,0.57295 v 8.0799 c 0,0.29234 -0.213908,0.47941 -0.549574,0.47941 z"
|
||||
id="path3830" />
|
||||
<path
|
||||
id="path3826"
|
||||
d="m 39.643747,44.647073 h -3.706693 v 88.793887 h 1.734386 v 3.68763 c 0,0.41206 0.346123,1.10727 0.639284,1.10727 h 1.291346 c 0.23689,0 0.450287,-0.18294 0.450287,-0.45028 v -17.26235 c 0,-2.28046 -1.425903,-2.30845 -1.425903,-3.0285 v -9.15565 c 0,-0.64968 1.391472,-0.80953 1.391472,-2.66601 V 75.364695 c 0,-1.840243 -1.391472,-1.964013 -1.391472,-2.666016 l 0.03307,-9.003641 c 0,-0.620152 1.391473,-1.097836 1.391473,-2.747865 l -7.93e-4,-15.823609 c 0.0024,-0.259744 -0.125479,-0.476491 -0.406334,-0.476491 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
id="path3832"
|
||||
d="m 30.154591,38.984701 v -1.107942 c 0,-0.332553 -0.339662,-0.7028 -0.7028,-0.7028 h -6.945312 c -10.713919,0 -20.3040802,8.641797 -21.9475795,14.775418 -0.07498,0.279813 -0.101862,0.841899 0.385869,0.841899 h 1.2336173 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
id="path3925"
|
||||
d="m 36.251186,39.39185 v 109.19024 c 0,0.2708 -0.133853,0.47543 -0.475422,0.47543 H 22.641681 c -13.6735372,0 -22.5093895,-12.09084 -22.5093895,-22.50939 V 60.947123 c 0,-15.315427 15.0189105,-22.415219 22.4152215,-22.415219 h 12.909171 c 0.657781,0 0.794502,0.361942 0.794502,0.859946 z"
|
||||
style="fill:#00bbdb;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<circle
|
||||
id="path3871"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="19.236444"
|
||||
cy="90.101624"
|
||||
r="4.0085444" />
|
||||
<circle
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3873"
|
||||
cx="19.236444"
|
||||
cy="106.67232"
|
||||
r="4.0085444" />
|
||||
<circle
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3875"
|
||||
transform="rotate(90)"
|
||||
cx="98.386986"
|
||||
cy="-10.951083"
|
||||
r="4.0085444" />
|
||||
<circle
|
||||
transform="rotate(90)"
|
||||
id="path3877"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="98.386986"
|
||||
cy="-27.521791"
|
||||
r="4.0085444" />
|
||||
<rect
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="Back"
|
||||
width="5.8704429"
|
||||
height="1.6867187"
|
||||
x="27.454165"
|
||||
y="48.675072"
|
||||
class="button" />
|
||||
<circle
|
||||
id="leftStickBackground"
|
||||
style="display:inline;fill-opacity:1;stroke:#000000;stroke-width:0.264439;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="19.268621"
|
||||
cy="67.195892"
|
||||
r="8.4832029" />
|
||||
<g
|
||||
id="LeftStick"
|
||||
class="button"
|
||||
style="display:inline">
|
||||
<circle
|
||||
id="path3907"
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="19.268621"
|
||||
cy="67.195892"
|
||||
r="8.4832029" />
|
||||
<circle
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3909"
|
||||
cx="19.268621"
|
||||
cy="67.254738"
|
||||
r="5.9489326" />
|
||||
<path
|
||||
id="path3911"
|
||||
d="m 18.961234,60.286636 c 5.3e-5,-0.567529 0.0016,-0.902846 0.0042,-0.920872 0.0079,-0.0547 0.03481,-0.106976 0.07652,-0.148682 0.12573,-0.12573 0.339551,-0.08842 0.417084,0.07277 0.02906,0.06043 0.02704,-0.01283 0.02711,0.983676 l 6.1e-5,0.900091 -0.185587,-2.17e-4 c -0.102071,-1.19e-4 -0.220205,0.0011 -0.262517,0.0028 l -0.07693,0.003 7.9e-5,-0.892521 z"
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="path3913"
|
||||
d="m 19.194179,75.214639 c -0.103042,-0.01154 -0.191744,-0.08817 -0.224182,-0.193662 -0.0065,-0.02098 -0.0071,-0.08735 -0.0082,-0.857049 l -0.0013,-0.83431 0.06106,0.0034 c 0.03359,0.0019 0.151787,0.0034 0.262666,0.0034 h 0.201596 l -0.0011,0.835091 -0.0011,0.835091 -0.0092,0.02687 c -0.02764,0.08078 -0.09145,0.144222 -0.172333,0.171315 -0.02641,0.0088 -0.07656,0.01347 -0.107987,0.0099 v 0 z"
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="path3915"
|
||||
d="m 25.349998,67.27271 c 0,-0.09385 -0.0016,-0.212042 -0.0034,-0.262662 l -0.0034,-0.09203 0.854977,0.0011 0.854977,0.0011 0.03032,0.01109 c 0.114072,0.04167 0.18497,0.146846 0.177263,0.262977 -0.0043,0.06436 -0.02954,0.121094 -0.07432,0.166859 -0.03514,0.03591 -0.06816,0.05496 -0.129127,0.07451 -0.01318,0.0042 -0.188249,0.0056 -0.861963,0.0065 l -0.845428,0.0013 v -0.170629 0 z"
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="path3917"
|
||||
d="m 11.413899,67.438823 c -0.0569,-0.0075 -0.112456,-0.03622 -0.152974,-0.07906 -0.140957,-0.149016 -0.06985,-0.386953 0.130236,-0.435806 0.01865,-0.0046 0.182329,-0.0056 0.912762,-0.0057 l 0.890024,-1.49e-4 -0.0033,0.08785 c -0.0019,0.04832 -0.0033,0.166452 -0.0033,0.26252 v 0.174667 l -0.874363,-5.29e-4 c -0.480901,-2.65e-4 -0.885529,-0.0021 -0.899171,-0.0038 z"
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</g>
|
||||
<rect
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect3919"
|
||||
width="6.8130207"
|
||||
height="6.647656"
|
||||
x="21.930986"
|
||||
y="116.11076"
|
||||
ry="0.89296871" />
|
||||
<circle
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3921"
|
||||
cx="25.312696"
|
||||
cy="119.49245"
|
||||
r="2.3399088" />
|
||||
<g
|
||||
id="dpad">
|
||||
<path
|
||||
style="stroke:#000000;stroke-width:0.904694;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="DpadRight"
|
||||
d="m 134.97306,862.5577 0,-11.46794 9.93153,5.73397 z"
|
||||
transform="matrix(0.29245602,0,0,0.29245602,-13.019911,-152.18812)" />
|
||||
<path
|
||||
transform="matrix(-0.29245602,0,0,0.29245602,51.534858,-152.18812)"
|
||||
d="m 134.97306,862.5577 0,-11.46794 9.93153,5.73397 z"
|
||||
id="DpadLeft"
|
||||
style="stroke:#000000;stroke-width:0.904694;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
transform="matrix(0,-0.29245602,0.29245602,0,-231.35192,130.71823)"
|
||||
d="m 134.97306,862.5577 0,-11.46794 9.93153,5.73397 z"
|
||||
id="DpadUp"
|
||||
style="stroke:#000000;stroke-width:0.904694;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
style="stroke:#000000;stroke-width:0.904694;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="DpadDown"
|
||||
d="m 134.97306,862.5577 0,-11.46794 9.93153,5.73397 z"
|
||||
transform="matrix(0,0.29245602,0.29245602,0,-231.35192,66.16345)" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 35.904558,11.76891 h 1.501193 l 0.0042,-0.958829 h 1.605518 c 0.420423,0 0.553932,0.382418 0.553932,0.959443 v 8.792554 c 0,0.41027 -0.387932,0.732996 -0.733002,0.732996 h -1.083786 c -0.295275,0 -0.312341,-0.319455 -0.342768,-0.593693 V 20.117742 H 35.92972 Z"
|
||||
id="path4172" />
|
||||
<path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 37.406703,20.100131 V 11.831902"
|
||||
id="path4174" />
|
||||
<path
|
||||
id="path4176"
|
||||
d="m 29.267115,6.5371897 v 1.852083 h 4.960937 v -1.885156 c 0,-0.2437079 -0.213122,-0.3536949 -0.40513,-0.4051559 -1.385438,-0.371211 -2.876788,-0.352399 -4.192005,0 -0.207354,0.05556 -0.363802,0.1947589 -0.363802,0.4382289 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
id="path4180"
|
||||
d="m 24.419382,23.304492 c -0.20619,0.740701 -1.629331,2.124948 -3.05062,2.447316 0,0.421429 0.247492,1.005602 0.760043,1.005602 h 4.8877 c 1.029362,0 0.770335,3.348196 -0.897149,3.348196 h -1.68701 c -3.504935,1.440339 -6.635141,1.469734 -10.056019,0 h -1.777365 c -1.5485,0 -1.867296,-3.317637 -0.888947,-3.317637 h 4.794436 c 0.695272,0 1.020762,-0.486277 0.979963,-0.979963 -1.247881,-0.516758 -2.36212,-1.193906 -2.991167,-2.45327 v -0.46773 h 9.930315 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:#00bbdb;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="m 3.1349058,23.187797 c -0.970783,0 -2.8442713,-1.929288 -2.8442713,-2.968281 v -6.81302 c 0.520621,-1.975089 1.5200053,-4.1207013 4.7625003,-4.7625003 0.566261,0 0.859896,-0.533533 0.859896,-0.859896 3.06951,-4.0709057 6.4024682,-6.1659029 9.8226572,-7.6067708 h 7.242969 c 3.115389,1.3968147 4.852326,3.4828691 6.217708,5.7546869 v 1.2567709 c 0,0.518822 0.42971,1.058334 0.992187,1.058334 h 6.019271 V 23.196079 Z"
|
||||
id="path4178" />
|
||||
<path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 1.9949328,14.65448 H 30.215662 v 3.251237 c 0,1.098243 -1.401771,2.731037 -2.73104,2.731037 H 3.8156258 c -1.444993,0 -2.5142903,-1.396424 -2.5142903,-2.514288 0,-1.34669 -0.1161,-3.467986 0.6935973,-3.467986 z"
|
||||
id="path4164" />
|
||||
<path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 4.1190738,11.099795 H 29.998912 C 29.985313,5.7111738 27.335498,0.1322915 22.239297,0.1322915 l -7.239421,0.086701 C 7.9123758,1.5120668 6.4557758,6.6026927 4.1190738,11.099795 Z"
|
||||
id="path4162" />
|
||||
<path
|
||||
id="path4184"
|
||||
d="M 22.071205,26.744075 H 16.944902"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
id="path4188"
|
||||
d="M 21.408926,25.730509 H 17.338602"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
id="path4192"
|
||||
d="m 1036.7923,1045.6825 v 38"
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3994)"
|
||||
transform="matrix(0,0.26458333,-0.26458333,0,301.07298,-244.20874)" />
|
||||
<g
|
||||
id="LeftTrigger">
|
||||
<path
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.00334412;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 17.550756,7.0784747 v -0.211688 l 0.613645,-0.7579259 c 0.337506,-0.416859 0.613646,-0.7587585 0.613646,-0.7597745 0,-0.00106 -0.245293,-0.00185 -0.545092,-0.00185 H 17.687866 V 5.1516039 4.9559737 h 0.854421 0.854419 v 0.1817132 0.1817105 l -0.64054,0.7889164 -0.640541,0.7889159 0.665621,7.93e-4 0.665623,7.94e-4 v 0.195623 0.195622 h -0.948055 -0.948058 v -0.211688 z"
|
||||
id="path4216" />
|
||||
<path
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.00334412;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 19.761215,6.1330998 V 4.976037 h 0.234087 0.234088 v 0.9614328 0.9614319 h 0.586891 0.586893 v 0.19563 0.19563 h -0.820981 -0.820978 z"
|
||||
id="path4218" />
|
||||
</g>
|
||||
<path
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.00334412;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 18.76467,17.759127 v -1.157063 h 0.234087 0.234088 v 0.961433 0.961429 h 0.586893 0.586891 v 0.195633 0.19563 H 19.585648 18.76467 Z"
|
||||
id="LeftShoulder" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 37 KiB |
331
src/Ryujinx/Assets/Icons/Controller_JoyConRight_Settings.svg
Normal file
331
src/Ryujinx/Assets/Icons/Controller_JoyConRight_Settings.svg
Normal file
@ -0,0 +1,331 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="Layer_1"
|
||||
width="1000.8"
|
||||
height="1300"
|
||||
x="0"
|
||||
y="0"
|
||||
version="1.1"
|
||||
viewBox="0 0 1000.8 1300"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1"><filter
|
||||
id="filter3994"
|
||||
color-interpolation-filters="sRGB"
|
||||
y="-0.059999999"
|
||||
height="1.12"
|
||||
x="-inf"
|
||||
width="inf"><feGaussianBlur
|
||||
stdDeviation="0.95"
|
||||
id="feGaussianBlur3996" /></filter><filter
|
||||
id="filter3994-6"
|
||||
color-interpolation-filters="sRGB"
|
||||
y="-0.059999999"
|
||||
height="1.12"
|
||||
x="-inf"
|
||||
width="inf"><feGaussianBlur
|
||||
stdDeviation="0.95"
|
||||
id="feGaussianBlur3996-1" /></filter></defs><metadata
|
||||
id="metadata85" /><style
|
||||
type="text/css"
|
||||
id="style1">#buttons{fill:#ffffff;}
|
||||
.button{fill:#44484c;}</style><g
|
||||
id="right"
|
||||
transform="matrix(8.7009664,0,0,8.7009664,-1454.1279,-3.6543895e-8)"><path
|
||||
style="fill:#ff5f53;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 225.11734,63.613186 h 1.11085 v 9.132265 h -1.08746 c -0.37981,0 -0.57295,-0.264917 -0.57295,-0.572958 V 64.0926 c 0,-0.292343 0.21391,-0.479414 0.54956,-0.479414 z"
|
||||
id="path3822" /><path
|
||||
id="path3824"
|
||||
d="m 225.11734,117.52618 h 1.11085 v -9.13226 h -1.08746 c -0.37981,0 -0.57295,0.26491 -0.57295,0.57295 v 8.0799 c 0,0.29234 0.21391,0.47941 0.54956,0.47941 z"
|
||||
style="fill:#ff5f53;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 224.97703,44.647072 h 3.70671 v 88.793878 h -1.7344 v 3.68763 c 0,0.41206 -0.34613,1.10727 -0.63928,1.10727 h -1.29136 c -0.23688,0 -0.45026,-0.18294 -0.45026,-0.45028 v -17.26235 c 0,-2.28046 1.42589,-2.30845 1.42589,-3.0285 v -9.15565 c 0,-0.64968 -1.39147,-0.80953 -1.39147,-2.66601 V 75.364694 c 0,-1.840243 1.39147,-1.964013 1.39147,-2.666016 l -0.0331,-9.003641 c 0,-0.620152 -1.39147,-1.097836 -1.39147,-2.747865 V 45.123563 c -0.003,-0.259744 0.12546,-0.476491 0.40632,-0.476491 z"
|
||||
id="path3820" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 234.36593,38.9847 v -1.107942 c 0,-0.332553 0.33967,-0.7028 0.70281,-0.7028 h 6.94532 c 10.7139,0 20.30407,8.641797 21.94758,14.775418 0.075,0.279813 0.10186,0.841899 -0.38587,0.841899 h -1.23362 z"
|
||||
id="path3818" /><path
|
||||
style="fill:#ff5f53;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 228.32187,39.555208 V 148.74544 c 0,0.2708 0.13385,0.47542 0.47543,0.47542 h 13.13407 c 13.67354,0 22.5094,-12.09083 22.5094,-22.50938 V 61.110481 c 0,-15.315427 -15.01891,-22.415219 -22.41523,-22.415219 h -12.90918 c -0.65778,0 -0.79449,0.361942 -0.79449,0.859946 z"
|
||||
id="path3923" /><path
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="m 231.30632,48.674491 h 2.01287 v -1.987818 h 1.67042 v 1.996168 h 1.97948 v 1.670436 h -1.97112 v 2.062985 h -1.69548 v -2.062985 h -1.98784 z"
|
||||
id="Start"
|
||||
class="button" /><circle
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3095"
|
||||
cx="245.18909"
|
||||
cy="58.209881"
|
||||
r="4.0085444" /><circle
|
||||
id="path3865"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="245.18909"
|
||||
cy="74.780586"
|
||||
r="4.0085444" /><circle
|
||||
transform="rotate(90)"
|
||||
id="path3867"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="66.495232"
|
||||
cy="-236.90369"
|
||||
r="4.0085444" /><circle
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3869"
|
||||
transform="rotate(90)"
|
||||
cx="66.495232"
|
||||
cy="-253.47443"
|
||||
r="4.0085444" /><circle
|
||||
id="path3879"
|
||||
style="fill:#999595;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="239.16745"
|
||||
cy="117.94165"
|
||||
r="4.4528017" /><circle
|
||||
style="fill:#3a3d40;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3881"
|
||||
cx="239.16742"
|
||||
cy="117.98216"
|
||||
r="3.3256173" /><path
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 236.82817,117.80498 h 0.6572 v 2.06838 c 1.10172,0 2.33288,0 3.3343,0 v -2.06838 h 0.65718 c -0.72404,-0.67331 -1.69847,-1.59279 -2.32434,-2.16524 -0.75748,0.69632 -1.55583,1.44766 -2.32434,2.16524 z m 1.54956,0.0346 c 0.51263,0 1.08492,0 1.54956,0 v 1.32129 c -0.5121,0 -1.08427,0 -1.54956,0 z"
|
||||
id="path3883" /><circle
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3888-2"
|
||||
cx="245.23871"
|
||||
cy="97.259186"
|
||||
r="8.4832029" /><g
|
||||
id="RightStick"
|
||||
class="button"><circle
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3888"
|
||||
cx="245.23871"
|
||||
cy="97.259186"
|
||||
r="8.4832029" /><circle
|
||||
id="path3892"
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
cx="245.23871"
|
||||
cy="97.318031"
|
||||
r="5.9489326" /><path
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 244.93127,90.349919 c 6e-5,-0.56753 0.003,-0.90285 0.004,-0.92087 0.008,-0.0547 0.0348,-0.10698 0.0765,-0.14869 0.12573,-0.12573 0.33957,-0.0884 0.41709,0.0728 0.029,0.0604 0.027,-0.0128 0.0271,0.98367 l 6e-5,0.900086 -0.18558,-2.1e-4 c -0.10208,-1.2e-4 -0.22022,0.001 -0.26252,0.003 l -0.0769,0.003 8e-5,-0.892526 z"
|
||||
id="path3899" /><path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 245.16421,105.27792 c -0.10303,-0.0115 -0.19174,-0.0882 -0.22418,-0.19366 -0.006,-0.021 -0.007,-0.0874 -0.008,-0.85705 l -0.003,-0.83431 0.0611,0.003 c 0.0336,0.002 0.15179,0.003 0.26268,0.003 h 0.20158 v 0.83509 0.83509 l -0.009,0.0269 c -0.0276,0.0808 -0.0914,0.14422 -0.17232,0.17131 -0.0265,0.009 -0.0766,0.0135 -0.10798,0.01 v 0 z"
|
||||
id="path3901" /><path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 251.32003,97.335985 c 0,-0.0938 -0.003,-0.21204 -0.003,-0.26266 l -0.003,-0.092 0.85498,0.001 0.855,10e-4 0.0303,0.0111 c 0.11406,0.0417 0.18497,0.14685 0.17725,0.26298 -0.004,0.0644 -0.0295,0.121092 -0.0743,0.166862 -0.0351,0.0359 -0.0682,0.055 -0.12911,0.0745 -0.0132,0.004 -0.18825,0.006 -0.86196,0.007 l -0.84543,10e-4 v -0.170632 0 z"
|
||||
id="path3903" /><path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.00826823;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 237.38395,97.502107 c -0.0569,-0.008 -0.11247,-0.0362 -0.15298,-0.0791 -0.14097,-0.149022 -0.0698,-0.386962 0.13023,-0.435812 0.0188,-0.005 0.18232,-0.006 0.91276,-0.006 l 0.89003,-1.4e-4 -0.003,0.0878 c -0.003,0.0483 -0.003,0.16646 -0.003,0.26252 v 0.174672 l -0.87437,-5.3e-4 c -0.4809,-2.6e-4 -0.88553,-0.002 -0.89916,-0.004 z"
|
||||
id="path3905" /></g><g
|
||||
id="buttons"><path
|
||||
style="stroke:none;stroke-width:0.264583"
|
||||
d="m 243.25289,60.355075 c 0,-0.0029 0.32536,-0.501412 0.72302,-1.108578 l 0.72303,-1.104053 -0.64079,-0.989807 c -0.35243,-0.54438 -0.64887,-1.001289 -0.65874,-1.015312 l -0.018,-0.0254 h 0.49586 0.49585 l 0.42516,0.681726 c 0.23384,0.374967 0.42924,0.680376 0.43421,0.678709 0.005,-0.0029 0.19529,-0.307393 0.42291,-0.679397 l 0.41381,-0.676407 0.4885,-0.0029 c 0.26866,-0.0029 0.48847,0 0.48847,0.0029 0,0.0029 -0.2945,0.464634 -0.6545,1.026662 -0.37467,0.58502 -0.65283,1.028224 -0.65061,1.036744 0.003,0.0082 0.32589,0.499559 0.71943,1.091988 0.39354,0.592402 0.71554,1.0795 0.71554,1.082463 0,0.0029 -0.22849,0.005 -0.50779,0.005 h -0.50779 l -0.47199,-0.737632 c -0.38118,-0.595762 -0.47418,-0.73533 -0.48339,-0.725408 -0.006,0.0066 -0.22109,0.338852 -0.47734,0.738029 l -0.46593,0.725752 h -0.50448 c -0.27747,0 -0.5045,-0.0029 -0.5045,-0.0045 z"
|
||||
id="X" /><path
|
||||
style="stroke:none;stroke-width:0.264583"
|
||||
d="m 251.44666,68.578086 c 0.0159,-0.04217 1.60489,-4.121546 1.62507,-4.172241 l 0.023,-0.05794 h 0.44749 0.44749 l 0.84585,2.111613 c 0.46522,1.161388 0.84796,2.116852 0.85055,2.123281 0.004,0.0093 -0.0887,0.01111 -0.45704,0.009 l -0.4618,-0.0026 -0.18592,-0.484399 -0.18593,-0.484399 -0.85288,0.0026 -0.85288,0.0026 -0.17585,0.484452 -0.17584,0.484426 h -0.44876 c -0.42357,0 -0.44844,-0.0026 -0.44265,-0.01614 z m 2.6729,-1.673542 c -0.003,-0.0064 -0.13496,-0.363246 -0.29456,-0.793062 -0.15957,-0.429816 -0.29292,-0.778193 -0.2963,-0.774197 -0.005,0.0053 -0.3456,0.938741 -0.57272,1.567259 -0.003,0.009 0.11697,0.01138 0.5819,0.01138 0.46506,0 0.5852,-0.0026 0.58168,-0.01138 z"
|
||||
id="A" /><path
|
||||
style="stroke:none;stroke-width:0.264583"
|
||||
d="m 243.61635,74.871836 v -2.119339 l 1.04767,0.0045 c 1.04455,0.0045 1.15731,0.0079 1.36678,0.03969 0.31785,0.04826 0.57875,0.210662 0.75094,0.467466 0.26178,0.390393 0.21953,0.899689 -0.103,1.241584 -0.0752,0.07972 -0.14721,0.13552 -0.24757,0.19177 l -0.0779,0.04366 0.0631,0.0209 c 0.15386,0.05075 0.31663,0.143987 0.42588,0.243999 0.12821,0.117316 0.23862,0.303504 0.28662,0.483314 0.022,0.08186 0.0243,0.108189 0.0251,0.26215 0,0.133508 -0.003,0.187933 -0.0161,0.247623 -0.0534,0.243126 -0.17126,0.469159 -0.32763,0.628544 -0.20013,0.203968 -0.44561,0.30742 -0.79624,0.335571 -0.17465,0.01402 -1.02536,0.02733 -1.76633,0.02762 h -0.63137 v -2.119339 z m 2.11238,1.397661 c 0.19571,-0.01614 0.30006,-0.05593 0.40346,-0.153458 0.10692,-0.100859 0.15121,-0.217382 0.15087,-0.396848 0,-0.276649 -0.15034,-0.470483 -0.41069,-0.531337 -0.15079,-0.03524 -0.23929,-0.03998 -0.82907,-0.04434 l -0.57587,-0.0042 v 0.56978 0.569807 h 0.57587 c 0.3167,-8e-5 0.62515,-0.0042 0.68543,-0.0093 v 0 z m -0.34084,-1.831075 c 0.30885,-0.0087 0.37068,-0.01773 0.48202,-0.07046 0.11647,-0.05524 0.19852,-0.147002 0.23728,-0.265377 0.0265,-0.08091 0.0265,-0.246115 -2.7e-4,-0.328348 -0.0499,-0.154278 -0.16449,-0.253629 -0.3406,-0.295354 -0.0549,-0.01296 -0.15099,-0.01561 -0.68143,-0.01905 l -0.61749,-0.0037 v 0.495591 0.495617 h 0.32145 c 0.17682,0 0.44635,-0.004 0.59899,-0.0082 v 0 z"
|
||||
id="B" /><path
|
||||
style="display:inline;stroke:none;stroke-width:0.264583"
|
||||
d="m 236.49961,67.784945 v -0.891461 l -0.77073,-1.218485 c -0.42392,-0.670137 -0.77076,-1.220179 -0.77076,-1.222269 0,-0.0026 0.22119,-0.0037 0.49152,-0.0037 l 0.49154,1.06e-4 0.49842,0.839153 c 0.37071,0.624072 0.50107,0.836374 0.50864,0.828331 0.006,-0.0058 0.2272,-0.38362 0.49241,-0.839232 l 0.48218,-0.828437 h 0.4848 0.48477 l -0.0132,0.0209 c -0.007,0.01138 -0.35669,0.563192 -0.77642,1.226185 l -0.76311,1.205415 v 0.887572 0.887571 h -0.42 -0.41998 v -0.891487 z"
|
||||
id="Y" /></g><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 191.83897,40.180331 c -2.66361,0 -6.92952,2.444663 -10.28988,2.759557 -0.71165,0 -1.19269,0.71711 -1.19269,1.262848 v 2.38538 c 0,0.791848 1.14593,1.389081 1.14593,2.081363 h 8.95687 z"
|
||||
id="path3930" /><path
|
||||
style="fill:#ff5f53;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 188.684,62.683509 c 0,-5.087943 -6.22371,-8.710414 -8.3013,-13.411065 -0.12094,-0.451335 0.23601,-0.975651 0.67799,-0.975651 h 2.6293 c 4.06201,0 3.22524,-4.668785 5.93659,-6.746875 v 21.139517 z"
|
||||
id="path3928" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 186.98074,54.481428 h 1.85209 V 49.52049 h -1.88516 c -0.24371,0 -0.35369,0.213125 -0.40515,0.405144 -0.37122,1.385437 -0.3524,2.876767 0,4.191992 0.0556,0.207356 0.19475,0.363802 0.43822,0.363802 z"
|
||||
id="path3924" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 180.39923,46.246272 c 4.95552,0 5.85804,-1.554427 7.17683,-1.554427"
|
||||
id="path3932" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 195.21826,39.022718 c 0,0 5.57545,-0.03565 5.5659,0 -0.01,0.03565 0,-1.110837 0,-1.110837 0,-0.29836 -0.22111,-0.491109 -0.49112,-0.491109 h -4.54858 c -0.322,0 -0.5262,0.287943 -0.5262,0.526187 z"
|
||||
id="path3926" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 203.40338,46.681662 h 1.60197 c 0.13798,0 0.29231,0.164314 0.29231,0.292325 v 5.133245 c 0,0.161131 -0.16081,0.35664 -0.35663,0.35664 h -1.54932 z"
|
||||
id="path3942" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 203.89818,62.268486 c 0.0467,0 1.14591,0 1.14591,0 0.47839,0 0.66651,-0.291941 0.66651,-0.666504 0.17793,-2.017953 0.16751,-4.469606 0,-6.53641 0,-0.347313 -0.3302,-0.687922 -0.68406,-0.684043 h -1.39731 v 7.886957 z"
|
||||
id="path3948" /><path
|
||||
id="path3950"
|
||||
d="m 203.89818,70.547158 c 0.0467,0 1.14591,0 1.14591,0 0.47839,0 0.66651,-0.291941 0.66651,-0.666504 0.17793,-2.017953 0.16751,-4.469606 0,-6.536409 0,-0.347314 -0.3302,-0.687922 -0.68406,-0.684044 h -1.39731 v 7.886957 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 203.89818,78.82583 c 0.0467,0 1.14591,0 1.14591,0 0.47839,0 0.66651,-0.291941 0.66651,-0.666504 0.17793,-2.017953 0.16751,-4.469606 0,-6.536409 0,-0.347314 -0.3302,-0.687922 -0.68406,-0.684043 h -1.39731 v 7.886956 z"
|
||||
id="path3952" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 203.71564,92.250695 c 0.7407,0.20618 2.12495,1.62934 2.44732,3.05063 0.42142,0 1.0056,-0.24751 1.0056,-0.76005 v -4.887686 c 0,-1.02938 3.3482,-0.77035 3.3482,0.89715 v 1.687006 c 1.44034,3.50493 1.46973,6.635122 0,10.056025 v 1.77734 c 0,1.5485 -3.31764,1.86732 -3.31764,0.88896 v -4.79444 c 0,-0.695263 -0.48628,-1.020763 -0.97997,-0.979953 -0.51675,1.247883 -1.1939,2.362123 -2.45327,2.991163 h -0.46773 v -9.930325 z"
|
||||
id="path3986" /><path
|
||||
style="fill:#999595;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 203.40145,122.33051 h 1.5875 c 0.5131,0 0.44132,-8.71471 0,-8.71471 h -1.5875 z"
|
||||
id="path3998" /><path
|
||||
style="fill:#ff5f53;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 194.34129,38.730393 h 5.71326 c 2.19093,0 3.68795,2.151269 3.68795,4.115951 V 145.23062 c 0,2.30569 -1.84389,4.04578 -4.0458,4.04578 h -5.16832 c -3.23868,0 -6.06869,-3.32049 -6.06869,-6.06868 V 44.623685 c 0,-2.970212 3.40577,-5.893292 5.8816,-5.893292 z"
|
||||
id="path3918" /><rect
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect3922"
|
||||
width="10.22559"
|
||||
height="93.491112"
|
||||
x="191.06221"
|
||||
y="44.664806"
|
||||
rx="1.1287988"
|
||||
ry="1.1287988" /><rect
|
||||
style="fill:#2e2f31;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect3940"
|
||||
width="7.9375"
|
||||
height="16.172655"
|
||||
x="192.17319"
|
||||
y="120.92493"
|
||||
rx="1.1287988"
|
||||
ry="1.1287988" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3934"
|
||||
d="m 76,-387.61176 -8.184578,-14.17611 16.369155,0 z"
|
||||
transform="matrix(0.29141287,0,0,0.29141287,173.97803,249.09703)" /><path
|
||||
transform="matrix(0.29141287,0,0,0.29141287,173.97803,244.15263)"
|
||||
d="m 76,-387.61176 -8.184578,-14.17611 16.369155,0 z"
|
||||
id="path3936"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3938"
|
||||
d="m 76,-387.61176 -8.184578,-14.17611 16.369155,0 z"
|
||||
transform="matrix(0.29141287,0,0,0.29141287,173.97803,239.20823)" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 203.75697,48.759813 h 1.45521"
|
||||
id="path3944" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 203.74871,50.355582 h 1.49654"
|
||||
id="path3946" /><rect
|
||||
style="fill:#aaeeff;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect3954"
|
||||
width="1.9513021"
|
||||
height="1.9513021"
|
||||
x="195.14975"
|
||||
y="79.881424"
|
||||
rx="0.53348637"
|
||||
ry="0.53348637" /><rect
|
||||
ry="0.53348637"
|
||||
rx="0.53348637"
|
||||
y="84.213974"
|
||||
x="195.14975"
|
||||
height="1.9513021"
|
||||
width="1.9513021"
|
||||
id="rect3956"
|
||||
style="fill:#aaeeff;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><rect
|
||||
ry="0.53348637"
|
||||
rx="0.53348637"
|
||||
y="88.645752"
|
||||
x="195.14975"
|
||||
height="1.9513021"
|
||||
width="1.9513021"
|
||||
id="rect3958"
|
||||
style="fill:#aaeeff;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><rect
|
||||
style="fill:#aaeeff;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect3960"
|
||||
width="1.9513021"
|
||||
height="1.9513021"
|
||||
x="195.14975"
|
||||
y="92.978302"
|
||||
rx="0.53348637"
|
||||
ry="0.53348637" /><circle
|
||||
style="fill:#2e2f31;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3962"
|
||||
cx="196.10172"
|
||||
cy="100.48306"
|
||||
r="1.5743958" /><rect
|
||||
style="fill:#999595;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect3964"
|
||||
width="4.0018229"
|
||||
height="9.2934895"
|
||||
x="194.15756"
|
||||
y="108.258"
|
||||
rx="1.4264551"
|
||||
ry="1.3933822" /><rect
|
||||
ry="1.3933822"
|
||||
rx="1.4264551"
|
||||
y="63.58707"
|
||||
x="194.15756"
|
||||
height="9.2934895"
|
||||
width="4.0018229"
|
||||
id="rect3966"
|
||||
style="fill:#999595;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><g
|
||||
id="SingleRightTrigger1"><path
|
||||
style="stroke:#000000;stroke-width:0.00353806;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 196.12845,68.242846 h -1.14456 l 8e-5,-0.499748 c 5e-5,-0.27487 0.003,-0.541808 0.003,-0.593214 0.0122,-0.335912 0.0581,-0.482473 0.19028,-0.609404 0.14709,-0.141174 0.37312,-0.199747 0.59762,-0.154871 0.12509,0.025 0.23516,0.08775 0.31917,0.181964 0.0772,0.08655 0.13753,0.225682 0.16179,0.372968 0.005,0.03268 0.007,0.03609 0.0109,0.03008 0.003,-0.0038 0.0204,-0.03066 0.0398,-0.05985 0.081,-0.122563 0.15007,-0.192424 0.28789,-0.2912 0.08,-0.05732 0.1198,-0.08298 0.42137,-0.271735 l 0.25501,-0.1596 v 0.272667 c 0,0.215259 -3e-5,0.273531 -0.003,0.276746 -0.003,0.0024 -0.13737,0.09322 -0.29985,0.202152 -0.46937,0.314672 -0.5225,0.353915 -0.57814,0.426911 -0.0552,0.0724 -0.0689,0.130566 -0.0719,0.305628 l -0.003,0.110558 h 0.47808 0.4781 v 0.229982 0.22997 h -1.14456 z m -0.17875,-1.036653 c -0.004,-0.07511 -0.0116,-0.141507 -0.019,-0.170215 -0.0228,-0.08667 -0.0901,-0.154516 -0.17902,-0.180131 -0.0332,-0.0096 -0.11732,-0.01355 -0.15412,-0.0073 -0.0893,0.01511 -0.15145,0.06 -0.18907,0.136544 -0.0361,0.07341 -0.0386,0.107151 -0.0387,0.51921 l -8e-5,0.278622 h 0.29189 0.29192 l -3e-5,-0.255629 c 0,-0.140587 -0.003,-0.285073 -0.004,-0.321078 z"
|
||||
id="path3980" /><path
|
||||
style="stroke:#000000;stroke-width:0.00357862;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 197.31463,69.464205 c -0.0276,0.349896 -0.15028,0.57472 -0.39108,0.716672 -0.0966,0.05693 -0.25265,0.10728 -0.37471,0.120896 l -0.022,0.0024 -0.003,-0.01204 c -0.004,-0.02209 -0.0429,-0.436258 -0.0413,-0.437798 0,-7.93e-4 0.0175,-0.0049 0.0372,-0.0092 0.0196,-0.0042 0.055,-0.0141 0.0786,-0.02193 0.18298,-0.0608 0.28408,-0.175368 0.31713,-0.359391 0.0101,-0.05627 0.0111,-0.179504 0.003,-0.238916 -0.0124,-0.0793 -0.0329,-0.13956 -0.0635,-0.186923 -0.042,-0.065 -0.10097,-0.113795 -0.16415,-0.135983 -0.0357,-0.01254 -0.10745,-0.01617 -0.14301,-0.0072 -0.0698,0.01759 -0.11543,0.0597 -0.15713,0.144986 -0.0292,0.05981 -0.0497,0.128021 -0.11494,0.38399 -0.0594,0.233122 -0.0915,0.327528 -0.1501,0.442497 -0.056,0.109762 -0.13237,0.196009 -0.23,0.259654 -0.10385,0.06769 -0.22175,0.09919 -0.35163,0.09395 -0.11192,-0.0045 -0.21569,-0.03873 -0.31009,-0.102254 -0.0694,-0.04674 -0.11832,-0.09587 -0.16587,-0.166751 -0.0969,-0.14446 -0.14496,-0.335544 -0.14491,-0.575985 5e-5,-0.268025 0.054,-0.471233 0.16486,-0.621223 0.0287,-0.03879 0.0896,-0.09974 0.1284,-0.12841 0.10475,-0.07745 0.22252,-0.120354 0.36336,-0.132395 0.0217,-0.0019 0.0397,-0.0029 0.0403,-0.0024 0.003,0.0016 0.0217,0.461698 0.0204,0.463121 0,7.94e-4 -0.0153,0.0043 -0.0327,0.0081 -0.0982,0.0214 -0.17716,0.06442 -0.22135,0.120573 -0.0342,0.04344 -0.0573,0.09982 -0.0703,0.171505 -0.0103,0.0573 -0.0114,0.186582 -0.003,0.247142 0.0148,0.09486 0.0476,0.172292 0.0942,0.222626 0.0494,0.05334 0.13436,0.06876 0.20273,0.03678 0.0311,-0.01455 0.0677,-0.04974 0.0896,-0.08607 0.0406,-0.06726 0.0793,-0.183383 0.13163,-0.394364 0.0631,-0.254667 0.11104,-0.399426 0.16819,-0.508135 0.0925,-0.175927 0.24591,-0.293883 0.43061,-0.331026 0.0669,-0.01347 0.11663,-0.01733 0.19307,-0.01508 0.0537,0.0016 0.0776,0.004 0.11134,0.01109 0.18626,0.03933 0.34679,0.149262 0.4423,0.302906 0.0672,0.108156 0.11023,0.245999 0.13057,0.418557 0.006,0.04614 0.0101,0.261104 0.006,0.305972 v 0 z"
|
||||
id="path3982" /></g><g
|
||||
id="SingleLeftTrigger1"><path
|
||||
style="stroke:#000000;stroke-width:0.00353806;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 197.34244,114.04543 c -0.0161,0.26704 -0.0954,0.46815 -0.24006,0.60956 -0.10769,0.10529 -0.25516,0.17938 -0.42786,0.21496 -0.0395,0.008 -0.11107,0.0198 -0.11213,0.0183 0,-5.3e-4 -0.01,-0.0955 -0.0206,-0.21125 -0.0111,-0.11578 -0.0209,-0.21648 -0.022,-0.22378 -0.003,-0.0117 0,-0.0133 0.005,-0.0133 0.0124,0 0.0677,-0.0138 0.10834,-0.0269 0.19362,-0.0629 0.29713,-0.1901 0.32044,-0.39394 0.005,-0.0468 0.003,-0.16702 -0.005,-0.21064 -0.0201,-0.1152 -0.0652,-0.20032 -0.13653,-0.2574 -0.0557,-0.0446 -0.10123,-0.0607 -0.17163,-0.0609 -0.0502,0 -0.0782,0.006 -0.11145,0.0255 -0.023,0.0133 -0.0546,0.0446 -0.0729,0.0722 -0.0438,0.0659 -0.0652,0.1313 -0.14298,0.43634 -0.0486,0.19057 -0.0776,0.28142 -0.12168,0.38029 -0.0597,0.1339 -0.13859,0.23102 -0.24667,0.30356 -0.0596,0.04 -0.13629,0.0717 -0.20921,0.0865 -0.0347,0.007 -0.0501,0.008 -0.1211,0.008 -0.0704,-8e-5 -0.086,-0.001 -0.11591,-0.008 -0.17476,-0.0391 -0.32139,-0.1482 -0.4073,-0.3032 -0.0386,-0.0696 -0.0708,-0.15982 -0.0889,-0.24915 -0.0398,-0.19606 -0.0318,-0.45578 0.0196,-0.63905 0.0238,-0.0846 0.0644,-0.17267 0.10869,-0.23529 0.0304,-0.0431 0.10604,-0.11863 0.14946,-0.14939 0.0695,-0.0492 0.16082,-0.0887 0.24681,-0.10675 0.0435,-0.009 0.099,-0.0163 0.12628,-0.0163 h 0.0161 l 3e-5,0.0204 c 2e-5,0.0112 0.004,0.1087 0.009,0.2167 0.005,0.108 0.009,0.20197 0.009,0.20883 v 0.0125 l -0.0309,0.006 c -0.0694,0.0141 -0.13877,0.045 -0.1809,0.0803 -0.082,0.0689 -0.11723,0.17063 -0.11718,0.33883 3e-5,0.11077 0.0148,0.18678 0.0511,0.26075 0.042,0.0858 0.0874,0.12084 0.16159,0.12472 0.0552,0.003 0.0935,-0.0103 0.132,-0.0455 0.0617,-0.0564 0.10247,-0.15984 0.17213,-0.43654 0.0542,-0.21541 0.0786,-0.29731 0.12026,-0.40333 0.0739,-0.18834 0.17608,-0.30894 0.32345,-0.38197 0.0882,-0.0437 0.16936,-0.0629 0.28657,-0.0678 0.13917,-0.006 0.26699,0.0274 0.38915,0.10122 0.13658,0.0825 0.23426,0.21023 0.29001,0.37916 0.0486,0.14727 0.0699,0.34326 0.0582,0.53545 v 0 z"
|
||||
id="path3978" /><path
|
||||
style="stroke:#000000;stroke-width:0.00357862;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 196.15959,112.42202 h -1.14872 v -0.23261 -0.2326 h 0.95549 0.95549 v -0.58153 -0.58153 h 0.19326 0.19322 v 0.81413 0.81414 z"
|
||||
id="path3984" /></g><path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 207.15522,94.598865 v 5.126312"
|
||||
id="path3988" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 206.14166,95.261155 v 4.070322"
|
||||
id="path3990" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3994-6)"
|
||||
d="m 966.57659,655.39154 v 38"
|
||||
id="path3992"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,-45.219666,-81.138444)" /><path
|
||||
id="path4136"
|
||||
d="m 228.83717,11.768909 h -1.50119 l -0.004,-0.958829 h -1.60551 c -0.42043,0 -0.55393,0.382418 -0.55393,0.959443 v 8.792554 c 0,0.41027 0.38793,0.732996 0.733,0.732996 h 1.08378 c 0.29528,0 0.31234,-0.319455 0.34277,-0.593693 v -0.583639 h 1.48016 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path4138"
|
||||
d="M 227.33503,20.10013 V 11.831901"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 235.47461,6.5371895 v 1.852083 h -4.96093 v -1.885156 c 0,-0.243708 0.21312,-0.353695 0.40513,-0.405156 1.38543,-0.371211 2.87678,-0.352399 4.192,0 0.20736,0.05556 0.3638,0.194759 0.3638,0.438229 z"
|
||||
id="path4158" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 240.25852,23.377177 c 0.20619,0.740701 1.62933,2.124948 3.05062,2.447317 0,0.421428 -0.24749,1.005601 -0.76004,1.005601 h -4.8877 c -1.02937,0 -0.77034,3.348197 0.89714,3.348197 h 1.68701 c 3.50494,1.440338 6.63515,1.469734 10.05602,0 h 1.77737 c 1.5485,0 1.86729,-3.317637 0.88895,-3.317637 h -4.79444 c -0.69527,0 -1.02076,-0.486278 -0.97996,-0.979964 1.24788,-0.516758 2.36212,-1.193906 2.99116,-2.45327 v -0.46773 h -9.93031 z"
|
||||
id="path4146" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 240.77544,23.022239 c 0,0.04675 0,1.14591 0,1.14591 0,0.478393 -0.29195,0.666512 -0.66652,0.666512 -2.01795,0.177932 -4.4696,0.167508 -6.5364,0 -0.34731,0 -0.68791,-0.3302 -0.68405,-0.684054 V 22.75329 h 7.88697 z"
|
||||
id="path4154" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 242.6067,26.81676 h 5.1263"
|
||||
id="path4148" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 231.55516,22.329004 v 1.601972 c 0,0.137981 0.1643,0.292312 0.29231,0.292312 h 5.13323 c 0.16113,0 0.35666,-0.160814 0.35666,-0.356632 v -1.54932 z"
|
||||
id="path4142" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 243.26897,25.803195 h 4.07033"
|
||||
id="path4150" /><path
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 257.33277,23.022239 c 0,0.04675 0,1.14591 0,1.14591 0,0.478393 -0.29194,0.666512 -0.66649,0.666512 -2.01797,0.177932 -4.4696,0.167508 -6.53642,0 -0.34732,0 -0.68792,-0.3302 -0.68403,-0.684054 V 22.75329 h 7.88694 z"
|
||||
id="path4144" /><path
|
||||
transform="matrix(0,0.26458333,0.26458333,0,-36.395078,-244.13604)"
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3994-6)"
|
||||
d="m 1036.7923,1045.6825 v 38"
|
||||
id="path4152" /><path
|
||||
id="path4160"
|
||||
d="m 249.05295,23.022239 c 0,0.04675 0,1.14591 0,1.14591 0,0.478393 -0.29194,0.666512 -0.66648,0.666512 -2.01798,0.177932 -4.46961,0.167508 -6.53643,0 -0.34732,0 -0.68792,-0.3302 -0.68403,-0.684054 V 22.75329 h 7.88694 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path4140"
|
||||
d="m 261.60682,23.187796 c 0.97078,0 2.84427,-1.929288 2.84427,-2.968281 v -6.81302 c -0.52062,-1.975089 -1.52,-4.1207005 -4.7625,-4.7624995 -0.56626,0 -0.85989,-0.533533 -0.85989,-0.859896 -3.06951,-4.0709055 -6.40247,-6.1659027 -9.82266,-7.6067706 h -7.24297 c -3.11539,1.3968147 -4.85232,3.4828691 -6.21771,5.7546866 v 1.256771 c 0,0.518822 -0.42971,1.058334 -0.99218,1.058334 h -6.01927 V 23.196078 Z"
|
||||
style="fill:#ff5f53;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /><path
|
||||
id="path4166"
|
||||
d="m 260.58066,11.099794 h -25.87985 c 0.0135,-5.3886205 2.66343,-10.9675025 7.75962,-10.9675025 l 7.23943,0.086701 c 7.0875,1.2930743 8.54411,6.3837 10.8808,10.8808015 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
id="path4168"
|
||||
d="m 262.70479,14.654479 h -28.22072 v 3.251237 c 0,1.098243 1.40179,2.731037 2.73105,2.731037 h 23.66899 c 1.445,0 2.51429,-1.396424 2.51429,-2.514288 0,-1.34669 0.11609,-3.467986 -0.69361,-3.467986 z"
|
||||
style="fill:#44484c;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:0.00490037;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 244.14165,17.660905 v -1.210392 l 0.55007,6.6e-5 c 0.30252,3.7e-5 0.59417,0.0024 0.64807,0.0049 0.23492,0.01161 0.38338,0.0444 0.48519,0.107175 0.15637,0.09641 0.26062,0.266599 0.28702,0.468542 0.008,0.06163 0.003,0.197848 -0.009,0.254818 -0.0416,0.191362 -0.15044,0.334386 -0.32104,0.421952 -0.0654,0.03355 -0.16529,0.06695 -0.24334,0.08133 -0.0324,0.006 -0.0588,0.01204 -0.0587,0.01349 8e-5,0.0016 0.0175,0.01312 0.0385,0.0259 0.0679,0.04108 0.1387,0.09714 0.19921,0.157761 0.0982,0.09839 0.16031,0.1876 0.42134,0.605501 0.0884,0.141499 0.16391,0.262231 0.16783,0.268296 l 0.007,0.01103 h -0.28882 -0.28884 l -0.19288,-0.287896 c -0.36298,-0.541718 -0.41074,-0.605218 -0.49914,-0.663437 -0.0681,-0.04484 -0.12798,-0.05709 -0.3008,-0.06151 l -0.11637,-0.003 v 0.50791 0.507905 h -0.24257 -0.24257 v -1.210392 z m 1.19824,-0.197742 c 0.10322,-0.01349 0.15547,-0.03687 0.20572,-0.09207 0.0507,-0.0557 0.0711,-0.115819 0.0711,-0.210029 0,-0.130434 -0.0482,-0.216286 -0.14822,-0.264017 -0.0777,-0.03708 -0.0652,-0.03607 -0.47538,-0.03847 l -0.36629,-0.0021 v 0.309613 0.309613 l 0.3271,-0.0024 c 0.25813,-0.0019 0.33952,-0.004 0.386,-0.01011 v 0 z"
|
||||
id="RightShoulder" /><g
|
||||
id="RightTrigger"><path
|
||||
style="fill-opacity:1;stroke-width:0.00490037;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 245.17564,6.1446855 v -1.208246 l 0.62601,0.00212 c 0.66757,0.00212 0.68236,0.00273 0.82087,0.027509 0.0828,0.014843 0.13729,0.031819 0.19719,0.061523 0.0639,0.03167 0.10575,0.06235 0.15301,0.112101 0.0781,0.08226 0.1333,0.187278 0.16198,0.308377 0.0119,0.0507 0.013,0.06345 0.0132,0.162814 8e-5,0.08425 -0.003,0.117107 -0.008,0.150355 -0.0561,0.275579 -0.24551,0.44872 -0.56002,0.512053 -0.0592,0.01193 -0.066,0.01426 -0.0579,0.02011 0.005,0.0036 0.0317,0.02135 0.0591,0.03937 0.14401,0.09456 0.23318,0.189458 0.36915,0.392957 0.0495,0.07413 0.38581,0.60892 0.3906,0.621162 0.003,0.0049 -0.0559,0.0059 -0.2858,0.0049 l -0.28824,-0.0013 -0.21582,-0.321297 c -0.30739,-0.457573 -0.34192,-0.505973 -0.40719,-0.570571 -0.09,-0.08909 -0.15473,-0.109487 -0.36116,-0.113864 l -0.12173,-0.0027 v 0.505378 0.505375 h -0.24257 -0.24257 v -1.208244 z m 1.17118,-0.193157 c 0.0797,-0.0088 0.11668,-0.01786 0.15986,-0.03914 0.0694,-0.03421 0.11798,-0.09792 0.13692,-0.179525 0.0103,-0.04405 0.0101,-0.136223 -2.6e-4,-0.175921 -0.0313,-0.12047 -0.12489,-0.194789 -0.26226,-0.208325 -0.0257,-0.0026 -0.19817,-0.0046 -0.38346,-0.0047 l -0.33689,-1.7e-4 v 0.309306 0.309304 l 0.31729,-0.0026 c 0.18902,-0.0016 0.33811,-0.0049 0.36875,-0.0083 v 0 z"
|
||||
id="path4234" /><path
|
||||
style="fill-opacity:1;stroke-width:0.00490037;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 242.89694,7.1346265 v -0.218302 l 0.63461,-0.783873 c 0.34904,-0.431131 0.6346,-0.784953 0.6346,-0.786276 0,-0.0013 -0.2536,-0.0024 -0.56354,-0.0024 h -0.56353 V 5.1404305 4.9370639 h 0.8845 0.88453 v 0.1874836 0.187481 l -0.66442,0.818322 -0.6644,0.818319 0.6889,0.0013 0.68892,0.0013 v 0.200898 0.200898 h -0.98007 -0.9801 v -0.2183 z"
|
||||
id="path4236" /></g></g></svg>
|
After Width: | Height: | Size: 36 KiB |
291
src/Ryujinx/Assets/Icons/Controller_ProCon_Settings.svg
Normal file
291
src/Ryujinx/Assets/Icons/Controller_ProCon_Settings.svg
Normal file
@ -0,0 +1,291 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 27.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 1050 1050.5"
|
||||
style="enable-background:new 0 0 1050 1050.5;"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs7" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style1">
|
||||
.st0{fill:#20221F;}
|
||||
.st1{fill:#3B3B3B;}
|
||||
.st2{fill:#121212;}
|
||||
.st3{fill:#444542;}
|
||||
.st4{fill:#FFFFFF;}
|
||||
.st5{fill:#444542;stroke:#FFFFFF;stroke-width:2;stroke-miterlimit:10;}
|
||||
.st6{fill:#454644;}
|
||||
.st7{fill:#454644;stroke:#FFFFFF;stroke-width:2;stroke-miterlimit:10;}
|
||||
.st8{fill:#3B3C3A;}
|
||||
.st9{font-family:'Helvetica-Bold';}
|
||||
.st10{font-size:40px;}
|
||||
.st11{fill:#0D0D0A;}
|
||||
</style>
|
||||
<g
|
||||
id="Front">
|
||||
<path
|
||||
id="Right_Grip_00000028282943321285403220000008369785803052272051_"
|
||||
class="st0"
|
||||
d="M766,850.5 c34,28.2,27.6,35.9,68.5,108.5c36.7,74.7,64.4,104.4,125.1,84.1v0c95.3-57.9,59.3-145.3,43.6-275.2c-10-60.6-35.6-190.3-35.6-190.3 L766,850.5z" />
|
||||
<path
|
||||
id="Left_Grip"
|
||||
class="st0"
|
||||
d="M82.3,577.6c0,0-25.6,129.7-35.6,190.3C31,897.8-5,985.1,90.3,1043v0 c60.8,20.3,88.4-9.4,125.1-84.1c40.9-72.7,34.5-80.3,68.5-108.5L82.3,577.6z" />
|
||||
<path
|
||||
id="Right_Bumper_00000006710349871522532470000011078040965381267594_"
|
||||
class="st1"
|
||||
d="M676.3,378.4 c10.1-4.3,39.7-22.5,58.7-19.7c59.5,0.9,166.7,17.7,172.6,81.2" />
|
||||
<path
|
||||
id="Left_Bumper_00000024680414077879639570000011759596763560342154_"
|
||||
class="st1"
|
||||
d="M142.4,439.9 c5.9-63.4,113-80.2,172.6-81.2c19-2.8,48.6,15.4,58.7,19.7" />
|
||||
<path
|
||||
id="Background_00000141418846164053065470000016150094984198570163_"
|
||||
class="st2"
|
||||
d="M766,850.5 c35.5-30.8,68.5-74.7,96-113.5c26.9-36.3,94.7-136.7,105.6-159.3c0-2.4-6.3-30.1-12.8-56.2C919.1,361.9,702.2,378.1,525,378.1 c-177.4,0-394.1-16.2-429.9,143.3c-6.5,26-12.8,53.8-12.8,56.2c10.9,22.6,78.8,123,105.6,159.3c27.5,38.8,60.5,82.8,96,113.5" />
|
||||
<g
|
||||
id="Directional_Pad">
|
||||
<path
|
||||
id="Background_00000032628022449190479560000015279211462520783249_"
|
||||
class="st3"
|
||||
d="M466.2,683.5h-40c-2.8,0-5-2.2-5-5v-40 c0-2.8-2.2-5-5-5h-30c-2.8,0-5,2.2-5,5v40c0,2.8-2.2,5-5,5h-40c-2.8,0-5,2.2-5,5v30c0,2.8,2.2,5,5,5h40c2.8,0,5,2.2,5,5v40 c0,2.8,2.2,5,5,5h30c2.8,0,5-2.2,5-5v-40c0-2.8,2.2-5,5-5h40c2.8,0,5-2.2,5-5v-30C471.2,685.8,469,683.5,466.2,683.5z" />
|
||||
<g
|
||||
id="Arrows">
|
||||
<g
|
||||
id="g1">
|
||||
<polygon
|
||||
class="st4"
|
||||
points="393.7,746 408.7,746 401.2,761 "
|
||||
id="DpadDown" />
|
||||
</g>
|
||||
<g
|
||||
id="g2">
|
||||
<polygon
|
||||
class="st4"
|
||||
points="358.7,696 358.7,711 343.7,703.5 "
|
||||
id="DpadLeft" />
|
||||
</g>
|
||||
<g
|
||||
id="g3">
|
||||
<polygon
|
||||
class="st4"
|
||||
points="408.7,661 393.7,661 401.2,646 "
|
||||
id="DpadUp" />
|
||||
</g>
|
||||
<g
|
||||
id="g4">
|
||||
<polygon
|
||||
class="st4"
|
||||
points="443.7,711 443.7,696 458.7,703.5 "
|
||||
id="DpadRight" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="R_Thumbstick_00000152226188525111835500000011838297421350334865_">
|
||||
<circle
|
||||
id="Background_00000035532849542660068350000006517224202948159422_"
|
||||
class="st0"
|
||||
cx="650.59998"
|
||||
cy="703.5"
|
||||
r="55"
|
||||
style="display:inline" />
|
||||
<circle
|
||||
id="RightStick"
|
||||
class="st5"
|
||||
cx="650.6"
|
||||
cy="703.5"
|
||||
r="45" />
|
||||
</g>
|
||||
<g
|
||||
id="L_Thumbstick_00000047032468231999382210000005512347386782594484_">
|
||||
<circle
|
||||
id="Background_00000182502673988292164000000007125719133096369561_"
|
||||
class="st0"
|
||||
cx="240.2"
|
||||
cy="564.8"
|
||||
r="55" />
|
||||
<circle
|
||||
id="LeftStick"
|
||||
class="st5"
|
||||
cx="240.2"
|
||||
cy="564.8"
|
||||
r="45" />
|
||||
</g>
|
||||
<g
|
||||
id="Minus_Button">
|
||||
<circle
|
||||
id="_Background_00000120554951013892796430000015877571645746699662_"
|
||||
class="st6"
|
||||
cx="401"
|
||||
cy="489.3"
|
||||
r="22.5" />
|
||||
<polyline
|
||||
id="Back"
|
||||
class="st4"
|
||||
points="386.2,491.8 386.2,486.8 416.2,486.8 416.2,491.8 " />
|
||||
</g>
|
||||
<g
|
||||
id="Plus_Button">
|
||||
<circle
|
||||
id="_Background"
|
||||
class="st6"
|
||||
cx="650.4"
|
||||
cy="489.6"
|
||||
r="22.5" />
|
||||
<polygon
|
||||
id="Start"
|
||||
class="st4"
|
||||
points="665.6,487.1 653.1,487.1 653.1,474.4 648.1,474.4 648.1,487.1 635.6,487.1 635.6,492.1 648.1,492.1 648.1,504.4 653.1,504.4 653.1,492.1 665.6,492.1 " />
|
||||
</g>
|
||||
<g
|
||||
id="Home_Button_00000029758737660217614780000001403165237001195407_">
|
||||
<circle
|
||||
id="_Background_00000132788487854287834010000009548421243227981499_"
|
||||
class="st6"
|
||||
cx="605.4"
|
||||
cy="564.8"
|
||||
r="22.5" />
|
||||
<path
|
||||
id="Home"
|
||||
class="st4"
|
||||
d="M605.4,549.8l-15,15h5v15h20v-15h5L605.4,549.8z M610.4,574.8h-10v-10h10V574.8z" />
|
||||
</g>
|
||||
<g
|
||||
id="Capture_Button_00000105394663133565750060000017455731898661404072_">
|
||||
<path
|
||||
class="st6"
|
||||
d="M468.6,586.5h-30c-2.8,0-5-2.2-5-5v-29.5c0-2.8,2.2-5,5-5h30c2.8,0,5,2.2,5,5v29.5 C473.6,584.2,471.4,586.5,468.6,586.5z"
|
||||
id="path4" />
|
||||
<circle
|
||||
class="st7"
|
||||
cx="453.6"
|
||||
cy="566.7"
|
||||
r="15"
|
||||
id="circle4" />
|
||||
</g>
|
||||
<g
|
||||
id="Buttons_00000023239109225132251950000005218343074279628213_">
|
||||
<g
|
||||
id="A_Button">
|
||||
<circle
|
||||
id="Background_00000006699118933065716380000004636085088820886913_"
|
||||
class="st8"
|
||||
cx="863.9"
|
||||
cy="564.8"
|
||||
r="35" />
|
||||
<text
|
||||
transform="matrix(1 0 0 1 849.4224 578.6607)"
|
||||
class="st4 st9 st10"
|
||||
id="A">A</text>
|
||||
</g>
|
||||
<g
|
||||
id="X_Button">
|
||||
<circle
|
||||
id="Background_00000083074713085756701790000016893839312974798515_"
|
||||
class="st8"
|
||||
cx="793.9"
|
||||
cy="494.8"
|
||||
r="35" />
|
||||
<text
|
||||
transform="matrix(1 0 0 1 780.5266 508.6604)"
|
||||
class="st4 st9 st10"
|
||||
id="X">X</text>
|
||||
</g>
|
||||
<g
|
||||
id="Y_Button_00000100344340438574137780000014238704828967683973_">
|
||||
<circle
|
||||
id="Background_00000137100455694543496620000011124722786613194377_"
|
||||
class="st8"
|
||||
cx="723.9"
|
||||
cy="564.8"
|
||||
r="35" />
|
||||
<text
|
||||
transform="matrix(1 0 0 1 710.5263 578.661)"
|
||||
class="st4 st9 st10"
|
||||
id="Y">Y</text>
|
||||
</g>
|
||||
<g
|
||||
id="B_Button_00000041994261956088037220000013468634544777304733_">
|
||||
<circle
|
||||
id="Background_00000096038108578846046800000001873940014252420514_"
|
||||
class="st8"
|
||||
cx="793.9"
|
||||
cy="634.8"
|
||||
r="35" />
|
||||
<text
|
||||
transform="matrix(1 0 0 1 780.9706 648.6605)"
|
||||
class="st4 st9 st10"
|
||||
id="B">B</text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="Top_Down">
|
||||
<path
|
||||
id="Left_Grip_00000026131988385328425370000016677941743356253314_"
|
||||
class="st0"
|
||||
d="M219.2,78.5 c-12.5-17.6-25.9-42.3-45.6-58.6C153.5,3.3,112.1-4.7,87.1,5.8c-13.9,5.8-33.4,33.1-42.7,52.8C33.9,80.9,30.4,109.9,32,141.4 c1.2,25.1,5.3,51.7,14.2,78.6c0,0,14.3,53.8,42.8,80.8c11.2,10.6,35,26.6,35,26.6l116-217.5C240,109.9,224.6,86.2,219.2,78.5z" />
|
||||
<path
|
||||
id="Right_Grip_00000016782759094708820330000002450847065936193693_"
|
||||
class="st0"
|
||||
d="M828.6,78.5 c12.5-17.6,25.9-42.3,45.6-58.6c20.1-16.6,61.4-24.5,86.5-14.1c13.9,5.8,33.4,33.1,42.7,52.8c10.5,22.3,13.9,51.3,12.4,82.8 c-1.2,25.1-5.3,51.7-14.2,78.6c0,0-14.3,53.8-42.8,80.8c-11.2,10.6-35,26.6-35,26.6l-116-217.5C807.8,109.9,823.2,86.2,828.6,78.5z " />
|
||||
<path
|
||||
id="Background_00000169534857628063347190000007586592143875928969_"
|
||||
class="st11"
|
||||
d="M866,122.2 c66.3,18.7,85.1,128.8,69,186c-2.5,54.2-148.9,15.3-265.1,31.2c-41.1,1.7-91.8,2.4-145.9,2.3c-54.1,0-104.8-0.6-145.9-2.3 c-116.2-15.9-262.6,23.1-265.1-31.2c-16.1-57.1,2.6-167.3,69-186l60.5-18.8l38.9-1.9c40.2,0.1,142.8,0,242.7,0 c99.9,0,202.4,0.1,242.7,0l38.9,1.9L866,122.2z" />
|
||||
<g
|
||||
id="ZL_Trigger_00000005254517714433203260000014117442438696169895_">
|
||||
<path
|
||||
id="Background_00000111870097528015387240000017384507710402295183_"
|
||||
class="st1"
|
||||
d="M145.9,239.2 c15.2-97.4,38.1-147.2,141.7-137c8.2,16.4,43.3,83,50.6,105.7C280.6,227.2,204.7,225.6,145.9,239.2z" />
|
||||
<text
|
||||
id="LeftTrigger"
|
||||
transform="matrix(1.0139 0 0 1 218.3906 179.3992)"
|
||||
class="st4 st9 st10">ZL</text>
|
||||
</g>
|
||||
<g
|
||||
id="ZR_Trigger">
|
||||
<path
|
||||
id="Background_00000133526766189752063450000016781240006605114763_"
|
||||
class="st1"
|
||||
d="M716.2,207.9 c7.4-22.7,42.5-89.3,50.6-105.7c103.7-10.2,126.5,39.6,141.7,137C849.8,225.6,773.8,227.2,716.2,207.9z" />
|
||||
|
||||
<text
|
||||
id="RightTrigger"
|
||||
transform="matrix(1.0139 0 0 1 784.2356 179.3992)"
|
||||
class="st4 st9 st10">ZR</text>
|
||||
</g>
|
||||
<g
|
||||
id="R_Trigger_00000085939413106284991650000014018840000393673094_">
|
||||
<path
|
||||
id="Background"
|
||||
class="st1"
|
||||
d="M664,318.5c7-10.1,27.8-78.4,45.4-78.7C1040.8,243.7,897.1,334,664,318.5z" />
|
||||
<text
|
||||
id="RightShoulder"
|
||||
transform="matrix(1 0 0 1 769.6461 292.8947)"
|
||||
class="st4 st9 st10">R</text>
|
||||
</g>
|
||||
<g
|
||||
id="L_Trigger">
|
||||
<path
|
||||
id="Background_00000043427985111927735300000011910735497762731703_"
|
||||
class="st1"
|
||||
d="M340.6,238.6 c17.6,0.3,38.4,68.6,45.4,78.7C152.9,332.8,9.2,242.6,340.6,238.6z" />
|
||||
|
||||
<text
|
||||
id="LeftShoulder"
|
||||
transform="matrix(1 0 0 1 253.7327 291.7279)"
|
||||
class="st4 st9 st10">L</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.5 KiB |
@ -4298,12 +4298,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabSystemAudioBackendSDL2",
|
||||
"ID": "SettingsTabSystemAudioBackendSDL3",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "SDL2",
|
||||
"en_US": "SDL3",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
|
@ -1,6 +1,6 @@
|
||||
using DiscordRPC;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
using Ryujinx.Audio.Backends.SDL3;
|
||||
using Ryujinx.Ava;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
using Ryujinx.Common.Configuration;
|
||||
@ -157,7 +157,7 @@ namespace Ryujinx.Headless
|
||||
config = new StandardControllerInputConfig
|
||||
{
|
||||
Version = InputConfig.CurrentVersion,
|
||||
Backend = InputBackendType.GamepadSDL2,
|
||||
Backend = InputBackendType.GamepadSDL3,
|
||||
Id = null,
|
||||
ControllerType = ControllerType.JoyconPair,
|
||||
DeadzoneLeft = 0.1f,
|
||||
@ -335,7 +335,7 @@ namespace Ryujinx.Headless
|
||||
_accountManager,
|
||||
_userChannelPersistence,
|
||||
renderer,
|
||||
new SDL2HardwareDeviceDriver(),
|
||||
new SDL3HardwareDeviceDriver(),
|
||||
options.DramSize,
|
||||
window,
|
||||
options.SystemLanguage,
|
||||
|
@ -21,8 +21,8 @@ using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL2;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.Input.SDL3;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@ -59,7 +59,7 @@ namespace Ryujinx.Headless
|
||||
AutoResetEvent invoked = new(false);
|
||||
|
||||
// MacOS must perform SDL polls from the main thread.
|
||||
SDL2Driver.MainThreadDispatcher = action =>
|
||||
SDL3Driver.MainThreadDispatcher = action =>
|
||||
{
|
||||
invoked.Reset();
|
||||
|
||||
@ -180,7 +180,7 @@ namespace Ryujinx.Headless
|
||||
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, option.UserProfile);
|
||||
_userChannelPersistence = new UserChannelPersistence();
|
||||
|
||||
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
|
||||
_inputManager = new InputManager(new SDL3KeyboardDriver(), new SDL3GamepadDriver());
|
||||
|
||||
GraphicsConfig.EnableShaderCache = !option.DisableShaderCache;
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using SharpMetal.QuartzCore;
|
||||
using System.Runtime.Versioning;
|
||||
using static SDL2.SDL;
|
||||
using static SDL3.SDL;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
{
|
||||
@ -35,7 +35,7 @@ namespace Ryujinx.Headless
|
||||
_caMetalLayer = new CAMetalLayer(SDL_Metal_GetLayer(SDL_Metal_CreateView(WindowHandle)));
|
||||
}
|
||||
|
||||
SDL2Driver.MainThreadDispatcher?.Invoke(CreateLayer);
|
||||
SDL3Driver.MainThreadDispatcher?.Invoke(CreateLayer);
|
||||
}
|
||||
|
||||
protected override void InitializeRenderer() { }
|
||||
|
@ -5,15 +5,15 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.OpenGL;
|
||||
using Ryujinx.Input.HLE;
|
||||
using System;
|
||||
using static SDL2.SDL;
|
||||
using static SDL3.SDL;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
{
|
||||
class OpenGLWindow : WindowBase
|
||||
{
|
||||
private static void CheckResult(int result)
|
||||
private static void CheckResult(bool result)
|
||||
{
|
||||
if (result < 0)
|
||||
if (!result)
|
||||
{
|
||||
throw new InvalidOperationException($"SDL_GL function returned an error: {SDL_GetError()}");
|
||||
}
|
||||
@ -21,21 +21,21 @@ namespace Ryujinx.Headless
|
||||
|
||||
private static void SetupOpenGLAttributes(bool sharedContext, GraphicsDebugLevel debugLevel)
|
||||
{
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 3));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_COMPATIBILITY));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_FLAGS, debugLevel != GraphicsDebugLevel.None ? (int)SDL_GLcontext.SDL_GL_CONTEXT_DEBUG_FLAG : 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, sharedContext ? 1 : 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_MAJOR_VERSION, 3));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_MINOR_VERSION, 3));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_PROFILE_MASK, (int)SDL_GLProfile.SDL_GL_CONTEXT_PROFILE_COMPATIBILITY));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_FLAGS, debugLevel != GraphicsDebugLevel.None ? (int)SDL_GLcontext.SDL_GL_CONTEXT_DEBUG_FLAG : 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, sharedContext ? 1 : 0));
|
||||
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_ACCELERATED_VISUAL, 1));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_RED_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_GREEN_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_BLUE_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_ALPHA_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_DEPTH_SIZE, 16));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_STENCIL_SIZE, 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_DOUBLEBUFFER, 1));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_STEREO, 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_ACCELERATED_VISUAL, 1));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_RED_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_GREEN_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_BLUE_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_ALPHA_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_DEPTH_SIZE, 16));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_STENCIL_SIZE, 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_DOUBLEBUFFER, 1));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_STEREO, 0));
|
||||
}
|
||||
|
||||
private class OpenToolkitBindingsContext : IBindingsContext
|
||||
@ -46,35 +46,35 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
}
|
||||
|
||||
private class SDL2OpenGLContext : IOpenGLContext
|
||||
private class SDL3OpenGLContext : IOpenGLContext
|
||||
{
|
||||
private readonly nint _context;
|
||||
private readonly nint _window;
|
||||
private readonly bool _shouldDisposeWindow;
|
||||
|
||||
public SDL2OpenGLContext(nint context, nint window, bool shouldDisposeWindow = true)
|
||||
public SDL3OpenGLContext(nint context, nint window, bool shouldDisposeWindow = true)
|
||||
{
|
||||
_context = context;
|
||||
_window = window;
|
||||
_shouldDisposeWindow = shouldDisposeWindow;
|
||||
}
|
||||
|
||||
public static SDL2OpenGLContext CreateBackgroundContext(SDL2OpenGLContext sharedContext)
|
||||
public static SDL3OpenGLContext CreateBackgroundContext(SDL3OpenGLContext sharedContext)
|
||||
{
|
||||
sharedContext.MakeCurrent();
|
||||
|
||||
// Ensure we share our contexts.
|
||||
SetupOpenGLAttributes(true, GraphicsDebugLevel.None);
|
||||
nint windowHandle = SDL_CreateWindow("Ryujinx background context window", 0, 0, 1, 1, SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_HIDDEN);
|
||||
nint windowHandle = SDL_CreateWindow("Ryujinx background context window", 1, 1, SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_HIDDEN);
|
||||
nint context = SDL_GL_CreateContext(windowHandle);
|
||||
|
||||
GL.LoadBindings(new OpenToolkitBindingsContext());
|
||||
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0));
|
||||
|
||||
CheckResult(SDL_GL_MakeCurrent(windowHandle, nint.Zero));
|
||||
|
||||
return new SDL2OpenGLContext(context, windowHandle);
|
||||
return new SDL3OpenGLContext(context, windowHandle);
|
||||
}
|
||||
|
||||
public void MakeCurrent()
|
||||
@ -84,9 +84,7 @@ namespace Ryujinx.Headless
|
||||
return;
|
||||
}
|
||||
|
||||
int res = SDL_GL_MakeCurrent(_window, _context);
|
||||
|
||||
if (res != 0)
|
||||
if (!SDL_GL_MakeCurrent(_window, _context))
|
||||
{
|
||||
string errorMessage = $"SDL_GL_CreateContext failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
@ -100,7 +98,7 @@ namespace Ryujinx.Headless
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
SDL_GL_DeleteContext(_context);
|
||||
SDL_GL_DestroyContext(_context);
|
||||
|
||||
if (_shouldDisposeWindow)
|
||||
{
|
||||
@ -108,8 +106,8 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SDL2OpenGLContext _openGLContext;
|
||||
|
||||
private SDL3OpenGLContext _openGLContext;
|
||||
|
||||
public OpenGLWindow(
|
||||
InputManager inputManager,
|
||||
@ -141,10 +139,10 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
|
||||
// NOTE: The window handle needs to be disposed by the thread that created it and is handled separately.
|
||||
_openGLContext = new SDL2OpenGLContext(context, WindowHandle, false);
|
||||
_openGLContext = new SDL3OpenGLContext(context, WindowHandle, false);
|
||||
|
||||
// First take exclusivity on the OpenGL context.
|
||||
((OpenGLRenderer)Renderer).InitializeBackgroundContext(SDL2OpenGLContext.CreateBackgroundContext(_openGLContext));
|
||||
((OpenGLRenderer)Renderer).InitializeBackgroundContext(SDL3OpenGLContext.CreateBackgroundContext(_openGLContext));
|
||||
|
||||
_openGLContext.MakeCurrent();
|
||||
|
||||
@ -160,7 +158,7 @@ namespace Ryujinx.Headless
|
||||
else if (IsFullscreen)
|
||||
{
|
||||
// NOTE: grabbing the main display's dimensions directly as OpenGL doesn't scale along like the VulkanWindow.
|
||||
if (SDL_GetDisplayBounds(DisplayId, out SDL_Rect displayBounds) < 0)
|
||||
if (!SDL_GetDisplayBounds((uint)DisplayId, out SDL_Rect displayBounds))
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Could not retrieve display bounds: {SDL_GetError()}");
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using static SDL2.SDL;
|
||||
using static SDL3.SDL;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
{
|
||||
@ -50,7 +50,7 @@ namespace Ryujinx.Headless
|
||||
|
||||
void CreateSurface()
|
||||
{
|
||||
if (SDL_Vulkan_CreateSurface(WindowHandle, instance, out surfaceHandle) == SDL_bool.SDL_FALSE)
|
||||
if (!SDL_Vulkan_CreateSurface(WindowHandle, instance, IntPtr.Zero, out surfaceHandle))
|
||||
{
|
||||
string errorMessage = $"SDL_Vulkan_CreateSurface failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
@ -60,9 +60,9 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL2Driver.MainThreadDispatcher != null)
|
||||
if (SDL3Driver.MainThreadDispatcher != null)
|
||||
{
|
||||
SDL2Driver.MainThreadDispatcher(CreateSurface);
|
||||
SDL3Driver.MainThreadDispatcher(CreateSurface);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -74,23 +74,19 @@ namespace Ryujinx.Headless
|
||||
|
||||
public unsafe string[] GetRequiredInstanceExtensions()
|
||||
{
|
||||
if (SDL_Vulkan_GetInstanceExtensions(WindowHandle, out uint extensionsCount, nint.Zero) == SDL_bool.SDL_TRUE)
|
||||
nint rawExtensions = SDL_Vulkan_GetInstanceExtensions(out uint count);
|
||||
IntPtr[] extensionPointers = new IntPtr[count];
|
||||
|
||||
Marshal.Copy(rawExtensions, extensionPointers, 0, (int)count);
|
||||
if (rawExtensions != nint.Zero)
|
||||
{
|
||||
nint[] rawExtensions = new nint[(int)extensionsCount];
|
||||
string[] extensions = new string[(int)extensionsCount];
|
||||
|
||||
fixed (nint* rawExtensionsPtr = rawExtensions)
|
||||
string[] extensions = new string[(int)count];
|
||||
for (int i = 0; i < extensions.Length; i++)
|
||||
{
|
||||
if (SDL_Vulkan_GetInstanceExtensions(WindowHandle, out extensionsCount, (nint)rawExtensionsPtr) == SDL_bool.SDL_TRUE)
|
||||
{
|
||||
for (int i = 0; i < extensions.Length; i++)
|
||||
{
|
||||
extensions[i] = Marshal.PtrToStringUTF8(rawExtensions[i]);
|
||||
}
|
||||
|
||||
return extensions;
|
||||
}
|
||||
extensions[i] = Marshal.PtrToStringUTF8(extensionPointers[i]);
|
||||
}
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
string errorMessage = $"SDL_Vulkan_GetInstanceExtensions failed with error \"{SDL_GetError()}\"";
|
||||
|
@ -1,7 +1,7 @@
|
||||
using Humanizer;
|
||||
using LibHac.Ns;
|
||||
using LibHac.Util;
|
||||
using Ryujinx.Ava;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
@ -16,8 +16,8 @@ using Ryujinx.HLE.Loaders.Processes;
|
||||
using Ryujinx.HLE.UI;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL2;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.Input.SDL3;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
@ -25,7 +25,7 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using static SDL2.SDL;
|
||||
using static SDL3.SDL;
|
||||
using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing;
|
||||
using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter;
|
||||
using Switch = Ryujinx.HLE.Switch;
|
||||
@ -38,7 +38,7 @@ namespace Ryujinx.Headless
|
||||
protected const int DefaultWidth = 1280;
|
||||
protected const int DefaultHeight = 720;
|
||||
private const int TargetFps = 60;
|
||||
private SDL_WindowFlags DefaultFlags = SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI | SDL_WindowFlags.SDL_WINDOW_RESIZABLE | SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS | SDL_WindowFlags.SDL_WINDOW_SHOWN;
|
||||
private SDL_WindowFlags DefaultFlags = SDL_WindowFlags.SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WindowFlags.SDL_WINDOW_RESIZABLE | SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS;
|
||||
private SDL_WindowFlags FullscreenFlag = 0;
|
||||
|
||||
private static readonly ConcurrentQueue<Action> _mainThreadActions = new();
|
||||
@ -71,7 +71,7 @@ namespace Ryujinx.Headless
|
||||
public ScalingFilter ScalingFilter { get; set; }
|
||||
public int ScalingFilterLevel { get; set; }
|
||||
|
||||
protected SDL2MouseDriver MouseDriver;
|
||||
protected SDL3MouseDriver MouseDriver;
|
||||
private readonly InputManager _inputManager;
|
||||
private readonly IKeyboard _keyboardInterface;
|
||||
protected readonly GraphicsDebugLevel GlLogLevel;
|
||||
@ -100,7 +100,7 @@ namespace Ryujinx.Headless
|
||||
HideCursorMode hideCursorMode,
|
||||
bool ignoreControllerApplet)
|
||||
{
|
||||
MouseDriver = new SDL2MouseDriver(hideCursorMode);
|
||||
MouseDriver = new SDL3MouseDriver(hideCursorMode);
|
||||
_inputManager = inputManager;
|
||||
_inputManager.SetMouseDriver(MouseDriver);
|
||||
NpadManager = _inputManager.CreateNpadManager();
|
||||
@ -117,7 +117,7 @@ namespace Ryujinx.Headless
|
||||
_ignoreControllerApplet = ignoreControllerApplet;
|
||||
HostUITheme = new HeadlessHostUiTheme();
|
||||
|
||||
SDL2Driver.Instance.Initialize();
|
||||
SDL3Driver.Instance.Initialize();
|
||||
}
|
||||
|
||||
public void Initialize(Switch device, List<InputConfig> inputConfigs, bool enableKeyboard, bool enableMouse)
|
||||
@ -156,11 +156,11 @@ namespace Ryujinx.Headless
|
||||
{
|
||||
fixed (byte* iconPtr = iconBytes)
|
||||
{
|
||||
nint rwOpsStruct = SDL_RWFromConstMem((nint)iconPtr, iconBytes.Length);
|
||||
nint iconHandle = SDL_LoadBMP_RW(rwOpsStruct, 1);
|
||||
nint rwOpsStruct = SDL_IOFromConstMem((nint)iconPtr, (nuint)iconBytes.Length);
|
||||
SDL_Surface* iconHandle = SDL_LoadBMP_IO(rwOpsStruct, true);
|
||||
|
||||
SDL_SetWindowIcon(WindowHandle, iconHandle);
|
||||
SDL_FreeSurface(iconHandle);
|
||||
SDL_SetWindowIcon(WindowHandle, (nint)iconHandle);
|
||||
SDL_DestroySurface((nint)iconHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -168,7 +168,7 @@ namespace Ryujinx.Headless
|
||||
private void InitializeWindow()
|
||||
{
|
||||
ProcessResult activeProcess = Device.Processes.ActiveApplication;
|
||||
ApplicationControlProperty nacp = activeProcess.ApplicationControlProperties;
|
||||
ApplicationControlProperty nacp = activeProcess.ApplicationControlProperties;
|
||||
int desiredLanguage = (int)Device.System.State.DesiredTitleLanguage;
|
||||
|
||||
string titleNameSection = string.IsNullOrWhiteSpace(nacp.Title[desiredLanguage].NameString.ToString()) ? string.Empty : $" - {nacp.Title[desiredLanguage].NameString.ToString()}";
|
||||
@ -184,16 +184,16 @@ namespace Ryujinx.Headless
|
||||
Width = ExclusiveFullscreenWidth;
|
||||
Height = ExclusiveFullscreenHeight;
|
||||
|
||||
DefaultFlags = SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
DefaultFlags = SDL_WindowFlags.SDL_WINDOW_HIGH_PIXEL_DENSITY;
|
||||
FullscreenFlag = SDL_WindowFlags.SDL_WINDOW_FULLSCREEN;
|
||||
}
|
||||
else if (IsFullscreen)
|
||||
{
|
||||
DefaultFlags = SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
FullscreenFlag = SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
DefaultFlags = SDL_WindowFlags.SDL_WINDOW_HIGH_PIXEL_DENSITY;
|
||||
FullscreenFlag = SDL_WindowFlags.SDL_WINDOW_FULLSCREEN;
|
||||
}
|
||||
|
||||
WindowHandle = SDL_CreateWindow($"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}", SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId), SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId), Width, Height, DefaultFlags | FullscreenFlag | WindowFlags);
|
||||
WindowHandle = SDL_CreateWindow($"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}", Width, Height, DefaultFlags | FullscreenFlag | WindowFlags);
|
||||
|
||||
if (WindowHandle == nint.Zero)
|
||||
{
|
||||
@ -207,16 +207,18 @@ namespace Ryujinx.Headless
|
||||
SetWindowIcon();
|
||||
|
||||
_windowId = SDL_GetWindowID(WindowHandle);
|
||||
SDL2Driver.Instance.RegisterWindow(_windowId, HandleWindowEvent);
|
||||
SDL3Driver.Instance.RegisterWindow(_windowId, HandleWindowEvent);
|
||||
}
|
||||
|
||||
private void HandleWindowEvent(SDL_Event evnt)
|
||||
{
|
||||
if (evnt.type == SDL_EventType.SDL_WINDOWEVENT)
|
||||
if (evnt.type >= (uint)SDL_EventType.SDL_EVENT_WINDOW_FIRST &&
|
||||
evnt.type <= (uint)SDL_EventType.SDL_EVENT_WINDOW_LAST)
|
||||
{
|
||||
switch (evnt.window.windowEvent)
|
||||
switch (evnt.window.type)
|
||||
{
|
||||
case SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
case SDL_EventType.SDL_EVENT_WINDOW_RESIZED:
|
||||
case SDL_EventType.SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
|
||||
// Unlike on Windows, this event fires on macOS when triggering fullscreen mode.
|
||||
// And promptly crashes the process because `Renderer?.window.SetSize` is undefined.
|
||||
// As we don't need this to fire in either case we can test for fullscreen.
|
||||
@ -229,7 +231,7 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE:
|
||||
case SDL_EventType.SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
Exit();
|
||||
break;
|
||||
}
|
||||
@ -409,7 +411,7 @@ namespace Ryujinx.Headless
|
||||
// Get screen touch position
|
||||
if (!_enableMouse)
|
||||
{
|
||||
hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as SDL2MouseDriver).IsButtonPressed(MouseButton.Button1), _aspectRatio.ToFloat());
|
||||
hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as SDL3MouseDriver).IsButtonPressed(MouseButton.Button1), _aspectRatio.ToFloat());
|
||||
}
|
||||
|
||||
if (!hasTouch)
|
||||
@ -516,25 +518,33 @@ namespace Ryujinx.Headless
|
||||
|
||||
public bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText)
|
||||
{
|
||||
SDL_MessageBoxData data = new()
|
||||
SDL_MessageBoxButtonData[] buttons = new SDL_MessageBoxButtonData[buttonsText.Length];
|
||||
unsafe
|
||||
{
|
||||
title = title,
|
||||
message = message,
|
||||
buttons = new SDL_MessageBoxButtonData[buttonsText.Length],
|
||||
numbuttons = buttonsText.Length,
|
||||
window = WindowHandle,
|
||||
};
|
||||
|
||||
for (int i = 0; i < buttonsText.Length; i++)
|
||||
{
|
||||
data.buttons[i] = new SDL_MessageBoxButtonData
|
||||
for (int i = 0; i < buttonsText.Length; i++)
|
||||
{
|
||||
buttonid = i,
|
||||
text = buttonsText[i],
|
||||
};
|
||||
}
|
||||
fixed (byte* button = buttonsText[i].ToBytes())
|
||||
{
|
||||
buttons[i] = new SDL_MessageBoxButtonData { buttonID = i, text = button, };
|
||||
}
|
||||
}
|
||||
|
||||
SDL_ShowMessageBox(ref data, out int _);
|
||||
fixed (byte* t = title.ToBytes())
|
||||
fixed (byte* m = message.ToBytes())
|
||||
fixed (SDL_MessageBoxButtonData* b = buttons)
|
||||
{
|
||||
SDL_MessageBoxData data = new()
|
||||
{
|
||||
title = t,
|
||||
message = m,
|
||||
buttons = b,
|
||||
numbuttons = buttonsText.Length,
|
||||
window = WindowHandle,
|
||||
};
|
||||
|
||||
SDL_ShowMessageBox(ref data, out int _);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -552,11 +562,11 @@ namespace Ryujinx.Headless
|
||||
TouchScreenManager?.Dispose();
|
||||
NpadManager.Dispose();
|
||||
|
||||
SDL2Driver.Instance.UnregisterWindow(_windowId);
|
||||
SDL3Driver.Instance.UnregisterWindow(_windowId);
|
||||
|
||||
SDL_DestroyWindow(WindowHandle);
|
||||
|
||||
SDL2Driver.Instance.Dispose();
|
||||
SDL3Driver.Instance.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -144,11 +143,6 @@ namespace Ryujinx.Ava.Input
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLed(uint packedRgb)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.UI, "SetLed called on an AvaloniaKeyboard");
|
||||
}
|
||||
|
||||
public void SetTriggerThreshold(float triggerThreshold) { }
|
||||
|
||||
public void Rumble(float lowFrequency, float highFrequency, uint durationMs) { }
|
||||
|
@ -59,8 +59,6 @@ namespace Ryujinx.Ava.Input
|
||||
return new AvaloniaKeyboard(this, _keyboardIdentifers[0], LocaleManager.Instance[LocaleKeys.AllKeyboards]);
|
||||
}
|
||||
|
||||
public IEnumerable<IGamepad> GetGamepads() => [GetGamepad("0")];
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
@ -75,11 +74,6 @@ namespace Ryujinx.Ava.Input
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetLed(uint packedRgb)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.UI, "SetLed called on an AvaloniaMouse");
|
||||
}
|
||||
|
||||
public void SetTriggerThreshold(float triggerThreshold)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
@ -3,7 +3,6 @@ using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using MouseButton = Ryujinx.Input.MouseButton;
|
||||
using Size = System.Drawing.Size;
|
||||
@ -135,8 +134,6 @@ namespace Ryujinx.Ava.Input
|
||||
return new AvaloniaMouse(this);
|
||||
}
|
||||
|
||||
public IEnumerable<IGamepad> GetGamepads() => [GetGamepad("0")];
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
|
@ -5,11 +5,9 @@ using Gommon;
|
||||
using Projektanker.Icons.Avalonia;
|
||||
using Projektanker.Icons.Avalonia.FontAwesome;
|
||||
using Projektanker.Icons.Avalonia.MaterialDesign;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Ava.Utilities;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
using Ryujinx.Ava.Utilities.SystemInfo;
|
||||
using Ryujinx.Common;
|
||||
@ -19,7 +17,7 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.SystemInterop;
|
||||
using Ryujinx.Graphics.Vulkan.MoltenVK;
|
||||
using Ryujinx.Headless;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@ -126,9 +124,9 @@ namespace Ryujinx.Ava
|
||||
// Initialize Discord integration.
|
||||
DiscordIntegrationModule.Initialize();
|
||||
|
||||
// Initialize SDL2 driver
|
||||
SDL2Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input);
|
||||
|
||||
// Initialize SDL3 driver
|
||||
SDL3Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input);
|
||||
|
||||
ReloadConfig();
|
||||
|
||||
WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor();
|
||||
|
@ -11,6 +11,7 @@
|
||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
<CETCompat>false</CETCompat>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="BuildValidationProj" BeforeTargets="BeforeBuild">
|
||||
@ -70,12 +71,12 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL3\Ryujinx.Audio.Backends.SDL3.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input.SDL3\Ryujinx.Input.SDL3.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
@ -134,6 +135,10 @@
|
||||
<None Remove="Assets\Icons\Controller_JoyConPair.svg" />
|
||||
<None Remove="Assets\Icons\Controller_JoyConRight.svg" />
|
||||
<None Remove="Assets\Icons\Controller_ProCon.svg" />
|
||||
<None Remove="Assets\Icons\Controller_JoyConLeft_Settings.svg" />
|
||||
<None Remove="Assets\Icons\Controller_JoyConPair_Settings.svg" />
|
||||
<None Remove="Assets\Icons\Controller_JoyConRight_Settings.svg" />
|
||||
<None Remove="Assets\Icons\Controller_ProCon_Settings.svg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -156,6 +161,10 @@
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_JoyConPair.svg" />
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_JoyConRight.svg" />
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_ProCon.svg" />
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_JoyConLeft_Settings.svg" />
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_JoyConPair_Settings.svg" />
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_JoyConRight_Settings.svg" />
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_ProCon_Settings.svg" />
|
||||
<EmbeddedResource Include="Assets\UIImages\Icon_NCA.png" />
|
||||
<EmbeddedResource Include="Assets\UIImages\Icon_NRO.png" />
|
||||
<EmbeddedResource Include="Assets\UIImages\Icon_NSO.png" />
|
||||
@ -174,6 +183,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Assets\Fonts\Mono\" />
|
||||
<Folder Include="bin\Debug\net9.0\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
146
src/Ryujinx/UI/Controls/Motion.cs
Normal file
146
src/Ryujinx/UI/Controls/Motion.cs
Normal file
@ -0,0 +1,146 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public class Motion : Control
|
||||
{
|
||||
private double _XRotation = 0;
|
||||
private double _YRotation = 0;
|
||||
private double _ZRotation = 0;
|
||||
private bool _isRight;
|
||||
double length = 11;
|
||||
double width = 4;
|
||||
double height = 27;
|
||||
|
||||
public void UpdateRotationFromMotionData(Vector3 accelerometerData, Vector3 gyroData, bool isRight = false)
|
||||
{
|
||||
_XRotation = Math.Atan2(-accelerometerData.Y, -accelerometerData.Z) * 180 / Math.PI;
|
||||
|
||||
//TODO: issue
|
||||
_YRotation = Math.Atan2(-accelerometerData.X, -accelerometerData.Y) * 180 / Math.PI;
|
||||
|
||||
_ZRotation = Math.Atan2(accelerometerData.X, -accelerometerData.Z) * 180 / Math.PI;
|
||||
|
||||
_isRight = isRight;
|
||||
}
|
||||
|
||||
public override void Render(DrawingContext context)
|
||||
{
|
||||
base.Render(context);
|
||||
|
||||
double size = new[] { length, width, height }.Max();
|
||||
var centerX = _isRight ? -size : size;
|
||||
|
||||
DrawCube(context, centerX, size, _XRotation, 0, 0);
|
||||
if (_isRight)
|
||||
{
|
||||
DrawCube(context, centerX, 4 * size, -_YRotation - 90, 180, 90);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawCube(context, centerX, 4 * size, _YRotation + 90, 0, 90);
|
||||
}
|
||||
DrawCube(context, centerX, 7 * size, 0, 0, _ZRotation);
|
||||
}
|
||||
|
||||
private void DrawCube(DrawingContext context, double centerX, double centerY, double xRotation,
|
||||
double yRotation, double zRotation)
|
||||
{
|
||||
Point3D[] cubeVertices =
|
||||
[
|
||||
new(-length, -height, -width), new(length, -height, -width), new(length, height, -width),
|
||||
new(-length, height, -width), new(-length, -height, width), new(length, -height, width),
|
||||
new(length, height, width), new(-length, height, width)
|
||||
];
|
||||
|
||||
Point[] projectedPoints = new Point[cubeVertices.Length];
|
||||
|
||||
Point3D[] rotatedVertices = new Point3D[cubeVertices.Length];
|
||||
for (int i = 0; i < cubeVertices.Length; i++)
|
||||
{
|
||||
Point3D rotatedPoint = RotatePoint(cubeVertices[i], xRotation, yRotation, zRotation);
|
||||
rotatedVertices[i] = rotatedPoint;
|
||||
|
||||
double projectedX = centerX + rotatedPoint.X / (1 - rotatedPoint.Z / 200);
|
||||
double projectedY = centerY + rotatedPoint.Y / (1 - rotatedPoint.Z / 200);
|
||||
|
||||
projectedPoints[i] = new Point(projectedX, projectedY);
|
||||
}
|
||||
|
||||
int[][] cubeFaces =
|
||||
[
|
||||
[0, 1, 2, 3], [4, 5, 6, 7], [0, 1, 5, 4], [2, 3, 7, 6],
|
||||
[0, 3, 7, 4], [1, 2, 6, 5]
|
||||
];
|
||||
|
||||
IImmutableSolidColorBrush[] faceColors = _isRight
|
||||
?
|
||||
[
|
||||
Brushes.DimGray, Brushes.IndianRed, Brushes.DimGray, Brushes.IndianRed, Brushes.DimGray,
|
||||
Brushes.IndianRed
|
||||
]
|
||||
:
|
||||
[
|
||||
Brushes.DimGray, Brushes.SkyBlue, Brushes.DimGray, Brushes.SkyBlue, Brushes.SkyBlue, Brushes.DimGray
|
||||
];
|
||||
|
||||
|
||||
var sortedFaces = cubeFaces
|
||||
.Select((face, index) => new
|
||||
{
|
||||
Face = face,
|
||||
Color = faceColors[index],
|
||||
MinZ = face.Min(vertexIndex => rotatedVertices[vertexIndex].Z)
|
||||
})
|
||||
.OrderBy(faceData => faceData.MinZ)
|
||||
.ToArray();
|
||||
|
||||
foreach (var faceData in sortedFaces)
|
||||
{
|
||||
int[] face = faceData.Face;
|
||||
IImmutableSolidColorBrush color = faceData.Color;
|
||||
|
||||
PathGeometry faceGeometry = new();
|
||||
PathFigure faceFigure = new()
|
||||
{
|
||||
StartPoint = projectedPoints[face[0]], IsClosed = true, IsFilled = true
|
||||
};
|
||||
|
||||
for (int j = 1; j < face.Length; j++)
|
||||
{
|
||||
faceFigure.Segments.Add(new LineSegment { Point = projectedPoints[face[j]] });
|
||||
}
|
||||
|
||||
faceGeometry.Figures.Add(faceFigure);
|
||||
|
||||
context.DrawGeometry(color, new Pen(Brushes.White), faceGeometry);
|
||||
}
|
||||
}
|
||||
|
||||
private Point3D RotatePoint(Point3D point, double xRotation, double yRotation, double zRotation)
|
||||
{
|
||||
double radX = xRotation * Math.PI / 180;
|
||||
double radY = yRotation * Math.PI / 180;
|
||||
double radZ = zRotation * Math.PI / 180;
|
||||
|
||||
double cosX = Math.Cos(radX), sinX = Math.Sin(radX);
|
||||
double cosY = Math.Cos(radY), sinY = Math.Sin(radY);
|
||||
double cosZ = Math.Cos(radZ), sinZ = Math.Sin(radZ);
|
||||
|
||||
double newX = cosY * cosZ * point.X + (cosY * sinZ * point.Y) - (sinY * point.Z);
|
||||
double newY = (sinX * sinY * cosZ - cosX * sinZ) * point.X + (sinX * sinY * sinZ + cosX * cosZ) * point.Y +
|
||||
sinX * cosY * point.Z;
|
||||
double newZ = (cosX * sinY * cosZ + sinX * sinZ) * point.X + (cosX * sinY * sinZ - sinX * cosZ) * point.Y +
|
||||
cosX * cosY * point.Z;
|
||||
|
||||
return new Point3D(newX, newY, newZ);
|
||||
}
|
||||
|
||||
private record Point3D(double X, double Y, double Z);
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
using Avalonia.Media;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
@ -409,58 +408,6 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _enableLedChanging;
|
||||
|
||||
public bool EnableLedChanging
|
||||
{
|
||||
get => _enableLedChanging;
|
||||
set
|
||||
{
|
||||
_enableLedChanging = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowLedColorPicker => !TurnOffLed && !UseRainbowLed;
|
||||
|
||||
private bool _turnOffLed;
|
||||
|
||||
public bool TurnOffLed
|
||||
{
|
||||
get => _turnOffLed;
|
||||
set
|
||||
{
|
||||
_turnOffLed = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(ShowLedColorPicker));
|
||||
}
|
||||
}
|
||||
|
||||
private bool _useRainbowLed;
|
||||
|
||||
public bool UseRainbowLed
|
||||
{
|
||||
get => _useRainbowLed;
|
||||
set
|
||||
{
|
||||
_useRainbowLed = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(ShowLedColorPicker));
|
||||
}
|
||||
}
|
||||
|
||||
private Color _ledColor;
|
||||
|
||||
public Color LedColor
|
||||
{
|
||||
get => _ledColor;
|
||||
set
|
||||
{
|
||||
_ledColor = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public GamepadInputConfig(InputConfig config)
|
||||
{
|
||||
@ -536,28 +483,15 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||
WeakRumble = controllerInput.Rumble.WeakRumble;
|
||||
StrongRumble = controllerInput.Rumble.StrongRumble;
|
||||
}
|
||||
|
||||
if (controllerInput.Led != null)
|
||||
{
|
||||
EnableLedChanging = controllerInput.Led.EnableLed;
|
||||
TurnOffLed = controllerInput.Led.TurnOffLed;
|
||||
UseRainbowLed = controllerInput.Led.UseRainbow;
|
||||
uint rawColor = controllerInput.Led.LedColor;
|
||||
byte alpha = (byte)(rawColor >> 24);
|
||||
byte red = (byte)(rawColor >> 16);
|
||||
byte green = (byte)(rawColor >> 8);
|
||||
byte blue = (byte)(rawColor % 256);
|
||||
LedColor = new Color(alpha, red, green, blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public InputConfig GetConfig()
|
||||
{
|
||||
StandardControllerInputConfig config = new()
|
||||
var config = new StandardControllerInputConfig
|
||||
{
|
||||
Id = Id,
|
||||
Backend = InputBackendType.GamepadSDL2,
|
||||
Backend = InputBackendType.GamepadSDL3,
|
||||
PlayerIndex = PlayerIndex,
|
||||
ControllerType = ControllerType,
|
||||
LeftJoycon = new LeftJoyconCommonConfig<GamepadInputId>
|
||||
@ -606,13 +540,6 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||
WeakRumble = WeakRumble,
|
||||
StrongRumble = StrongRumble,
|
||||
},
|
||||
Led = new LedConfigController
|
||||
{
|
||||
EnableLed = EnableLedChanging,
|
||||
TurnOffLed = this.TurnOffLed,
|
||||
UseRainbow = UseRainbowLed,
|
||||
LedColor = LedColor.ToUInt32()
|
||||
},
|
||||
Version = InputConfig.CurrentVersion,
|
||||
DeadzoneLeft = DeadzoneLeft,
|
||||
DeadzoneRight = DeadzoneRight,
|
||||
|
@ -5,7 +5,6 @@ using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models.Input;
|
||||
using Ryujinx.Ava.UI.Views.Input;
|
||||
using Ryujinx.UI.Views.Input;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
{
|
||||
@ -41,7 +40,11 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
|
||||
[ObservableProperty] private SvgImage _image;
|
||||
|
||||
public InputViewModel ParentModel { get; }
|
||||
public readonly InputViewModel ParentModel;
|
||||
|
||||
[ObservableProperty] private string _leftStickPosition;
|
||||
|
||||
[ObservableProperty] private string _rightStickPosition;
|
||||
|
||||
public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config)
|
||||
{
|
||||
@ -60,11 +63,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
{
|
||||
await RumbleInputView.Show(this);
|
||||
}
|
||||
|
||||
public async void ShowLedConfig()
|
||||
{
|
||||
await LedInputView.Show(this);
|
||||
}
|
||||
|
||||
public void OnParentModelChanged()
|
||||
{
|
||||
@ -72,5 +70,10 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
IsRight = ParentModel.IsRight;
|
||||
Image = ParentModel.Image;
|
||||
}
|
||||
|
||||
public void UpdateImageCss(string css)
|
||||
{
|
||||
Image = new SvgImage { Source = ParentModel.Image.Source, Css = css };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ using Avalonia.Controls;
|
||||
using Avalonia.Svg.Skia;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Input;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
@ -35,10 +34,10 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
public partial class InputViewModel : BaseModel, IDisposable
|
||||
{
|
||||
private const string Disabled = "disabled";
|
||||
private const string ProControllerResource = "Ryujinx/Assets/Icons/Controller_ProCon.svg";
|
||||
private const string JoyConPairResource = "Ryujinx/Assets/Icons/Controller_JoyConPair.svg";
|
||||
private const string JoyConLeftResource = "Ryujinx/Assets/Icons/Controller_JoyConLeft.svg";
|
||||
private const string JoyConRightResource = "Ryujinx/Assets/Icons/Controller_JoyConRight.svg";
|
||||
private const string ProControllerResource = "Ryujinx/Assets/Icons/Controller_ProCon_Settings.svg";
|
||||
private const string JoyConPairResource = "Ryujinx/Assets/Icons/Controller_JoyConPair_Settings.svg";
|
||||
private const string JoyConLeftResource = "Ryujinx/Assets/Icons/Controller_JoyConLeft_Settings.svg";
|
||||
private const string JoyConRightResource = "Ryujinx/Assets/Icons/Controller_JoyConRight_Settings.svg";
|
||||
private const string KeyboardString = "keyboard";
|
||||
private const string ControllerString = "controller";
|
||||
private readonly MainWindow _mainWindow;
|
||||
@ -55,18 +54,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
|
||||
public IGamepadDriver AvaloniaKeyboardDriver { get; }
|
||||
|
||||
private IGamepad _selectedGamepad;
|
||||
|
||||
public IGamepad SelectedGamepad
|
||||
{
|
||||
get => _selectedGamepad;
|
||||
private set
|
||||
{
|
||||
_selectedGamepad = value;
|
||||
OnPropertiesChanged(nameof(HasLed), nameof(CanClearLed));
|
||||
}
|
||||
}
|
||||
public IGamepad SelectedGamepad { get; private set; }
|
||||
|
||||
public ObservableCollection<PlayerModel> PlayerIndexes { get; set; }
|
||||
public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; }
|
||||
@ -81,9 +69,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
public bool IsRight { get; set; }
|
||||
public bool IsLeft { get; set; }
|
||||
|
||||
public bool HasLed => SelectedGamepad.Features.HasFlag(GamepadFeaturesFlag.Led);
|
||||
public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense");
|
||||
|
||||
public bool IsModified { get; set; }
|
||||
public event Action NotifyChangesEvent;
|
||||
|
||||
@ -198,7 +183,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
|
||||
image.Source = source;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
||||
@ -595,7 +579,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
config = new StandardControllerInputConfig
|
||||
{
|
||||
Version = InputConfig.CurrentVersion,
|
||||
Backend = InputBackendType.GamepadSDL2,
|
||||
Backend = InputBackendType.GamepadSDL3,
|
||||
Id = id,
|
||||
ControllerType = ControllerType.ProController,
|
||||
DeadzoneLeft = 0.1f,
|
||||
@ -612,8 +596,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
ButtonMinus = ConfigGamepadInputId.Minus,
|
||||
ButtonL = ConfigGamepadInputId.LeftShoulder,
|
||||
ButtonZl = ConfigGamepadInputId.LeftTrigger,
|
||||
ButtonSl = ConfigGamepadInputId.Unbound,
|
||||
ButtonSr = ConfigGamepadInputId.Unbound,
|
||||
ButtonSl = ConfigGamepadInputId.SingleLeftTrigger0,
|
||||
ButtonSr = ConfigGamepadInputId.SingleRightTrigger0,
|
||||
},
|
||||
LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
|
||||
{
|
||||
@ -631,8 +615,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
ButtonPlus = ConfigGamepadInputId.Plus,
|
||||
ButtonR = ConfigGamepadInputId.RightShoulder,
|
||||
ButtonZr = ConfigGamepadInputId.RightTrigger,
|
||||
ButtonSl = ConfigGamepadInputId.Unbound,
|
||||
ButtonSr = ConfigGamepadInputId.Unbound,
|
||||
ButtonSl = ConfigGamepadInputId.SingleLeftTrigger1,
|
||||
ButtonSr = ConfigGamepadInputId.SingleRightTrigger1,
|
||||
},
|
||||
RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
|
||||
{
|
||||
|
@ -1,53 +0,0 @@
|
||||
using Avalonia.Media;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
{
|
||||
public partial class LedInputViewModel : BaseModel
|
||||
{
|
||||
public required InputViewModel ParentModel { get; init; }
|
||||
|
||||
public RelayCommand LedDisabledChanged => Commands.Create(() =>
|
||||
{
|
||||
if (!EnableLedChanging) return;
|
||||
|
||||
if (TurnOffLed)
|
||||
ParentModel.SelectedGamepad.ClearLed();
|
||||
else
|
||||
ParentModel.SelectedGamepad.SetLed(LedColor.ToUInt32());
|
||||
});
|
||||
|
||||
[ObservableProperty] private bool _enableLedChanging;
|
||||
[ObservableProperty] private Color _ledColor;
|
||||
|
||||
public bool ShowLedColorPicker => !TurnOffLed && !UseRainbowLed;
|
||||
|
||||
private bool _turnOffLed;
|
||||
|
||||
public bool TurnOffLed
|
||||
{
|
||||
get => _turnOffLed;
|
||||
set
|
||||
{
|
||||
_turnOffLed = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(ShowLedColorPicker));
|
||||
}
|
||||
}
|
||||
|
||||
private bool _useRainbowLed;
|
||||
|
||||
public bool UseRainbowLed
|
||||
{
|
||||
get => _useRainbowLed;
|
||||
set
|
||||
{
|
||||
_useRainbowLed = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(ShowLedColorPicker));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Gommon;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.OpenAL;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
using Ryujinx.Audio.Backends.SDL3;
|
||||
using Ryujinx.Audio.Backends.SoundIo;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
@ -212,7 +212,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public bool EnableDebug { get; set; }
|
||||
public bool IsOpenAlEnabled { get; set; }
|
||||
public bool IsSoundIoEnabled { get; set; }
|
||||
public bool IsSDL2Enabled { get; set; }
|
||||
public bool IsSDL3Enabled { get; set; }
|
||||
public bool IsCustomResolutionScaleActive => _resolutionScale == 4;
|
||||
public bool IsScalingFilterActive => _scalingFilter == (int)Ryujinx.Common.Configuration.ScalingFilter.Fsr;
|
||||
|
||||
@ -373,13 +373,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported;
|
||||
IsSoundIoEnabled = SoundIoHardwareDeviceDriver.IsSupported;
|
||||
IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported;
|
||||
IsSDL3Enabled = SDL3HardwareDeviceDriver.IsSupported;
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
OnPropertyChanged(nameof(IsOpenAlEnabled));
|
||||
OnPropertyChanged(nameof(IsSoundIoEnabled));
|
||||
OnPropertyChanged(nameof(IsSDL2Enabled));
|
||||
OnPropertyChanged(nameof(IsSDL3Enabled));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,9 @@
|
||||
x:DataType="viewModels:ControllerInputViewModel"
|
||||
x:CompileBindings="True"
|
||||
mc:Ignorable="d"
|
||||
Focusable="True">
|
||||
Focusable="True"
|
||||
Unloaded="Control_OnUnloaded"
|
||||
>
|
||||
<Design.DataContext>
|
||||
<viewModels:ControllerInputViewModel />
|
||||
</Design.DataContext>
|
||||
@ -184,6 +186,9 @@
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
Text="{ext:Locale ControllerSettingsStickDeadzone}" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
Text="{Binding LeftStickPosition }" />
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
@ -318,6 +323,19 @@
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Canvas>
|
||||
<controls:Motion
|
||||
x:Name="LeftCubeCanvas"
|
||||
Canvas.Left="5"
|
||||
Canvas.Top="5"
|
||||
ZIndex="1" />
|
||||
|
||||
<controls:Motion
|
||||
x:Name="RightCubeCanvas"
|
||||
Canvas.Right="5"
|
||||
Canvas.Top="5"
|
||||
ZIndex="1" />
|
||||
</Canvas>
|
||||
<!-- Controller Picture -->
|
||||
<Image
|
||||
Margin="0,10"
|
||||
@ -429,7 +447,7 @@
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<!-- Motion, Rumble, LED -->
|
||||
<!-- Motion + Rumble -->
|
||||
<StackPanel
|
||||
Margin="0,10,0,0"
|
||||
Spacing="5"
|
||||
@ -487,32 +505,6 @@
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="0,-1,0,0">
|
||||
<Grid IsVisible="{Binding ParentModel.HasLed}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox
|
||||
Margin="10, 10, 5, 10"
|
||||
MinWidth="0"
|
||||
Grid.Column="0"
|
||||
IsChecked="{Binding Config.EnableLedChanging, Mode=TwoWay}">
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsLed}" />
|
||||
</CheckBox>
|
||||
<Button
|
||||
Margin="10"
|
||||
Grid.Column="1"
|
||||
Command="{Binding ShowLedConfig}">
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsConfigureGeneral}" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<!-- Right Controls -->
|
||||
@ -743,6 +735,9 @@
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
Text="{ext:Locale ControllerSettingsStickDeadzone}" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
Text="{Binding RightStickPosition }" />
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
|
@ -4,12 +4,19 @@ using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.LogicalTree;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Avalonia.Threading;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models.Input;
|
||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using Ryujinx.HLE.HOS.Services.Hid;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.Assigner;
|
||||
using Ryujinx.Input.HLE;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Button = Ryujinx.Input.Button;
|
||||
using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
|
||||
|
||||
@ -18,6 +25,8 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
public partial class ControllerInputView : UserControl
|
||||
{
|
||||
private ButtonKeyAssigner _currentAssigner;
|
||||
private volatile bool _isRunning = true;
|
||||
private const float StickMaxPosition = 3;
|
||||
|
||||
public ControllerInputView()
|
||||
{
|
||||
@ -38,6 +47,8 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StartUpdatingData();
|
||||
}
|
||||
|
||||
protected override void OnPointerReleased(PointerReleasedEventArgs e)
|
||||
@ -222,7 +233,7 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
{
|
||||
IButtonAssigner assigner;
|
||||
|
||||
ControllerInputViewModel controllerInputViewModel = DataContext as ControllerInputViewModel;
|
||||
var controllerInputViewModel = DataContext as ControllerInputViewModel;
|
||||
|
||||
assigner = new GamepadButtonAssigner(
|
||||
controllerInputViewModel.ParentModel.SelectedGamepad,
|
||||
@ -235,14 +246,86 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
|
||||
foreach (IGamepad gamepad in RyujinxApp.MainWindow.InputManager.GamepadDriver.GetGamepads())
|
||||
{
|
||||
gamepad?.ClearLed();
|
||||
}
|
||||
|
||||
_currentAssigner?.Cancel();
|
||||
_currentAssigner = null;
|
||||
}
|
||||
|
||||
private void Control_OnUnloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_isRunning = false;
|
||||
}
|
||||
|
||||
private string BuildSvgCss(IGamepad gamepad, GamepadInputConfig config, JoystickPosition leftPosition,
|
||||
JoystickPosition rightPosition)
|
||||
{
|
||||
gamepad.SetConfiguration(config.GetConfig());
|
||||
StringBuilder sb = new();
|
||||
for (int i = 0; i < (int)GamepadInputId.Count; i++)
|
||||
{
|
||||
GamepadButtonInputId button = (GamepadButtonInputId)i;
|
||||
if (gamepad.GetMappedStateSnapshot().IsPressed(button))
|
||||
{
|
||||
sb.Append($"#{button}{{fill:#00bbdb;}}");
|
||||
}
|
||||
}
|
||||
|
||||
sb.Append(
|
||||
$"#LeftStick{{transform: translate ({(float)leftPosition.Dx / short.MaxValue * StickMaxPosition} {-(float)leftPosition.Dy / short.MaxValue * StickMaxPosition});}}");
|
||||
sb.Append(
|
||||
$"#RightStick{{transform: translate ({(float)rightPosition.Dx / short.MaxValue * StickMaxPosition} {-(float)rightPosition.Dy / short.MaxValue * StickMaxPosition});}}");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private void StartUpdatingData()
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
while (_isRunning)
|
||||
{
|
||||
var viewModel = DataContext as ControllerInputViewModel;
|
||||
if (viewModel != null)
|
||||
{
|
||||
IGamepad gamepad = viewModel.ParentModel.SelectedGamepad;
|
||||
var config = viewModel.Config;
|
||||
JoystickPosition leftPosition = default, rightposition = default;
|
||||
if (config.LeftJoystick != StickInputId.Unbound)
|
||||
{
|
||||
var stickInputId = (Ryujinx.Input.StickInputId)(int)config.LeftJoystick;
|
||||
(float leftAxisX, float leftAxisY) = gamepad.GetStick(stickInputId);
|
||||
leftPosition = NpadController.GetJoystickPosition(leftAxisX, leftAxisY,
|
||||
config.DeadzoneLeft, config.RangeLeft);
|
||||
viewModel.LeftStickPosition = $"{leftPosition.Dx}, {leftPosition.Dy}";
|
||||
}
|
||||
|
||||
if (config.RightJoystick != StickInputId.Unbound)
|
||||
{
|
||||
StickInputId stickInputId = config.RightJoystick;
|
||||
(float rightAxisX, float rightAxisY) =
|
||||
gamepad.GetStick((Ryujinx.Input.StickInputId)stickInputId);
|
||||
rightposition = NpadController.GetJoystickPosition(rightAxisX, rightAxisY,
|
||||
config.DeadzoneRight, config.RangeRight);
|
||||
viewModel.RightStickPosition = $"{rightposition.Dx}, {rightposition.Dy}";
|
||||
}
|
||||
|
||||
viewModel.UpdateImageCss(BuildSvgCss(gamepad, config, leftPosition, rightposition));
|
||||
|
||||
// 假设你已获得加速度计和陀螺仪数据
|
||||
Vector3 accelerometerData = gamepad.GetMotionData(MotionInputId.Accelerometer);
|
||||
Vector3 gyroscopeData = gamepad.GetMotionData(MotionInputId.Gyroscope);
|
||||
|
||||
LeftCubeCanvas.UpdateRotationFromMotionData(accelerometerData, gyroscopeData);
|
||||
LeftCubeCanvas.InvalidateVisual();
|
||||
|
||||
Vector3 rightAccelerometer = gamepad.GetMotionData(MotionInputId.RightAccelerometer);
|
||||
Vector3 rightGyroscope = gamepad.GetMotionData(MotionInputId.RightGyroscope);
|
||||
|
||||
RightCubeCanvas.UpdateRotationFromMotionData(rightAccelerometer, rightGyroscope, true);
|
||||
RightCubeCanvas.InvalidateVisual();
|
||||
}
|
||||
|
||||
await Task.Delay(16);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels.Input"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:DataType="viewModels:LedInputViewModel"
|
||||
x:Class="Ryujinx.UI.Views.Input.LedInputView">
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center">
|
||||
<StackPanel Orientation="Horizontal" IsVisible="{Binding ParentModel.CanClearLed}">
|
||||
<TextBlock MinWidth="75" MaxWidth="150" Text="{ext:Locale ControllerSettingsLedColorDisable}" />
|
||||
<CheckBox
|
||||
Margin="5"
|
||||
MinWidth="0"
|
||||
IsChecked="{Binding TurnOffLed, Mode=TwoWay}"
|
||||
Command="{Binding LedDisabledChanged}">
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" IsEnabled="{Binding !TurnOffLed}">
|
||||
<TextBlock MinWidth="75" MaxWidth="150" Text="{ext:Locale ControllerSettingsLedColorRainbow}" />
|
||||
<CheckBox
|
||||
Margin="5"
|
||||
MinWidth="0"
|
||||
IsChecked="{Binding UseRainbowLed, Mode=TwoWay}">
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" IsEnabled="{Binding ShowLedColorPicker}">
|
||||
<TextBlock MinWidth="75" MaxWidth="150" Text="{ext:Locale ControllerSettingsLedColor}" />
|
||||
<ui:ColorPickerButton
|
||||
Margin="5"
|
||||
IsMoreButtonVisible="False"
|
||||
UseColorPalette="False"
|
||||
UseColorTriangle="False"
|
||||
UseColorWheel="False"
|
||||
ShowAcceptDismissButtons="False"
|
||||
IsAlphaEnabled="False"
|
||||
AttachedToVisualTree="ColorPickerButton_OnAttachedToVisualTree"
|
||||
ColorChanged="ColorPickerButton_OnColorChanged"
|
||||
Color="{Binding LedColor, Mode=TwoWay}">
|
||||
</ui:ColorPickerButton>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</UserControl>
|
@ -1,75 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Models.Input;
|
||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||
using Ryujinx.Ava.UI.Views.Input;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.UI.Views.Input
|
||||
{
|
||||
public partial class LedInputView : UserControl
|
||||
{
|
||||
private readonly LedInputViewModel _viewModel;
|
||||
|
||||
public LedInputView(ControllerInputViewModel viewModel)
|
||||
{
|
||||
DataContext = _viewModel = new LedInputViewModel
|
||||
{
|
||||
ParentModel = viewModel.ParentModel,
|
||||
TurnOffLed = viewModel.Config.TurnOffLed,
|
||||
EnableLedChanging = viewModel.Config.EnableLedChanging,
|
||||
LedColor = viewModel.Config.LedColor,
|
||||
UseRainbowLed = viewModel.Config.UseRainbowLed,
|
||||
};
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void ColorPickerButton_OnColorChanged(ColorPickerButton sender, ColorButtonColorChangedEventArgs args)
|
||||
{
|
||||
if (!args.NewColor.HasValue) return;
|
||||
if (DataContext is not LedInputViewModel lvm) return;
|
||||
if (!lvm.EnableLedChanging) return;
|
||||
if (lvm.TurnOffLed) return;
|
||||
|
||||
lvm.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32());
|
||||
}
|
||||
|
||||
private void ColorPickerButton_OnAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (DataContext is not LedInputViewModel lvm) return;
|
||||
if (!lvm.EnableLedChanging) return;
|
||||
if (lvm.TurnOffLed) return;
|
||||
|
||||
lvm.ParentModel.SelectedGamepad.SetLed(lvm.LedColor.ToUInt32());
|
||||
}
|
||||
|
||||
public static async Task Show(ControllerInputViewModel viewModel)
|
||||
{
|
||||
LedInputView content = new(viewModel);
|
||||
|
||||
ContentDialog contentDialog = new()
|
||||
{
|
||||
Title = LocaleManager.Instance[LocaleKeys.ControllerLedTitle],
|
||||
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
|
||||
SecondaryButtonText = string.Empty,
|
||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
|
||||
Content = content,
|
||||
};
|
||||
contentDialog.PrimaryButtonClick += (sender, args) =>
|
||||
{
|
||||
GamepadInputConfig config = viewModel.Config;
|
||||
config.EnableLedChanging = content._viewModel.EnableLedChanging;
|
||||
config.LedColor = content._viewModel.LedColor;
|
||||
config.UseRainbowLed = content._viewModel.UseRainbowLed;
|
||||
config.TurnOffLed = content._viewModel.TurnOffLed;
|
||||
};
|
||||
|
||||
await contentDialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,8 +43,10 @@
|
||||
<ComboBoxItem IsEnabled="{Binding IsSoundIoEnabled}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemAudioBackendSoundIO}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem IsEnabled="{Binding IsSDL2Enabled}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemAudioBackendSDL2}" />
|
||||
<ComboBoxItem IsEnabled="{Binding IsSDL3Enabled}">
|
||||
<TextBlock>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemAudioBackendSDL3}" />
|
||||
</TextBlock>
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
|
@ -28,7 +28,7 @@ using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL2;
|
||||
using Ryujinx.Input.SDL3;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -108,8 +108,8 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
|
||||
|
||||
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL3GamepadDriver());
|
||||
|
||||
_ = this.GetObservable(IsActiveProperty).Subscribe(it => ViewModel.IsActive = it);
|
||||
this.ScalingChanged += OnScalingChanged;
|
||||
}
|
||||
|
@ -4,14 +4,9 @@ using Avalonia.Input;
|
||||
using FluentAvalonia.Core;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Key = Avalonia.Input.Key;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
@ -111,12 +106,6 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
protected override void OnClosing(WindowClosingEventArgs e)
|
||||
{
|
||||
HotkeysPage.Dispose();
|
||||
|
||||
foreach (IGamepad gamepad in RyujinxApp.MainWindow.InputManager.GamepadDriver.GetGamepads())
|
||||
{
|
||||
gamepad?.ClearLed();
|
||||
}
|
||||
|
||||
InputPage.Dispose();
|
||||
base.OnClosing(e);
|
||||
}
|
||||
|
@ -9,6 +9,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
Dummy,
|
||||
OpenAl,
|
||||
SoundIo,
|
||||
SDL2,
|
||||
SDL3,
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
/// <summary>
|
||||
/// The current version of the file format
|
||||
/// </summary>
|
||||
public const int CurrentVersion = 61;
|
||||
public const int CurrentVersion = 60;
|
||||
|
||||
/// <summary>
|
||||
/// Version of the configuration file format
|
||||
|
@ -1,4 +1,3 @@
|
||||
using Avalonia.Media;
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.Utilities.Configuration.System;
|
||||
using Ryujinx.Ava.Utilities.Configuration.UI;
|
||||
@ -264,12 +263,15 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
}),
|
||||
(30, static cff =>
|
||||
{
|
||||
foreach (StandardControllerInputConfig config in cff.InputConfig.OfType<StandardControllerInputConfig>())
|
||||
foreach (InputConfig config in cff.InputConfig)
|
||||
{
|
||||
config.Rumble = new RumbleConfigController
|
||||
if (config is StandardControllerInputConfig controllerConfig)
|
||||
{
|
||||
EnableRumble = false, StrongRumble = 1f, WeakRumble = 1f,
|
||||
};
|
||||
controllerConfig.Rumble = new RumbleConfigController
|
||||
{
|
||||
EnableRumble = false, StrongRumble = 1f, WeakRumble = 1f,
|
||||
};
|
||||
}
|
||||
}
|
||||
}),
|
||||
(31, static cff => cff.BackendThreading = BackendThreading.Auto),
|
||||
@ -414,20 +416,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
// so as a compromise users who want to use it will simply need to re-enable it once after updating.
|
||||
cff.IgnoreApplet = false;
|
||||
}),
|
||||
(60, static cff => cff.StartNoUI = false),
|
||||
(61, static cff =>
|
||||
{
|
||||
foreach (StandardControllerInputConfig config in cff.InputConfig.OfType<StandardControllerInputConfig>())
|
||||
{
|
||||
config.Led = new LedConfigController
|
||||
{
|
||||
EnableLed = false,
|
||||
TurnOffLed = false,
|
||||
UseRainbow = false,
|
||||
LedColor = new Color(255, 5, 1, 253).ToUInt32()
|
||||
};
|
||||
}
|
||||
})
|
||||
(60, static cff => cff.StartNoUI = false)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
System.EnableInternetAccess.Value = false;
|
||||
System.EnableFsIntegrityChecks.Value = true;
|
||||
System.FsGlobalAccessLogMode.Value = 0;
|
||||
System.AudioBackend.Value = AudioBackend.SDL2;
|
||||
System.AudioBackend.Value = AudioBackend.SDL3;
|
||||
System.AudioVolume.Value = 1;
|
||||
System.MemoryManagerMode.Value = MemoryManagerMode.HostMappedUnsafe;
|
||||
System.DramSize.Value = MemoryConfiguration.MemoryConfiguration4GiB;
|
||||
|
Loading…
x
Reference in New Issue
Block a user