diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e1c8741207..21706e77ea 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,14 +59,12 @@ jobs: run: ./build.sh ValidateSolution - name: Pack (CI) if: ${{ github.repository != 'dotnet/Silk.NET' || !startsWith(github.ref, 'refs/tags/') }} - # TODO build native mixins such as BuildLibSilkDroid # Use a release NUKE so it doesn't interfere with the debug build - run: dotnet run --project build/nuke/Silk.NET.NUKE.csproj -c Release -- Pack --configuration Debug --msbuild-properties VersionSuffix=build${{ github.run_number }}.0 ContinuousIntegrationBuild=true + run: dotnet run --project build/nuke/Silk.NET.NUKE.csproj -c Release -- Pack --configuration Debug --msbuild-properties VersionSuffix=${{ github.event_name != 'pull_request' && format('build{0}.0', github.run_number) || format('pr{0}.{1}', github.event.number, github.run_number) }} ContinuousIntegrationBuild=true env: ANDROID_HOME: /Users/runner/Library/Android/sdk - name: Pack (CD) if: ${{ github.repository == 'dotnet/Silk.NET' && startsWith(github.ref, 'refs/tags/') }} - # TODO build native mixins such as BuildLibSilkDroid run: ./build.sh Pack --configuration Release --msbuild-properties ContinuousIntegrationBuild=true - name: Upload Unsigned Artifacts to Actions uses: actions/upload-artifact@v4 @@ -76,10 +74,10 @@ jobs: if-no-files-found: warn retention-days: 1 - name: Push to Experimental Feed - if: ${{ github.repository == 'dotnet/Silk.NET' && github.event_name != 'pull_request' }} + if: ${{ github.repository == 'dotnet/Silk.NET' }} run: ./build.sh PushToNuGet --skip Clean Restore Compile Pack --nuget-feed https://dotnet.github.io/Silk.NET/nuget/experimental/index.json --nuget-username ${{ secrets.EXP_NUGET_USERNAME }} --nuget-password ${{ secrets.EXP_NUGET_PASSWORD }} --nuget-api-key ${{ secrets.EXP_NUGET_PASSWORD }} - name: Push to GitHub Packages - if: ${{ github.repository == 'dotnet/Silk.NET' && github.event_name != 'pull_request' }} + if: ${{ github.repository == 'dotnet/Silk.NET' }} run: ./build.sh PushToNuGet --skip Clean Restore Compile Pack --nuget-feed https://nuget.pkg.github.com/dotnet/index.json --nuget-api-key ${{ secrets.GITHUB_TOKEN }} PushRelease: name: Push Release to NuGet diff --git a/.github/workflows/sdl2.yml b/.github/workflows/sdl2.yml index 8e4b5daa70..8bbb742c5d 100644 --- a/.github/workflows/sdl2.yml +++ b/.github/workflows/sdl2.yml @@ -10,6 +10,8 @@ on: - "build/cmake/*" - build/nuke/Native/Core.cs - build/nuke/Native/SDL2.cs + - build/nuke/Native/SilkDroid.cs + - build/nuke/Build.Support.cs - .github/workflows/sdl2.yml jobs: Build: @@ -21,15 +23,12 @@ jobs: - os: ubuntu-22.04 name: Linux nuke_invoke: ./build.sh - - os: windows-2022 + - os: windows-latest # runner is not setup for android yet name: Windows - nuke_invoke: ./build.cmd - extras: | - pwsh build\Install-WindowsSDK.ps1 + nuke_invoke: ./build.cmd BuildLibSilkDroid - os: macos-14 name: Darwin nuke_invoke: ./build.sh - extras: "" name: ${{ matrix.env.name }} Build runs-on: ${{ matrix.env.os }} steps: @@ -42,12 +41,52 @@ jobs: git -c submodule.third_party/git-hooks.update=none submodule update --init --recursive build/submodules/SDL git config --local user.email "9011267+dotnet-bot@users.noreply.github.com" git config --local user.name "The Silk.NET Automaton" - - - name: Extra prerequisites + - name: Setup .NET Core + uses: actions/setup-dotnet@v3 + if: runner.os == 'Windows' + with: + dotnet-version: | + 6.0.201 + 7.0.* + 8.0.* + env: + DOTNET_INSTALL_DIR: ~/.dotnet + - name: Setup PowerShell Core + continue-on-error: true + if: runner.os == 'Windows' + run: dotnet tool install --global PowerShell + - name: Setup Java JDK 21 + uses: actions/setup-java@v2.3.0 + if: runner.os == 'Windows' + with: + java-version: 21 + distribution: "temurin" + - name: Set PATH + if: runner.os == 'Windows' + id: set_path run: | - echo running extras - ${{ matrix.env.extras }} - + echo "cur_path=$env:PATH\n" >> $env:GITHUB_OUTPUT + - name: Setup Android Environment + uses: android-actions/setup-android@v3 + if: runner.os == 'Windows' + env: + PATH: "${{ env.DOTNET_ROOT }};${{ env.DOTNET_ROOT }}\\tools;${{ steps.set_path.outputs.cur_path }}" + - name: Install Android Platforms + if: runner.os == 'Windows' + run: | + sdkmanager --install "build-tools;30.0.2" + sdkmanager --install "platform-tools" + sdkmanager --install "platforms;android-31" + sdkmanager --install "platforms;android-33" + sdkmanager --install "platforms;android-34" + sdkmanager --install "ndk-bundle" + sdkmanager --install "ndk;21.4.7075529" + - name: Setup Java JDK 11 + uses: actions/setup-java@v2.3.0 + if: runner.os == 'Windows' + with: + java-version: 11 + distribution: "temurin" # Install CMake - uses: lukka/get-cmake@latest if: runner.os != 'Linux' @@ -110,10 +149,13 @@ jobs: env: PUSHABLE_GITHUB_TOKEN: ${{ secrets.PUSHABLE_GITHUB_TOKEN }} + - name: Install Workloads + if: runner.os == 'Windows' + run: dotnet workload install android ios maccatalyst + - name: Build SDL2 if: runner.os != 'Linux' - run: ${{ matrix.env.nuke_invoke }} SDL2 + run: ${{ matrix.env.nuke_invoke }} SDL2 ${{ runner.os == 'Windows' && format('{0} {1}', '--native true --android-home-value', env.ANDROID_HOME) || '' }} env: PUSHABLE_GITHUB_TOKEN: ${{ secrets.PUSHABLE_GITHUB_TOKEN }} - diff --git a/build/nuke/Build.Support.cs b/build/nuke/Build.Support.cs index 2a3565fee0..fee64a97a7 100644 --- a/build/nuke/Build.Support.cs +++ b/build/nuke/Build.Support.cs @@ -35,7 +35,7 @@ partial class Build /// - Microsoft VSCode https://nuke.build/vscode public static int Main() => Execute(x => x.Compile); - [Parameter("Outputs build warnings instead of keeping the MSBuild logging quiet with just errors.")] + [Nuke.Common.Parameter("Outputs build warnings instead of keeping the MSBuild logging quiet with just errors.")] bool Warnings; static int IndexOfOrThrow(string x, char y) @@ -126,6 +126,18 @@ Dictionary ProcessedMsbuildProperties { // probably hasn't existed yet, don't care. } + try + { + if (Native) + { + DotNet("workload install android"); + } + } + catch + { + // oh well. maybe it's already installed? + } + GenerateSolution(); } ); diff --git a/build/nuke/Native/Core.cs b/build/nuke/Native/Core.cs index b1c51f3f14..1f8ac63fb1 100644 --- a/build/nuke/Native/Core.cs +++ b/build/nuke/Native/Core.cs @@ -29,7 +29,7 @@ partial class Build { [Nuke.Common.Parameter("Build native code")] readonly bool Native; - [CanBeNull] string AndroidHomeValue; + [Nuke.Common.Parameter("Android home. Will be determined from dotnet if not provided.")] [CanBeNull] string AndroidHomeValue; static string JobsArg => string.IsNullOrWhiteSpace(GitHubActions.Instance?.Job) ? $" -j{Jobs}" @@ -66,7 +66,8 @@ public void PrUpdatedNativeBinary(string name) Git("fetch --all", RootDirectory); Git("pull"); - Git($"add -f src/Native/*/runtimes/*/native/*", RootDirectory); + Git("add -f src/Native/*/runtimes/*/native/*", RootDirectory); + Git("add **/*.aar **/*.java **/PublicAPI.Unshipped.txt", RootDirectory); var newBranch = $"ci/{curBranch}/{name.ToLower().Replace(' ', '_')}_bins"; var curCommit = GitCurrentCommit(RootDirectory); var commitCmd = InheritedShell diff --git a/build/nuke/Native/SilkDroid.cs b/build/nuke/Native/SilkDroid.cs index 4394174c79..6f84705b9f 100644 --- a/build/nuke/Native/SilkDroid.cs +++ b/build/nuke/Native/SilkDroid.cs @@ -36,6 +36,12 @@ string AndroidHome return AndroidHomeValue; } + if ((Environment.GetEnvironmentVariable("ANDROID_HOME") ?? Environment.GetEnvironmentVariable("ANDROID_SDK_ROOT")) is {} sdk) + { + AndroidHomeValue = sdk; + return sdk; + } + var utils = RootDirectory / "build" / "utilities"; DotNet($"build \"{utils / "android_probe.proj"}\" /t:GetAndroidJar"); AndroidHomeValue = (AbsolutePath) File.ReadAllText(utils / "android.jar.gen.txt") / ".." / ".." / ".."; @@ -80,6 +86,7 @@ string AndroidHome var envVars = CreateEnvVarDictionary(); envVars["ANDROID_HOME"] = AndroidHome; + envVars["ANDROID_SDK_ROOT"] = AndroidHome; foreach (var ndk in Directory.GetDirectories((AbsolutePath) AndroidHome / "ndk") .OrderByDescending(x => Version.Parse(Path.GetFileName(x)))) @@ -87,7 +94,7 @@ string AndroidHome envVars["ANDROID_NDK_HOME"] = ndk; } - using var process = StartShell($".{Path.PathSeparator}gradlew build", silkDroid, envVars); + using var process = StartShell($".{Path.DirectorySeparatorChar}gradlew build", silkDroid, envVars); process.AssertZeroExitCode(); var ret = process.Output; CopyFile @@ -96,6 +103,12 @@ string AndroidHome SourceDirectory / "Windowing" / "Silk.NET.Windowing.Sdl" / "Android" / "app-release.aar", FileExistsPolicy.Overwrite ); + + // Not expecting this to succeed, but we need to do this so we generate the bindings to go in the public API. + InheritedShell($"dotnet build \"{SourceDirectory / "Windowing" / "Silk.NET.Windowing.Sdl" / "Silk.NET.Windowing.Sdl.csproj"}\"").AssertWaitForExit(); + + // Update the public API. + InheritedShell(string.Format(FormatDeclCmd, SourceDirectory / "Windowing" / "Silk.NET.Windowing.Sdl" / "Silk.NET.Windowing.Sdl.csproj")).AssertZeroExitCode(); return ret; } ) diff --git a/build/props/bindings.props b/build/props/bindings.props index d8e575fc5f..c9731ee1f3 100644 --- a/build/props/bindings.props +++ b/build/props/bindings.props @@ -38,6 +38,7 @@ + diff --git a/src/Input/Silk.NET.Input/Silk.NET.Input.csproj b/src/Input/Silk.NET.Input/Silk.NET.Input.csproj index b1212d5d5e..4dcfac33d8 100644 --- a/src/Input/Silk.NET.Input/Silk.NET.Input.csproj +++ b/src/Input/Silk.NET.Input/Silk.NET.Input.csproj @@ -1,14 +1,25 @@  - netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0 + netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net6.0-android;net6.0-ios;net6.0-maccatalyst true - - + + + + + + + + + + + + + diff --git a/src/Lab/Experiments/InputTest/Program.cs b/src/Lab/Experiments/InputTest/Program.cs index 063daa0455..2fbebc4b9b 100644 --- a/src/Lab/Experiments/InputTest/Program.cs +++ b/src/Lab/Experiments/InputTest/Program.cs @@ -4,7 +4,6 @@ using System.Drawing; using System.Linq; using System.Numerics; -using Silk.NET.GLFW; using Silk.NET.Input; using Silk.NET.Windowing; using MouseButton = Silk.NET.Input.MouseButton; diff --git a/src/Lab/Experiments/Triangle/Program.cs b/src/Lab/Experiments/Triangle/Program.cs index 7146df2e56..fad1f494fd 100644 --- a/src/Lab/Experiments/Triangle/Program.cs +++ b/src/Lab/Experiments/Triangle/Program.cs @@ -7,7 +7,6 @@ using Silk.NET.Maths; using Silk.NET.OpenGL; using Silk.NET.Windowing; -using SdlProvider = Silk.NET.SDL.SdlProvider; using Shader = SampleBase.Shader; namespace Triangle diff --git a/src/Lab/Experiments/TriangleNET6/Program.cs b/src/Lab/Experiments/TriangleNET6/Program.cs index 4db0ec8338..0861fbd205 100644 --- a/src/Lab/Experiments/TriangleNET6/Program.cs +++ b/src/Lab/Experiments/TriangleNET6/Program.cs @@ -2,22 +2,13 @@ Triangle.Program.Run(); #elif __IOS__ using Silk.NET.Windowing; -using Silk.NET.Windowing.Sdl; using Silk.NET.Windowing.Sdl.iOS; using Silk.NET.Input.Sdl; -using Silk.NET.SDL; -using System; -using Triangle; -static void Run(string[] args) +SdlInput.RegisterPlatform(); +SilkMobile.RunApp(args, _ => { Triangle.Program.API = new GraphicsAPI(ContextAPI.OpenGLES, ContextProfile.Core, ContextFlags.Default, new APIVersion(3, 0)); Triangle.Program.Run(); -} - -SdlInput.RegisterPlatform(); -Console.WriteLine("Hello, world!"); -SilkMobile.RunApp(args, Run); -Console.WriteLine("Goodbye, world!"); -SdlProvider.SDL.Value.ThrowError(); +}); #endif diff --git a/src/Lab/Experiments/TriangleNET6/TriangleNET6.csproj b/src/Lab/Experiments/TriangleNET6/TriangleNET6.csproj index 04d06ac8bd..1f570e2f91 100644 --- a/src/Lab/Experiments/TriangleNET6/TriangleNET6.csproj +++ b/src/Lab/Experiments/TriangleNET6/TriangleNET6.csproj @@ -8,7 +8,9 @@ preview $(NETCoreSdkRuntimeIdentifier) android-arm64;android-x86;android-arm;android-x64 - full + full + Full + true @@ -38,13 +40,26 @@ <_Silk_NativePackages_SDL_StaticPath Condition="'$(RuntimeIdentifier)' == 'tvos-arm64'">$(MSBuildThisFileDirectory)..\..\..\Native\Silk.NET.SDL.Native\runtimes\tvos\native\libSDL2.a + Static True True False - -framework AudioToolbox -framework AVFoundation -framework CoreAudio -framework CoreBluetooth -framework CoreFoundation -framework CoreGraphics -framework CoreHaptics -framework CoreMotion -framework CoreVideo -framework GameController -framework IOKit -framework Metal -framework OpenGLES -framework QuartzCore -framework UIKit + -framework AudioToolbox -framework AVFoundation -framework CoreAudio -framework CoreBluetooth -framework CoreFoundation -framework CoreGraphics -framework CoreHaptics -framework CoreMotion -framework CoreVideo -framework GameController -framework IOKit -framework Metal -framework OpenGLES -framework QuartzCore -framework UIKit -framework AVFAudio -framework Foundation + + + <_Silk_NET_SDL_ExternalPInvokes>@(SilkExternalPInvoke) + <_Silk_NET_SDL_InternalPInvokes>@(SilkInternalPInvoke) + <_Silk_NET_SDL_Opts>@(RuntimeHostConfigurationOption) + + + + + + + diff --git a/src/Native/Directory.Build.targets b/src/Native/Directory.Build.targets index db9d98ff21..04dd247447 100644 --- a/src/Native/Directory.Build.targets +++ b/src/Native/Directory.Build.targets @@ -1,6 +1,7 @@ + diff --git a/src/Native/Silk.NET.SDL.Native/Silk.NET.SDL.Native.csproj b/src/Native/Silk.NET.SDL.Native/Silk.NET.SDL.Native.csproj index 70aace360a..ce821436dd 100644 --- a/src/Native/Silk.NET.SDL.Native/Silk.NET.SDL.Native.csproj +++ b/src/Native/Silk.NET.SDL.Native/Silk.NET.SDL.Native.csproj @@ -5,7 +5,7 @@ netstandard2.0;net4.6.1 Ultz.Native.SDL - 2.30.1 + 2.30.1.1 .NET Foundation and Contributors true Zlib diff --git a/src/Native/Silk.NET.SDL.Native/build/netstandard2.0/Ultz.Native.SDL.targets b/src/Native/Silk.NET.SDL.Native/build/netstandard2.0/Ultz.Native.SDL.targets index ef6a1e30f5..0ef8f57256 100644 --- a/src/Native/Silk.NET.SDL.Native/build/netstandard2.0/Ultz.Native.SDL.targets +++ b/src/Native/Silk.NET.SDL.Native/build/netstandard2.0/Ultz.Native.SDL.targets @@ -12,7 +12,7 @@ True True False - -framework AudioToolbox -framework AVFoundation -framework CoreAudio -framework CoreBluetooth -framework CoreFoundation -framework CoreGraphics -framework CoreHaptics -framework CoreMotion -framework CoreVideo -framework GameController -framework IOKit -framework Metal -framework OpenGLES -framework QuartzCore -framework UIKit + -framework AudioToolbox -framework AVFoundation -framework CoreAudio -framework CoreBluetooth -framework CoreFoundation -framework CoreGraphics -framework CoreHaptics -framework CoreMotion -framework CoreVideo -framework GameController -framework IOKit -framework Metal -framework OpenGLES -framework QuartzCore -framework UIKit -framework Foundation diff --git a/src/Native/Silk.NET.SDL.Native/runtimes/ios/native/libSDL2.a b/src/Native/Silk.NET.SDL.Native/runtimes/ios/native/libSDL2.a index bffbab2026..e3469b70ff 100644 Binary files a/src/Native/Silk.NET.SDL.Native/runtimes/ios/native/libSDL2.a and b/src/Native/Silk.NET.SDL.Native/runtimes/ios/native/libSDL2.a differ diff --git a/src/Native/Silk.NET.SDL.Native/runtimes/iossimulator/native/libSDL2.a b/src/Native/Silk.NET.SDL.Native/runtimes/iossimulator/native/libSDL2.a index 9d172fabc8..efbf09b5d9 100644 Binary files a/src/Native/Silk.NET.SDL.Native/runtimes/iossimulator/native/libSDL2.a and b/src/Native/Silk.NET.SDL.Native/runtimes/iossimulator/native/libSDL2.a differ diff --git a/src/Native/Silk.NET.SDL.Native/runtimes/osx/native/libSDL2-2.0.dylib b/src/Native/Silk.NET.SDL.Native/runtimes/osx/native/libSDL2-2.0.dylib index 18a4d3ac9d..42c0d24617 100755 Binary files a/src/Native/Silk.NET.SDL.Native/runtimes/osx/native/libSDL2-2.0.dylib and b/src/Native/Silk.NET.SDL.Native/runtimes/osx/native/libSDL2-2.0.dylib differ diff --git a/src/Native/Silk.NET.SDL.Native/runtimes/tvos/native/libSDL2.a b/src/Native/Silk.NET.SDL.Native/runtimes/tvos/native/libSDL2.a index 677469f609..d6605533a1 100644 Binary files a/src/Native/Silk.NET.SDL.Native/runtimes/tvos/native/libSDL2.a and b/src/Native/Silk.NET.SDL.Native/runtimes/tvos/native/libSDL2.a differ diff --git a/src/Native/Silk.NET.SDL.Native/runtimes/tvossimulator/native/libSDL2.a b/src/Native/Silk.NET.SDL.Native/runtimes/tvossimulator/native/libSDL2.a index 0929bff817..5d8ea6f5b0 100644 Binary files a/src/Native/Silk.NET.SDL.Native/runtimes/tvossimulator/native/libSDL2.a and b/src/Native/Silk.NET.SDL.Native/runtimes/tvossimulator/native/libSDL2.a differ diff --git a/src/Native/Silk.NET.SDL.Native/runtimes/win-arm64/native/SDL2.dll b/src/Native/Silk.NET.SDL.Native/runtimes/win-arm64/native/SDL2.dll index 257cc3cd3d..72787dc5ea 100644 Binary files a/src/Native/Silk.NET.SDL.Native/runtimes/win-arm64/native/SDL2.dll and b/src/Native/Silk.NET.SDL.Native/runtimes/win-arm64/native/SDL2.dll differ diff --git a/src/Native/Silk.NET.SDL.Native/runtimes/win-x64/native/SDL2.dll b/src/Native/Silk.NET.SDL.Native/runtimes/win-x64/native/SDL2.dll index 348d1d70a2..fff4b44527 100644 Binary files a/src/Native/Silk.NET.SDL.Native/runtimes/win-x64/native/SDL2.dll and b/src/Native/Silk.NET.SDL.Native/runtimes/win-x64/native/SDL2.dll differ diff --git a/src/Native/Silk.NET.SDL.Native/runtimes/win-x86/native/SDL2.dll b/src/Native/Silk.NET.SDL.Native/runtimes/win-x86/native/SDL2.dll index 6298da2f0c..387fcf304c 100644 Binary files a/src/Native/Silk.NET.SDL.Native/runtimes/win-x86/native/SDL2.dll and b/src/Native/Silk.NET.SDL.Native/runtimes/win-x86/native/SDL2.dll differ diff --git a/src/Windowing/Silk.NET.Windowing.Sdl/Android/Metadata.xml b/src/Windowing/Silk.NET.Windowing.Sdl/Android/Metadata.xml index 20343fb691..bd7b677196 100644 --- a/src/Windowing/Silk.NET.Windowing.Sdl/Android/Metadata.xml +++ b/src/Windowing/Silk.NET.Windowing.Sdl/Android/Metadata.xml @@ -19,4 +19,6 @@ path="/api/package[@name='org.libsdl.app']/class[@name='SDLControllerManager']/field[@name='mHapticHandler']"/> + diff --git a/src/Windowing/Silk.NET.Windowing.Sdl/Android/app-release.aar b/src/Windowing/Silk.NET.Windowing.Sdl/Android/app-release.aar index 24c099088f..99d2061d8f 100644 Binary files a/src/Windowing/Silk.NET.Windowing.Sdl/Android/app-release.aar and b/src/Windowing/Silk.NET.Windowing.Sdl/Android/app-release.aar differ diff --git a/src/Windowing/Silk.NET.Windowing.Sdl/PublicAPI/net6.0-android/PublicAPI.Unshipped.txt b/src/Windowing/Silk.NET.Windowing.Sdl/PublicAPI/net6.0-android/PublicAPI.Unshipped.txt index 7dc5c58110..6de42df6ee 100644 --- a/src/Windowing/Silk.NET.Windowing.Sdl/PublicAPI/net6.0-android/PublicAPI.Unshipped.txt +++ b/src/Windowing/Silk.NET.Windowing.Sdl/PublicAPI/net6.0-android/PublicAPI.Unshipped.txt @@ -1 +1,53 @@ #nullable enable +Org.Libsdl.App.SDLSurface +Org.Libsdl.App.SDLSurface.MDisplay.get -> Android.Views.Display? +Org.Libsdl.App.SDLSurface.MDisplay.set -> void +Org.Libsdl.App.SDLSurface.MHeight.get -> float +Org.Libsdl.App.SDLSurface.MHeight.set -> void +Org.Libsdl.App.SDLSurface.MIsSurfaceReady.get -> bool +Org.Libsdl.App.SDLSurface.MIsSurfaceReady.set -> void +Org.Libsdl.App.SDLSurface.MSensorManager.get -> Android.Hardware.SensorManager? +Org.Libsdl.App.SDLSurface.MSensorManager.set -> void +Org.Libsdl.App.SDLSurface.MWidth.get -> float +Org.Libsdl.App.SDLSurface.MWidth.set -> void +Org.Libsdl.App.SDLSurface.SDLSurface(Android.Content.Context? context) -> void +Org.Libsdl.App.SDLSurface.SDLSurface(System.IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer) -> void +override Org.Libsdl.App.SDLSurface.JniPeerMembers.get -> Java.Interop.JniPeerMembers! +override Org.Libsdl.App.SDLSurface.ThresholdClass.get -> System.IntPtr +override Org.Libsdl.App.SDLSurface.ThresholdType.get -> System.Type! +static Org.Libsdl.App.SDLActivity.DestroyCustomCursor(int cursorID) -> void +static Org.Libsdl.App.SDLActivity.HandleKeyEvent(Android.Views.View? v, int keyCode, Android.Views.KeyEvent? e, Android.Views.InputMethods.IInputConnection? ic) -> bool +static Org.Libsdl.App.SDLActivity.NativeGetHintBoolean(string? p0, bool p1) -> bool +static Org.Libsdl.App.SDLActivity.NativeGetVersion() -> string? +static Org.Libsdl.App.SDLActivity.NativeSetScreenResolution(int p0, int p1, int p2, int p3, float p4) -> void +static Org.Libsdl.App.SDLAudioManager.AddAudioDevice(bool p0, int p1) -> void +static Org.Libsdl.App.SDLAudioManager.AudioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) -> int[]? +static Org.Libsdl.App.SDLAudioManager.CaptureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) -> int[]? +static Org.Libsdl.App.SDLAudioManager.GetAudioInputDevices() -> int[]? +static Org.Libsdl.App.SDLAudioManager.GetAudioOutputDevices() -> int[]? +static Org.Libsdl.App.SDLAudioManager.MContext.get -> Android.Content.Context? +static Org.Libsdl.App.SDLAudioManager.MContext.set -> void +static Org.Libsdl.App.SDLAudioManager.Open(bool isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) -> int[]? +static Org.Libsdl.App.SDLAudioManager.Release(Android.Content.Context? context) -> void +static Org.Libsdl.App.SDLAudioManager.RemoveAudioDevice(bool p0, int p1) -> void +static Org.Libsdl.App.SDLAudioManager.SetContext(Android.Content.Context? context) -> void +static Org.Libsdl.App.SDLControllerManager.NativeAddJoystick(int p0, string? p1, string? p2, int p3, int p4, bool p5, int p6, int p7, int p8, int p9, int p10) -> int +virtual Org.Libsdl.App.HIDDeviceManager.Initialize(bool usb, bool bluetooth) -> bool +virtual Org.Libsdl.App.SDLActivity.CreateSDLSurface(Android.Content.Context? context) -> Org.Libsdl.App.SDLSurface? +virtual Org.Libsdl.App.SDLSurface.EnableSensor(int sensortype, bool enabled) -> void +virtual Org.Libsdl.App.SDLSurface.HandlePause() -> void +virtual Org.Libsdl.App.SDLSurface.HandleResume() -> void +virtual Org.Libsdl.App.SDLSurface.NativeSurface.get -> Android.Views.Surface? +virtual Org.Libsdl.App.SDLSurface.OnAccuracyChanged(Android.Hardware.Sensor? sensor, Android.Hardware.SensorStatus accuracy) -> void +virtual Org.Libsdl.App.SDLSurface.OnKey(Android.Views.View? v, Android.Views.Keycode keyCode, Android.Views.KeyEvent? e) -> bool +virtual Org.Libsdl.App.SDLSurface.OnSensorChanged(Android.Hardware.SensorEvent? e) -> void +virtual Org.Libsdl.App.SDLSurface.OnTouch(Android.Views.View? v, Android.Views.MotionEvent? e) -> bool +virtual Org.Libsdl.App.SDLSurface.SurfaceChanged(Android.Views.ISurfaceHolder? holder, Android.Graphics.Format format, int width, int height) -> void +virtual Org.Libsdl.App.SDLSurface.SurfaceCreated(Android.Views.ISurfaceHolder? holder) -> void +virtual Org.Libsdl.App.SDLSurface.SurfaceDestroyed(Android.Views.ISurfaceHolder? holder) -> void +~override Silk.NET.Windowing.Sdl.Android.SilkActivity.SetOrientationBis(int w, int h, bool resizable, string hint) -> void +~static Silk.NET.Windowing.Sdl.SdlWindowing.CreateFrom(void* handle, Silk.NET.Core.Contexts.IGLContext? ctx = null) -> Silk.NET.Windowing.IView +~static Silk.NET.Windowing.Sdl.SdlWindowing.GetExistingApi(Silk.NET.Windowing.IView view) -> Silk.NET.SDL.Sdl? +~static Silk.NET.Windowing.Sdl.SdlWindowing.GetHandle(Silk.NET.Windowing.IView view) -> Silk.NET.SDL.Window* +~static Silk.NET.Windowing.Sdl.SdlWindowing.GetSysWMInfo(Silk.NET.Windowing.IView view) -> Silk.NET.SDL.SysWMInfo? +~static Silk.NET.Windowing.Sdl.SdlWindowing.IsViewSdl(Silk.NET.Windowing.IView view) -> bool diff --git a/src/Windowing/Silk.NET.Windowing.Sdl/PublicAPI/net6.0-ios/PublicAPI.Unshipped.txt b/src/Windowing/Silk.NET.Windowing.Sdl/PublicAPI/net6.0-ios/PublicAPI.Unshipped.txt index 7dc5c58110..c2b521843b 100644 --- a/src/Windowing/Silk.NET.Windowing.Sdl/PublicAPI/net6.0-ios/PublicAPI.Unshipped.txt +++ b/src/Windowing/Silk.NET.Windowing.Sdl/PublicAPI/net6.0-ios/PublicAPI.Unshipped.txt @@ -1 +1,2 @@ #nullable enable +static Silk.NET.Windowing.Sdl.iOS.SilkMobile.IsRunning.get -> bool diff --git a/src/Windowing/Silk.NET.Windowing.Sdl/PublicAPI/net6.0-maccatalyst/PublicAPI.Unshipped.txt b/src/Windowing/Silk.NET.Windowing.Sdl/PublicAPI/net6.0-maccatalyst/PublicAPI.Unshipped.txt index 7dc5c58110..c2b521843b 100644 --- a/src/Windowing/Silk.NET.Windowing.Sdl/PublicAPI/net6.0-maccatalyst/PublicAPI.Unshipped.txt +++ b/src/Windowing/Silk.NET.Windowing.Sdl/PublicAPI/net6.0-maccatalyst/PublicAPI.Unshipped.txt @@ -1 +1,2 @@ #nullable enable +static Silk.NET.Windowing.Sdl.iOS.SilkMobile.IsRunning.get -> bool diff --git a/src/Windowing/Silk.NET.Windowing.Sdl/SdlView.cs b/src/Windowing/Silk.NET.Windowing.Sdl/SdlView.cs index 258d7f2171..553231b63c 100644 --- a/src/Windowing/Silk.NET.Windowing.Sdl/SdlView.cs +++ b/src/Windowing/Silk.NET.Windowing.Sdl/SdlView.cs @@ -1,15 +1,21 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; using Silk.NET.Core; using Silk.NET.Core.Contexts; using Silk.NET.Maths; using Silk.NET.SDL; using Silk.NET.Windowing.Internals; +#if __IOS__ +using Silk.NET.Windowing.Sdl.iOS; +#endif // We can't import System because System has a type called nint on iOS and Mac Catalyst. // As such, throughout this file System is fully qualified. @@ -263,6 +269,34 @@ protected void CoreInitialize Sdl.ThrowError(); } +#if __IOS__ + private static bool _isRunning; + public override void Run(Action onFrame) + { + if (_isRunning) + { + throw new NotSupportedException("A view is already running in this mobile application."); + } + + if (!SilkMobile.IsRunning) + { + throw new InvalidOperationException + ( + "The view could not be created as the underlying mobile application is not running. On iOS, " + + "please wrap your main function in a call to SilkMobile.RunApp to ensure that application " + + "lifecycles can be managed properly." + ); + } + + // This is not correct, we should be using SDL_iPhoneSetAnimationCallback and then letting + // SDL_UIKitRunApp take care of the lifetime, but this would be a breaking change as Run in 2.X is expected + // to only exit when the view does. We'll fix this properly in 3.0. + _isRunning = true; + base.Run(onFrame); + _isRunning = false; + } +#endif + protected override void CoreReset() { if (SdlWindow == null) diff --git a/src/Windowing/Silk.NET.Windowing.Sdl/iOS/SilkMobile.cs b/src/Windowing/Silk.NET.Windowing.Sdl/iOS/SilkMobile.cs index 64144f571f..041f35cc87 100644 --- a/src/Windowing/Silk.NET.Windowing.Sdl/iOS/SilkMobile.cs +++ b/src/Windowing/Silk.NET.Windowing.Sdl/iOS/SilkMobile.cs @@ -12,14 +12,15 @@ static SilkMobile() { SearchPathContainer.Platform = UnderlyingPlatform.IOS; } - - private static bool _running; + + public static bool IsRunning { get; private set; } + private static MainFunction? CurrentMain { get; set; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public unsafe delegate void MainFunction(int numArgs, byte** args); [DllImport("__Internal", EntryPoint = "SDL_UIKitRunApp")] private static extern unsafe void CoreRunApp(int numArgs, byte** args, nint callback); - + public static unsafe void RunApp(int numArgs, byte** args, nint callback) { BeginRun(); @@ -78,21 +79,21 @@ public static unsafe void RunApp(IReadOnlyList args, System.Action CurrentMain!(numArgs, args); - + private static unsafe nint GetCallMainPtr() => Marshal.GetFunctionPointerForDelegate((MainFunction) CallMain); private static void BeginRun() { - if (_running) + if (IsRunning) { throw new System.InvalidOperationException("App already running."); } SdlWindowing.RegisterPlatform(); - _running = true; + IsRunning = true; } - private static void EndRun() => _running = false; + private static void EndRun() => IsRunning = false; } } diff --git a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java index 65c5a42370..ee5521fd5e 100644 --- a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java +++ b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java @@ -186,7 +186,7 @@ public BluetoothGatt getGatt() { // Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead // of TRANSPORT_LE. Let's force ourselves to connect low energy. private BluetoothGatt connectGatt(boolean managed) { - if (Build.VERSION.SDK_INT >= 23) { + if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) { try { return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE); } catch (Exception e) { @@ -429,7 +429,7 @@ public void run() { } }); } - } + } else if (newState == 0) { mIsConnected = false; } diff --git a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/HIDDeviceManager.java b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/HIDDeviceManager.java index cf3c9267fb..e7281fdf26 100644 --- a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/HIDDeviceManager.java +++ b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/HIDDeviceManager.java @@ -170,7 +170,7 @@ private void initializeUSB() { Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol()); Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount()); - // Get endpoint details + // Get endpoint details for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++) { UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi); @@ -251,6 +251,8 @@ private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterfa 0x20d6, // PowerA 0x24c6, // PowerA 0x2c22, // Qanba + 0x2dc8, // 8BitDo + 0x9886, // ASTRO Gaming }; if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC && @@ -271,15 +273,19 @@ private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterfa final int XB1_IFACE_SUBCLASS = 71; final int XB1_IFACE_PROTOCOL = 208; final int[] SUPPORTED_VENDORS = { + 0x03f0, // HP + 0x044f, // Thrustmaster 0x045e, // Microsoft 0x0738, // Mad Catz 0x0e6f, // PDP 0x0f0d, // Hori + 0x10f5, // Turtle Beach 0x1532, // Razer Wildcat 0x20d6, // PowerA 0x24c6, // PowerA - 0x2dc8, /* 8BitDo */ + 0x2dc8, // 8BitDo 0x2e24, // Hyperkin + 0x3537, // GameSir }; if (usbInterface.getId() == 0 && @@ -353,13 +359,19 @@ private void connectHIDDeviceUSB(UsbDevice usbDevice) { private void initializeBluetooth() { Log.d(TAG, "Initializing Bluetooth"); - if (Build.VERSION.SDK_INT <= 30 && + if (Build.VERSION.SDK_INT >= 31 /* Android 12 */ && + mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH_CONNECT, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH_CONNECT"); + return; + } + + if (Build.VERSION.SDK_INT <= 30 /* Android 11.0 (R) */ && mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) { Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH"); return; } - if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18)) { + if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18 /* Android 4.3 (JELLY_BEAN_MR2) */)) { Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE"); return; } @@ -524,7 +536,7 @@ public void setFrozen(boolean frozen) { for (HIDDevice device : mDevicesById.values()) { device.setFrozen(frozen); } - } + } } ////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -573,7 +585,7 @@ public boolean openDevice(int deviceID) { try { final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31 int flags; - if (Build.VERSION.SDK_INT >= 31) { + if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) { flags = FLAG_MUTABLE; } else { flags = 0; diff --git a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java index d20fe80bc6..bfe0cf954d 100644 --- a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java +++ b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java @@ -52,7 +52,7 @@ public int getProductId() { @Override public String getSerialNumber() { String result = null; - if (Build.VERSION.SDK_INT >= 21) { + if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) { try { result = mDevice.getSerialNumber(); } @@ -74,7 +74,7 @@ public int getVersion() { @Override public String getManufacturerName() { String result = null; - if (Build.VERSION.SDK_INT >= 21) { + if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) { result = mDevice.getManufacturerName(); } if (result == null) { @@ -86,7 +86,7 @@ public String getManufacturerName() { @Override public String getProductName() { String result = null; - if (Build.VERSION.SDK_INT >= 21) { + if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) { result = mDevice.getProductName(); } if (result == null) { diff --git a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDL.java b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDL.java index dafc0cb87d..44c21c1c75 100644 --- a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDL.java +++ b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDL.java @@ -29,6 +29,7 @@ public static void initialize() { // This function stores the current activity (SDL or not) public static void setContext(Context context) { + SDLAudioManager.setContext(context); mContext = context; } diff --git a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLActivity.java b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLActivity.java index eef4ee870b..fd5a056e3f 100644 --- a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -60,8 +60,8 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener { private static final String TAG = "SDL"; private static final int SDL_MAJOR_VERSION = 2; - private static final int SDL_MINOR_VERSION = 26; - private static final int SDL_MICRO_VERSION = 2; + private static final int SDL_MINOR_VERSION = 30; + private static final int SDL_MICRO_VERSION = 1; /* // Display InputType.SOURCE/CLASS of events and devices // @@ -93,7 +93,7 @@ public static void debugSource(int sources, String prefix) { s2 = s_copy & InputDevice.SOURCE_ANY; // keep source only, no class; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (Build.VERSION.SDK_INT >= 23) { tst = InputDevice.SOURCE_BLUETOOTH_STYLUS; if ((s & tst) == tst) src += " BLUETOOTH_STYLUS"; s2 &= ~tst; @@ -107,7 +107,7 @@ public static void debugSource(int sources, String prefix) { if ((s & tst) == tst) src += " GAMEPAD"; s2 &= ~tst; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (Build.VERSION.SDK_INT >= 21) { tst = InputDevice.SOURCE_HDMI; if ((s & tst) == tst) src += " HDMI"; s2 &= ~tst; @@ -146,7 +146,7 @@ public static void debugSource(int sources, String prefix) { if ((s & tst) == tst) src += " TOUCHSCREEN"; s2 &= ~tst; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + if (Build.VERSION.SDK_INT >= 18) { tst = InputDevice.SOURCE_TOUCH_NAVIGATION; if ((s & tst) == tst) src += " TOUCH_NAVIGATION"; s2 &= ~tst; @@ -170,7 +170,7 @@ public static void debugSource(int sources, String prefix) { */ public static boolean mIsResumedCalled, mHasFocus; - public static final boolean mHasMultiWindow = (Build.VERSION.SDK_INT >= 24); + public static final boolean mHasMultiWindow = (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */); // Cursor types // private static final int SDL_SYSTEM_CURSOR_NONE = -1; @@ -224,9 +224,9 @@ public enum NativeState { protected static SDLGenericMotionListener_API12 getMotionListener() { if (mMotionListener == null) { - if (Build.VERSION.SDK_INT >= 26) { + if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) { mMotionListener = new SDLGenericMotionListener_API26(); - } else if (Build.VERSION.SDK_INT >= 24) { + } else if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { mMotionListener = new SDLGenericMotionListener_API24(); } else { mMotionListener = new SDLGenericMotionListener_API12(); @@ -393,7 +393,7 @@ public void onClick(DialogInterface dialog,int id) { mHIDDeviceManager = HIDDeviceManager.acquire(this); // Set up the surface - mSurface = createSDLSurface(getApplication()); + mSurface = createSDLSurface(this); mLayout = new RelativeLayout(this); mLayout.addView(mSurface); @@ -404,7 +404,7 @@ public void onClick(DialogInterface dialog,int id) { SDLActivity.onNativeOrientationChanged(mCurrentOrientation); try { - if (Build.VERSION.SDK_INT < 24) { + if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) { mCurrentLocale = getContext().getResources().getConfiguration().locale; } else { mCurrentLocale = getContext().getResources().getConfiguration().getLocales().get(0); @@ -588,6 +588,8 @@ protected void onDestroy() { mHIDDeviceManager = null; } + SDLAudioManager.release(this); + if (SDLActivity.mBrokenLibraries) { super.onDestroy(); return; @@ -766,7 +768,7 @@ public void handleMessage(Message msg) { } break; case COMMAND_CHANGE_WINDOW_STYLE: - if (Build.VERSION.SDK_INT >= 19) { + if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) { if (context instanceof Activity) { Window window = ((Activity) context).getWindow(); if (window != null) { @@ -841,7 +843,7 @@ boolean sendCommand(int command, Object data) { msg.obj = data; boolean result = commandHandler.sendMessage(msg); - if (Build.VERSION.SDK_INT >= 19) { + if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) { if (command == COMMAND_CHANGE_WINDOW_STYLE) { // Ensure we don't return until the resize has actually happened, // or 500ms have passed. @@ -969,15 +971,18 @@ public void setOrientationBis(int w, int h, boolean resizable, String hint) /* If set, hint "explicitly controls which UI orientations are allowed". */ if (hint.contains("LandscapeRight") && hint.contains("LandscapeLeft")) { orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; - } else if (hint.contains("LandscapeRight")) { - orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; } else if (hint.contains("LandscapeLeft")) { + orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; + } else if (hint.contains("LandscapeRight")) { orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; } - if (hint.contains("Portrait") && hint.contains("PortraitUpsideDown")) { + /* exact match to 'Portrait' to distinguish with PortraitUpsideDown */ + boolean contains_Portrait = hint.contains("Portrait ") || hint.endsWith("Portrait"); + + if (contains_Portrait && hint.contains("PortraitUpsideDown")) { orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; - } else if (hint.contains("Portrait")) { + } else if (contains_Portrait) { orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; } else if (hint.contains("PortraitUpsideDown")) { orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; @@ -1090,7 +1095,7 @@ public static boolean supportsRelativeMouse() // thus SDK version 27. If we are in DeX mode and not API 27 or higher, as a result, // we should stick to relative mode. // - if ((Build.VERSION.SDK_INT < 27) && isDeXMode()) { + if (Build.VERSION.SDK_INT < 27 /* Android 8.1 (O_MR1) */ && isDeXMode()) { return false; } @@ -1180,7 +1185,7 @@ public static boolean isChromebook() { * This method is called by SDL using JNI. */ public static boolean isDeXMode() { - if (Build.VERSION.SDK_INT < 24) { + if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) { return false; } try { @@ -1340,23 +1345,6 @@ public static boolean handleKeyEvent(View v, int keyCode, KeyEvent event, InputC } } - if ((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - if (isTextInputEvent(event)) { - if (ic != null) { - ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1); - } else { - SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1); - } - } - onNativeKeyDown(keyCode); - return true; - } else if (event.getAction() == KeyEvent.ACTION_UP) { - onNativeKeyUp(keyCode); - return true; - } - } - if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) { // on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses // they are ignored here because sending them as mouse input to SDL is messy @@ -1371,6 +1359,21 @@ public static boolean handleKeyEvent(View v, int keyCode, KeyEvent event, InputC } } + if (event.getAction() == KeyEvent.ACTION_DOWN) { + if (isTextInputEvent(event)) { + if (ic != null) { + ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1); + } else { + SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1); + } + } + onNativeKeyDown(keyCode); + return true; + } else if (event.getAction() == KeyEvent.ACTION_UP) { + onNativeKeyUp(keyCode); + return true; + } + return false; } @@ -1617,7 +1620,7 @@ public boolean onKey(DialogInterface d, int keyCode, KeyEvent event) { private final Runnable rehideSystemUi = new Runnable() { @Override public void run() { - if (Build.VERSION.SDK_INT >= 19) { + if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) { int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | @@ -1670,7 +1673,7 @@ public static int createCustomCursor(int[] colors, int width, int height, int ho Bitmap bitmap = Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888); ++mLastCursorID; - if (Build.VERSION.SDK_INT >= 24) { + if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { try { mCursors.put(mLastCursorID, PointerIcon.create(bitmap, hotSpotX, hotSpotY)); } catch (Exception e) { @@ -1686,7 +1689,7 @@ public static int createCustomCursor(int[] colors, int width, int height, int ho * This method is called by SDL using JNI. */ public static void destroyCustomCursor(int cursorID) { - if (Build.VERSION.SDK_INT >= 24) { + if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { try { mCursors.remove(cursorID); } catch (Exception e) { @@ -1700,7 +1703,7 @@ public static void destroyCustomCursor(int cursorID) { */ public static boolean setCustomCursor(int cursorID) { - if (Build.VERSION.SDK_INT >= 24) { + if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { try { mSurface.setPointerIcon(mCursors.get(cursorID)); } catch (Exception e) { @@ -1755,7 +1758,7 @@ public static boolean setSystemCursor(int cursorID) { cursor_type = 1002; //PointerIcon.TYPE_HAND; break; } - if (Build.VERSION.SDK_INT >= 24) { + if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { try { mSurface.setPointerIcon(PointerIcon.getSystemIcon(SDL.getContext(), cursor_type)); } catch (Exception e) { @@ -1769,7 +1772,7 @@ public static boolean setSystemCursor(int cursorID) { * This method is called by SDL using JNI. */ public static void requestPermission(String permission, int requestCode) { - if (Build.VERSION.SDK_INT < 23) { + if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) { nativePermissionResult(requestCode, true); return; } @@ -1798,7 +1801,7 @@ public static int openURL(String url) i.setData(Uri.parse(url)); int flags = Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_MULTIPLE_TASK; - if (Build.VERSION.SDK_INT >= 21) { + if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) { flags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT; } else { flags |= Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET; @@ -2002,6 +2005,18 @@ public boolean setComposingText(CharSequence text, int newCursorPosition) { @Override public boolean deleteSurroundingText(int beforeLength, int afterLength) { + if (Build.VERSION.SDK_INT <= 29 /* Android 10.0 (Q) */) { + // Workaround to capture backspace key. Ref: http://stackoverflow.com/questions>/14560344/android-backspace-in-webview-baseinputconnection + // and https://bugzilla.libsdl.org/show_bug.cgi?id=2265 + if (beforeLength > 0 && afterLength == 0) { + // backspace(s) + while (beforeLength-- > 0) { + nativeGenerateScancodeForUnichar('\b'); + } + return true; + } + } + if (!super.deleteSurroundingText(beforeLength, afterLength)) { return false; } diff --git a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLAudioManager.java b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLAudioManager.java index 2bfc718609..7c821a4097 100644 --- a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLAudioManager.java +++ b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLAudioManager.java @@ -1,5 +1,8 @@ package org.libsdl.app; +import android.content.Context; +import android.media.AudioDeviceCallback; +import android.media.AudioDeviceInfo; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioRecord; @@ -8,34 +11,67 @@ import android.os.Build; import android.util.Log; -public class SDLAudioManager -{ +import java.util.Arrays; + +public class SDLAudioManager { protected static final String TAG = "SDLAudio"; protected static AudioTrack mAudioTrack; protected static AudioRecord mAudioRecord; + protected static Context mContext; + + private static final int[] NO_DEVICES = {}; + + private static AudioDeviceCallback mAudioDeviceCallback; public static void initialize() { mAudioTrack = null; mAudioRecord = null; + mAudioDeviceCallback = null; + + if(Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) + { + mAudioDeviceCallback = new AudioDeviceCallback() { + @Override + public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) { + Arrays.stream(addedDevices).forEach(deviceInfo -> addAudioDevice(deviceInfo.isSink(), deviceInfo.getId())); + } + + @Override + public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) { + Arrays.stream(removedDevices).forEach(deviceInfo -> removeAudioDevice(deviceInfo.isSink(), deviceInfo.getId())); + } + }; + } + } + + public static void setContext(Context context) { + mContext = context; + if (context != null) { + registerAudioDeviceCallback(); + } + } + + public static void release(Context context) { + unregisterAudioDeviceCallback(context); } // Audio protected static String getAudioFormatString(int audioFormat) { switch (audioFormat) { - case AudioFormat.ENCODING_PCM_8BIT: - return "8-bit"; - case AudioFormat.ENCODING_PCM_16BIT: - return "16-bit"; - case AudioFormat.ENCODING_PCM_FLOAT: - return "float"; - default: - return Integer.toString(audioFormat); + case AudioFormat.ENCODING_PCM_8BIT: + return "8-bit"; + case AudioFormat.ENCODING_PCM_16BIT: + return "16-bit"; + case AudioFormat.ENCODING_PCM_FLOAT: + return "float"; + default: + return Integer.toString(audioFormat); } } - protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) { + protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) { int channelConfig; int sampleSize; int frameSize; @@ -43,14 +79,14 @@ protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz"); /* On older devices let's use known good settings */ - if (Build.VERSION.SDK_INT < 21) { + if (Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) { if (desiredChannels > 2) { desiredChannels = 2; } } /* AudioTrack has sample rate limitation of 48000 (fixed in 5.0.2) */ - if (Build.VERSION.SDK_INT < 22) { + if (Build.VERSION.SDK_INT < 22 /* Android 5.1 (LOLLIPOP_MR1) */) { if (sampleRate < 8000) { sampleRate = 8000; } else if (sampleRate > 48000) { @@ -59,7 +95,7 @@ protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, } if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) { - int minSDKVersion = (isCapture ? 23 : 21); + int minSDKVersion = (isCapture ? 23 /* Android 6.0 (M) */ : 21 /* Android 5.0 (LOLLIPOP) */); if (Build.VERSION.SDK_INT < minSDKVersion) { audioFormat = AudioFormat.ENCODING_PCM_16BIT; } @@ -120,7 +156,7 @@ protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER; break; case 8: - if (Build.VERSION.SDK_INT >= 23) { + if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) { channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND; } else { Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround"); @@ -201,6 +237,10 @@ protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, return null; } + if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) { + mAudioRecord.setPreferredDevice(getOutputAudioDeviceInfo(deviceId)); + } + mAudioRecord.startRecording(); } @@ -224,6 +264,10 @@ protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, return null; } + if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) { + mAudioTrack.setPreferredDevice(getInputAudioDeviceInfo(deviceId)); + } + mAudioTrack.play(); } @@ -238,11 +282,73 @@ protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, return results; } + private static AudioDeviceInfo getInputAudioDeviceInfo(int deviceId) { + if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { + AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) + .filter(deviceInfo -> deviceInfo.getId() == deviceId) + .findFirst() + .orElse(null); + } else { + return null; + } + } + + private static AudioDeviceInfo getOutputAudioDeviceInfo(int deviceId) { + if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { + AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) + .filter(deviceInfo -> deviceInfo.getId() == deviceId) + .findFirst() + .orElse(null); + } else { + return null; + } + } + + private static void registerAudioDeviceCallback() { + if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { + AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null); + } + } + + private static void unregisterAudioDeviceCallback(Context context) { + if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { + AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback); + } + } + + /** + * This method is called by SDL using JNI. + */ + public static int[] getAudioOutputDevices() { + if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { + AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).mapToInt(AudioDeviceInfo::getId).toArray(); + } else { + return NO_DEVICES; + } + } + + /** + * This method is called by SDL using JNI. + */ + public static int[] getAudioInputDevices() { + if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { + AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).mapToInt(AudioDeviceInfo::getId).toArray(); + } else { + return NO_DEVICES; + } + } + /** * This method is called by SDL using JNI. */ - public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) { - return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames); + public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) { + return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId); } /** @@ -254,6 +360,11 @@ public static void audioWriteFloatBuffer(float[] buffer) { return; } + if (android.os.Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) { + Log.e(TAG, "Attempted to make an incompatible audio call with uninitialized audio! (floating-point output is supported since Android 5.0 Lollipop)"); + return; + } + for (int i = 0; i < buffer.length;) { int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING); if (result > 0) { @@ -326,18 +437,22 @@ public static void audioWriteByteBuffer(byte[] buffer) { /** * This method is called by SDL using JNI. */ - public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) { - return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames); + public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) { + return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId); } /** This method is called by SDL using JNI. */ public static int captureReadFloatBuffer(float[] buffer, boolean blocking) { - return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING); + if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) { + return 0; + } else { + return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING); + } } /** This method is called by SDL using JNI. */ public static int captureReadShortBuffer(short[] buffer, boolean blocking) { - if (Build.VERSION.SDK_INT < 23) { + if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) { return mAudioRecord.read(buffer, 0, buffer.length); } else { return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING); @@ -346,7 +461,7 @@ public static int captureReadShortBuffer(short[] buffer, boolean blocking) { /** This method is called by SDL using JNI. */ public static int captureReadByteBuffer(byte[] buffer, boolean blocking) { - if (Build.VERSION.SDK_INT < 23) { + if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) { return mAudioRecord.read(buffer, 0, buffer.length); } else { return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING); @@ -391,4 +506,9 @@ public static void audioSetThreadPriority(boolean iscapture, int device_id) { } public static native int nativeSetupJNI(); + + public static native void removeAudioDevice(boolean isCapture, int deviceId); + + public static native void addAudioDevice(boolean isCapture, int deviceId); + } diff --git a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLControllerManager.java b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLControllerManager.java index 82373d9d6f..d6913f1571 100644 --- a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLControllerManager.java +++ b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLControllerManager.java @@ -24,7 +24,7 @@ public class SDLControllerManager public static native int nativeAddJoystick(int device_id, String name, String desc, int vendor_id, int product_id, boolean is_accelerometer, int button_mask, - int naxes, int nhats, int nballs); + int naxes, int axis_mask, int nhats, int nballs); public static native int nativeRemoveJoystick(int device_id); public static native int nativeAddHaptic(int device_id, String name); public static native int nativeRemoveHaptic(int device_id); @@ -42,7 +42,7 @@ public static native void onNativeHat(int device_id, int hat_id, public static void initialize() { if (mJoystickHandler == null) { - if (Build.VERSION.SDK_INT >= 19) { + if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) { mJoystickHandler = new SDLJoystickHandler_API19(); } else { mJoystickHandler = new SDLJoystickHandler_API16(); @@ -50,7 +50,7 @@ public static void initialize() { } if (mHapticHandler == null) { - if (Build.VERSION.SDK_INT >= 26) { + if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) { mHapticHandler = new SDLHapticHandler_API26(); } else { mHapticHandler = new SDLHapticHandler(); @@ -168,6 +168,32 @@ public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) { arg1Axis = MotionEvent.AXIS_GAS; } + // Make sure the AXIS_Z is sorted between AXIS_RY and AXIS_RZ. + // This is because the usual pairing are: + // - AXIS_X + AXIS_Y (left stick). + // - AXIS_RX, AXIS_RY (sometimes the right stick, sometimes triggers). + // - AXIS_Z, AXIS_RZ (sometimes the right stick, sometimes triggers). + // This sorts the axes in the above order, which tends to be correct + // for Xbox-ish game pads that have the right stick on RX/RY and the + // triggers on Z/RZ. + // + // Gamepads that don't have AXIS_Z/AXIS_RZ but use + // AXIS_LTRIGGER/AXIS_RTRIGGER are unaffected by this. + // + // References: + // - https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input + // - https://www.kernel.org/doc/html/latest/input/gamepad.html + if (arg0Axis == MotionEvent.AXIS_Z) { + arg0Axis = MotionEvent.AXIS_RZ - 1; + } else if (arg0Axis > MotionEvent.AXIS_Z && arg0Axis < MotionEvent.AXIS_RZ) { + --arg0Axis; + } + if (arg1Axis == MotionEvent.AXIS_Z) { + arg1Axis = MotionEvent.AXIS_RZ - 1; + } else if (arg1Axis > MotionEvent.AXIS_Z && arg1Axis < MotionEvent.AXIS_RZ) { + --arg1Axis; + } + return arg0Axis - arg1Axis; } } @@ -210,7 +236,7 @@ public void pollInputDevices() { mJoysticks.add(joystick); SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, getVendorId(joystickDevice), getProductId(joystickDevice), false, - getButtonMask(joystickDevice), joystick.axes.size(), joystick.hats.size()/2, 0); + getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, 0); } } } @@ -291,6 +317,9 @@ public int getProductId(InputDevice joystickDevice) { public int getVendorId(InputDevice joystickDevice) { return 0; } + public int getAxisMask(List ranges) { + return -1; + } public int getButtonMask(InputDevice joystickDevice) { return -1; } @@ -308,6 +337,43 @@ public int getVendorId(InputDevice joystickDevice) { return joystickDevice.getVendorId(); } + @Override + public int getAxisMask(List ranges) { + // For compatibility, keep computing the axis mask like before, + // only really distinguishing 2, 4 and 6 axes. + int axis_mask = 0; + if (ranges.size() >= 2) { + // ((1 << SDL_GAMEPAD_AXIS_LEFTX) | (1 << SDL_GAMEPAD_AXIS_LEFTY)) + axis_mask |= 0x0003; + } + if (ranges.size() >= 4) { + // ((1 << SDL_GAMEPAD_AXIS_RIGHTX) | (1 << SDL_GAMEPAD_AXIS_RIGHTY)) + axis_mask |= 0x000c; + } + if (ranges.size() >= 6) { + // ((1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)) + axis_mask |= 0x0030; + } + // Also add an indicator bit for whether the sorting order has changed. + // This serves to disable outdated gamecontrollerdb.txt mappings. + boolean have_z = false; + boolean have_past_z_before_rz = false; + for (InputDevice.MotionRange range : ranges) { + int axis = range.getAxis(); + if (axis == MotionEvent.AXIS_Z) { + have_z = true; + } else if (axis > MotionEvent.AXIS_Z && axis < MotionEvent.AXIS_RZ) { + have_past_z_before_rz = true; + } + } + if (have_z && have_past_z_before_rz) { + // If both these exist, the compare() function changed sorting order. + // Set a bit to indicate this fact. + axis_mask |= 0x8000; + } + return axis_mask; + } + @Override public int getButtonMask(InputDevice joystickDevice) { int button_mask = 0; @@ -743,7 +809,7 @@ public boolean onGenericMotion(View v, MotionEvent event) { @Override public boolean supportsRelativeMouse() { - return (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27)); + return (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */); } @Override @@ -753,7 +819,7 @@ public boolean inRelativeMode() { @Override public boolean setRelativeMouseEnabled(boolean enabled) { - if (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27)) { + if (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */) { if (enabled) { SDLActivity.getContentView().requestPointerCapture(); } else { diff --git a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLSurface.java b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLSurface.java index dcd26d495c..0857e4b6f3 100644 --- a/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLSurface.java +++ b/src/Windowing/SilkDroid/app/src/main/java/org/libsdl/app/SDLSurface.java @@ -116,7 +116,7 @@ public void surfaceChanged(SurfaceHolder holder, int nDeviceHeight = height; try { - if (Build.VERSION.SDK_INT >= 17) { + if (Build.VERSION.SDK_INT >= 17 /* Android 4.2 (JELLY_BEAN_MR1) */) { DisplayMetrics realMetrics = new DisplayMetrics(); mDisplay.getRealMetrics( realMetrics ); nDeviceWidth = realMetrics.widthPixels; @@ -163,7 +163,7 @@ public void surfaceChanged(SurfaceHolder holder, // Don't skip in MultiWindow. if (skip) { - if (Build.VERSION.SDK_INT >= 24) { + if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { if (SDLActivity.mSingleton.isInMultiWindowMode()) { Log.v("SDL", "Don't skip in Multi-Window"); skip = false;