Merge master and update Android to SDL3

This commit is contained in:
hwsmm
2024-04-15 21:56:52 +09:00
240 changed files with 7168 additions and 4266 deletions

View File

@@ -13,7 +13,7 @@ jobs:
- arm64
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- uses: ilammy/setup-nasm@v1
@@ -23,7 +23,7 @@ jobs:
arch: ${{ matrix.arch }}
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: macOS-${{ matrix.arch }}
path: macOS-${{ matrix.arch }}
@@ -34,13 +34,13 @@ jobs:
needs: build-macos
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: macOS-x86_64
path: macOS-x86_64
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: macOS-arm64
path: macOS-arm64
@@ -49,7 +49,7 @@ jobs:
run: osu.Framework.NativeLibs/scripts/ffmpeg/combine_dylibs.sh
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: macOS-universal
path: macOS-universal
@@ -65,7 +65,7 @@ jobs:
- x64
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install dependencies
run: |
@@ -78,7 +78,7 @@ jobs:
arch: ${{ matrix.arch }}
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: win-${{ matrix.arch }}
path: win-${{ matrix.arch }}
@@ -91,7 +91,7 @@ jobs:
image: mstorsjo/llvm-mingw:latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Build
run: osu.Framework.NativeLibs/scripts/ffmpeg/build-win.sh
@@ -99,7 +99,7 @@ jobs:
arch: arm64
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: win-arm64
path: win-arm64
@@ -115,13 +115,13 @@ jobs:
sudo apt-get install nasm libva-dev libvdpau-dev
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Build
run: osu.Framework.NativeLibs/scripts/ffmpeg/build-linux.sh
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: linux-x64
path: linux-x64
@@ -136,30 +136,30 @@ jobs:
- build-linux
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: macOS-universal
path: osu.Framework.NativeLibs/runtimes/osx/native
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: linux-x64
path: osu.Framework.NativeLibs/runtimes/linux-x64/native
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: win-arm64
path: osu.Framework.NativeLibs/runtimes/win-arm64/native
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: win-x64
path: osu.Framework.NativeLibs/runtimes/win-x64/native
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: win-x86
path: osu.Framework.NativeLibs/runtimes/win-x86/native
- uses: peter-evans/create-pull-request@v4
- uses: peter-evans/create-pull-request@v6
with:
commit-message: Update FFmpeg binaries
title: Update FFmpeg binaries

View File

@@ -7,10 +7,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install .NET 8.0.x
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: "8.0.x"
@@ -21,7 +21,7 @@ jobs:
run: dotnet restore osu-framework.Desktop.slnf
- name: Restore inspectcode cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ github.workspace }}/inspectcode
key: inspectcode-${{ hashFiles('.config/dotnet-tools.json', '.github/workflows/ci.yml', 'osu-framework.sln*', 'osu-framework*.slnf', '.editorconfig', '.globalconfig', 'CodeAnalysis/*', '**/*.csproj', '**/*.props') }}
@@ -67,15 +67,15 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install .NET 8.0.x
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: "8.0.x"
- name: Setup Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
- name: Install httpbin
run: go install github.com/mccutchen/go-httpbin/v2/cmd/go-httpbin@latest
@@ -106,16 +106,16 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup JDK 11
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: microsoft
java-version: 11
- name: Install .NET 8.0.x
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: "8.0.x"
@@ -131,10 +131,10 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install .NET 8.0.x
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: "8.0.x"

View File

@@ -40,14 +40,14 @@ jobs:
needs: check-if-tag
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set Artifacts Directory
id: artifactsPath
run: echo "::set-output name=NUGET_ARTIFACTS::${{github.workspace}}/artifacts"
- name: Setup .NET 8.0.x
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: "8.0.x"
@@ -55,7 +55,7 @@ jobs:
run: dotnet pack -c Release osu.Framework.NativeLibs /p:Configuration=Release /p:Version=${{needs.check-if-tag.outputs.version}} /p:GenerateDocumentationFile=true -o ${{steps.artifactsPath.outputs.nuget_artifacts}}
- name: Upload Artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: osu-framework-nativelibs
path: ${{steps.artifactsPath.outputs.nuget_artifacts}}/*.nupkg

View File

@@ -47,14 +47,14 @@ jobs:
shell: powershell
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set artifacts directory
id: artifactsPath
run: echo "::set-output name=NUGET_ARTIFACTS::${{github.workspace}}\artifacts"
- name: Install .NET 8.0.x
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: "8.0.x"
@@ -62,7 +62,7 @@ jobs:
run: dotnet pack -c Release osu.Framework /p:Version=${{ github.ref_name }} /p:GenerateDocumentationFile=true /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg -o ${{steps.artifactsPath.outputs.nuget_artifacts}}
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: osu-framework
path: |
@@ -78,14 +78,14 @@ jobs:
environment: production
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set artifacts Directory
id: artifactsPath
run: echo "::set-output name=NUGET_ARTIFACTS::${{github.workspace}}/artifacts"
- name: Install .NET 8.0.x
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: "8.0.x"
@@ -96,7 +96,7 @@ jobs:
run: dotnet pack -c Release osu.Framework.Templates /p:Configuration=Release /p:Version=${{ github.ref_name }} /p:GenerateDocumentationFile=true /p:NoDefaultExcludes=true -o ${{steps.artifactsPath.outputs.nuget_artifacts}}
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: osu-framework-templates
path: ${{steps.artifactsPath.outputs.nuget_artifacts}}/*.nupkg
@@ -113,19 +113,19 @@ jobs:
shell: powershell
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set artifacts directory
id: artifactsPath
run: echo "::set-output name=NUGET_ARTIFACTS::${{github.workspace}}\artifacts"
- name: Install .NET 8.0.x
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: "8.0.x"
- name: Setup JDK 11
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: microsoft
java-version: 11
@@ -137,7 +137,7 @@ jobs:
run: dotnet pack -c Release osu.Framework.Android /p:Version=${{ github.ref_name }} /p:GenerateDocumentationFile=true -o ${{steps.artifactsPath.outputs.nuget_artifacts}}
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: osu-framework-android
path: ${{steps.artifactsPath.outputs.nuget_artifacts}}\*.nupkg
@@ -154,14 +154,14 @@ jobs:
shell: bash
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set artifacts directory
id: artifactsPath
run: echo "::set-output name=NUGET_ARTIFACTS::${{github.workspace}}/artifacts"
- name: Install .NET 8.0.x
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: "8.0.x"
@@ -174,7 +174,7 @@ jobs:
run: dotnet pack -c Release osu.Framework.iOS /p:Version=${{ github.ref_name }} /p:GenerateDocumentationFile=true -o ${{steps.artifactsPath.outputs.nuget_artifacts}}
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: osu-framework-ios
path: ${{steps.artifactsPath.outputs.nuget_artifacts}}/*.nupkg

View File

@@ -25,7 +25,7 @@ jobs:
timeout-minutes: 5
steps:
- name: Annotate CI run with test results
uses: dorny/test-reporter@v1.6.0
uses: dorny/test-reporter@v1.8.0
with:
artifact: osu-framework-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}}-${{matrix.os.configuration}}
name: Test Results (${{matrix.os.prettyname}}, ${{matrix.threadingMode}}, ${{matrix.os.configuration}})

View File

@@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Benchmarks" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Framework.Benchmarks/bin/Debug/net5.0/osu.Framework.Benchmarks.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Framework.Benchmarks/bin/Debug/net8.0/osu.Framework.Benchmarks.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Framework.Benchmarks/bin/Debug/net5.0" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Framework.Benchmarks/bin/Debug/net8.0" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net5.0" />
<option name="PROJECT_TFM" value="net8.0" />
<method v="2">
<option name="Build" />
</method>

View File

@@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="SampleGame" type="DotNetProject" factoryName=".NET Project" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/SampleGame.Desktop/bin/Debug/net5.0/SampleGame.Desktop.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/SampleGame.Desktop/bin/Debug/net8.0/SampleGame.Desktop.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/SampleGame.Desktop/bin/Debug/net5.0" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/SampleGame.Desktop/bin/Debug/net8.0" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net5.0" />
<option name="PROJECT_TFM" value="net8.0" />
<method v="2">
<option name="Build" />
</method>

View File

@@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="VisualTests" type="DotNetProject" factoryName=".NET Project" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Framework.Tests/bin/Debug/net5.0/osu.Framework.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Framework.Tests/bin/Debug/net8.0/osu.Framework.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Framework.Tests/bin/Debug/net5.0" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Framework.Tests/bin/Debug/net8.0" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net5.0" />
<option name="PROJECT_TFM" value="net8.0" />
<method v="2">
<option name="Build" />
</method>

20
.vscode/launch.json vendored
View File

@@ -21,13 +21,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Framework.Tests/bin/Debug/net6.0/osu.Framework.Tests.dll",
"${workspaceRoot}/osu.Framework.Tests/bin/Debug/net8.0/osu.Framework.Tests.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Framework.Tests/bin/Debug/net6.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Framework.Tests/bin/Debug/net8.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@@ -38,13 +38,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Framework.Tests/bin/Release/net6.0/osu.Framework.Tests.dll",
"${workspaceRoot}/osu.Framework.Tests/bin/Release/net8.0/osu.Framework.Tests.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Framework.Tests/bin/Release/net6.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Framework.Tests/bin/Release/net8.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@@ -55,13 +55,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/SampleGame.Desktop/bin/Debug/net6.0/SampleGame.Desktop.dll",
"${workspaceRoot}/SampleGame.Desktop/bin/Debug/net8.0/SampleGame.Desktop.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/SampleGame.Desktop/bin/Debug/net6.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/SampleGame.Desktop/bin/Debug/net8.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@@ -72,13 +72,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/SampleGame.Desktop/bin/Release/net6.0/SampleGame.Desktop.dll",
"${workspaceRoot}/SampleGame.Desktop/bin/Release/net8.0/SampleGame.Desktop.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/SampleGame.Desktop/bin/Release/net6.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/SampleGame.Desktop/bin/Release/net8.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@@ -89,7 +89,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Framework.Benchmarks/bin/Release/net6.0/osu.Framework.Benchmarks.dll",
"${workspaceRoot}/osu.Framework.Benchmarks/bin/Release/net8.0/osu.Framework.Benchmarks.dll",
"--filter",
"*",
],
@@ -97,7 +97,7 @@
"preLaunchTask": "Build Benchmarks",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Framework.Benchmarks/bin/Release/net6.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Framework.Benchmarks/bin/Release/net8.0:${env:LD_LIBRARY_PATH}"
}
},
}

View File

@@ -12,3 +12,4 @@ M:System.Char.ToLower(System.Char);char.ToLower() changes behaviour depending on
M:System.Char.ToUpper(System.Char);char.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use char.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture.
M:System.String.ToLower();string.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
M:System.String.ToUpper();string.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
M:System.Reflection.Assembly.GetEntryAssembly();Use osu.Framework.RuntimeInfo.EntryAssembly instead

View File

@@ -71,7 +71,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Framework.SourceGenerat
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Framework.SourceGeneration.Tests", "osu.Framework.SourceGeneration.Tests\osu.Framework.SourceGeneration.Tests.csproj", "{A0DDCF0A-A352-4CC6-8E6E-0E16CAA50CDD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SDL2-CS-Android", "..\SDL2-CS\SDL2-CS-Android\SDL2-CS-Android.csproj", "{E577CC6E-3616-4951-BDDE-F766291D623F}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CodeAnalysis", "CodeAnalysis", "{50334A1F-990D-45FA-A1FE-C92F1994D97B}"
ProjectSection(SolutionItems) = preProject
CodeAnalysis\BannedSymbols.txt = CodeAnalysis\BannedSymbols.txt
CodeAnalysis\osu-framework.ruleset = CodeAnalysis\osu-framework.ruleset
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SDL3-CS-Android", "..\SDL3-CS\SDL3-CS-Android\SDL3-CS-Android.csproj", "{7C43DA3A-1707-4A56-B9E1-247A695FD294}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -367,18 +373,18 @@ Global
{A0DDCF0A-A352-4CC6-8E6E-0E16CAA50CDD}.Release|iPhone.Build.0 = Release|Any CPU
{A0DDCF0A-A352-4CC6-8E6E-0E16CAA50CDD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{A0DDCF0A-A352-4CC6-8E6E-0E16CAA50CDD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{E577CC6E-3616-4951-BDDE-F766291D623F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E577CC6E-3616-4951-BDDE-F766291D623F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E577CC6E-3616-4951-BDDE-F766291D623F}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{E577CC6E-3616-4951-BDDE-F766291D623F}.Debug|iPhone.Build.0 = Debug|Any CPU
{E577CC6E-3616-4951-BDDE-F766291D623F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{E577CC6E-3616-4951-BDDE-F766291D623F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{E577CC6E-3616-4951-BDDE-F766291D623F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E577CC6E-3616-4951-BDDE-F766291D623F}.Release|Any CPU.Build.0 = Release|Any CPU
{E577CC6E-3616-4951-BDDE-F766291D623F}.Release|iPhone.ActiveCfg = Release|Any CPU
{E577CC6E-3616-4951-BDDE-F766291D623F}.Release|iPhone.Build.0 = Release|Any CPU
{E577CC6E-3616-4951-BDDE-F766291D623F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{E577CC6E-3616-4951-BDDE-F766291D623F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{7C43DA3A-1707-4A56-B9E1-247A695FD294}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C43DA3A-1707-4A56-B9E1-247A695FD294}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C43DA3A-1707-4A56-B9E1-247A695FD294}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{7C43DA3A-1707-4A56-B9E1-247A695FD294}.Debug|iPhone.Build.0 = Debug|Any CPU
{7C43DA3A-1707-4A56-B9E1-247A695FD294}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{7C43DA3A-1707-4A56-B9E1-247A695FD294}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{7C43DA3A-1707-4A56-B9E1-247A695FD294}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C43DA3A-1707-4A56-B9E1-247A695FD294}.Release|Any CPU.Build.0 = Release|Any CPU
{7C43DA3A-1707-4A56-B9E1-247A695FD294}.Release|iPhone.ActiveCfg = Release|Any CPU
{7C43DA3A-1707-4A56-B9E1-247A695FD294}.Release|iPhone.Build.0 = Release|Any CPU
{7C43DA3A-1707-4A56-B9E1-247A695FD294}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{7C43DA3A-1707-4A56-B9E1-247A695FD294}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -397,6 +403,7 @@ Global
{0309CF11-621A-4F23-8FBA-A583303A8531} = {139F6FAA-EF59-4ADC-A69B-33CA7D1C1D4E}
{7AA1DB5D-78DB-4693-AE50-D6078F5A0CAB} = {5307589D-87A1-459B-BBD2-1077AA25EB1E}
{48783186-230D-4048-A97A-E4F1DF43BF5C} = {139F6FAA-EF59-4ADC-A69B-33CA7D1C1D4E}
{50334A1F-990D-45FA-A1FE-C92F1994D97B} = {D29A2153-C171-457E-A147-720976BA430F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {27D12E10-E38E-4ECE-8DD1-77E8C387B2DD}

View File

@@ -775,9 +775,19 @@ See the LICENCE file in the repository root for full licence text.&#xD;
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=StaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypeParameters/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=236f7aa5_002D7b06_002D43ca_002Dbf2a_002D9b31bfcff09a/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=2c62818f_002D621b_002D4425_002Dadc9_002D78611099bfcb/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Type parameters"&gt;&lt;ElementKinds&gt;&lt;Kind Name="TYPE_PARAMETER" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=4a98fdf6_002D7d98_002D4f5a_002Dafeb_002Dea44ad98c70c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=669e5282_002Dfb4b_002D4e90_002D91e7_002D07d269d04b60/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Constant fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=76f79b1e_002Dece7_002D4df2_002Da322_002D1bd7fea25eb7/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local functions"&gt;&lt;ElementKinds&gt;&lt;Kind Name="LOCAL_FUNCTION" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=8b8504e3_002Df0be_002D4c14_002D9103_002Dc732f2bddc15/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Enum members"&gt;&lt;ElementKinds&gt;&lt;Kind Name="ENUM_MEMBER" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=9d1af99b_002Dbefe_002D48a4_002D9eb3_002D661384e29869/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="private methods"&gt;&lt;ElementKinds&gt;&lt;Kind Name="ASYNC_METHOD" /&gt;&lt;Kind Name="METHOD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=9ffbe43b_002Dc610_002D411b_002D9839_002D1416a146d9b0/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public" Description="internal/protected/public methods"&gt;&lt;ElementKinds&gt;&lt;Kind Name="ASYNC_METHOD" /&gt;&lt;Kind Name="METHOD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a4c2df6c_002Db202_002D48d5_002Db077_002De678cb548c25/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="private properties"&gt;&lt;ElementKinds&gt;&lt;Kind Name="PROPERTY" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a4f433b8_002Dabcd_002D4e55_002Da08f_002D82e78cef0f0c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local constants"&gt;&lt;ElementKinds&gt;&lt;Kind Name="LOCAL_CONSTANT" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=c873eafb_002Dd57f_002D481d_002D8c93_002D77f6863c2f88/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static readonly fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=f9fce829_002De6f4_002D4cb2_002D80f1_002D5497c44f51df/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=fd562728_002Dc23d_002D417f_002Da19f_002D9d854247fbea/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public" Description="internal/protected/public properties"&gt;&lt;ElementKinds&gt;&lt;Kind Name="PROPERTY" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FCONSTANT/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FFUNCTION/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
@@ -843,6 +853,7 @@ See the LICENCE file in the repository root for full licence text.&#xD;
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/Environment/UnitTesting/NUnitProvider/SetCurrentDirectoryTo/@EntryValue">TestFolder</s:String>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=28A2A5FC43E07C488A4BC7430879479E/@KeyIndexDefined">True</s:Boolean>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=28A2A5FC43E07C488A4BC7430879479E/Applicability/=Live/@EntryIndexedValue">True</s:Boolean>

View File

@@ -14,6 +14,8 @@ using Org.Libsdl.App;
using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Platform;
using SDL;
using Debug = System.Diagnostics.Debug;
namespace osu.Framework.Android
{
@@ -74,17 +76,14 @@ namespace osu.Framework.Android
host?.Collect();
}
protected override string[] GetLibraries() => new string[] { "SDL2" };
protected override string[] GetLibraries() => new string[] { "SDL3" };
protected override SDLSurface CreateSDLSurface(Context context) => new AndroidGameSurface(this, context);
protected override SDLSurface CreateSDLSurface(Context? context) => new AndroidGameSurface(this, context);
protected override IRunnable CreateSDLMainRunnable() => new Runnable(() =>
{
// blocks back button
SDL2.SDL.SDL_SetHint(SDL2.SDL.SDL_HINT_ANDROID_TRAP_BACK_BUTTON, "1");
// accelerometer spams input thread
SDL2.SDL.SDL_SetHint(SDL2.SDL.SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
SDL3.SDL_SetHint(SDL3.SDL_HINT_ANDROID_TRAP_BACK_BUTTON, "1"u8);
// hints are here because they don't apply well in another location such as SDL2Window
@@ -99,6 +98,9 @@ namespace osu.Framework.Android
protected override void OnCreate(Bundle? savedInstanceState)
{
Debug.Assert(RuntimeInfo.EntryAssembly.IsNull(), "RuntimeInfo.EntryAssembly should be null on Android and therefore needs to be manually updated.");
RuntimeInfo.EntryAssembly = GetType().Assembly;
// The default current directory on android is '/'.
// On some devices '/' maps to the app data directory. On others it maps to the root of the internal storage.
// In order to have a consistent current directory on all devices the full path of the app data directory is set as the current directory.

View File

@@ -10,6 +10,7 @@ using osu.Framework.Android.Graphics.Textures;
using osu.Framework.Android.Graphics.Video;
using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
@@ -20,7 +21,7 @@ using Uri = Android.Net.Uri;
namespace osu.Framework.Android
{
public class AndroidGameHost : SDL2GameHost
public class AndroidGameHost : SDL3GameHost
{
private readonly AndroidGameActivity activity;
@@ -56,11 +57,9 @@ namespace osu.Framework.Android
public override Storage GetStorage(string path) => new AndroidStorage(path, this);
public override IEnumerable<string> UserStoragePaths => new[]
{
public override IEnumerable<string> UserStoragePaths
// not null as internal "external storage" is always available.
Application.Context.GetExternalFilesDir(string.Empty).AsNonNull().ToString(),
};
=> Application.Context.GetExternalFilesDir(string.Empty).AsNonNull().ToString().Yield();
public override bool OpenFileExternally(string filename) => false;

View File

@@ -21,7 +21,7 @@ namespace osu.Framework.Android
public BindableSafeArea SafeAreaPadding { get; } = new BindableSafeArea();
public AndroidGameSurface(AndroidGameActivity activity, Context context)
public AndroidGameSurface(AndroidGameActivity activity, Context? context)
: base(context)
{
init();

View File

@@ -6,7 +6,7 @@ using osu.Framework.Platform;
namespace osu.Framework.Android
{
internal class AndroidGameWindow : SDL2Window
internal class AndroidGameWindow : SDL3Window
{
public override IntPtr DisplayHandle => AndroidGameActivity.Surface.NativeSurface?.Handle ?? IntPtr.Zero;

View File

@@ -15,7 +15,7 @@
<PackageTags>osu game framework</PackageTags>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\SDL2-CS\SDL2-CS-Android\SDL2-CS-Android.csproj" />
<ProjectReference Include="..\..\SDL3-CS\SDL3-CS-Android\SDL3-CS-Android.csproj" />
<ProjectReference Include="..\osu.Framework\osu.Framework.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -11,15 +11,27 @@ namespace osu.Framework.Benchmarks
{
private readonly BindableInt source1 = new BindableInt();
private readonly BindableInt source2 = new BindableInt();
private readonly AggregateBindable<int> boundAggregate = new AggregateBindable<int>(((i, j) => i + j));
private readonly AggregateBindable<int> aggregate = new AggregateBindable<int>(((i, j) => i + j));
[GlobalSetup]
public void GlobalSetup()
{
boundAggregate.AddSource(source1);
boundAggregate.AddSource(source2);
}
[Benchmark]
public void AggregateRecalculation()
public void AddRemoveSource()
{
var aggregate = new AggregateBindable<int>(((i, j) => i + j));
aggregate.AddSource(source1);
aggregate.AddSource(source2);
aggregate.RemoveAllSources();
}
[Benchmark]
public void SetValue()
{
for (int i = 0; i < 100; i++)
{
source1.Value = i;

View File

@@ -0,0 +1,131 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using osu.Framework.Graphics.Rendering.Deferred.Allocation;
using osu.Framework.Graphics.Rendering.Deferred.Events;
namespace osu.Framework.Benchmarks
{
public class BenchmarkEventList
{
// Used for benchmark-local testing.
private ResourceAllocator localAllocator = null!;
private List<RenderEvent> localEventList = null!;
// Used for benchmark-static testing.
// 0: Basic events
// 1: Events with data
// 2: Mixed events
private readonly (ResourceAllocator allocator, List<RenderEvent> list)[] staticItems = new (ResourceAllocator allocator, List<RenderEvent> list)[3];
[GlobalSetup]
public void GlobalSetup()
{
localAllocator = new ResourceAllocator();
localEventList = new List<RenderEvent>();
for (int i = 0; i < staticItems.Length; i++)
{
ResourceAllocator allocator = new ResourceAllocator();
staticItems[i] = (allocator, new List<RenderEvent>());
}
for (int i = 0; i < 10000; i++)
{
staticItems[0].list.Add(RenderEvent.Init(new FlushEvent(new ResourceReference(1), 10)));
staticItems[1].list.Add(RenderEvent.Init(new AddPrimitiveToBatchEvent(new ResourceReference(0), staticItems[1].allocator.AllocateRegion(1024))));
staticItems[2].list.Add(i % 2 == 0
? RenderEvent.Init(new FlushEvent(new ResourceReference(1), 10))
: RenderEvent.Init(new AddPrimitiveToBatchEvent(new ResourceReference(0), staticItems[2].allocator.AllocateRegion(1024))));
}
}
[Benchmark]
public void Write()
{
localAllocator.NewFrame();
for (int i = 0; i < 10000; i++)
localEventList.Add(RenderEvent.Init(new FlushEvent()));
}
[Benchmark]
public void WriteWithData()
{
localAllocator.NewFrame();
for (int i = 0; i < 10000; i++)
localEventList.Add(RenderEvent.Init(new AddPrimitiveToBatchEvent(new ResourceReference(0), localAllocator.AllocateRegion(1024))));
}
[Benchmark]
public int Read()
{
int totalVertices = 0;
foreach (var renderEvent in staticItems[0].list)
{
switch (renderEvent.Type)
{
case RenderEventType.Flush:
FlushEvent e = (FlushEvent)renderEvent;
totalVertices += e.VertexCount;
break;
}
}
return totalVertices;
}
[Benchmark]
public int ReadWithData()
{
int data = 0;
foreach (var renderEvent in staticItems[1].list)
{
switch (renderEvent.Type)
{
case RenderEventType.AddPrimitiveToBatch:
AddPrimitiveToBatchEvent e = (AddPrimitiveToBatchEvent)renderEvent;
foreach (byte b in staticItems[1].allocator.GetRegion(e.Memory))
data += b;
break;
}
}
return data;
}
[Benchmark]
public int ReadMixed()
{
int data = 0;
foreach (var renderEvent in staticItems[2].list)
{
switch (renderEvent.Type)
{
case RenderEventType.Flush:
{
FlushEvent e = (FlushEvent)renderEvent;
data += e.VertexCount;
break;
}
case RenderEventType.AddPrimitiveToBatch:
{
AddPrimitiveToBatchEvent e = (AddPrimitiveToBatchEvent)renderEvent;
foreach (byte b in staticItems[2].allocator.GetRegion(e.Memory))
data += b;
break;
}
}
}
return data;
}
}
}

View File

@@ -19,13 +19,16 @@ FFMPEG_FLAGS+=(
pushd . > /dev/null
prep_ffmpeg linux-x64
# Apply patch from upstream to fix errors with new binutils versions:
# Ticket: https://fftrac-bg.ffmpeg.org/ticket/10405
# This patch should be removed when FFmpeg is updated to >=6.1
patch -p1 < "$SCRIPT_PATH/fix-binutils-2.41.patch"
build_ffmpeg
popd > /dev/null
# gcc creates multiple symlinks per .so file for versioning.
# We want to delete the symlinks to prevent weird behaviour with GitHub actions.
# We delete the symlinks and rename the real files to include the major library version
rm linux-x64/*.so
for f in linux-x64/*.so.*.*.*; do
mv -v "$f" "${f%.*.*.*}"
mv -vf "$f" "${f%.*.*}"
done
rm linux-x64/*.so.*

View File

@@ -0,0 +1,76 @@
From effadce6c756247ea8bae32dc13bb3e6f464f0eb Mon Sep 17 00:00:00 2001
From: =?utf8?q?R=C3=A9mi=20Denis-Courmont?= <remi@remlab.net>
Date: Sun, 16 Jul 2023 18:18:02 +0300
Subject: [PATCH] avcodec/x86/mathops: clip constants used with shift
instructions within inline assembly
Fixes assembling with binutil as >= 2.41
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavcodec/x86/mathops.h | 26 +++++++++++++++++++++++---
1 file changed, 23 insertions(+), 3 deletions(-)
diff --git a/libavcodec/x86/mathops.h b/libavcodec/x86/mathops.h
index 6298f5ed19..ca7e2dffc1 100644
--- a/libavcodec/x86/mathops.h
+++ b/libavcodec/x86/mathops.h
@@ -35,12 +35,20 @@
static av_always_inline av_const int MULL(int a, int b, unsigned shift)
{
int rt, dummy;
+ if (__builtin_constant_p(shift))
__asm__ (
"imull %3 \n\t"
"shrdl %4, %%edx, %%eax \n\t"
:"=a"(rt), "=d"(dummy)
- :"a"(a), "rm"(b), "ci"((uint8_t)shift)
+ :"a"(a), "rm"(b), "i"(shift & 0x1F)
);
+ else
+ __asm__ (
+ "imull %3 \n\t"
+ "shrdl %4, %%edx, %%eax \n\t"
+ :"=a"(rt), "=d"(dummy)
+ :"a"(a), "rm"(b), "c"((uint8_t)shift)
+ );
return rt;
}
@@ -113,19 +121,31 @@ __asm__ volatile(\
// avoid +32 for shift optimization (gcc should do that ...)
#define NEG_SSR32 NEG_SSR32
static inline int32_t NEG_SSR32( int32_t a, int8_t s){
+ if (__builtin_constant_p(s))
__asm__ ("sarl %1, %0\n\t"
: "+r" (a)
- : "ic" ((uint8_t)(-s))
+ : "i" (-s & 0x1F)
);
+ else
+ __asm__ ("sarl %1, %0\n\t"
+ : "+r" (a)
+ : "c" ((uint8_t)(-s))
+ );
return a;
}
#define NEG_USR32 NEG_USR32
static inline uint32_t NEG_USR32(uint32_t a, int8_t s){
+ if (__builtin_constant_p(s))
__asm__ ("shrl %1, %0\n\t"
: "+r" (a)
- : "ic" ((uint8_t)(-s))
+ : "i" (-s & 0x1F)
);
+ else
+ __asm__ ("shrl %1, %0\n\t"
+ : "+r" (a)
+ : "c" ((uint8_t)(-s))
+ );
return a;
}
--
2.30.2

View File

@@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="TemplateGame.Desktop" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/TemplateGame.Desktop/bin/Debug/net5.0/TemplateGame.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/TemplateGame.Desktop/bin/Debug/net8.0/TemplateGame.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/TemplateGame.Desktop/bin/Debug/net5.0" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/TemplateGame.Desktop/bin/Debug/net8.0" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net5.0" />
<option name="PROJECT_TFM" value="net8.0" />
<method v="2">
<option name="Build" enabled="true" />
</method>

View File

@@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="TemplateGame.Tests" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/TemplateGame.Game.Tests/bin/Debug/net5.0/TemplateGame.Game.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/TemplateGame.Game.Tests/bin/Debug/net8.0/TemplateGame.Game.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/TemplateGame.Game.Tests/bin/Debug/net5.0" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/TemplateGame.Game.Tests/bin/Debug/net8.0" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net5.0" />
<option name="PROJECT_TFM" value="net8.0" />
<method v="2">
<option name="Build" enabled="true" />
</method>

View File

@@ -9,13 +9,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/TemplateGame.Game.Tests/bin/Debug/net6.0/TemplateGame.Game.Tests.dll",
"${workspaceRoot}/TemplateGame.Game.Tests/bin/Debug/net8.0/TemplateGame.Game.Tests.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Tests, Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/TemplateGame.Game.Tests/bin/Debug/net6.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/TemplateGame.Game.Tests/bin/Debug/net8.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@@ -26,13 +26,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/TemplateGame.Game.Tests/bin/Release/net6.0/TemplateGame.Game.Tests.dll",
"${workspaceRoot}/TemplateGame.Game.Tests/bin/Release/net8.0/TemplateGame.Game.Tests.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Tests, Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/TemplateGame.Game.Tests/bin/Release/net6.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/TemplateGame.Game.Tests/bin/Release/net8.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@@ -43,13 +43,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/TemplateGame.Desktop/bin/Debug/net6.0/TemplateGame.dll",
"${workspaceRoot}/TemplateGame.Desktop/bin/Debug/net8.0/TemplateGame.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Desktop, Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/TemplateGame.Desktop/bin/Debug/net6.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/TemplateGame.Desktop/bin/Debug/net8.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@@ -60,13 +60,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/TemplateGame.Desktop/bin/Debug/net6.0/TemplateGame.dll",
"${workspaceRoot}/TemplateGame.Desktop/bin/Debug/net8.0/TemplateGame.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Desktop, Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/TemplateGame.Desktop/bin/Debug/net6.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/TemplateGame.Desktop/bin/Debug/net8.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"

View File

@@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="FlappyDon.Desktop" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/FlappyDon.Desktop/bin/Debug/net5.0/FlappyDon.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/FlappyDon.Desktop/bin/Debug/net8.0/FlappyDon.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/FlappyDon.Desktop/bin/Debug/net5.0" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/FlappyDon.Desktop/bin/Debug/net8.0" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net5.0" />
<option name="PROJECT_TFM" value="net8.0" />
<method v="2">
<option name="Build" enabled="true" />
</method>

View File

@@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="FlappyDon.Tests" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/FlappyDon.Game.Tests/bin/Debug/net5.0/FlappyDon.Game.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/FlappyDon.Game.Tests/bin/Debug/net8.0/FlappyDon.Game.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/FlappyDon.Game.Tests/bin/Debug/net5.0" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/FlappyDon.Game.Tests/bin/Debug/net8.0" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net5.0" />
<option name="PROJECT_TFM" value="net8.0" />
<method v="2">
<option name="Build" enabled="true" />
</method>

View File

@@ -9,13 +9,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/FlappyDon.Game.Tests/bin/Debug/net6.0/FlappyDon.Game.Tests.dll",
"${workspaceRoot}/FlappyDon.Game.Tests/bin/Debug/net8.0/FlappyDon.Game.Tests.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Tests, Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/FlappyDon.Game.Tests/bin/Debug/net6.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/FlappyDon.Game.Tests/bin/Debug/net8.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@@ -26,13 +26,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/FlappyDon.Game.Tests/bin/Release/net6.0/FlappyDon.Game.Tests.dll",
"${workspaceRoot}/FlappyDon.Game.Tests/bin/Release/net8.0/FlappyDon.Game.Tests.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Tests, Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/FlappyDon.Game.Tests/bin/Release/net6.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/FlappyDon.Game.Tests/bin/Release/net8.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@@ -43,13 +43,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/FlappyDon.Desktop/bin/Debug/net6.0/FlappyDon.dll",
"${workspaceRoot}/FlappyDon.Desktop/bin/Debug/net8.0/FlappyDon.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Desktop, Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/FlappyDon.Desktop/bin/Debug/net6.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/FlappyDon.Desktop/bin/Debug/net8.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@@ -60,13 +60,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/FlappyDon.Desktop/bin/Debug/net6.0/FlappyDon.dll",
"${workspaceRoot}/FlappyDon.Desktop/bin/Debug/net8.0/FlappyDon.dll",
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Desktop, Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/FlappyDon.Desktop/bin/Debug/net6.0:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/FlappyDon.Desktop/bin/Debug/net8.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"

View File

@@ -121,6 +121,14 @@ namespace osu.Framework.Tests.Visual.Containers
assertSpriteTextCount(0);
}
[Test]
public void TestWordSplittingEdgeCases()
{
AddStep("set latin text", () => textContainer.Text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer mattis eu turpis vitae posuere. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam mauris nibh, faucibus maximus ornare eu, ultrices ut ipsum. Proin rhoncus, nunc et faucibus pretium, nisl nunc dapibus massa, et scelerisque nibh ligula id odio. Praesent dapibus ex sed nunc egestas, in placerat risus mattis. Nulla sed ligula velit. Vestibulum auctor porta eros et condimentum. Etiam laoreet nunc nec lacinia pulvinar. Mauris hendrerit, mi at aliquet condimentum, ex ex cursus dolor, non porta erat eros id justo. Cras malesuada tincidunt nunc, at tincidunt risus eleifend id. Maecenas hendrerit venenatis mi et lobortis. Etiam sem tortor, elementum eget lacus non, porta tristique quam. Morbi sed lacinia odio. Phasellus ut pretium nunc. Fusce vitae mollis magna, vel scelerisque dui. ");
AddStep("set url", () => textContainer.Text = "https://osu.ppy.sh/home/news/2024-03-27-osutaiko-world-cup-2024-round-of-32-recap");
AddStep("set cjk text", () => textContainer.Text = "日本の桜は世界中から観光客を引きつけています。寿司は美味しい伝統的な日本食です。東京タワーは景色が美しいです。速い新幹線は、便利な交通手段です。富士山は、その美しさと完全な形状で知られています。日本文化は、優雅さと繊細さを象徴しています。抹茶は特別な日本の茶です。着物は、伝統的な日本の衣装で、特別な場面でよく着用されます。");
}
private void assertSpriteTextCount(int count)
=> AddAssert($"text flow has {count} sprite texts", () => textContainer.ChildrenOfType<SpriteText>().Count() == count);

View File

@@ -82,7 +82,7 @@ namespace osu.Framework.Tests.Visual.Graphics
Size = new Vector2(circle_radius),
RelativePositionAxes = Axes.Both,
Position = new Vector2(xPos, yPos),
Current = { Value = 1 }
Progress = 1
});
circlesContainerBuffered.Add(new CircularProgress
@@ -91,7 +91,7 @@ namespace osu.Framework.Tests.Visual.Graphics
Size = new Vector2(circle_radius),
RelativePositionAxes = Axes.Both,
Position = new Vector2(xPos, yPos),
Current = { Value = 1 }
Progress = 1
});
}
}

View File

@@ -20,23 +20,37 @@ namespace osu.Framework.Tests.Visual.Input
{
public partial class TestSceneTabletInput : FrameworkTestScene
{
private readonly FillFlowContainer contentFlow;
private readonly SpriteText tabletInfo;
private readonly TabletAreaVisualiser areaVisualizer;
private readonly FillFlowContainer penButtonFlow;
private readonly FillFlowContainer auxButtonFlow;
private IBindable<TabletInfo?> tablet = new Bindable<TabletInfo?>();
private IBindable<bool> tabletEnabled = new Bindable<bool>();
[Resolved]
private FrameworkConfigManager frameworkConfigManager { get; set; } = null!;
public TestSceneTabletInput()
{
var penButtonFlow = new FillFlowContainer
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
};
var auxButtonFlow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
tabletInfo = new SpriteText(),
areaVisualizer = new TabletAreaVisualiser(),
penButtonFlow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
},
auxButtonFlow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
},
}
};
for (int i = 0; i < 8; i++)
@@ -44,13 +58,6 @@ namespace osu.Framework.Tests.Visual.Input
for (int i = 0; i < 16; i++)
auxButtonFlow.Add(new AuxiliaryButtonHandler(i));
Child = contentFlow = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new[] { penButtonFlow, auxButtonFlow }
};
}
[Resolved]
@@ -64,9 +71,17 @@ namespace osu.Framework.Tests.Visual.Input
if (tabletHandler != null)
{
areaVisualizer.AreaSize.BindTo(tabletHandler.AreaSize);
areaVisualizer.AreaOffset.BindTo(tabletHandler.AreaOffset);
tablet = tabletHandler.Tablet.GetBoundCopy();
tablet.BindValueChanged(_ => updateState(), true);
tabletEnabled = tabletHandler.Enabled.GetBoundCopy();
tabletEnabled.BindValueChanged(_ => updateState(), true);
AddToggleStep("toggle tablet handling", t => tabletHandler.Enabled.Value = t);
contentFlow.Insert(-1, new TabletAreaVisualiser(tabletHandler));
AddSliderStep("change width", 0, 1, 1f,
width => tabletHandler.AreaSize.Value = new Vector2(
tabletHandler.AreaSize.Default.X * width,
@@ -92,20 +107,25 @@ namespace osu.Framework.Tests.Visual.Input
enabled ? ConfineMouseMode.Always : ConfineMouseMode.Never));
}
private void updateState()
{
if (tabletEnabled.Value)
tabletInfo.Text = tablet.Value != null ? $"Name: {tablet.Value.Name} Size: {tablet.Value.Size}" : "No tablet detected!";
else
tabletInfo.Text = "Tablet input is disabled.";
areaVisualizer.Alpha = penButtonFlow.Alpha = auxButtonFlow.Alpha = tablet.Value != null && tabletEnabled.Value ? 1 : 0;
}
private partial class TabletAreaVisualiser : CompositeDrawable
{
private readonly OpenTabletDriverHandler handler;
public readonly Bindable<Vector2> AreaSize = new Bindable<Vector2>();
public readonly Bindable<Vector2> AreaOffset = new Bindable<Vector2>();
private Box fullArea = null!;
private Container activeArea = null!;
private Bindable<Vector2> areaSize = null!;
private Bindable<Vector2> areaOffset = null!;
public TabletAreaVisualiser(OpenTabletDriverHandler handler)
{
this.handler = handler;
}
private SpriteText areaText = null!;
[BackgroundDependencyLoader]
private void load()
@@ -119,8 +139,8 @@ namespace osu.Framework.Tests.Visual.Input
{
fullArea = new Box
{
Width = handler.AreaSize.Default.X,
Height = handler.AreaSize.Default.Y,
Width = AreaSize.Default.X,
Height = AreaSize.Default.Y,
Colour = FrameworkColour.GreenDark
},
activeArea = new Container
@@ -133,14 +153,13 @@ namespace osu.Framework.Tests.Visual.Input
RelativeSizeAxes = Axes.Both,
Colour = FrameworkColour.YellowGreen
},
new SpriteText
areaText = new SpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = "Active area"
}
}
}
},
}
};
}
@@ -149,13 +168,15 @@ namespace osu.Framework.Tests.Visual.Input
{
base.LoadComplete();
areaSize = handler.AreaSize.GetBoundCopy();
areaSize.BindValueChanged(size => activeArea.Size = size.NewValue, true);
areaSize.DefaultChanged += fullSize => fullArea.Size = fullSize.NewValue;
fullArea.Size = areaSize.Default;
AreaSize.BindValueChanged(size =>
{
activeArea.Size = size.NewValue;
areaText.Text = $"Active area: {size.NewValue}";
}, true);
AreaSize.DefaultChanged += fullSize => fullArea.Size = fullSize.NewValue;
fullArea.Size = AreaSize.Default;
areaOffset = handler.AreaOffset.GetBoundCopy();
areaOffset.BindValueChanged(offset => activeArea.Position = offset.NewValue, true);
AreaOffset.BindValueChanged(offset => activeArea.Position = offset.NewValue, true);
}
}

View File

@@ -17,7 +17,7 @@ namespace osu.Framework.Tests.Visual.Performance
base.Update();
foreach (var p in Flow.OfType<CircularProgress>())
p.Current.Value = (p.Current.Value + (Time.Elapsed * RNG.NextSingle()) / 1000) % 1;
p.Progress = (p.Progress + (Time.Elapsed * RNG.NextSingle()) / 1000) % 1;
}
}
}

View File

@@ -24,7 +24,7 @@ namespace osu.Framework.Tests.Visual.Platform
private readonly SpriteText currentWindowMode = new SpriteText();
private readonly SpriteText currentDisplay = new SpriteText();
private SDL2Window? window;
private SDL3Window? window;
private readonly Bindable<WindowMode> windowMode = new Bindable<WindowMode>();
public TestSceneBorderless()
@@ -57,7 +57,7 @@ namespace osu.Framework.Tests.Visual.Platform
[BackgroundDependencyLoader]
private void load(FrameworkConfigManager config, GameHost host)
{
window = host.Window as SDL2Window;
window = host.Window as SDL3Window;
config.BindWith(FrameworkSetting.WindowMode, windowMode);
windowMode.BindValueChanged(mode => currentWindowMode.Text = $"Window Mode: {mode.NewValue}", true);

View File

@@ -44,7 +44,7 @@ namespace osu.Framework.Tests.Visual.Platform
WindowMode startingMode = getWindowModeForState(startingState);
// this shouldn't be necessary, but SDL2DesktopWindow doesn't set the config WindowMode when changing the WindowState only.
// this shouldn't be necessary, but SDL3DesktopWindow doesn't set the config WindowMode when changing the WindowState only.
AddStep($"switch to {startingMode}", () => window.WindowMode.Value = startingMode);
AddStep($"switch to {startingState}", () => window.WindowState = startingState);

View File

@@ -0,0 +1,85 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Reflection;
using System.Runtime.CompilerServices;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Framework.Platform;
namespace osu.Framework.Tests.Visual.Platform
{
public partial class TestSceneDebugUtils : FrameworkTestScene
{
private readonly TextFlowContainer textFlow;
private bool isHeadlessTestRun;
public TestSceneDebugUtils()
{
Child = textFlow = new TextFlowContainer
{
RelativeSizeAxes = Axes.Both
};
}
[BackgroundDependencyLoader]
private void load(GameHost host)
{
isHeadlessTestRun = host.Window == null;
}
[Test]
public void LogStatics()
{
AddStep("log DebugUtils statics", () =>
{
textFlow.Clear();
log(DebugUtils.IsNUnitRunning);
log(DebugUtils.IsDebugBuild);
log(RuntimeInfo.EntryAssembly);
#pragma warning disable RS0030
log(Assembly.GetEntryAssembly());
#pragma warning restore RS0030
});
}
[Test]
public void TestIsNUnitRunning()
{
AddAssert("check IsNUnitRunning", () => DebugUtils.IsNUnitRunning, () => Is.EqualTo(isHeadlessTestRun));
}
[Test]
public void TestIsDebugBuild()
{
AddAssert("check IsDebugBuild", () => DebugUtils.IsDebugBuild, () => Is.EqualTo(
#if DEBUG
true
#else
false
#endif
));
}
[Test]
public void TestEntryAssembly()
{
AddAssert("check RuntimeInfo.EntryAssembly", () => RuntimeInfo.EntryAssembly.FullName, () => Does.StartWith("osu.Framework.Tests"));
}
/// <summary>
/// Logs the <paramref name="name"/> and <paramref name="value"/> on the screen and in the logs.
/// </summary>
private void log<T>(T value, [CallerArgumentExpression("value")] string? name = null)
{
string text = $"{name}: {value}";
textFlow.AddParagraph(text);
Logger.Log(text);
}
}
}

View File

@@ -129,7 +129,7 @@ namespace osu.Framework.Tests.Visual.Platform
if (window.SupportedWindowModes.Contains(WindowMode.Fullscreen))
{
AddStep("change to fullscreen", () => windowMode.Value = WindowMode.Fullscreen);
AddAssert("window position updated", () => ((SDL2Window)window).Position, () => Is.EqualTo(window.CurrentDisplayBindable.Value.Bounds.Location));
AddAssert("window position updated", () => ((SDL3Window)window).Position, () => Is.EqualTo(window.CurrentDisplayBindable.Value.Bounds.Location));
testResolution(1920, 1080);
testResolution(1280, 960);
testResolution(9999, 9999);

View File

@@ -31,12 +31,12 @@ namespace osu.Framework.Tests.Visual.Platform
[Resolved]
private FrameworkConfigManager config { get; set; }
private SDL2Window sdlWindow;
private SDL3Window sdlWindow;
[BackgroundDependencyLoader]
private void load()
{
sdlWindow = (SDL2Window)host.Window;
sdlWindow = (SDL3Window)host.Window;
Children = new Drawable[]
{
new FillFlowContainer

View File

@@ -36,7 +36,7 @@ namespace osu.Framework.Tests.Visual.Platform
private static readonly Color4 window_fill = new Color4(95, 113, 197, 255);
private static readonly Color4 window_stroke = new Color4(36, 59, 166, 255);
private SDL2Window? window;
private SDL3Window? window;
private readonly Bindable<WindowMode> windowMode = new Bindable<WindowMode>();
private readonly Bindable<Display> currentDisplay = new Bindable<Display>();
@@ -90,7 +90,7 @@ namespace osu.Framework.Tests.Visual.Platform
[BackgroundDependencyLoader]
private void load(FrameworkConfigManager config, GameHost host)
{
window = host.Window as SDL2Window;
window = host.Window as SDL3Window;
config.BindWith(FrameworkSetting.WindowMode, windowMode);
if (window != null)

View File

@@ -145,23 +145,23 @@ namespace osu.Framework.Tests.Visual.UserInterface
switch (rotateMode)
{
case 0:
clock.Current.Value = Time.Current % (period * 2) / period - 1;
clock.Progress = Time.Current % (period * 2) / period - 1;
break;
case 1:
clock.Current.Value = Time.Current % period / period;
clock.Progress = Time.Current % period / period;
break;
case 2:
clock.Current.Value = Time.Current % period / period - 1;
clock.Progress = Time.Current % period / period - 1;
break;
case 3:
clock.Current.Value = Time.Current % transition_period / transition_period / 5 - 0.1f;
clock.Progress = Time.Current % transition_period / transition_period / 5 - 0.1f;
break;
case 4:
clock.Current.Value = (Time.Current % transition_period / transition_period / 5 - 0.1f + 2) % 2 - 1;
clock.Progress = (Time.Current % transition_period / transition_period / 5 - 0.1f + 2) % 2 - 1;
break;
}
}
@@ -245,19 +245,19 @@ namespace osu.Framework.Tests.Visual.UserInterface
switch (tf)
{
case 0:
clock.FillTo(0).Then().FillTo(1, 1000).Loop();
clock.ProgressTo(0).Then().ProgressTo(1, 1000).Loop();
break;
case 1:
clock.FillTo(1).Then().FillTo(0, 1000).Loop();
clock.ProgressTo(1).Then().ProgressTo(0, 1000).Loop();
break;
case 2:
clock.FillTo(0, 1000).Then().FillTo(1, 1000).Loop();
clock.ProgressTo(0, 1000).Then().ProgressTo(1, 1000).Loop();
break;
case 3:
clock.FillTo(0).Then().FillTo(1, 1000, Easing.InOutQuart).Loop();
clock.ProgressTo(0).Then().ProgressTo(1, 1000, Easing.InOutQuart).Loop();
break;
}
}

View File

@@ -86,9 +86,27 @@ Line below";
}
[Test]
public void TestFencedCode()
public void TestIndentedCodeBlock()
{
AddStep("Markdown Fenced Code", () =>
AddStep("Markdown Indented Code Block", () =>
{
markdownContainer.Text = @"
[Escape me]
[[Escape me]]
{{
x = ""5"" # This assignment will not output anything
x # This expression will print 5
x + 1 # This expression will print 6
}}
";
});
}
[Test]
public void TestFencedCodeBlock()
{
AddStep("Markdown Fenced Code Block", () =>
{
markdownContainer.Text = @"```scriban-html

View File

@@ -0,0 +1,148 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Framework.Testing;
using osuTK;
using osuTK.Input;
namespace osu.Framework.Tests.Visual.UserInterface
{
public partial class TestSceneTabControlEvents : ManualInputManagerTestScene
{
private EventQueuesTabControl tabControl = null!;
[SetUpSteps]
public void SetUpSteps()
{
AddStep("add tab control", () => Child = tabControl = new EventQueuesTabControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(150, 40),
Items = Enum.GetValues<TestEnum>(),
});
AddAssert("selected tab queue empty", () => tabControl.UserTabSelectionChangedQueue.Count == 0);
}
[Test]
public void TestClickSendsEvent()
{
AddStep("click second tab", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<TabItem<TestEnum>>().ElementAt(1));
InputManager.Click(MouseButton.Left);
});
AddAssert("selected tab = second", () => tabControl.Current.Value == TestEnum.Second);
AddAssert("selected tab queue has \"second\"", () => tabControl.UserTabSelectionChangedQueue.Dequeue().Value == TestEnum.Second);
}
[Test]
public void TestClickSameTabDoesNotSendEvent()
{
AddAssert("first tab selected", () => tabControl.Current.Value == TestEnum.First);
AddStep("click first tab", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<TabItem<TestEnum>>().First());
InputManager.Click(MouseButton.Left);
});
AddAssert("first tab still selected", () => tabControl.Current.Value == TestEnum.First);
AddAssert("selected tab queue empty", () => tabControl.UserTabSelectionChangedQueue.Count == 0);
}
[Test]
public void TestSelectItemMethodSendsEvent()
{
AddStep("call select item", () => tabControl.SelectItem(TestEnum.Second));
AddAssert("selected tab queue has \"second\"", () => tabControl.UserTabSelectionChangedQueue.Dequeue().Value == TestEnum.Second);
}
[Test]
public void TestSwitchTabMethodSendsEvent()
{
AddStep("set switchable", () => tabControl.IsSwitchable = true);
AddStep("call switch tab", () => tabControl.SwitchTab(1));
AddAssert("selected tab = second", () => tabControl.Current.Value == TestEnum.Second);
AddAssert("selected tab queue has \"second\"", () => tabControl.UserTabSelectionChangedQueue.Dequeue().Value == TestEnum.Second);
AddStep("call switch tab", () => tabControl.SwitchTab(-1));
AddAssert("selected tab = second", () => tabControl.Current.Value == TestEnum.First);
AddAssert("selected tab queue has \"second\"", () => tabControl.UserTabSelectionChangedQueue.Dequeue().Value == TestEnum.First);
}
[Test]
public void TestSwitchUsingKeyBindingSendsEvent()
{
AddStep("set switchable", () => tabControl.IsSwitchable = true);
AddStep("switch forward", () => InputManager.Keys(PlatformAction.DocumentNext));
AddAssert("selected tab = second", () => tabControl.Current.Value == TestEnum.Second);
AddAssert("selected tab queue has \"second\"", () => tabControl.UserTabSelectionChangedQueue.Dequeue().Value == TestEnum.Second);
AddStep("switch backward", () => InputManager.Keys(PlatformAction.DocumentPrevious));
AddAssert("selected tab = second", () => tabControl.Current.Value == TestEnum.First);
AddAssert("selected tab queue has \"second\"", () => tabControl.UserTabSelectionChangedQueue.Dequeue().Value == TestEnum.First);
}
[Test]
public void TestSwitchOnRemovalDoesNotSendEvent()
{
AddStep("set switchable", () => tabControl.IsSwitchable = true);
AddStep("remove first tab", () => tabControl.RemoveItem(TestEnum.First));
AddAssert("selected tab = second", () => tabControl.Current.Value == TestEnum.Second);
AddAssert("selected tab queue still empty", () => tabControl.UserTabSelectionChangedQueue.Count == 0);
}
[Test]
public void TestBindableChangeDoesNotSendEvent()
{
AddStep("set selected tab = second", () => tabControl.Current.Value = TestEnum.Second);
AddAssert("selected tab queue still empty", () => tabControl.UserTabSelectionChangedQueue.Count == 0);
}
[TearDownSteps]
public void TearDownSteps()
{
AddAssert("selected tab queue empty", () => tabControl.UserTabSelectionChangedQueue.Count == 0);
}
private partial class EventQueuesTabControl : BasicTabControl<TestEnum>
{
public readonly Queue<TabItem<TestEnum>> UserTabSelectionChangedQueue = new Queue<TabItem<TestEnum>>();
private Box background = null!;
[BackgroundDependencyLoader]
private void load()
{
AddInternal(background = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = FrameworkColour.YellowGreen,
Alpha = 0,
});
}
protected override void OnUserTabSelectionChanged(TabItem<TestEnum> item)
{
UserTabSelectionChangedQueue.Enqueue(item);
background.FadeOutFromOne(500);
}
}
private enum TestEnum
{
First,
Second,
}
}
}

View File

@@ -5,7 +5,6 @@
using System.Linq;
using NUnit.Framework;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
@@ -828,6 +827,60 @@ namespace osu.Framework.Tests.Visual.UserInterface
AddAssert("all text selected", () => textBox.SelectedText, () => Is.EqualTo(textBox.Text));
}
[Test]
public void TestCursorMovementWithSelection()
{
TextBox textBox = null;
AddStep("add textbox", () =>
{
textBoxes.Add(textBox = new BasicTextBox
{
Size = new Vector2(300, 40),
Text = "cwm fjord glyphs vext bank quiz",
ReadOnly = false
});
});
AddStep("focus textbox", () =>
{
InputManager.MoveMouseTo(textBox);
InputManager.Click(MouseButton.Left);
});
// left char move should put cursor at left end of selection
AddStep("select all", () => textBox.SelectAll());
AddStep("move cursor backward (char)", () => InputManager.Keys(PlatformAction.MoveBackwardChar));
AddStep("select next word", () => InputManager.Keys(PlatformAction.SelectForwardWord));
AddAssert("first word selected", () => textBox.SelectedText == "cwm");
// forward word move should put cursor at right end of selection
AddStep("move cursor forward (word)", () => InputManager.Keys(PlatformAction.MoveForwardWord));
AddStep("select next word", () => InputManager.Keys(PlatformAction.SelectForwardWord));
AddAssert("second word selected", () => textBox.SelectedText == " fjord");
// same thing but for "back-facing" selection
AddStep("move cursor forward (word)", () => InputManager.Keys(PlatformAction.MoveForwardWord));
AddStep("select previous word", () => InputManager.Keys(PlatformAction.SelectBackwardWord));
AddAssert("second word selected", () => textBox.SelectedText == "fjord");
// right char move should put cursor at right end of selection
AddStep("select all", () => textBox.SelectAll());
AddStep("move cursor forward (char)", () => InputManager.Keys(PlatformAction.MoveForwardChar));
AddStep("select previous word", () => InputManager.Keys(PlatformAction.SelectBackwardWord));
AddAssert("last word selected", () => textBox.SelectedText == "quiz");
// backward word move should put cursor at left end of selection
AddStep("move cursor backward (word)", () => InputManager.Keys(PlatformAction.MoveBackwardWord));
AddStep("select previous word", () => InputManager.Keys(PlatformAction.SelectBackwardWord));
AddAssert("second-from-last word selected", () => textBox.SelectedText == "bank ");
// same thing but for "front-facing" selection
AddStep("move cursor backward (word)", () => InputManager.Keys(PlatformAction.MoveBackwardWord));
AddStep("select next word", () => InputManager.Keys(PlatformAction.SelectForwardWord));
AddAssert("second-from-last word selected", () => textBox.SelectedText == "bank");
}
private void prependString(InsertableTextBox textBox, string text)
{
InputManager.Keys(PlatformAction.MoveBackwardLine);
@@ -870,7 +923,7 @@ namespace osu.Framework.Tests.Visual.UserInterface
private partial class NumberTextBox : BasicTextBox
{
protected override bool CanAddCharacter(char character) => character.IsAsciiDigit();
protected override bool CanAddCharacter(char character) => char.IsAsciiDigit(character);
protected override bool AllowIme => false;
}

View File

@@ -2,14 +2,14 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using AVFoundation;
using Foundation;
using ManagedBass;
using ManagedBass.Fx;
using ManagedBass.Mix;
using ObjCRuntime;
using SDL2;
using SDL;
namespace osu.Framework.iOS
{
@@ -22,20 +22,20 @@ namespace osu.Framework.iOS
private static readonly OutputVolumeObserver output_volume_observer = new OutputVolumeObserver();
public static void Main(Game target)
public static unsafe void Main(Game target)
{
NativeLibrary.SetDllImportResolver(typeof(Bass).Assembly, (_, assembly, path) => NativeLibrary.Load("@rpath/bass.framework/bass", assembly, path));
NativeLibrary.SetDllImportResolver(typeof(BassFx).Assembly, (_, assembly, path) => NativeLibrary.Load("@rpath/bass_fx.framework/bass_fx", assembly, path));
NativeLibrary.SetDllImportResolver(typeof(BassMix).Assembly, (_, assembly, path) => NativeLibrary.Load("@rpath/bassmix.framework/bassmix", assembly, path));
NativeLibrary.SetDllImportResolver(typeof(SDL3).Assembly, (_, assembly, path) => NativeLibrary.Load("@rpath/SDL3.framework/SDL3", assembly, path));
game = target;
SDL.PrepareLibraryForIOS();
SDL.SDL_UIKitRunApp(0, IntPtr.Zero, main);
SDL3.SDL_RunApp(0, null, &main, IntPtr.Zero);
}
[MonoPInvokeCallback(typeof(SDL.SDL_main_func))]
private static int main(int argc, IntPtr argv)
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static unsafe int main(int argc, byte** argv)
{
var audioSession = AVAudioSession.SharedInstance();
audioSession.AddObserver(output_volume_observer, output_volume, NSKeyValueObservingOptions.New, 0);

View File

@@ -21,7 +21,7 @@ using UIKit;
namespace osu.Framework.iOS
{
public class IOSGameHost : SDL2GameHost
public class IOSGameHost : SDL3GameHost
{
public IOSGameHost()
: base(string.Empty)

View File

@@ -4,16 +4,18 @@
using System;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using ObjCRuntime;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Platform;
using SDL2;
using SDL;
using UIKit;
namespace osu.Framework.iOS
{
internal class IOSWindow : SDL2Window
internal class IOSWindow : SDL3Window
{
private UIWindow? window;
@@ -34,10 +36,10 @@ namespace osu.Framework.iOS
{
}
protected override void UpdateWindowStateAndSize(WindowState state, Display display, DisplayMode displayMode)
protected override unsafe void UpdateWindowStateAndSize(WindowState state, Display display, DisplayMode displayMode)
{
// This sets the status bar to hidden.
SDL.SDL_SetWindowFullscreen(SDLWindowHandle, (uint)SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN);
SDL3.SDL_SetWindowFullscreen(SDLWindowHandle, SDL_bool.SDL_TRUE);
// Don't run base logic at all. Let's keep things simple.
}
@@ -50,7 +52,7 @@ namespace osu.Framework.iOS
updateSafeArea();
}
protected override void RunMainLoop()
protected override unsafe void RunMainLoop()
{
// Delegate running the main loop to CADisplayLink.
//
@@ -60,11 +62,11 @@ namespace osu.Framework.iOS
// iOS may be a good forward direction if this ever comes up, as a user may see a potentially higher
// frame rate with multi-threaded mode turned on, but it is going to give them worse input latency
// and higher power usage.
SDL.SDL_iPhoneSetEventPump(SDL.SDL_bool.SDL_FALSE);
SDL.SDL_iPhoneSetAnimationCallback(SDLWindowHandle, 1, runFrame, ObjectHandle.Handle);
SDL3.SDL_iPhoneSetEventPump(SDL_bool.SDL_FALSE);
SDL3.SDL_iPhoneSetAnimationCallback(SDLWindowHandle, 1, &runFrame, ObjectHandle.Handle);
}
[ObjCRuntime.MonoPInvokeCallback(typeof(SDL.SDL_iPhoneAnimationCallback))]
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static void runFrame(IntPtr userdata)
{
var handle = new ObjectHandle<IOSWindow>(userdata);

View File

@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using JetBrains.Annotations;
namespace osu.Framework.Allocation
{
@@ -58,7 +59,7 @@ namespace osu.Framework.Allocation
/// </summary>
/// <param name="sender">The sender which should appear in the <paramref name="action"/> callback.</param>
/// <param name="action">The action to invoke during disposal.</param>
public InvokeOnDisposal(T sender, Action<T> action)
public InvokeOnDisposal(T sender, [RequireStaticDelegate(IsError = true)] Action<T> action)
{
this.sender = sender;
this.action = action ?? throw new ArgumentNullException(nameof(action));

View File

@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using JetBrains.Annotations;
namespace osu.Framework.Allocation
{
@@ -62,7 +63,7 @@ namespace osu.Framework.Allocation
/// </summary>
/// <param name="sender">The sender which should appear in the <paramref name="action"/> callback.</param>
/// <param name="action">The action to invoke during disposal.</param>
public ValueInvokeOnDisposal(T sender, Action<T> action)
public ValueInvokeOnDisposal(T sender, [RequireStaticDelegate(IsError = true)] Action<T> action)
{
this.sender = sender;
this.action = action ?? throw new ArgumentNullException(nameof(action));

View File

@@ -74,8 +74,7 @@ namespace osu.Framework.Audio
{
ThreadSafety.EnsureNotUpdateThread();
if (IsDisposed)
throw new ObjectDisposedException(ToString(), "Can not update disposed audio components.");
ObjectDisposedException.ThrowIf(IsDisposed, this);
FrameStatistics.Add(StatisticsCounterType.TasksRun, PendingActions.Count);
FrameStatistics.Increment(StatisticsCounterType.Components);

View File

@@ -10,7 +10,6 @@ using System.Runtime.InteropServices;
using ManagedBass;
using ManagedBass.Mix;
using osu.Framework.Bindables;
using osu.Framework.Development;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Statistics;
@@ -158,7 +157,7 @@ namespace osu.Framework.Audio.Mixing.Bass
/// If successful, the position is returned.
/// </returns>
public long ChannelGetPosition(IBassAudioChannel channel, PositionFlags mode = PositionFlags.Bytes)
=> BassMix.ChannelGetPosition(channel.Handle);
=> BassMix.ChannelGetPosition(channel.Handle, mode);
/// <summary>
/// Sets the playback position of a channel.
@@ -421,13 +420,7 @@ namespace osu.Framework.Audio.Mixing.Bass
// Effects with greatest priority are stored at the front of the list.
effect.Priority = -i;
if (effect.Handle != 0)
{
// Todo: Temporary bypass to attempt to fix failing test runs.
if (!DebugUtils.IsNUnitRunning)
ManagedBass.Bass.FXSetPriority(effect.Handle, effect.Priority);
}
else
if (effect.Handle == 0)
effect.Handle = ManagedBass.Bass.ChannelSetFX(Handle, effect.Effect.FXType, effect.Priority);
ManagedBass.Bass.FXSetParameters(effect.Handle, effect.Effect);

View File

@@ -33,8 +33,7 @@ namespace osu.Framework.Audio.Sample
public SampleChannel GetChannel()
{
if (IsDisposed)
throw new ObjectDisposedException(ToString(), "Can not get a channel from a disposed sample.");
ObjectDisposedException.ThrowIf(IsDisposed, this);
var channel = CreateChannel();
channel.OnPlay = onPlay;

View File

@@ -22,8 +22,7 @@ namespace osu.Framework.Audio.Sample
public virtual void Play()
{
if (IsDisposed)
throw new ObjectDisposedException(ToString(), "Can not play disposed sample channels.");
ObjectDisposedException.ThrowIf(IsDisposed, this);
Played = true;
OnPlay?.Invoke(this);

View File

@@ -38,7 +38,7 @@ namespace osu.Framework.Audio.Sample
public Sample Get(string name)
{
if (IsDisposed) throw new ObjectDisposedException($"Cannot retrieve items for an already disposed {nameof(SampleStore)}");
ObjectDisposedException.ThrowIf(IsDisposed, this);
if (string.IsNullOrEmpty(name)) return null;

View File

@@ -256,8 +256,7 @@ namespace osu.Framework.Audio.Track
public override void Start()
{
if (IsDisposed)
throw new ObjectDisposedException(ToString(), "Can not start disposed tracks.");
ObjectDisposedException.ThrowIf(IsDisposed, this);
StartAsync().WaitSafely();
}

View File

@@ -29,7 +29,7 @@ namespace osu.Framework.Audio.Track
public Track GetVirtual(double length = double.PositiveInfinity, string name = "virtual")
{
if (IsDisposed) throw new ObjectDisposedException($"Cannot retrieve items for an already disposed {nameof(TrackStore)}");
ObjectDisposedException.ThrowIf(IsDisposed, this);
var track = new TrackVirtual(length, name);
AddItem(track);
@@ -38,7 +38,7 @@ namespace osu.Framework.Audio.Track
public Track Get(string name)
{
if (IsDisposed) throw new ObjectDisposedException($"Cannot retrieve items for an already disposed {nameof(TrackStore)}");
ObjectDisposedException.ThrowIf(IsDisposed, this);
if (string.IsNullOrEmpty(name)) return null;

View File

@@ -99,14 +99,30 @@ namespace osu.Framework.Audio.Track
int decodeStream = Bass.CreateStream(StreamSystem.NoBuffer, BassFlags.Decode | BassFlags.Float, fileCallbacks.Callbacks, fileCallbacks.Handle);
if (decodeStream == 0)
{
logBassError("could not create stream");
return;
}
float[]? sampleBuffer = null;
try
{
Bass.ChannelGetInfo(decodeStream, out ChannelInfo info);
if (!Bass.ChannelGetInfo(decodeStream, out ChannelInfo info))
{
logBassError("could not retrieve channel information");
return;
}
long length = Bass.ChannelGetLength(decodeStream);
if (length < 0)
{
logBassError("could not retrieve channel length");
return;
}
// Each "point" is generated from a number of samples, each sample contains a number of channels
int samplesPerPoint = (int)(info.Frequency * resolution * info.Channels);
@@ -127,6 +143,13 @@ namespace osu.Framework.Audio.Track
while (length > 0)
{
length = Bass.ChannelGetData(decodeStream, sampleBuffer, bytesPerIteration);
if (length < 0 && Bass.LastError != Errors.Ended)
{
logBassError("could not retrieve sample data");
return;
}
int samplesRead = (int)(length / bytes_per_sample);
// Each point is composed of multiple samples
@@ -157,9 +180,20 @@ namespace osu.Framework.Audio.Track
}
}
Bass.ChannelSetPosition(decodeStream, 0);
if (!Bass.ChannelSetPosition(decodeStream, 0))
{
logBassError("could not reset channel position");
return;
}
length = Bass.ChannelGetLength(decodeStream);
if (length < 0)
{
logBassError("could not retrieve channel length");
return;
}
// Read FFT data
float[] bins = new float[fft_bins];
int currentPoint = 0;
@@ -168,6 +202,13 @@ namespace osu.Framework.Audio.Track
while (length > 0)
{
length = Bass.ChannelGetData(decodeStream, bins, (int)fft_samples);
if (length < 0 && Bass.LastError != Errors.Ended)
{
logBassError("could not retrieve FFT data");
return;
}
currentByte += length;
float lowIntensity = computeIntensity(info, bins, low_min, mid_min);
@@ -191,7 +232,9 @@ namespace osu.Framework.Audio.Track
}
finally
{
Bass.StreamFree(decodeStream);
if (!Bass.StreamFree(decodeStream))
logBassError("failed to free decode stream");
fileCallbacks.Dispose();
data.Dispose();
@@ -201,6 +244,8 @@ namespace osu.Framework.Audio.Track
ArrayPool<float>.Shared.Return(sampleBuffer);
}
}, cancelSource.Token);
void logBassError(string reason) => Logger.Log($"BASS failure while reading waveform: {reason} ({Bass.LastError})");
}
private float computeIntensity(ChannelInfo info, float[] bins, float startFrequency, float endFrequency)

View File

@@ -5,7 +5,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace osu.Framework.Bindables
{
@@ -52,7 +51,7 @@ namespace osu.Framework.Bindables
return;
var boundCopy = bindable.GetBoundCopy();
sourceMapping.Add(new WeakRefPair(new WeakReference<IBindable<T>>(bindable), boundCopy));
sourceMapping.Add(new WeakRefPair(bindable.GetWeakReference(), boundCopy));
boundCopy.BindValueChanged(recalculateAggregate, true);
}
}
@@ -65,20 +64,26 @@ namespace osu.Framework.Bindables
{
lock (sourceMapping)
{
var weak = findExistingPair(bindable);
if (weak != null)
if (findExistingPair(bindable) is WeakRefPair pair)
{
weak.BoundCopy.UnbindAll();
sourceMapping.Remove(weak);
pair.BoundCopy.UnbindAll();
sourceMapping.Remove(pair);
}
recalculateAggregate();
}
}
private WeakRefPair findExistingPair(IBindable<T> bindable) =>
sourceMapping.FirstOrDefault(p => p.WeakReference.TryGetTarget(out var target) && target == bindable);
private WeakRefPair? findExistingPair(IBindable<T> bindable)
{
foreach (var p in sourceMapping)
{
if (p.WeakReference.TryGetTarget(out var target) && target == bindable)
return p;
}
return null;
}
private void recalculateAggregate(ValueChangedEvent<T> obj = null)
{
@@ -112,16 +117,6 @@ namespace osu.Framework.Bindables
}
}
private class WeakRefPair
{
public readonly WeakReference<IBindable<T>> WeakReference;
public readonly IBindable<T> BoundCopy;
public WeakRefPair(WeakReference<IBindable<T>> weakReference, IBindable<T> boundCopy)
{
WeakReference = weakReference;
BoundCopy = boundCopy;
}
}
private readonly record struct WeakRefPair(WeakReference<Bindable<T>> WeakReference, IBindable<T> BoundCopy);
}
}

View File

@@ -440,6 +440,8 @@ namespace osu.Framework.Bindables
IBindable IBindable.GetBoundCopy() => GetBoundCopy();
WeakReference<Bindable<T>> IBindable<T>.GetWeakReference() => weakReference;
IBindable<T> IBindable<T>.GetBoundCopy() => GetBoundCopy();
/// <inheritdoc cref="IBindable{T}.GetBoundCopy"/>

View File

@@ -14,7 +14,7 @@ namespace osu.Framework.Bindables
public override void Parse(object? input, IFormatProvider provider)
{
if (input == null) throw new ArgumentNullException(nameof(input));
ArgumentNullException.ThrowIfNull(input);
if (input is "1")
Value = true;

View File

@@ -18,7 +18,7 @@ namespace osu.Framework.Bindables
public override void Parse(object? input, IFormatProvider provider)
{
if (input == null) throw new ArgumentNullException(nameof(input));
ArgumentNullException.ThrowIfNull(input);
switch (input)
{

View File

@@ -19,7 +19,7 @@ namespace osu.Framework.Bindables
public override void Parse(object? input, IFormatProvider provider)
{
if (input == null) throw new ArgumentNullException(nameof(input));
ArgumentNullException.ThrowIfNull(input);
switch (input)
{

View File

@@ -23,7 +23,7 @@ namespace osu.Framework.Bindables
public override void Parse(object? input, IFormatProvider provider)
{
if (input == null) throw new ArgumentNullException(nameof(input));
ArgumentNullException.ThrowIfNull(input);
switch (input)
{

View File

@@ -111,5 +111,10 @@ namespace osu.Framework.Bindables
/// <inheritdoc cref="IBindable.GetBoundCopy"/>
IBindable<T> GetBoundCopy();
/// <summary>
/// Retrieves a weak reference to this bindable.
/// </summary>
internal WeakReference<Bindable<T>> GetWeakReference();
}
}

View File

@@ -1,7 +1,9 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.ComponentModel;
using osu.Framework.Graphics.OpenGL;
namespace osu.Framework.Configuration
{
@@ -19,10 +21,27 @@ namespace osu.Framework.Configuration
[Description("Direct3D 11")]
Direct3D11,
/// <summary>
/// Uses <see cref="GLRenderer"/>.
/// </summary>
[Description("OpenGL")]
OpenGL,
// Can be removed 20240820
[Obsolete]
[Description("OpenGL (Legacy)")]
OpenGLLegacy,
[Description("Metal (Experimental)")]
Deferred_Metal = 32,
[Description("Vulkan (Experimental)")]
Deferred_Vulkan,
[Description("Direct3D 11 (Experimental)")]
Deferred_Direct3D11,
[Description("OpenGL (Experimental)")]
Deferred_OpenGL
}
}

View File

@@ -17,11 +17,15 @@ namespace osu.Framework.Development
private static readonly Lazy<bool> is_nunit_running = new Lazy<bool>(() =>
{
#pragma warning disable RS0030
var entry = Assembly.GetEntryAssembly();
#pragma warning restore RS0030
string? assemblyName = entry?.GetName().Name;
// when running under nunit + netcore, entry assembly becomes nunit itself (testhost, Version=15.0.0.0), which isn't what we want.
// when running under nunit + Rider > 2020.2 EAP6, entry assembly becomes ReSharperTestRunner[32|64], which isn't what we want.
bool entryIsKnownTestAssembly = entry != null && (entry.Location.Contains("testhost") || entry.Location.Contains("ReSharperTestRunner"));
bool entryIsKnownTestAssembly = entry != null && (assemblyName!.Contains("testhost") || assemblyName.Contains("ReSharperTestRunner"));
// null assembly can indicate nunit, but it can also indicate native code (e.g. android).
// to distinguish nunit runs from android launches, check the class name of the current test.
@@ -33,6 +37,8 @@ namespace osu.Framework.Development
}
);
internal static Assembly NUnitTestAssembly => nunit_test_assembly.Value;
private static readonly Lazy<Assembly> nunit_test_assembly = new Lazy<Assembly>(() =>
{
Debug.Assert(IsNUnitRunning);
@@ -45,7 +51,7 @@ namespace osu.Framework.Development
public static bool IsDebugBuild => is_debug_build.Value;
private static readonly Lazy<bool> is_debug_build = new Lazy<bool>(() =>
isDebugAssembly(typeof(DebugUtils).Assembly) || isDebugAssembly(GetEntryAssembly())
isDebugAssembly(typeof(DebugUtils).Assembly) || isDebugAssembly(RuntimeInfo.EntryAssembly)
);
/// <summary>
@@ -56,18 +62,5 @@ namespace osu.Framework.Development
// https://stackoverflow.com/a/2186634
private static bool isDebugAssembly(Assembly? assembly) => assembly?.GetCustomAttributes(false).OfType<DebuggableAttribute>().Any(da => da.IsJITTrackingEnabled) ?? false;
/// <summary>
/// Gets the entry assembly, or calling assembly otherwise.
/// When running under NUnit, the assembly of the current test will be returned instead.
/// </summary>
/// <returns>The entry assembly (usually obtained via <see cref="Assembly.GetEntryAssembly()"/>.</returns>
public static Assembly GetEntryAssembly()
{
if (IsNUnitRunning)
return nunit_test_assembly.Value;
return Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly();
}
}
}

View File

@@ -12,7 +12,7 @@ namespace osu.Framework.Extensions
{
/// <summary>
/// Temporary extension functions for bridging between osuTK, System.Drawing, and System.Numerics
/// Can be removed when the SDL2 migration is complete.
/// Can be removed when the SDL3 migration is complete.
/// </summary>
public static class BridgingExtensions
{

View File

@@ -119,8 +119,7 @@ namespace osu.Framework.Extensions.Color4Extensions
/// <param name="scalar">A scalar to multiply with</param>
public static Color4 Multiply(this Color4 colour, float scalar)
{
if (scalar < 0)
throw new ArgumentOutOfRangeException(nameof(scalar), scalar, "Can not multiply colours by negative values.");
ArgumentOutOfRangeException.ThrowIfNegative(scalar);
return new Color4(
Math.Min(1, colour.R * scalar),

View File

@@ -332,7 +332,8 @@ namespace osu.Framework.Extensions
/// </remarks>
/// <param name="character">The character to check.</param>
/// <returns>True if the character is an ASCII digit.</returns>
public static bool IsAsciiDigit(this char character) => character >= '0' && character <= '9';
[Obsolete("Use char.IsAsciiDigit.")] // can be removed 20240901
public static bool IsAsciiDigit(this char character) => char.IsAsciiDigit(character);
/// <summary>
/// Converts an osuTK <see cref="DisplayDevice"/> to a <see cref="Display"/> structure.
@@ -349,7 +350,7 @@ namespace osu.Framework.Extensions
/// <param name="resolution">The <see cref="DisplayResolution"/> to convert.</param>
/// <returns>A <see cref="DisplayMode"/> structure populated with the corresponding properties.</returns>
internal static DisplayMode ToDisplayMode(this DisplayResolution resolution) =>
new DisplayMode(null, new Size(resolution.Width, resolution.Height), resolution.BitsPerPixel, (int)Math.Round(resolution.RefreshRate), 0);
new DisplayMode(null, new Size(resolution.Width, resolution.Height), resolution.BitsPerPixel, resolution.RefreshRate, 0);
/// <summary>
/// Checks whether the provided URL is a safe protocol to execute a system <see cref="Process.Start()"/> call with.

View File

@@ -63,7 +63,7 @@ namespace osu.Framework.Extensions
public static byte[] ReadBytesToArray(this Stream stream, int length)
{
byte[] bytes = new byte[length];
stream.ReadToFill(bytes);
stream.ReadExactly(bytes);
return bytes;
}
@@ -78,7 +78,7 @@ namespace osu.Framework.Extensions
public static async Task<byte[]> ReadBytesToArrayAsync(this Stream stream, int length, CancellationToken cancellationToken = default)
{
byte[] bytes = new byte[length];
await stream.ReadToFillAsync(bytes, cancellationToken).ConfigureAwait(false);
await stream.ReadExactlyAsync(bytes, cancellationToken).ConfigureAwait(false);
return bytes;
}
@@ -117,19 +117,8 @@ namespace osu.Framework.Extensions
/// <param name="stream">The stream to read.</param>
/// <param name="buffer">The buffer to read into.</param>
/// <exception cref="EndOfStreamException">Throws if the stream didn't have enough content to fill the buffer.</exception>
public static void ReadToFill(this Stream stream, Span<byte> buffer)
{
Span<byte> remainingBuffer = buffer;
while (!remainingBuffer.IsEmpty)
{
int bytesRead = stream.Read(remainingBuffer);
remainingBuffer = remainingBuffer[bytesRead..];
if (bytesRead == 0)
throw new EndOfStreamException();
}
}
[Obsolete("Use Stream.ReadExactly")] // can be removed 20240901
public static void ReadToFill(this Stream stream, Span<byte> buffer) => stream.ReadExactly(buffer);
/// <summary>
/// Reads bytes from a stream until the provided buffer is full.
@@ -138,18 +127,7 @@ namespace osu.Framework.Extensions
/// <param name="buffer">The buffer to read into.</param>
/// <param name="cancellationToken">A cancellation token.</param>
/// <exception cref="EndOfStreamException">Throws if the stream didn't have enough content to fill the buffer.</exception>
public static async Task ReadToFillAsync(this Stream stream, Memory<byte> buffer, CancellationToken cancellationToken = default)
{
Memory<byte> remainingBuffer = buffer;
while (!remainingBuffer.IsEmpty)
{
int bytesRead = await stream.ReadAsync(remainingBuffer, cancellationToken).ConfigureAwait(false);
remainingBuffer = remainingBuffer[bytesRead..];
if (bytesRead == 0)
throw new EndOfStreamException();
}
}
[Obsolete("Use Stream.ReadExactlyAsync")] // can be removed 20240901
public static Task ReadToFillAsync(this Stream stream, Memory<byte> buffer, CancellationToken cancellationToken = default) => stream.ReadExactlyAsync(buffer, cancellationToken).AsTask();
}
}

View File

@@ -17,6 +17,7 @@ namespace osu.Framework
public static int? StagingBufferType { get; }
public static int? VertexBufferCount { get; }
public static bool NoStructuredBuffers { get; }
public static string? DeferredRendererEventsOutputPath { get; }
static FrameworkEnvironment()
{
@@ -34,6 +35,8 @@ namespace osu.Framework
StagingBufferType = stagingBufferImplementation;
NoStructuredBuffers = parseBool(Environment.GetEnvironmentVariable("OSU_GRAPHICS_NO_SSBO")) ?? false;
DeferredRendererEventsOutputPath = Environment.GetEnvironmentVariable("DEFERRED_RENDERER_EVENTS_OUTPUT");
}
private static bool? parseBool(string? value)

View File

@@ -46,8 +46,7 @@ namespace osu.Framework.Graphics.Audio
get => resolution;
set
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value));
ArgumentOutOfRangeException.ThrowIfNegative(value);
if (resolution == value)
return;

View File

@@ -150,7 +150,7 @@ namespace osu.Framework.Graphics
frameBuffer.Bind();
return new ValueInvokeOnDisposal<IFrameBuffer>(frameBuffer, b => b.Unbind());
return new ValueInvokeOnDisposal<IFrameBuffer>(frameBuffer, static b => b.Unbind());
}
private IDisposable establishFrameBufferViewport(IRenderer renderer)
@@ -175,7 +175,7 @@ namespace osu.Framework.Graphics
renderer.PushScissor(new RectangleI(0, 0, (int)frameBufferSize.X, (int)frameBufferSize.Y));
renderer.PushScissorOffset(screenSpaceMaskingRect.Location);
return new ValueInvokeOnDisposal<(BufferedDrawNode node, IRenderer renderer)>((this, renderer), tup => tup.node.returnViewport(tup.renderer));
return new ValueInvokeOnDisposal<(BufferedDrawNode node, IRenderer renderer)>((this, renderer), static tup => tup.node.returnViewport(tup.renderer));
}
private void returnViewport(IRenderer renderer)

View File

@@ -67,8 +67,7 @@ namespace osu.Framework.Graphics
/// <exception cref="ArgumentOutOfRangeException">If <paramref name="effectBufferCount"/> is less than 0.</exception>
public BufferedDrawNodeSharedData(int effectBufferCount, RenderBufferFormat[] mainBufferFormats = null, bool pixelSnapping = false, bool clipToRootNode = false)
{
if (effectBufferCount < 0)
throw new ArgumentOutOfRangeException(nameof(effectBufferCount), "Must be positive.");
ArgumentOutOfRangeException.ThrowIfNegative(effectBufferCount);
this.mainBufferFormats = mainBufferFormats;
PixelSnapping = pixelSnapping;

View File

@@ -93,8 +93,7 @@ namespace osu.Framework.Graphics
/// <param name="scalar">The value that the existing alpha will be multiplied by.</param>
public Colour4 MultiplyAlpha(float scalar)
{
if (scalar < 0)
throw new ArgumentOutOfRangeException(nameof(scalar), scalar, "Cannot multiply alpha by a negative value.");
ArgumentOutOfRangeException.ThrowIfNegative(scalar);
return new Colour4(R, G, B, Math.Min(1f, A * scalar));
}
@@ -174,8 +173,7 @@ namespace osu.Framework.Graphics
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="scalar"/> is negative.</exception>
public static Colour4 operator *(Colour4 colour, float scalar)
{
if (scalar < 0)
throw new ArgumentOutOfRangeException(nameof(scalar), scalar, "Cannot multiply colours by negative values.");
ArgumentOutOfRangeException.ThrowIfNegative(scalar);
return new Colour4(Vector4.Min(colour.Vector * scalar, Vector4.One));
}
@@ -188,8 +186,7 @@ namespace osu.Framework.Graphics
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="scalar"/> is zero or negative.</exception>
public static Colour4 operator /(Colour4 colour, float scalar)
{
if (scalar <= 0)
throw new ArgumentOutOfRangeException(nameof(scalar), scalar, "Cannot divide colours by non-positive values.");
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(scalar);
return colour * (1 / scalar);
}

View File

@@ -107,7 +107,7 @@ namespace osu.Framework.Graphics.Containers
using (BindFrameBuffer(target))
{
float radians = MathUtils.DegreesToRadians(blurRotation);
float radians = float.DegreesToRadians(blurRotation);
blurParametersBuffer.Data = blurParametersBuffer.Data with
{

View File

@@ -145,8 +145,7 @@ namespace osu.Framework.Graphics.Containers
EnsureMutationAllowed($"load components via {nameof(LoadComponentsAsync)}");
if (IsDisposed)
throw new ObjectDisposedException(ToString());
ObjectDisposedException.ThrowIf(IsDisposed, this);
disposalCancellationSource ??= new CancellationTokenSource();
@@ -222,8 +221,7 @@ namespace osu.Framework.Graphics.Containers
if (LoadState < LoadState.Loading)
throw new InvalidOperationException($"May not invoke {nameof(LoadComponent)} prior to this {nameof(CompositeDrawable)} being loaded.");
if (IsDisposed)
throw new ObjectDisposedException(ToString());
ObjectDisposedException.ThrowIf(IsDisposed, this);
loadComponents(components.ToList(), Dependencies, false);
}

View File

@@ -220,9 +220,7 @@ namespace osu.Framework.Graphics.Containers
throw new InvalidOperationException("Content may not be added to itself.");
ArgumentNullException.ThrowIfNull(drawable);
if (drawable.IsDisposed)
throw new ObjectDisposedException(nameof(drawable));
ObjectDisposedException.ThrowIf(drawable.IsDisposed, drawable);
if (Content == this)
AddInternal(drawable);

View File

@@ -139,7 +139,7 @@ namespace osu.Framework.Graphics.Containers
}
private readonly Cached cellContent = new Cached();
private readonly LayoutValue cellLayout = new LayoutValue(Invalidation.DrawInfo | Invalidation.RequiredParentSizeToFit);
private readonly LayoutValue cellLayout = new LayoutValue(Invalidation.DrawSize);
private readonly LayoutValue cellChildLayout = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.Presence, InvalidationSource.Child);
private CellContainer[,] cells = new CellContainer[0, 0];
@@ -420,11 +420,8 @@ namespace osu.Framework.Graphics.Containers
/// <param name="maxSize">The maximum size of this row or column.</param>
public Dimension(GridSizeMode mode = GridSizeMode.Distributed, float size = 0, float minSize = 0, float maxSize = float.MaxValue)
{
if (minSize < 0)
throw new ArgumentOutOfRangeException(nameof(minSize), "Must be greater than 0.");
if (minSize > maxSize)
throw new ArgumentOutOfRangeException(nameof(minSize), $"Must be less than {nameof(maxSize)}.");
ArgumentOutOfRangeException.ThrowIfNegative(minSize);
ArgumentOutOfRangeException.ThrowIfGreaterThan(minSize, maxSize);
Mode = mode;
Size = size;

View File

@@ -9,23 +9,32 @@ using osuTK.Graphics;
namespace osu.Framework.Graphics.Containers.Markdown
{
/// <summary>
/// Visualises a fenced code block.
/// Visualises an indented/fenced code block.
/// </summary>
/// <code>
/// ```
/// code
/// code1
/// code2
/// code3
/// ```
/// </code>
public partial class MarkdownFencedCodeBlock : CompositeDrawable, IMarkdownTextFlowComponent
/// <code>
///
/// code1
/// code2
/// code3
///
/// </code>
public partial class MarkdownCodeBlock : CompositeDrawable, IMarkdownTextFlowComponent
{
private readonly FencedCodeBlock fencedCodeBlock;
private readonly CodeBlock codeBlock;
[Resolved]
private IMarkdownTextFlowComponent parentFlowComponent { get; set; } = null!;
public MarkdownFencedCodeBlock(FencedCodeBlock fencedCodeBlock)
public MarkdownCodeBlock(CodeBlock codeBlock)
{
this.fencedCodeBlock = fencedCodeBlock;
this.codeBlock = codeBlock;
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
@@ -42,8 +51,8 @@ namespace osu.Framework.Graphics.Containers.Markdown
};
// Markdig sometimes appends empty lines to the processed block, only add original lines to the container
for (int i = 0; i < fencedCodeBlock.Lines.Count; i++)
textFlowContainer.AddParagraph(fencedCodeBlock.Lines.Lines[i].ToString());
for (int i = 0; i < codeBlock.Lines.Count; i++)
textFlowContainer.AddParagraph(codeBlock.Lines.Lines[i].ToString());
}
protected virtual Drawable CreateBackground() => new Box

View File

@@ -241,8 +241,8 @@ namespace osu.Framework.Graphics.Containers.Markdown
container.Add(CreateQuoteBlock(quoteBlock));
break;
case FencedCodeBlock fencedCodeBlock:
container.Add(CreateFencedCodeBlock(fencedCodeBlock));
case CodeBlock codeBlock:
container.Add(CreateCodeBlock(codeBlock));
break;
case Table table:
@@ -311,11 +311,11 @@ namespace osu.Framework.Graphics.Containers.Markdown
protected virtual MarkdownQuoteBlock CreateQuoteBlock(QuoteBlock quoteBlock) => new MarkdownQuoteBlock(quoteBlock);
/// <summary>
/// Creates the visualiser for a <see cref="FencedCodeBlock"/>.
/// Creates the visualiser for a <see cref="CodeBlock"/>.
/// </summary>
/// <param name="fencedCodeBlock">The <see cref="FencedCodeBlock"/> to visualise.</param>
/// <param name="codeBlock">The <see cref="CodeBlock"/> to visualise.</param>
/// <returns>The visualiser.</returns>
protected virtual MarkdownFencedCodeBlock CreateFencedCodeBlock(FencedCodeBlock fencedCodeBlock) => new MarkdownFencedCodeBlock(fencedCodeBlock);
protected virtual MarkdownCodeBlock CreateCodeBlock(CodeBlock codeBlock) => new MarkdownCodeBlock(codeBlock);
/// <summary>
/// Creates the visualiser for a <see cref="Table"/>.

View File

@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using osu.Framework.Graphics.Sprites;
@@ -88,7 +89,13 @@ namespace osu.Framework.Graphics.Containers
for (int i = 0; i < text.Length; i++)
{
if (i == 0 || char.IsSeparator(text[i - 1]) || char.IsControl(text[i - 1]))
if (i == 0
|| char.IsSeparator(text[i - 1])
|| char.IsControl(text[i - 1])
|| char.GetUnicodeCategory(text[i - 1]) == UnicodeCategory.DashPunctuation
|| text[i - 1] == '/'
|| text[i - 1] == '\\'
|| (isCjkCharacter(text[i - 1]) && !char.IsPunctuation(text[i])))
{
words.Add(builder.ToString());
builder.Clear();
@@ -101,6 +108,8 @@ namespace osu.Framework.Graphics.Containers
words.Add(builder.ToString());
return words.ToArray();
bool isCjkCharacter(char c) => c >= '\x2E80' && c <= '\x9FFF';
}
protected virtual TSpriteText CreateSpriteText(TextFlowContainer textFlowContainer)

View File

@@ -148,8 +148,8 @@ namespace osu.Framework.Graphics.Cursor
progress.FadeColour(Color4.SkyBlue)
.TransformTo(nameof(progress.InnerRadius), 0.2f)
.TransformTo(nameof(progress.InnerRadius), 0.3f, 150, Easing.OutQuint)
.TransformBindableTo(progress.Current, 0)
.TransformBindableTo(progress.Current, 1, duration / 3 * 2);
.ProgressTo(0)
.ProgressTo(1, duration / 3 * 2);
using (BeginDelayedSequence(duration / 3 * 2))
{
@@ -165,7 +165,7 @@ namespace osu.Framework.Graphics.Cursor
{
this.FadeOut(400, Easing.OutQuint);
progress.TransformBindableTo(progress.Current, 0, 400, Easing.OutQuint)
progress.ProgressTo(0, 400, Easing.OutQuint)
.TransformTo(nameof(progress.InnerRadius), 0.2f, 50, Easing.OutQuint);
}
}

View File

@@ -5,9 +5,10 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Extensions.ListExtensions;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Lists;
namespace osu.Framework.Graphics.Cursor
{
@@ -84,14 +85,21 @@ namespace osu.Framework.Graphics.Cursor
// Assuming we did _not_ end up terminating, then all found drawables are children of ours
// and need to be added.
childDrawables.UnionWith(newChildDrawables);
foreach (var newChild in newChildDrawables)
childDrawables.Add(newChild);
// Keep track of child drawables whose effects are managed by a nested effect container.
// Note, that nested effect containers themselves could implement TTarget and
// are still our own responsibility to handle.
nestedTtcChildDrawables.UnionWith(
((IEnumerable<IDrawable>)newChildDrawables).Reverse()
.SkipWhile(d => d.Parent == this || (!(d.Parent is TSelf) && !nestedTtcChildDrawables.Contains(d.Parent))));
for (int j = newChildDrawables.Count - 1; j >= 0; j--)
{
var d = newChildDrawables[j];
if (d.Parent == this || (!(d.Parent is TSelf) && !nestedTtcChildDrawables.Contains(d.Parent)))
continue;
nestedTtcChildDrawables.Add(d);
}
// Ignore drawables whose effects are managed by a nested effect container.
if (nestedTtcChildDrawables.Contains(candidate))
@@ -102,7 +110,9 @@ namespace osu.Framework.Graphics.Cursor
}
}
protected IEnumerable<TTarget> FindTargets()
private static readonly SlimReadOnlyListWrapper<TTarget> empty_list = new SlimReadOnlyListWrapper<TTarget>(new List<TTarget>(0));
protected SlimReadOnlyListWrapper<TTarget> FindTargets()
{
findTargetChildren();
@@ -112,14 +122,14 @@ namespace osu.Framework.Graphics.Cursor
newChildDrawables.Clear();
if (targetChildren.Count == 0)
return Enumerable.Empty<TTarget>();
return empty_list;
List<TTarget> result = new List<TTarget>(targetChildren);
result.Reverse();
targetChildren.Clear();
return result;
return result.AsSlimReadOnly();
}
}
}

View File

@@ -5,7 +5,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shapes;
@@ -184,7 +183,7 @@ namespace osu.Framework.Graphics.Cursor
object targetContent = getTargetContent(target);
if (targetContent is LocalisableString localisableString)
return !string.IsNullOrEmpty(localisableString.Data?.ToString());
return !LocalisableString.IsNullOrEmpty(localisableString);
return targetContent != null;
}
@@ -255,24 +254,34 @@ namespace osu.Framework.Graphics.Cursor
if (appearDelay > 0 && (recentMousePositions.Count == 0 || lastRecordedPositionTime - recentMousePositions[0].Time < appearDelay - positionRecordInterval))
return null;
recentMousePositions.RemoveAll(t => Time.Current - t.Time > appearDelay);
for (int i = recentMousePositions.Count - 1; i >= 0; i--)
{
if (Time.Current - recentMousePositions[i].Time > appearDelay)
recentMousePositions.RemoveAt(i);
}
// For determining whether to show a tooltip we first select only those positions
// which happened within a shorter, alpha-adjusted appear delay.
double alphaModifiedAppearDelay = (1 - CurrentTooltip.Alpha) * appearDelay;
var relevantPositions = recentMousePositions.Where(t => Time.Current - t.Time <= alphaModifiedAppearDelay);
// We then check whether all relevant positions fall within a radius of AppearRadius within the
// first relevant position. If so, then the mouse has stayed within a small circular region of
// AppearRadius for the duration of the modified appear delay, and we therefore want to display
// the tooltip.
Vector2 first = relevantPositions.FirstOrDefault().Position;
Vector2? first = null;
float appearRadiusSq = AppearRadius * AppearRadius;
if (relevantPositions.All(t => Vector2Extensions.DistanceSquared(t.Position, first) < appearRadiusSq))
return targetCandidate;
foreach (var mPos in recentMousePositions)
{
if (Time.Current - mPos.Time > alphaModifiedAppearDelay)
continue;
return null;
first ??= mPos.Position;
if (Vector2Extensions.DistanceSquared(mPos.Position, (Vector2)first) > appearRadiusSq)
return null;
}
return targetCandidate;
}
/// <summary>

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