diff --git a/VisualC-WinPhone/SDL/SDL_VS2012-WinPhone.vcxproj b/VisualC-WinPhone/SDL/SDL_VS2012-WinPhone.vcxproj new file mode 100644 index 000000000..5c5951528 --- /dev/null +++ b/VisualC-WinPhone/SDL/SDL_VS2012-WinPhone.vcxproj @@ -0,0 +1,403 @@ + + + + + Debug + Win32 + + + Debug + ARM + + + Release + Win32 + + + Release + ARM + + + + {33048af1-031a-4ce6-b61e-fad2db832e9e} + SDL + en-US + 11.0 + SDL-WinPhone + + + + DynamicLibrary + true + v110_wp80 + false + + + DynamicLibrary + true + v110_wp80 + false + + + DynamicLibrary + false + true + v110_wp80 + false + + + DynamicLibrary + false + true + v110_wp80 + false + + + + + + + + false + + + + _USRDLL;UNICODE;%(PreprocessorDefinitions) + NotUsing + pch.h + false + $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) + ..\..\include + false + + + Console + false + false + true + d3d11.lib;xaudio2.lib;WindowsPhoneCore.lib;RuntimeObject.lib;PhoneAppModelHost.lib;%(AdditionalDependencies) + + + + + _USRDLL;UNICODE;NDEBUG;%(PreprocessorDefinitions) + NotUsing + pch.h + false + $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) + ..\..\include + + + Console + false + false + true + d3d11.lib;xaudio2.lib;WindowsPhoneCore.lib;RuntimeObject.lib;PhoneAppModelHost.lib;%(AdditionalDependencies) + + + + + _USRDLL;UNICODE;%(PreprocessorDefinitions) + NotUsing + pch.h + false + $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) + ..\..\include + false + + + Console + false + false + true + d3d11.lib;xaudio2.lib;WindowsPhoneCore.lib;RuntimeObject.lib;PhoneAppModelHost.lib;%(AdditionalDependencies) + + + + + _USRDLL;UNICODE;NDEBUG;%(PreprocessorDefinitions) + NotUsing + pch.h + false + $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) + ..\..\include + + + Console + false + false + true + d3d11.lib;xaudio2.lib;WindowsPhoneCore.lib;RuntimeObject.lib;PhoneAppModelHost.lib;%(AdditionalDependencies) + + + + + true + + + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CompileAsCpp + CompileAsCpp + CompileAsCpp + CompileAsCpp + + + true + true + true + true + + + true + true + true + true + + + + + + + + + + + + + + + + + + + + + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + + + Pixel + Pixel + Pixel + Pixel + + + Pixel + Pixel + Pixel + Pixel + + + Vertex + Vertex + Vertex + Vertex + + + + + + + \ No newline at end of file diff --git a/VisualC-WinPhone/SDL/SDL_VS2012-WinPhone.vcxproj.filters b/VisualC-WinPhone/SDL/SDL_VS2012-WinPhone.vcxproj.filters new file mode 100644 index 000000000..4eb56ac03 --- /dev/null +++ b/VisualC-WinPhone/SDL/SDL_VS2012-WinPhone.vcxproj.filters @@ -0,0 +1,608 @@ + + + + + {02b21b9a-45a7-41ee-a8a6-e45d14aa28da} + + + {abc3a7e6-f955-4cb5-8340-fae0f653e9c1} + + + {fd67993e-5155-488b-9313-d1eb06a1b67e} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + GPU Shaders + + + GPU Shaders + + + GPU Shaders + + + \ No newline at end of file diff --git a/VisualC-WinRT/SDL/SDL_VS2012-WinRT.vcxproj b/VisualC-WinRT/SDL/SDL_VS2012-WinRT.vcxproj new file mode 100644 index 000000000..40a8ffd9e --- /dev/null +++ b/VisualC-WinRT/SDL/SDL_VS2012-WinRT.vcxproj @@ -0,0 +1,505 @@ + + + + + Debug + ARM + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + CompileAsCpp + CompileAsCpp + CompileAsCpp + CompileAsCpp + CompileAsCpp + CompileAsCpp + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Pixel + Pixel + Pixel + Pixel + Pixel + Pixel + 4.0_level_9_1 + 4.0_level_9_1 + 4.0_level_9_1 + 4.0_level_9_1 + 4.0_level_9_1 + 4.0_level_9_1 + + + Pixel + Pixel + Pixel + Pixel + Pixel + Pixel + 4.0_level_9_1 + 4.0_level_9_1 + 4.0_level_9_1 + 4.0_level_9_1 + + + Vertex + Vertex + Vertex + Vertex + Vertex + Vertex + 4.0_level_9_1 + 4.0_level_9_1 + 4.0_level_9_1 + 4.0_level_9_1 + 4.0_level_9_1 + 4.0_level_9_1 + + + + {aeaea3a2-d4e6-45b1-8ec6-53d84287fc14} + Win32Proj + SDL-WinRT + SDL_VS2012_WinRT + en-US + 11.0 + true + + + + DynamicLibrary + true + v110 + + + DynamicLibrary + true + v110 + + + DynamicLibrary + true + v110 + + + DynamicLibrary + false + true + v110 + + + DynamicLibrary + false + true + v110 + + + DynamicLibrary + false + true + v110 + + + + + + + + + + + + + + + + + + + + + + + + false + false + SDL + + + false + false + SDL + + + false + false + SDL + + + false + false + SDL + + + false + false + SDL + + + false + false + SDL + + + + NotUsing + false + ..\..\include;%(AdditionalIncludeDirectories) + _WINDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + false + false + xinput.lib;xaudio2.lib;d2d1.lib;d3d11.lib;dxgi.lib;ole32.lib;windowscodecs.lib;dwrite.lib;kernel32.lib;%(AdditionalDependencies) + + + + + NotUsing + false + ..\..\include;%(AdditionalIncludeDirectories) + _WINDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + false + false + xinput.lib;xaudio2.lib;d2d1.lib;d3d11.lib;dxgi.lib;ole32.lib;windowscodecs.lib;dwrite.lib;kernel32.lib;%(AdditionalDependencies) + + + + + NotUsing + false + ..\..\include;%(AdditionalIncludeDirectories) + _WINDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + false + false + xinput.lib;xaudio2.lib;d2d1.lib;d3d11.lib;dxgi.lib;ole32.lib;windowscodecs.lib;dwrite.lib;kernel32.lib;%(AdditionalDependencies) + + + + + NotUsing + false + ..\..\include;%(AdditionalIncludeDirectories) + _WINDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + false + false + xinput.lib;xaudio2.lib;d2d1.lib;d3d11.lib;dxgi.lib;ole32.lib;windowscodecs.lib;dwrite.lib;kernel32.lib;%(AdditionalDependencies) + + + + + NotUsing + false + ..\..\include;%(AdditionalIncludeDirectories) + _WINDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + false + false + xinput.lib;xaudio2.lib;d2d1.lib;d3d11.lib;dxgi.lib;ole32.lib;windowscodecs.lib;dwrite.lib;kernel32.lib;%(AdditionalDependencies) + + + + + NotUsing + false + ..\..\include;%(AdditionalIncludeDirectories) + _WINDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + false + false + xinput.lib;xaudio2.lib;d2d1.lib;d3d11.lib;dxgi.lib;ole32.lib;windowscodecs.lib;dwrite.lib;kernel32.lib;%(AdditionalDependencies) + + + + + + \ No newline at end of file diff --git a/VisualC-WinRT/SDL/SDL_VS2012-WinRT.vcxproj.filters b/VisualC-WinRT/SDL/SDL_VS2012-WinRT.vcxproj.filters new file mode 100644 index 000000000..74dd2dc86 --- /dev/null +++ b/VisualC-WinRT/SDL/SDL_VS2012-WinRT.vcxproj.filters @@ -0,0 +1,608 @@ + + + + + GPU Shaders + + + GPU Shaders + + + GPU Shaders + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Header Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files + + + Header Files + + + Source Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Header Files + + + Source Files + + + + + {20773b57-7034-4c24-af5a-334844585f1b} + + + {ddf04d85-6a87-4c5a-bc52-869b38f45a61} + + + {83ed1283-8234-4c77-99a8-ffcfd8ce7381} + + + \ No newline at end of file diff --git a/VisualC-WinRT/SDL_VS2012-WinRT.sln b/VisualC-WinRT/SDL_VS2012-WinRT.sln new file mode 100644 index 000000000..046e6b7ba --- /dev/null +++ b/VisualC-WinRT/SDL_VS2012-WinRT.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 2012 for Windows 8 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDL_VS2012-WinRT", "SDL\SDL_VS2012-WinRT.vcxproj", "{AEAEA3A2-D4E6-45B1-8EC6-53D84287FC14}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|ARM = Release|ARM + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AEAEA3A2-D4E6-45B1-8EC6-53D84287FC14}.Debug|ARM.ActiveCfg = Debug|ARM + {AEAEA3A2-D4E6-45B1-8EC6-53D84287FC14}.Debug|ARM.Build.0 = Debug|ARM + {AEAEA3A2-D4E6-45B1-8EC6-53D84287FC14}.Debug|Win32.ActiveCfg = Debug|Win32 + {AEAEA3A2-D4E6-45B1-8EC6-53D84287FC14}.Debug|Win32.Build.0 = Debug|Win32 + {AEAEA3A2-D4E6-45B1-8EC6-53D84287FC14}.Debug|x64.ActiveCfg = Debug|x64 + {AEAEA3A2-D4E6-45B1-8EC6-53D84287FC14}.Debug|x64.Build.0 = Debug|x64 + {AEAEA3A2-D4E6-45B1-8EC6-53D84287FC14}.Release|ARM.ActiveCfg = Release|ARM + {AEAEA3A2-D4E6-45B1-8EC6-53D84287FC14}.Release|ARM.Build.0 = Release|ARM + {AEAEA3A2-D4E6-45B1-8EC6-53D84287FC14}.Release|Win32.ActiveCfg = Release|Win32 + {AEAEA3A2-D4E6-45B1-8EC6-53D84287FC14}.Release|Win32.Build.0 = Release|Win32 + {AEAEA3A2-D4E6-45B1-8EC6-53D84287FC14}.Release|x64.ActiveCfg = Release|x64 + {AEAEA3A2-D4E6-45B1-8EC6-53D84287FC14}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/VisualC-WinRT/tests/loopwave/Assets/Logo.png b/VisualC-WinRT/tests/loopwave/Assets/Logo.png new file mode 100644 index 000000000..e26771cb3 Binary files /dev/null and b/VisualC-WinRT/tests/loopwave/Assets/Logo.png differ diff --git a/VisualC-WinRT/tests/loopwave/Assets/SmallLogo.png b/VisualC-WinRT/tests/loopwave/Assets/SmallLogo.png new file mode 100644 index 000000000..1eb0d9d52 Binary files /dev/null and b/VisualC-WinRT/tests/loopwave/Assets/SmallLogo.png differ diff --git a/VisualC-WinRT/tests/loopwave/Assets/SplashScreen.png b/VisualC-WinRT/tests/loopwave/Assets/SplashScreen.png new file mode 100644 index 000000000..c951e031b Binary files /dev/null and b/VisualC-WinRT/tests/loopwave/Assets/SplashScreen.png differ diff --git a/VisualC-WinRT/tests/loopwave/Assets/StoreLogo.png b/VisualC-WinRT/tests/loopwave/Assets/StoreLogo.png new file mode 100644 index 000000000..dcb672712 Binary files /dev/null and b/VisualC-WinRT/tests/loopwave/Assets/StoreLogo.png differ diff --git a/VisualC-WinRT/tests/loopwave/Package.appxmanifest b/VisualC-WinRT/tests/loopwave/Package.appxmanifest new file mode 100644 index 000000000..3b62bf1b1 --- /dev/null +++ b/VisualC-WinRT/tests/loopwave/Package.appxmanifest @@ -0,0 +1,42 @@ + + + + + + + loopwave_VS2012_WinRT + David + Assets\StoreLogo.png + + + + 6.2.1 + 6.2.1 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/VisualC-WinRT/tests/loopwave/loopwave_VS2012.vcxproj b/VisualC-WinRT/tests/loopwave/loopwave_VS2012.vcxproj new file mode 100644 index 000000000..14e57e5fd --- /dev/null +++ b/VisualC-WinRT/tests/loopwave/loopwave_VS2012.vcxproj @@ -0,0 +1,157 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM + + + Release + ARM + + + + {03fcc293-9406-49c2-acf6-6e7d460c3239} + loopwave_VS2012 + en-US + 11.0 + true + loopwave + + + + Application + true + v110 + + + Application + true + v110 + + + Application + true + v110 + + + Application + false + true + v110 + + + Application + false + true + v110 + + + Application + false + true + v110 + + + + + + + + + + + + + + + + + + + + + + + + + loopwave_VS2012_TemporaryKey.pfx + + + + d2d1.lib; d3d11.lib; dxgi.lib; ole32.lib; windowscodecs.lib; dwrite.lib; %(AdditionalDependencies) + + + pch.h + $(IntDir)pch.pch + $(ProjectDir);$(IntermediateOutputPath);$(ProjectDir)..\..\..\include;%(AdditionalIncludeDirectories) + 4453 + + + + + NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + NotUsing + NotUsing + NotUsing + + + + + _DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + NotUsing + NotUsing + NotUsing + + + + + + + + + + + Designer + + + + + + + false + false + false + false + false + false + + + + + + + + {aeaea3a2-d4e6-45b1-8ec6-53d84287fc14} + + + + + + \ No newline at end of file diff --git a/VisualC-WinRT/tests/loopwave/loopwave_VS2012_TemporaryKey.pfx b/VisualC-WinRT/tests/loopwave/loopwave_VS2012_TemporaryKey.pfx new file mode 100644 index 000000000..3c07b779f Binary files /dev/null and b/VisualC-WinRT/tests/loopwave/loopwave_VS2012_TemporaryKey.pfx differ diff --git a/VisualC-WinRT/tests/testthread/Assets/Logo.png b/VisualC-WinRT/tests/testthread/Assets/Logo.png new file mode 100644 index 000000000..e26771cb3 Binary files /dev/null and b/VisualC-WinRT/tests/testthread/Assets/Logo.png differ diff --git a/VisualC-WinRT/tests/testthread/Assets/SmallLogo.png b/VisualC-WinRT/tests/testthread/Assets/SmallLogo.png new file mode 100644 index 000000000..1eb0d9d52 Binary files /dev/null and b/VisualC-WinRT/tests/testthread/Assets/SmallLogo.png differ diff --git a/VisualC-WinRT/tests/testthread/Assets/SplashScreen.png b/VisualC-WinRT/tests/testthread/Assets/SplashScreen.png new file mode 100644 index 000000000..c951e031b Binary files /dev/null and b/VisualC-WinRT/tests/testthread/Assets/SplashScreen.png differ diff --git a/VisualC-WinRT/tests/testthread/Assets/StoreLogo.png b/VisualC-WinRT/tests/testthread/Assets/StoreLogo.png new file mode 100644 index 000000000..dcb672712 Binary files /dev/null and b/VisualC-WinRT/tests/testthread/Assets/StoreLogo.png differ diff --git a/VisualC-WinRT/tests/testthread/Package.appxmanifest b/VisualC-WinRT/tests/testthread/Package.appxmanifest new file mode 100644 index 000000000..f02b3a173 --- /dev/null +++ b/VisualC-WinRT/tests/testthread/Package.appxmanifest @@ -0,0 +1,42 @@ + + + + + + + testthread_VS2012_WinRT + David + Assets\StoreLogo.png + + + + 6.2.1 + 6.2.1 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/VisualC-WinRT/tests/testthread/testthread_VS2012.vcxproj b/VisualC-WinRT/tests/testthread/testthread_VS2012.vcxproj new file mode 100644 index 000000000..1940fe315 --- /dev/null +++ b/VisualC-WinRT/tests/testthread/testthread_VS2012.vcxproj @@ -0,0 +1,160 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM + + + Release + ARM + + + + {a8705bee-d01d-46a4-b2ab-feedfb5fdd11} + testthread_VS2012 + en-US + 11.0 + true + testthread + + + + Application + true + v110 + + + Application + true + v110 + + + Application + true + v110 + + + Application + false + true + v110 + + + Application + false + true + v110 + + + Application + false + true + v110 + + + + + + + + + + + + + + + + + + + + + + + + + testthread_VS2012_TemporaryKey.pfx + + + + d2d1.lib; d3d11.lib; dxgi.lib; ole32.lib; windowscodecs.lib; dwrite.lib; %(AdditionalDependencies) + + + pch.h + $(IntDir)pch.pch + $(ProjectDir);$(IntermediateOutputPath);$(ProjectDir)..\..\..\include;%(AdditionalIncludeDirectories) + 4453 + + + + + NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + NotUsing + NotUsing + NotUsing + false + false + false + + + + + _DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + NotUsing + NotUsing + NotUsing + false + false + false + + + + + + + + + + + Designer + + + + + + true + true + true + true + true + true + + + + + + {aeaea3a2-d4e6-45b1-8ec6-53d84287fc14} + + + + + + \ No newline at end of file diff --git a/VisualC-WinRT/tests/testthread/testthread_VS2012_TemporaryKey.pfx b/VisualC-WinRT/tests/testthread/testthread_VS2012_TemporaryKey.pfx new file mode 100644 index 000000000..97fd1e190 Binary files /dev/null and b/VisualC-WinRT/tests/testthread/testthread_VS2012_TemporaryKey.pfx differ diff --git a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj index f3122ecba..4d349d28a 100755 --- a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj @@ -547,6 +547,7 @@ ); name = CustomTemplate; sourceTree = ""; + usesTabs = 0; }; 56EA86F813E9EBF9002E47EB /* coreaudio */ = { isa = PBXGroup; diff --git a/include/SDL_config.h b/include/SDL_config.h index 7440940ad..9022af81e 100644 --- a/include/SDL_config.h +++ b/include/SDL_config.h @@ -31,6 +31,8 @@ /* Add any platform that doesn't build using the configure system. */ #if defined(__WIN32__) #include "SDL_config_windows.h" +#elif defined(__WINRT__) +#include "SDL_config_windowsrt.h" #elif defined(__MACOSX__) #include "SDL_config_macosx.h" #elif defined(__IPHONEOS__) diff --git a/include/SDL_config_windowsrt.h b/include/SDL_config_windowsrt.h new file mode 100644 index 000000000..b6ecc00fe --- /dev/null +++ b/include/SDL_config_windowsrt.h @@ -0,0 +1,180 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef _SDL_config_windows_h +#define _SDL_config_windows_h + +#include "SDL_platform.h" + +/* This is a set of defines to configure the SDL features */ + +#if !defined(_STDINT_H_) && (!defined(HAVE_STDINT_H) || !_HAVE_STDINT_H) +#if defined(__GNUC__) || defined(__DMC__) || defined(__WATCOMC__) +#define HAVE_STDINT_H 1 +#elif defined(_MSC_VER) +typedef signed __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef signed __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef signed __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; +#ifndef _UINTPTR_T_DEFINED +#ifdef _WIN64 +typedef unsigned __int64 uintptr_t; +#else +typedef unsigned int uintptr_t; +#endif +#define _UINTPTR_T_DEFINED +#endif +/* Older Visual C++ headers don't have the Win64-compatible typedefs... */ +#if ((_MSC_VER <= 1200) && (!defined(DWORD_PTR))) +#define DWORD_PTR DWORD +#endif +#if ((_MSC_VER <= 1200) && (!defined(LONG_PTR))) +#define LONG_PTR LONG +#endif +#else /* !__GNUC__ && !_MSC_VER */ +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed int int32_t; +typedef unsigned int uint32_t; +typedef signed long long int64_t; +typedef unsigned long long uint64_t; +#ifndef _SIZE_T_DEFINED_ +#define _SIZE_T_DEFINED_ +typedef unsigned int size_t; +#endif +typedef unsigned int uintptr_t; +#endif /* __GNUC__ || _MSC_VER */ +#endif /* !_STDINT_H_ && !HAVE_STDINT_H */ + +#ifdef _WIN64 +# define SIZEOF_VOIDP 8 +#else +# define SIZEOF_VOIDP 4 +#endif + +/* Useful headers */ +#define HAVE_LIBC 1 +#define HAVE_STDIO_H 1 +#define STDC_HEADERS 1 +#define HAVE_STRING_H 1 +#define HAVE_CTYPE_H 1 +#define HAVE_MATH_H 1 +#define HAVE_SIGNAL_H 1 + +/* C library functions */ +#define HAVE_MALLOC 1 +#define HAVE_CALLOC 1 +#define HAVE_REALLOC 1 +#define HAVE_FREE 1 +#define HAVE_ALLOCA 1 +#define HAVE_QSORT 1 +#define HAVE_ABS 1 +#define HAVE_MEMSET 1 +#define HAVE_MEMCPY 1 +#define HAVE_MEMMOVE 1 +#define HAVE_MEMCMP 1 +#define HAVE_STRLEN 1 +#define HAVE__STRREV 1 +#define HAVE__STRUPR 1 +//#define HAVE__STRLWR 1 // TODO, WinRT: consider using _strlwr_s instead +#define HAVE_STRCHR 1 +#define HAVE_STRRCHR 1 +#define HAVE_STRSTR 1 +//#define HAVE_ITOA 1 // TODO, WinRT: consider using _itoa_s instead +//#define HAVE__LTOA 1 // TODO, WinRT: consider using _ltoa_s instead +//#define HAVE__ULTOA 1 // TODO, WinRT: consider using _ultoa_s instead +#define HAVE_STRTOL 1 +#define HAVE_STRTOUL 1 +//#define HAVE_STRTOLL 1 +#define HAVE_STRTOD 1 +#define HAVE_ATOI 1 +#define HAVE_ATOF 1 +#define HAVE_STRCMP 1 +#define HAVE_STRNCMP 1 +#define HAVE__STRICMP 1 +#define HAVE__STRNICMP 1 +#define HAVE_VSNPRINTF 1 +//#define HAVE_SSCANF 1 // TODO, WinRT: consider using sscanf_s instead +#define HAVE_M_PI 1 +#define HAVE_ATAN 1 +#define HAVE_ATAN2 1 +#define HAVE_CEIL 1 +//#define HAVE_COPYSIGN 1 // TODO, WinRT: consider using _copysign instead +#define HAVE_COS 1 +#define HAVE_COSF 1 +#define HAVE_FABS 1 +#define HAVE_FLOOR 1 +#define HAVE_LOG 1 +#define HAVE_POW 1 +//#define HAVE_SCALBN 1 +#define HAVE_SIN 1 +#define HAVE_SINF 1 +#define HAVE_SQRT 1 +#define HAVE__FSEEKI64 1 + +/* Enable various audio drivers */ +#define SDL_AUDIO_DRIVER_XAUDIO2 1 +#define SDL_AUDIO_DRIVER_DISK 1 +#define SDL_AUDIO_DRIVER_DUMMY 1 + +/* Enable various input drivers */ +// TODO, WinRT: Get haptic support working +#define SDL_HAPTIC_DISABLED 1 + +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP +#define SDL_JOYSTICK_DISABLED 1 +#else +#define SDL_JOYSTICK_XINPUT 1 +#endif + +/* Enable various shared object loading systems */ +#define SDL_LOADSO_WINDOWS 1 + +/* Enable various threading systems */ +#define SDL_THREAD_STDCPP 1 + +/* Enable various timer systems */ +#define SDL_TIMER_WINDOWS 1 + +/* Enable various video drivers */ +#define SDL_VIDEO_DRIVER_WINRT 1 +#define SDL_VIDEO_DRIVER_DUMMY 1 + +// TODO, WinRT: Get a Direct3D 11 based renderer working in SDL. +/* Enable appropriate renderer(s) */ +#define SDL_VIDEO_RENDER_D3D11 1 + +/* Enable system power support */ +// TODO, WinRT: investigate system power support. The Win32-based APIs don't work on WinRT. +#define SDL_POWER_DISABLED 1 + +/* Enable assembly routines (Win64 doesn't have inline asm) */ +#ifndef _WIN64 +#define SDL_ASSEMBLY_ROUTINES 1 +#endif + +#endif /* _SDL_config_windows_h */ diff --git a/include/SDL_cpuinfo.h b/include/SDL_cpuinfo.h index dde3074f0..a722ee4d5 100644 --- a/include/SDL_cpuinfo.h +++ b/include/SDL_cpuinfo.h @@ -32,7 +32,7 @@ /* Need to do this here because intrin.h has C++ code in it */ /* Visual Studio 2005 has a bug where intrin.h conflicts with winnt.h */ -#if defined(_MSC_VER) && (_MSC_VER >= 1500) +#if defined(_MSC_VER) && (_MSC_VER >= 1500) && (defined(_M_IX86) || defined(_M_X64)) #include #ifndef _WIN64 #define __MMX__ diff --git a/include/SDL_stdinc.h b/include/SDL_stdinc.h index 7fa9c1049..bc39c2a90 100644 --- a/include/SDL_stdinc.h +++ b/include/SDL_stdinc.h @@ -71,6 +71,13 @@ # include #endif #ifdef HAVE_MATH_H +# if defined(__WINRT__) +/* Defining _USE_MATH_DEFINES is required to get M_PI to be defined on + Windows RT. See http://msdn.microsoft.com/en-us/library/4hwaceh6.aspx + for more information. +*/ +# define _USE_MATH_DEFINES +# endif # include #endif #if defined(HAVE_ICONV) && defined(HAVE_ICONV_H) diff --git a/include/SDL_system.h b/include/SDL_system.h index 26e9eaa0a..cb7490664 100644 --- a/include/SDL_system.h +++ b/include/SDL_system.h @@ -94,6 +94,71 @@ extern DECLSPEC const char * SDLCALL SDL_AndroidGetExternalStoragePath(); #endif /* __ANDROID__ */ +/* Platform specific functions for Windows RT */ +#if defined(__WINRT__) && __WINRT__ + +/** + * \brief Windows RT / Windows Phone path types + */ +typedef enum +{ + /** \brief The installed app's root directory. + Files here are likely to be read-only. */ + SDL_WINRT_PATH_INSTALLED_LOCATION, + + /** \brief The app's local data store. Files may be written here */ + SDL_WINRT_PATH_LOCAL_FOLDER, + + /** \brief The app's roaming data store. Unsupported on Windows Phone. + Files written here may be copied to other machines via a network + connection. + */ + SDL_WINRT_PATH_ROAMING_FOLDER, + + /** \brief The app's temporary data store. Unsupported on Windows Phone. + Files written here may be deleted at any time. */ + SDL_WINRT_PATH_TEMP_FOLDER +} SDL_WinRT_Path; + + +/** + * \brief Retrieves a Windows RT defined path on the local file system + * + * \note Documentation on most app-specific path types on Windows RT + * can be found on MSDN, at the URL: + * http://msdn.microsoft.com/en-us/library/windows/apps/hh464917.aspx + * + * \param pathType The type of path to retrieve. + * \ret A UCS-2 string (16-bit, wide-char) containing the path, or NULL + * if the path is not available for any reason. Not all paths are + * available on all versions of Windows. This is especially true on + * Windows Phone. Check the documentation for the given + * SDL_WinRT_Path for more information on which path types are + * supported where. + */ +extern DECLSPEC const wchar_t * SDLCALL SDL_WinRTGetFSPathUNICODE(SDL_WinRT_Path pathType); + +/** + * \brief Retrieves a Windows RT defined path on the local file system + * + * \note Documentation on most app-specific path types on Windows RT + * can be found on MSDN, at the URL: + * http://msdn.microsoft.com/en-us/library/windows/apps/hh464917.aspx + * + * \param pathType The type of path to retrieve. + * \ret A UTF-8 string (8-bit, multi-byte) containing the path, or NULL + * if the path is not available for any reason. Not all paths are + * available on all versions of Windows. This is especially true on + * Windows Phone. Check the documentation for the given + * SDL_WinRT_Path for more information on which path types are + * supported where. + */ +extern DECLSPEC const char * SDLCALL SDL_WinRTGetFSPathUTF8(SDL_WinRT_Path pathType); + + +#endif /* __WINRT__ */ + + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/include/SDL_syswm.h b/include/SDL_syswm.h index 5e4454f86..3b503ae74 100644 --- a/include/SDL_syswm.h +++ b/include/SDL_syswm.h @@ -56,6 +56,10 @@ struct SDL_SysWMinfo; #include #endif +#if defined(SDL_VIDEO_DRIVER_WINRT) +#include +#endif + /* This is the structure for custom window manager events */ #if defined(SDL_VIDEO_DRIVER_X11) #if defined(__APPLE__) && defined(__MACH__) @@ -90,6 +94,7 @@ typedef struct _NSWindow NSWindow; #include #else typedef struct _UIWindow UIWindow; +typedef struct _UIViewController UIViewController; #endif #endif @@ -100,6 +105,7 @@ typedef enum { SDL_SYSWM_UNKNOWN, SDL_SYSWM_WINDOWS, + SDL_SYSWM_WINDOWSRT, SDL_SYSWM_X11, SDL_SYSWM_DIRECTFB, SDL_SYSWM_COCOA, @@ -168,6 +174,12 @@ struct SDL_SysWMinfo HWND window; /**< The window handle */ } win; #endif +#if defined(SDL_VIDEO_DRIVER_WINRT) + struct + { + IUnknown * window; /**< The Windows RT CoreWindow */ + } winrt; +#endif #if defined(SDL_VIDEO_DRIVER_X11) struct { @@ -193,6 +205,7 @@ struct SDL_SysWMinfo struct { UIWindow *window; /* The UIKit window */ + UIViewController *viewcontroller; /* The UIKit view controller */ } uikit; #endif /* Can't have an empty union */ diff --git a/include/begin_code.h b/include/begin_code.h index dd1f0616d..8997a7220 100644 --- a/include/begin_code.h +++ b/include/begin_code.h @@ -41,7 +41,7 @@ # else # define DECLSPEC __declspec(export) # endif -# elif defined(__WIN32__) +# elif defined(__WIN32__) || defined(__WINRT__) # ifdef __BORLANDC__ # ifdef BUILD_SDL # define DECLSPEC @@ -62,7 +62,7 @@ /* By default SDL uses the C calling convention */ #ifndef SDLCALL -#if defined(__WIN32__) && !defined(__GNUC__) +#if (defined(__WIN32__) || defined(__WINRT__)) && !defined(__GNUC__) #define SDLCALL __cdecl #else #define SDLCALL diff --git a/src/SDL_assert.c b/src/SDL_assert.c index a4cf6434f..73ebfe5fc 100644 --- a/src/SDL_assert.c +++ b/src/SDL_assert.c @@ -37,8 +37,10 @@ #else /* fprintf, _exit(), etc. */ #include #include +#if ! defined(__WINRT__) #include #endif +#endif static SDL_assert_state SDL_PromptAssertion(const SDL_assert_data *data, void *userdata); diff --git a/src/SDL_log.c b/src/SDL_log.c index 2245946c6..a19a2c425 100644 --- a/src/SDL_log.c +++ b/src/SDL_log.c @@ -28,7 +28,7 @@ #include #endif -#if defined(__WIN32__) +#if defined(__WIN32__) || defined(__WINRT__) #include "core/windows/SDL_windows.h" #elif defined(__ANDROID__) #include @@ -315,7 +315,7 @@ static void SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, const char *message) { -#if defined(__WIN32__) +#if defined(__WIN32__) || defined(__WINRT__) /* Way too many allocations here, urgh */ /* Note: One can't call SDL_SetError here, since that function itself logs. */ { diff --git a/src/atomic/SDL_spinlock.c b/src/atomic/SDL_spinlock.c index f3aeea0e2..f90528c35 100644 --- a/src/atomic/SDL_spinlock.c +++ b/src/atomic/SDL_spinlock.c @@ -25,7 +25,7 @@ #include "SDL_timer.h" /* Don't do the check for Visual Studio 2005, it's safe here */ -#ifdef __WIN32__ +#if defined(__WIN32__) || defined(__WINRT__) #include "../core/windows/SDL_windows.h" #endif diff --git a/src/audio/xaudio2/SDL_xaudio2.c b/src/audio/xaudio2/SDL_xaudio2.c index a2c45ba86..19f7cdc5c 100644 --- a/src/audio/xaudio2/SDL_xaudio2.c +++ b/src/audio/xaudio2/SDL_xaudio2.c @@ -1,437 +1,583 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2013 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_config.h" - -#if SDL_AUDIO_DRIVER_XAUDIO2 - -#include "../../core/windows/SDL_windows.h" -#include "SDL_audio.h" -#include "../SDL_audio_c.h" -#include "../SDL_sysaudio.h" -#include "SDL_assert.h" - -#ifdef __GNUC__ -/* The configure script already did any necessary checking */ -# define SDL_XAUDIO2_HAS_SDK 1 -#else -#include /* XAudio2 exists as of the March 2008 DirectX SDK */ -#if (!defined(_DXSDK_BUILD_MAJOR) || (_DXSDK_BUILD_MAJOR < 1284)) -# pragma message("Your DirectX SDK is too old. Disabling XAudio2 support.") -#else -# define SDL_XAUDIO2_HAS_SDK 1 -#endif -#endif /* __GNUC__ */ - -#ifdef SDL_XAUDIO2_HAS_SDK - -#define INITGUID 1 -#include - -/* Hidden "this" pointer for the audio functions */ -#define _THIS SDL_AudioDevice *this - -struct SDL_PrivateAudioData -{ - IXAudio2 *ixa2; - IXAudio2SourceVoice *source; - IXAudio2MasteringVoice *mastering; - HANDLE semaphore; - Uint8 *mixbuf; - int mixlen; - Uint8 *nextbuf; -}; - - -static __inline__ char * -utf16_to_utf8(const WCHAR *S) -{ - /* !!! FIXME: this should be UTF-16, not UCS-2! */ - return SDL_iconv_string("UTF-8", "UCS-2", (char *)(S), - (SDL_wcslen(S)+1)*sizeof(WCHAR)); -} - -static void -XAUDIO2_DetectDevices(int iscapture, SDL_AddAudioDevice addfn) -{ - IXAudio2 *ixa2 = NULL; - UINT32 devcount = 0; - UINT32 i = 0; - - if (iscapture) { - SDL_SetError("XAudio2: capture devices unsupported."); - return; - } else if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) { - SDL_SetError("XAudio2: XAudio2Create() failed at detection."); - return; - } else if (IXAudio2_GetDeviceCount(ixa2, &devcount) != S_OK) { - SDL_SetError("XAudio2: IXAudio2::GetDeviceCount() failed."); - IXAudio2_Release(ixa2); - return; - } - - for (i = 0; i < devcount; i++) { - XAUDIO2_DEVICE_DETAILS details; - if (IXAudio2_GetDeviceDetails(ixa2, i, &details) == S_OK) { - char *str = utf16_to_utf8(details.DisplayName); - if (str != NULL) { - addfn(str); - SDL_free(str); /* addfn() made a copy of the string. */ - } - } - } - - IXAudio2_Release(ixa2); -} - -static void STDMETHODCALLTYPE -VoiceCBOnBufferEnd(THIS_ void *data) -{ - /* Just signal the SDL audio thread and get out of XAudio2's way. */ - SDL_AudioDevice *this = (SDL_AudioDevice *) data; - ReleaseSemaphore(this->hidden->semaphore, 1, NULL); -} - -static void STDMETHODCALLTYPE -VoiceCBOnVoiceError(THIS_ void *data, HRESULT Error) -{ - /* !!! FIXME: attempt to recover, or mark device disconnected. */ - SDL_assert(0 && "write me!"); -} - -/* no-op callbacks... */ -static void STDMETHODCALLTYPE VoiceCBOnStreamEnd(THIS) {} -static void STDMETHODCALLTYPE VoiceCBOnVoiceProcessPassStart(THIS_ UINT32 b) {} -static void STDMETHODCALLTYPE VoiceCBOnVoiceProcessPassEnd(THIS) {} -static void STDMETHODCALLTYPE VoiceCBOnBufferStart(THIS_ void *data) {} -static void STDMETHODCALLTYPE VoiceCBOnLoopEnd(THIS_ void *data) {} - - -static Uint8 * -XAUDIO2_GetDeviceBuf(_THIS) -{ - return this->hidden->nextbuf; -} - -static void -XAUDIO2_PlayDevice(_THIS) -{ - XAUDIO2_BUFFER buffer; - Uint8 *mixbuf = this->hidden->mixbuf; - Uint8 *nextbuf = this->hidden->nextbuf; - const int mixlen = this->hidden->mixlen; - IXAudio2SourceVoice *source = this->hidden->source; - HRESULT result = S_OK; - - if (!this->enabled) { /* shutting down? */ - return; - } - - /* Submit the next filled buffer */ - SDL_zero(buffer); - buffer.AudioBytes = mixlen; - buffer.pAudioData = nextbuf; - buffer.pContext = this; - - if (nextbuf == mixbuf) { - nextbuf += mixlen; - } else { - nextbuf = mixbuf; - } - this->hidden->nextbuf = nextbuf; - - result = IXAudio2SourceVoice_SubmitSourceBuffer(source, &buffer, NULL); - if (result == XAUDIO2_E_DEVICE_INVALIDATED) { - /* !!! FIXME: possibly disconnected or temporary lost. Recover? */ - } - - if (result != S_OK) { /* uhoh, panic! */ - IXAudio2SourceVoice_FlushSourceBuffers(source); - this->enabled = 0; - } -} - -static void -XAUDIO2_WaitDevice(_THIS) -{ - if (this->enabled) { - WaitForSingleObject(this->hidden->semaphore, INFINITE); - } -} - -static void -XAUDIO2_WaitDone(_THIS) -{ - IXAudio2SourceVoice *source = this->hidden->source; - XAUDIO2_VOICE_STATE state; - SDL_assert(!this->enabled); /* flag that stops playing. */ - IXAudio2SourceVoice_Discontinuity(source); - IXAudio2SourceVoice_GetState(source, &state); - while (state.BuffersQueued > 0) { - WaitForSingleObject(this->hidden->semaphore, INFINITE); - IXAudio2SourceVoice_GetState(source, &state); - } -} - - -static void -XAUDIO2_CloseDevice(_THIS) -{ - if (this->hidden != NULL) { - IXAudio2 *ixa2 = this->hidden->ixa2; - IXAudio2SourceVoice *source = this->hidden->source; - IXAudio2MasteringVoice *mastering = this->hidden->mastering; - - if (source != NULL) { - IXAudio2SourceVoice_Stop(source, 0, XAUDIO2_COMMIT_NOW); - IXAudio2SourceVoice_FlushSourceBuffers(source); - IXAudio2SourceVoice_DestroyVoice(source); - } - if (ixa2 != NULL) { - IXAudio2_StopEngine(ixa2); - } - if (mastering != NULL) { - IXAudio2MasteringVoice_DestroyVoice(mastering); - } - if (ixa2 != NULL) { - IXAudio2_Release(ixa2); - } - if (this->hidden->mixbuf != NULL) { - SDL_free(this->hidden->mixbuf); - } - if (this->hidden->semaphore != NULL) { - CloseHandle(this->hidden->semaphore); - } - - SDL_free(this->hidden); - this->hidden = NULL; - } -} - -static int -XAUDIO2_OpenDevice(_THIS, const char *devname, int iscapture) -{ - HRESULT result = S_OK; - WAVEFORMATEX waveformat; - int valid_format = 0; - SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format); - IXAudio2 *ixa2 = NULL; - IXAudio2SourceVoice *source = NULL; - UINT32 devId = 0; /* 0 == system default device. */ - - static IXAudio2VoiceCallbackVtbl callbacks_vtable = { - VoiceCBOnVoiceProcessPassStart, - VoiceCBOnVoiceProcessPassEnd, - VoiceCBOnStreamEnd, - VoiceCBOnBufferStart, - VoiceCBOnBufferEnd, - VoiceCBOnLoopEnd, - VoiceCBOnVoiceError - }; - - static IXAudio2VoiceCallback callbacks = { &callbacks_vtable }; - - if (iscapture) { - return SDL_SetError("XAudio2: capture devices unsupported."); - } else if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) { - return SDL_SetError("XAudio2: XAudio2Create() failed at open."); - } - - if (devname != NULL) { - UINT32 devcount = 0; - UINT32 i = 0; - - if (IXAudio2_GetDeviceCount(ixa2, &devcount) != S_OK) { - IXAudio2_Release(ixa2); - return SDL_SetError("XAudio2: IXAudio2_GetDeviceCount() failed."); - } - for (i = 0; i < devcount; i++) { - XAUDIO2_DEVICE_DETAILS details; - if (IXAudio2_GetDeviceDetails(ixa2, i, &details) == S_OK) { - char *str = utf16_to_utf8(details.DisplayName); - if (str != NULL) { - const int match = (SDL_strcmp(str, devname) == 0); - SDL_free(str); - if (match) { - devId = i; - break; - } - } - } - } - - if (i == devcount) { - IXAudio2_Release(ixa2); - return SDL_SetError("XAudio2: Requested device not found."); - } - } - - /* Initialize all variables that we clean on shutdown */ - this->hidden = (struct SDL_PrivateAudioData *) - SDL_malloc((sizeof *this->hidden)); - if (this->hidden == NULL) { - IXAudio2_Release(ixa2); - return SDL_OutOfMemory(); - } - SDL_memset(this->hidden, 0, (sizeof *this->hidden)); - - this->hidden->ixa2 = ixa2; - this->hidden->semaphore = CreateSemaphore(NULL, 1, 2, NULL); - if (this->hidden->semaphore == NULL) { - XAUDIO2_CloseDevice(this); - return SDL_SetError("XAudio2: CreateSemaphore() failed!"); - } - - while ((!valid_format) && (test_format)) { - switch (test_format) { - case AUDIO_U8: - case AUDIO_S16: - case AUDIO_S32: - case AUDIO_F32: - this->spec.format = test_format; - valid_format = 1; - break; - } - test_format = SDL_NextAudioFormat(); - } - - if (!valid_format) { - XAUDIO2_CloseDevice(this); - return SDL_SetError("XAudio2: Unsupported audio format"); - } - - /* Update the fragment size as size in bytes */ - SDL_CalculateAudioSpec(&this->spec); - - /* We feed a Source, it feeds the Mastering, which feeds the device. */ - this->hidden->mixlen = this->spec.size; - this->hidden->mixbuf = (Uint8 *) SDL_malloc(2 * this->hidden->mixlen); - if (this->hidden->mixbuf == NULL) { - XAUDIO2_CloseDevice(this); - return SDL_OutOfMemory(); - } - this->hidden->nextbuf = this->hidden->mixbuf; - SDL_memset(this->hidden->mixbuf, 0, 2 * this->hidden->mixlen); - - /* We use XAUDIO2_DEFAULT_CHANNELS instead of this->spec.channels. On - Xbox360, this means 5.1 output, but on Windows, it means "figure out - what the system has." It might be preferable to let XAudio2 blast - stereo output to appropriate surround sound configurations - instead of clamping to 2 channels, even though we'll configure the - Source Voice for whatever number of channels you supply. */ - result = IXAudio2_CreateMasteringVoice(ixa2, &this->hidden->mastering, - XAUDIO2_DEFAULT_CHANNELS, - this->spec.freq, 0, devId, NULL); - if (result != S_OK) { - XAUDIO2_CloseDevice(this); - return SDL_SetError("XAudio2: Couldn't create mastering voice"); - } - - SDL_zero(waveformat); - if (SDL_AUDIO_ISFLOAT(this->spec.format)) { - waveformat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - } else { - waveformat.wFormatTag = WAVE_FORMAT_PCM; - } - waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format); - waveformat.nChannels = this->spec.channels; - waveformat.nSamplesPerSec = this->spec.freq; - waveformat.nBlockAlign = - waveformat.nChannels * (waveformat.wBitsPerSample / 8); - waveformat.nAvgBytesPerSec = - waveformat.nSamplesPerSec * waveformat.nBlockAlign; - - result = IXAudio2_CreateSourceVoice(ixa2, &source, &waveformat, - XAUDIO2_VOICE_NOSRC | - XAUDIO2_VOICE_NOPITCH, - 1.0f, &callbacks, NULL, NULL); - if (result != S_OK) { - XAUDIO2_CloseDevice(this); - return SDL_SetError("XAudio2: Couldn't create source voice"); - } - this->hidden->source = source; - - /* Start everything playing! */ - result = IXAudio2_StartEngine(ixa2); - if (result != S_OK) { - XAUDIO2_CloseDevice(this); - return SDL_SetError("XAudio2: Couldn't start engine"); - } - - result = IXAudio2SourceVoice_Start(source, 0, XAUDIO2_COMMIT_NOW); - if (result != S_OK) { - XAUDIO2_CloseDevice(this); - return SDL_SetError("XAudio2: Couldn't start source voice"); - } - - return 0; /* good to go. */ -} - -static void -XAUDIO2_Deinitialize(void) -{ - WIN_CoUninitialize(); -} - -#endif /* SDL_XAUDIO2_HAS_SDK */ - - -static int -XAUDIO2_Init(SDL_AudioDriverImpl * impl) -{ -#ifndef SDL_XAUDIO2_HAS_SDK - SDL_SetError("XAudio2: SDL was built without XAudio2 support (old DirectX SDK)."); - return 0; /* no XAudio2 support, ever. Update your SDK! */ -#else - /* XAudio2Create() is a macro that uses COM; we don't load the .dll */ - IXAudio2 *ixa2 = NULL; - if (FAILED(WIN_CoInitialize())) { - SDL_SetError("XAudio2: CoInitialize() failed"); - return 0; - } - - if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) { - WIN_CoUninitialize(); - SDL_SetError("XAudio2: XAudio2Create() failed at initialization"); - return 0; /* not available. */ - } - IXAudio2_Release(ixa2); - - /* Set the function pointers */ - impl->DetectDevices = XAUDIO2_DetectDevices; - impl->OpenDevice = XAUDIO2_OpenDevice; - impl->PlayDevice = XAUDIO2_PlayDevice; - impl->WaitDevice = XAUDIO2_WaitDevice; - impl->WaitDone = XAUDIO2_WaitDone; - impl->GetDeviceBuf = XAUDIO2_GetDeviceBuf; - impl->CloseDevice = XAUDIO2_CloseDevice; - impl->Deinitialize = XAUDIO2_Deinitialize; - - return 1; /* this audio target is available. */ -#endif -} - -AudioBootStrap XAUDIO2_bootstrap = { - "xaudio2", "XAudio2", XAUDIO2_Init, 0 -}; - -#endif /* SDL_AUDIO_DRIVER_XAUDIO2 */ - -/* vi: set ts=4 sw=4 expandtab: */ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2013 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* WinRT NOTICE: + + A number of changes were warranted to SDL's XAudio2 backend in order to + get it compiling for Windows RT. + + When compiling for WinRT, XAudio2.h requires that it be compiled in a C++ + file, and not a straight C file. Trying to compile it as C leads to lots + of errors, at least with MSVC 2012 and Windows SDK 8.0, as of Nov 22, 2012. + To address this specific issue, a few changes were made to SDL_xaudio2.c: + + 1. SDL_xaudio2.c is compiled as a C++ file in WinRT builds. Exported + symbols, namely XAUDIO2_bootstrap, uses 'extern "C"' to make sure the + rest of SDL can access it. Non-WinRT builds continue to compile + SDL_xaudio2.c as a C file. + 2. A macro redefines variables named 'this' to '_this', to prevent compiler + errors (C2355 in Visual C++) related to 'this' being a reserverd keyword. + This hack may need to be altered in the future, particularly if C++'s + 'this' keyword needs to be used (within SDL_xaudio2.c). At the time + WinRT support was initially added to SDL's XAudio2 backend, this + capability was not needed. + 3. The C-style macros to invoke XAudio2's COM-based methods were + rewritten to be C++-friendly. These are provided in the file, + SDL_xaudio2_winrthelpers.h. + 4. IXAudio2::CreateSourceVoice, when used in C++, requires its callbacks to + be specified via a C++ class. SDL's XAudio2 backend was written with + C-style callbacks. A class to bridge these two interfaces, + SDL_XAudio2VoiceCallback, was written to make XAudio2 happy. Its methods + just call SDL's existing, C-style callbacks. + 5. Multiple checks for the __cplusplus macro were made, in appropriate + places. + + + A few additional changes to SDL's XAudio2 backend were warranted by API + changes to Windows. Many, but not all of these are documented by Microsoft + at: + http://blogs.msdn.com/b/chuckw/archive/2012/04/02/xaudio2-and-windows-8-consumer-preview.aspx + + 1. Windows' thread synchronization function, CreateSemaphore, was removed + from Windows RT. SDL's semaphore API was substituted instead. + 2. The method calls, IXAudio2::GetDeviceCount and IXAudio2::GetDeviceDetails + were removed from the XAudio2 API. Microsoft is telling developers to + use APIs in Windows::Foundation instead. + For SDL, the missing methods were reimplemented using the APIs Microsoft + said to use. + 3. CoInitialize and CoUninitialize are not available in Windows RT. + These calls were removed, as COM will have been initialized earlier, + at least by the call to the WinRT app's main function + (aka 'int main(Platform::Array^)). (DLudwig: + This was my understanding of how WinRT: the 'main' function uses + a tag of [MTAThread], which should initialize COM. My understanding + of COM is somewhat limited, and I may be incorrect here.) + 4. IXAudio2::CreateMasteringVoice changed its integer-based 'DeviceIndex' + argument to a string-based one, 'szDeviceId'. In Windows RT, the + string-based argument will be used. +*/ + +#include "SDL_config.h" + +#if SDL_AUDIO_DRIVER_XAUDIO2 + +#ifdef __cplusplus +extern "C" { +#endif +#include "../../core/windows/SDL_windows.h" +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "../SDL_sysaudio.h" +#include "SDL_assert.h" +#ifdef __cplusplus +} +#endif + +#if defined(__WINRT__) +# define SDL_XAUDIO2_HAS_SDK 1 +#endif +#if defined(__WIN32__) +#ifdef __GNUC__ +/* The configure script already did any necessary checking */ +# define SDL_XAUDIO2_HAS_SDK 1 +#elif defined(__WINRT__) +/* WinRT always has access to the .the XAudio 2 SD +# define SDL_XAUDIO2_HAS_SDK +#else +#include /* XAudio2 exists as of the March 2008 DirectX SDK */ +#if (!defined(_DXSDK_BUILD_MAJOR) || (_DXSDK_BUILD_MAJOR < 1284)) +# pragma message("Your DirectX SDK is too old. Disabling XAudio2 support.") +#else +# define SDL_XAUDIO2_HAS_SDK 1 +#endif +#endif + +#ifdef SDL_XAUDIO2_HAS_SDK + +#define INITGUID 1 +#include + +/* Hidden "this" pointer for the audio functions */ +#define _THIS SDL_AudioDevice *this + +#ifdef __cplusplus +#define this _this +#include "SDL_xaudio2_winrthelpers.h" +#endif + +struct SDL_PrivateAudioData +{ + IXAudio2 *ixa2; + IXAudio2SourceVoice *source; + IXAudio2MasteringVoice *mastering; + SDL_sem * semaphore; + Uint8 *mixbuf; + int mixlen; + Uint8 *nextbuf; +}; + + +static void +XAUDIO2_DetectDevices(int iscapture, SDL_AddAudioDevice addfn) +{ + IXAudio2 *ixa2 = NULL; + UINT32 devcount = 0; + UINT32 i = 0; + + if (iscapture) { + SDL_SetError("XAudio2: capture devices unsupported."); + return; + } else if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) { + SDL_SetError("XAudio2: XAudio2Create() failed at detection."); + return; + } else if (IXAudio2_GetDeviceCount(ixa2, &devcount) != S_OK) { + SDL_SetError("XAudio2: IXAudio2::GetDeviceCount() failed."); + IXAudio2_Release(ixa2); + return; + } + + for (i = 0; i < devcount; i++) { + XAUDIO2_DEVICE_DETAILS details; + if (IXAudio2_GetDeviceDetails(ixa2, i, &details) == S_OK) { + char *str = WIN_StringToUTF8(details.DisplayName); + if (str != NULL) { + addfn(str); + SDL_free(str); /* addfn() made a copy of the string. */ + } + } + } + + IXAudio2_Release(ixa2); +} + +static void STDMETHODCALLTYPE +VoiceCBOnBufferEnd(THIS_ void *data) +{ + /* Just signal the SDL audio thread and get out of XAudio2's way. */ + SDL_AudioDevice *this = (SDL_AudioDevice *) data; + SDL_SemPost(this->hidden->semaphore); +} + +static void STDMETHODCALLTYPE +VoiceCBOnVoiceError(THIS_ void *data, HRESULT Error) +{ + /* !!! FIXME: attempt to recover, or mark device disconnected. */ + SDL_assert(0 && "write me!"); +} + +/* no-op callbacks... */ +static void STDMETHODCALLTYPE VoiceCBOnStreamEnd(THIS) {} +static void STDMETHODCALLTYPE VoiceCBOnVoiceProcessPassStart(THIS_ UINT32 b) {} +static void STDMETHODCALLTYPE VoiceCBOnVoiceProcessPassEnd(THIS) {} +static void STDMETHODCALLTYPE VoiceCBOnBufferStart(THIS_ void *data) {} +static void STDMETHODCALLTYPE VoiceCBOnLoopEnd(THIS_ void *data) {} + +#if defined(__cplusplus) +class SDL_XAudio2VoiceCallback : public IXAudio2VoiceCallback +{ +public: + STDMETHOD_(void, OnBufferEnd)(void *pBufferContext) { + VoiceCBOnBufferEnd(pBufferContext); + } + STDMETHOD_(void, OnBufferStart)(void *pBufferContext) { + VoiceCBOnBufferStart(pBufferContext); + } + STDMETHOD_(void, OnLoopEnd)(void *pBufferContext) { + VoiceCBOnLoopEnd(pBufferContext); + } + STDMETHOD_(void, OnStreamEnd)() { + VoiceCBOnStreamEnd(); + } + STDMETHOD_(void, OnVoiceError)(void *pBufferContext, HRESULT Error) { + VoiceCBOnVoiceError(pBufferContext, Error); + } + STDMETHOD_(void, OnVoiceProcessingPassEnd)() { + VoiceCBOnVoiceProcessPassEnd(); + } + STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32 BytesRequired) { + VoiceCBOnVoiceProcessPassStart(BytesRequired); + } +}; +#endif + +static Uint8 * +XAUDIO2_GetDeviceBuf(_THIS) +{ + return this->hidden->nextbuf; +} + +static void +XAUDIO2_PlayDevice(_THIS) +{ + XAUDIO2_BUFFER buffer; + Uint8 *mixbuf = this->hidden->mixbuf; + Uint8 *nextbuf = this->hidden->nextbuf; + const int mixlen = this->hidden->mixlen; + IXAudio2SourceVoice *source = this->hidden->source; + HRESULT result = S_OK; + + if (!this->enabled) { /* shutting down? */ + return; + } + + /* Submit the next filled buffer */ + SDL_zero(buffer); + buffer.AudioBytes = mixlen; + buffer.pAudioData = nextbuf; + buffer.pContext = this; + + if (nextbuf == mixbuf) { + nextbuf += mixlen; + } else { + nextbuf = mixbuf; + } + this->hidden->nextbuf = nextbuf; + + result = IXAudio2SourceVoice_SubmitSourceBuffer(source, &buffer, NULL); + if (result == XAUDIO2_E_DEVICE_INVALIDATED) { + /* !!! FIXME: possibly disconnected or temporary lost. Recover? */ + } + + if (result != S_OK) { /* uhoh, panic! */ + IXAudio2SourceVoice_FlushSourceBuffers(source); + this->enabled = 0; + } +} + +static void +XAUDIO2_WaitDevice(_THIS) +{ + if (this->enabled) { + SDL_SemWait(this->hidden->semaphore); + } +} + +static void +XAUDIO2_WaitDone(_THIS) +{ + IXAudio2SourceVoice *source = this->hidden->source; + XAUDIO2_VOICE_STATE state; + SDL_assert(!this->enabled); /* flag that stops playing. */ + IXAudio2SourceVoice_Discontinuity(source); + IXAudio2SourceVoice_GetState(source, &state); + while (state.BuffersQueued > 0) { + SDL_SemWait(this->hidden->semaphore); + IXAudio2SourceVoice_GetState(source, &state); + } +} + + +static void +XAUDIO2_CloseDevice(_THIS) +{ + if (this->hidden != NULL) { + IXAudio2 *ixa2 = this->hidden->ixa2; + IXAudio2SourceVoice *source = this->hidden->source; + IXAudio2MasteringVoice *mastering = this->hidden->mastering; + + if (source != NULL) { + IXAudio2SourceVoice_Stop(source, 0, XAUDIO2_COMMIT_NOW); + IXAudio2SourceVoice_FlushSourceBuffers(source); + IXAudio2SourceVoice_DestroyVoice(source); + } + if (ixa2 != NULL) { + IXAudio2_StopEngine(ixa2); + } + if (mastering != NULL) { + IXAudio2MasteringVoice_DestroyVoice(mastering); + } + if (ixa2 != NULL) { + IXAudio2_Release(ixa2); + } + if (this->hidden->mixbuf != NULL) { + SDL_free(this->hidden->mixbuf); + } + if (this->hidden->semaphore != NULL) { + SDL_DestroySemaphore(this->hidden->semaphore); + } + + SDL_free(this->hidden); + this->hidden = NULL; + } +} + +static int +XAUDIO2_OpenDevice(_THIS, const char *devname, int iscapture) +{ + HRESULT result = S_OK; + WAVEFORMATEX waveformat; + int valid_format = 0; + SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format); + IXAudio2 *ixa2 = NULL; + IXAudio2SourceVoice *source = NULL; +#if defined(__WINRT__) + WCHAR devIdBuffer[256]; + LPCWSTR devId = 0; +#else + UINT32 devId = 0; /* 0 == system default device. */ +#endif + +#if defined(__cplusplus) + static SDL_XAudio2VoiceCallback callbacks; +#else + static IXAudio2VoiceCallbackVtbl callbacks_vtable = { + VoiceCBOnVoiceProcessPassStart, + VoiceCBOnVoiceProcessPassEnd, + VoiceCBOnStreamEnd, + VoiceCBOnBufferStart, + VoiceCBOnBufferEnd, + VoiceCBOnLoopEnd, + VoiceCBOnVoiceError + }; + + static IXAudio2VoiceCallback callbacks = { &callbacks_vtable }; +#endif // ! defined(__cplusplus) + +#if defined(__WINRT__) + SDL_zero(devIdBuffer); +#endif + + if (iscapture) { + return SDL_SetError("XAudio2: capture devices unsupported."); + } else if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) { + return SDL_SetError("XAudio2: XAudio2Create() failed at open."); + } + /* + XAUDIO2_DEBUG_CONFIGURATION debugConfig; + debugConfig.TraceMask = XAUDIO2_LOG_ERRORS; //XAUDIO2_LOG_WARNINGS | XAUDIO2_LOG_DETAIL | XAUDIO2_LOG_FUNC_CALLS | XAUDIO2_LOG_TIMING | XAUDIO2_LOG_LOCKS | XAUDIO2_LOG_MEMORY | XAUDIO2_LOG_STREAMING; + debugConfig.BreakMask = XAUDIO2_LOG_ERRORS; //XAUDIO2_LOG_WARNINGS; + debugConfig.LogThreadID = TRUE; + debugConfig.LogFileline = TRUE; + debugConfig.LogFunctionName = TRUE; + debugConfig.LogTiming = TRUE; + ixa2->SetDebugConfiguration(&debugConfig); + */ + +#if ! defined(__WINRT__) || WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP + if (devname != NULL) { + UINT32 devcount = 0; + UINT32 i = 0; + + if (IXAudio2_GetDeviceCount(ixa2, &devcount) != S_OK) { + IXAudio2_Release(ixa2); + return SDL_SetError("XAudio2: IXAudio2_GetDeviceCount() failed."); + } + for (i = 0; i < devcount; i++) { + XAUDIO2_DEVICE_DETAILS details; + if (IXAudio2_GetDeviceDetails(ixa2, i, &details) == S_OK) { + char *str = WIN_StringToUTF8(details.DisplayName); + if (str != NULL) { + const int match = (SDL_strcmp(str, devname) == 0); + SDL_free(str); + if (match) { +#if defined(__WINRT__) + wcsncpy_s(devIdBuffer, ARRAYSIZE(devIdBuffer), details.DeviceID, _TRUNCATE); + devId = (LPCWSTR) &devIdBuffer; +#else + devId = i; +#endif + break; + } + } + } + } + + if (i == devcount) { + IXAudio2_Release(ixa2); + return SDL_SetError("XAudio2: Requested device not found."); + } + } +#endif + + /* Initialize all variables that we clean on shutdown */ + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + if (this->hidden == NULL) { + IXAudio2_Release(ixa2); + return SDL_OutOfMemory(); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + this->hidden->ixa2 = ixa2; + this->hidden->semaphore = SDL_CreateSemaphore(1); + if (this->hidden->semaphore == NULL) { + XAUDIO2_CloseDevice(this); + return SDL_SetError("XAudio2: CreateSemaphore() failed!"); + } + + while ((!valid_format) && (test_format)) { + switch (test_format) { + case AUDIO_U8: + case AUDIO_S16: + case AUDIO_S32: + case AUDIO_F32: + this->spec.format = test_format; + valid_format = 1; + break; + } + test_format = SDL_NextAudioFormat(); + } + + if (!valid_format) { + XAUDIO2_CloseDevice(this); + return SDL_SetError("XAudio2: Unsupported audio format"); + } + + /* Update the fragment size as size in bytes */ + SDL_CalculateAudioSpec(&this->spec); + + /* We feed a Source, it feeds the Mastering, which feeds the device. */ + this->hidden->mixlen = this->spec.size; + this->hidden->mixbuf = (Uint8 *) SDL_malloc(2 * this->hidden->mixlen); + if (this->hidden->mixbuf == NULL) { + XAUDIO2_CloseDevice(this); + return SDL_OutOfMemory(); + } + this->hidden->nextbuf = this->hidden->mixbuf; + SDL_memset(this->hidden->mixbuf, 0, 2 * this->hidden->mixlen); + + /* We use XAUDIO2_DEFAULT_CHANNELS instead of this->spec.channels. On + Xbox360, this means 5.1 output, but on Windows, it means "figure out + what the system has." It might be preferable to let XAudio2 blast + stereo output to appropriate surround sound configurations + instead of clamping to 2 channels, even though we'll configure the + Source Voice for whatever number of channels you supply. */ + result = IXAudio2_CreateMasteringVoice(ixa2, &this->hidden->mastering, + XAUDIO2_DEFAULT_CHANNELS, + this->spec.freq, 0, devId, NULL); + if (result != S_OK) { + XAUDIO2_CloseDevice(this); + return SDL_SetError("XAudio2: Couldn't create mastering voice"); + } + + SDL_zero(waveformat); + if (SDL_AUDIO_ISFLOAT(this->spec.format)) { + waveformat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + } else { + waveformat.wFormatTag = WAVE_FORMAT_PCM; + } + waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format); + waveformat.nChannels = this->spec.channels; + waveformat.nSamplesPerSec = this->spec.freq; + waveformat.nBlockAlign = + waveformat.nChannels * (waveformat.wBitsPerSample / 8); + waveformat.nAvgBytesPerSec = + waveformat.nSamplesPerSec * waveformat.nBlockAlign; + waveformat.cbSize = sizeof(waveformat); + +#ifdef __WINRT__ + // DLudwig: for now, make XAudio2 do sample rate conversion, just to + // get the loopwave test to work. + // + // TODO, WinRT: consider removing WinRT-specific source-voice creation code from SDL_xaudio2.c + result = IXAudio2_CreateSourceVoice(ixa2, &source, &waveformat, + 0, + 1.0f, &callbacks, NULL, NULL); +#else + result = IXAudio2_CreateSourceVoice(ixa2, &source, &waveformat, + XAUDIO2_VOICE_NOSRC | + XAUDIO2_VOICE_NOPITCH, + 1.0f, &callbacks, NULL, NULL); + +#endif + if (result != S_OK) { + XAUDIO2_CloseDevice(this); + return SDL_SetError("XAudio2: Couldn't create source voice"); + } + this->hidden->source = source; + + /* Start everything playing! */ + result = IXAudio2_StartEngine(ixa2); + if (result != S_OK) { + XAUDIO2_CloseDevice(this); + return SDL_SetError("XAudio2: Couldn't start engine"); + } + + result = IXAudio2SourceVoice_Start(source, 0, XAUDIO2_COMMIT_NOW); + if (result != S_OK) { + XAUDIO2_CloseDevice(this); + return SDL_SetError("XAudio2: Couldn't start source voice"); + } + + return 0; /* good to go. */ +} + +static void +XAUDIO2_Deinitialize(void) +{ +#if defined(__WIN32__) + WIN_CoUninitialize(); +#endif +} + +#endif /* SDL_XAUDIO2_HAS_SDK */ + + +static int +XAUDIO2_Init(SDL_AudioDriverImpl * impl) +{ +#ifndef SDL_XAUDIO2_HAS_SDK + SDL_SetError("XAudio2: SDL was built without XAudio2 support (old DirectX SDK)."); + return 0; /* no XAudio2 support, ever. Update your SDK! */ +#else + /* XAudio2Create() is a macro that uses COM; we don't load the .dll */ + IXAudio2 *ixa2 = NULL; +#if defined(__WIN32__) + // TODO, WinRT: Investigate using CoInitializeEx here + if (FAILED(WIN_CoInitialize())) { + SDL_SetError("XAudio2: CoInitialize() failed"); + return 0; + } +#endif + + if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) { +#if defined(__WIN32__) + WIN_CoUninitialize(); +#endif + SDL_SetError("XAudio2: XAudio2Create() failed at initialization"); + return 0; /* not available. */ + } + IXAudio2_Release(ixa2); + + /* Set the function pointers */ + impl->DetectDevices = XAUDIO2_DetectDevices; + impl->OpenDevice = XAUDIO2_OpenDevice; + impl->PlayDevice = XAUDIO2_PlayDevice; + impl->WaitDevice = XAUDIO2_WaitDevice; + impl->WaitDone = XAUDIO2_WaitDone; + impl->GetDeviceBuf = XAUDIO2_GetDeviceBuf; + impl->CloseDevice = XAUDIO2_CloseDevice; + impl->Deinitialize = XAUDIO2_Deinitialize; + + return 1; /* this audio target is available. */ +#endif +} + +#if defined(__cplusplus) +extern "C" +#endif +AudioBootStrap XAUDIO2_bootstrap = { + "xaudio2", "XAudio2", XAUDIO2_Init, 0 +}; + +#endif /* SDL_AUDIO_DRIVER_XAUDIO2 */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/audio/xaudio2/SDL_xaudio2_winrthelpers.cpp b/src/audio/xaudio2/SDL_xaudio2_winrthelpers.cpp new file mode 100644 index 000000000..aa88fd6c1 --- /dev/null +++ b/src/audio/xaudio2/SDL_xaudio2_winrthelpers.cpp @@ -0,0 +1,69 @@ + +#include +#include "SDL_xaudio2_winrthelpers.h" + +#if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP +using Windows::Devices::Enumeration::DeviceClass; +using Windows::Devices::Enumeration::DeviceInformation; +using Windows::Devices::Enumeration::DeviceInformationCollection; +#endif + +HRESULT IXAudio2_GetDeviceCount(IXAudio2 * ixa2, UINT32 * devcount) +{ +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + // There doesn't seem to be any audio device enumeration on Windows Phone. + // In lieu of this, just treat things as if there is one and only one + // audio device. + *devcount = 1; + return S_OK; +#else + // TODO, WinRT: make xaudio2 device enumeration only happen once, and in the background + auto operation = DeviceInformation::FindAllAsync(DeviceClass::AudioRender); + while (operation->Status != Windows::Foundation::AsyncStatus::Completed) + { + } + + DeviceInformationCollection^ devices = operation->GetResults(); + *devcount = devices->Size; + return S_OK; +#endif +} + +HRESULT IXAudio2_GetDeviceDetails(IXAudio2 * unused, UINT32 index, XAUDIO2_DEVICE_DETAILS * details) +{ +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + // Windows Phone doesn't seem to have the same device enumeration APIs that + // Windows 8/RT has, or it doesn't have them at all. In lieu of this, + // just treat things as if there is one, and only one, default device. + if (index != 0) + { + return XAUDIO2_E_INVALID_CALL; + } + + if (details) + { + wcsncpy_s(details->DeviceID, ARRAYSIZE(details->DeviceID), L"default", _TRUNCATE); + wcsncpy_s(details->DisplayName, ARRAYSIZE(details->DisplayName), L"default", _TRUNCATE); + } + return S_OK; +#else + auto operation = DeviceInformation::FindAllAsync(DeviceClass::AudioRender); + while (operation->Status != Windows::Foundation::AsyncStatus::Completed) + { + } + + DeviceInformationCollection^ devices = operation->GetResults(); + if (index >= devices->Size) + { + return XAUDIO2_E_INVALID_CALL; + } + + DeviceInformation^ d = devices->GetAt(index); + if (details) + { + wcsncpy_s(details->DeviceID, ARRAYSIZE(details->DeviceID), d->Id->Data(), _TRUNCATE); + wcsncpy_s(details->DisplayName, ARRAYSIZE(details->DisplayName), d->Name->Data(), _TRUNCATE); + } + return S_OK; +#endif +} diff --git a/src/audio/xaudio2/SDL_xaudio2_winrthelpers.h b/src/audio/xaudio2/SDL_xaudio2_winrthelpers.h new file mode 100644 index 000000000..a72804faa --- /dev/null +++ b/src/audio/xaudio2/SDL_xaudio2_winrthelpers.h @@ -0,0 +1,40 @@ + +#pragma once + +// +// Re-implementation of methods removed from XAudio2 (in Windows RT): +// + +typedef struct XAUDIO2_DEVICE_DETAILS +{ + WCHAR DeviceID[256]; + WCHAR DisplayName[256]; + /* Other fields exist in the pre-Windows 8 version of this struct, however + they weren't used by SDL, so they weren't added. + */ +} XAUDIO2_DEVICE_DETAILS; + +HRESULT IXAudio2_GetDeviceCount(IXAudio2 * unused, UINT32 * devcount); +HRESULT IXAudio2_GetDeviceDetails(IXAudio2 * unused, UINT32 index, XAUDIO2_DEVICE_DETAILS * details); + + +// +// C-style macros to call XAudio2's methods in C++: +// + +#define IXAudio2_CreateMasteringVoice(A, B, C, D, E, F, G) (A)->CreateMasteringVoice((B), (C), (D), (E), (F), (G)) +#define IXAudio2_CreateSourceVoice(A, B, C, D, E, F, G, H) (A)->CreateSourceVoice((B), (C), (D), (E), (F), (G), (H)) +#define IXAudio2_QueryInterface(A, B, C) (A)->QueryInterface((B), (C)) +#define IXAudio2_Release(A) (A)->Release() +#define IXAudio2_StartEngine(A) (A)->StartEngine() +#define IXAudio2_StopEngine(A) (A)->StopEngine() + +#define IXAudio2MasteringVoice_DestroyVoice(A) (A)->DestroyVoice() + +#define IXAudio2SourceVoice_DestroyVoice(A) (A)->DestroyVoice() +#define IXAudio2SourceVoice_Discontinuity(A) (A)->Discontinuity() +#define IXAudio2SourceVoice_FlushSourceBuffers(A) (A)->FlushSourceBuffers() +#define IXAudio2SourceVoice_GetState(A, B) (A)->GetState((B)) +#define IXAudio2SourceVoice_Start(A, B, C) (A)->Start((B), (C)) +#define IXAudio2SourceVoice_Stop(A, B, C) (A)->Stop((B), (C)) +#define IXAudio2SourceVoice_SubmitSourceBuffer(A, B, C) (A)->SubmitSourceBuffer((B), (C)) diff --git a/src/core/windows/SDL_windows.c b/src/core/windows/SDL_windows.c index e69d25793..2388f93fa 100644 --- a/src/core/windows/SDL_windows.c +++ b/src/core/windows/SDL_windows.c @@ -20,21 +20,21 @@ */ #include "SDL_config.h" -#ifdef __WIN32__ +#if defined(__WIN32__) || defined(__WINRT__) #include "SDL_error.h" #include "SDL_windows.h" #include "SDL_assert.h" -#include /* for CoInitialize/CoUninitialize */ +#include /* for CoInitialize/CoUninitialize (Win32 only) */ /* Sets an error message based on GetLastError() */ int -WIN_SetError(const char *prefix) +WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr) { TCHAR buffer[1024]; char *message; - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 0, buffer, SDL_arraysize(buffer), NULL); message = WIN_StringToUTF8(buffer); SDL_SetError("%s%s%s", prefix ? prefix : "", prefix ? ": " : "", message); @@ -42,9 +42,24 @@ WIN_SetError(const char *prefix) return -1; } +/* Sets an error message based on GetLastError() */ +int +WIN_SetError(const char *prefix) +{ + return WIN_SetErrorFromHRESULT(prefix, GetLastError()); +} + HRESULT WIN_CoInitialize(void) { +#ifdef __WINRT__ + /* DLudwig: On WinRT, it is assumed that COM was initialized in main(). + CoInitializeEx is available (not CoInitialize though), however + on WinRT, main() is typically declared with the [MTAThread] + attribute, which, AFAIK, should initialize COM. + */ + return S_OK; +#else const HRESULT hr = CoInitialize(NULL); /* S_FALSE means success, but someone else already initialized. */ @@ -54,12 +69,15 @@ WIN_CoInitialize(void) } return hr; +#endif } void WIN_CoUninitialize(void) { +#ifndef __WINRT__ CoUninitialize(); +#endif } #endif /* __WIN32__ */ diff --git a/src/core/windows/SDL_windows.h b/src/core/windows/SDL_windows.h index 440c387d3..ce614a998 100644 --- a/src/core/windows/SDL_windows.h +++ b/src/core/windows/SDL_windows.h @@ -24,6 +24,7 @@ #ifndef _INCLUDED_WINDOWS_H #define _INCLUDED_WINDOWS_H +#if defined(__WIN32__) #define WIN32_LEAN_AND_MEAN #define STRICT #ifndef UNICODE @@ -31,6 +32,7 @@ #endif #undef _WIN32_WINNT #define _WIN32_WINNT 0x501 /* Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input */ +#endif #include @@ -43,6 +45,9 @@ #define WIN_UTF8ToString(S) SDL_iconv_string("ASCII", "UTF-8", (char *)(S), SDL_strlen(S)+1) #endif +/* Sets an error message based on a given HRESULT */ +extern int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr); + /* Sets an error message based on GetLastError(). Always return -1. */ extern int WIN_SetError(const char *prefix); diff --git a/src/core/windowsrt/SDL_winrtpaths.cpp b/src/core/windowsrt/SDL_winrtpaths.cpp new file mode 100644 index 000000000..4c10b78b7 --- /dev/null +++ b/src/core/windowsrt/SDL_winrtpaths.cpp @@ -0,0 +1,94 @@ +/* TODO, WinRT: include copyright info in SDL_winrtpaths.cpp + TODO, WinRT: add note to SDL_winrtpaths.cpp mentioning that /ZW must be used when compiling the file +*/ + +#include "SDL_config.h" + +#ifdef __WINRT__ + +extern "C" { +#include "SDL_error.h" +#include "SDL_stdinc.h" +#include "SDL_system.h" +#include "../windows/SDL_windows.h" +} + +#include +#include + +using namespace std; +using namespace Windows::Storage; + +extern "C" const wchar_t * +SDL_WinRTGetFSPathUNICODE(SDL_WinRT_Path pathType) +{ + switch (pathType) { + case SDL_WINRT_PATH_INSTALLED_LOCATION: + { + static wstring path; + if (path.empty()) { + path = Windows::ApplicationModel::Package::Current->InstalledLocation->Path->Data(); + } + return path.c_str(); + } + + case SDL_WINRT_PATH_LOCAL_FOLDER: + { + static wstring path; + if (path.empty()) { + path = ApplicationData::Current->LocalFolder->Path->Data(); + } + return path.c_str(); + } + +#if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP + case SDL_WINRT_PATH_ROAMING_FOLDER: + { + static wstring path; + if (path.empty()) { + path = ApplicationData::Current->RoamingFolder->Path->Data(); + } + return path.c_str(); + } + + case SDL_WINRT_PATH_TEMP_FOLDER: + { + static wstring path; + if (path.empty()) { + path = ApplicationData::Current->TemporaryFolder->Path->Data(); + } + return path.c_str(); + } +#endif + + default: + break; + } + + SDL_Unsupported(); + return NULL; +} + +extern "C" const char * +SDL_WinRTGetFSPathUTF8(SDL_WinRT_Path pathType) +{ + typedef unordered_map UTF8PathMap; + static UTF8PathMap utf8Paths; + + UTF8PathMap::iterator searchResult = utf8Paths.find(pathType); + if (searchResult != utf8Paths.end()) { + return searchResult->second.c_str(); + } + + const wchar_t * ucs2Path = SDL_WinRTGetFSPathUNICODE(pathType); + if (!ucs2Path) { + return NULL; + } + + char * utf8Path = WIN_StringToUTF8(ucs2Path); + utf8Paths[pathType] = utf8Path; + SDL_free(utf8Path); + return utf8Paths[pathType].c_str(); +} + +#endif /* __WINRT__ */ diff --git a/src/file/SDL_rwops.c b/src/file/SDL_rwops.c index afbc39aa0..3a6f7db66 100644 --- a/src/file/SDL_rwops.c +++ b/src/file/SDL_rwops.c @@ -1,757 +1,764 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2013 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -/* Need this so Linux systems define fseek64o, ftell64o and off64_t */ -#define _LARGEFILE64_SOURCE -#include "SDL_config.h" - -/* This file provides a general interface for SDL to read and write - data sources. It can easily be extended to files, memory, etc. -*/ - -#include "SDL_endian.h" -#include "SDL_rwops.h" - -#ifdef __APPLE__ -#include "cocoa/SDL_rwopsbundlesupport.h" -#endif /* __APPLE__ */ - -#ifdef ANDROID -#include "../core/android/SDL_android.h" -#include "SDL_system.h" -#endif - -#ifdef __WIN32__ - -/* Functions to read/write Win32 API file pointers */ - -#include "../core/windows/SDL_windows.h" - -#ifndef INVALID_SET_FILE_POINTER -#define INVALID_SET_FILE_POINTER 0xFFFFFFFF -#endif - -#define READAHEAD_BUFFER_SIZE 1024 - -static int SDLCALL -windows_file_open(SDL_RWops * context, const char *filename, const char *mode) -{ - UINT old_error_mode; - HANDLE h; - DWORD r_right, w_right; - DWORD must_exist, truncate; - int a_mode; - - if (!context) - return -1; /* failed (invalid call) */ - - context->hidden.windowsio.h = INVALID_HANDLE_VALUE; /* mark this as unusable */ - context->hidden.windowsio.buffer.data = NULL; - context->hidden.windowsio.buffer.size = 0; - context->hidden.windowsio.buffer.left = 0; - - /* "r" = reading, file must exist */ - /* "w" = writing, truncate existing, file may not exist */ - /* "r+"= reading or writing, file must exist */ - /* "a" = writing, append file may not exist */ - /* "a+"= append + read, file may not exist */ - /* "w+" = read, write, truncate. file may not exist */ - - must_exist = (SDL_strchr(mode, 'r') != NULL) ? OPEN_EXISTING : 0; - truncate = (SDL_strchr(mode, 'w') != NULL) ? CREATE_ALWAYS : 0; - r_right = (SDL_strchr(mode, '+') != NULL - || must_exist) ? GENERIC_READ : 0; - a_mode = (SDL_strchr(mode, 'a') != NULL) ? OPEN_ALWAYS : 0; - w_right = (a_mode || SDL_strchr(mode, '+') - || truncate) ? GENERIC_WRITE : 0; - - if (!r_right && !w_right) /* inconsistent mode */ - return -1; /* failed (invalid call) */ - - context->hidden.windowsio.buffer.data = - (char *) SDL_malloc(READAHEAD_BUFFER_SIZE); - if (!context->hidden.windowsio.buffer.data) { - return SDL_OutOfMemory(); - } - /* Do not open a dialog box if failure */ - old_error_mode = - SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); - - { - LPTSTR tstr = WIN_UTF8ToString(filename); - h = CreateFile(tstr, (w_right | r_right), - (w_right) ? 0 : FILE_SHARE_READ, NULL, - (must_exist | truncate | a_mode), - FILE_ATTRIBUTE_NORMAL, NULL); - SDL_free(tstr); - } - - /* restore old behavior */ - SetErrorMode(old_error_mode); - - if (h == INVALID_HANDLE_VALUE) { - SDL_free(context->hidden.windowsio.buffer.data); - context->hidden.windowsio.buffer.data = NULL; - SDL_SetError("Couldn't open %s", filename); - return -2; /* failed (CreateFile) */ - } - context->hidden.windowsio.h = h; - context->hidden.windowsio.append = a_mode ? SDL_TRUE : SDL_FALSE; - - return 0; /* ok */ -} - -static Sint64 SDLCALL -windows_file_size(SDL_RWops * context) -{ - LARGE_INTEGER size; - - if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) { - return SDL_SetError("windows_file_size: invalid context/file not opened"); - } - - if (!GetFileSizeEx(context->hidden.windowsio.h, &size)) { - return WIN_SetError("windows_file_size"); - } - - return size.QuadPart; -} - -static Sint64 SDLCALL -windows_file_seek(SDL_RWops * context, Sint64 offset, int whence) -{ - DWORD windowswhence; - LARGE_INTEGER windowsoffset; - - if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) { - return SDL_SetError("windows_file_seek: invalid context/file not opened"); - } - - /* FIXME: We may be able to satisfy the seek within buffered data */ - if (whence == RW_SEEK_CUR && context->hidden.windowsio.buffer.left) { - offset -= (long)context->hidden.windowsio.buffer.left; - } - context->hidden.windowsio.buffer.left = 0; - - switch (whence) { - case RW_SEEK_SET: - windowswhence = FILE_BEGIN; - break; - case RW_SEEK_CUR: - windowswhence = FILE_CURRENT; - break; - case RW_SEEK_END: - windowswhence = FILE_END; - break; - default: - return SDL_SetError("windows_file_seek: Unknown value for 'whence'"); - } - - windowsoffset.QuadPart = offset; - if (!SetFilePointerEx(context->hidden.windowsio.h, windowsoffset, &windowsoffset, windowswhence)) { - return WIN_SetError("windows_file_seek"); - } - return windowsoffset.QuadPart; -} - -static size_t SDLCALL -windows_file_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum) -{ - size_t total_need; - size_t total_read = 0; - size_t read_ahead; - DWORD byte_read; - - total_need = size * maxnum; - - if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE - || !total_need) - return 0; - - if (context->hidden.windowsio.buffer.left > 0) { - void *data = (char *) context->hidden.windowsio.buffer.data + - context->hidden.windowsio.buffer.size - - context->hidden.windowsio.buffer.left; - read_ahead = - SDL_min(total_need, context->hidden.windowsio.buffer.left); - SDL_memcpy(ptr, data, read_ahead); - context->hidden.windowsio.buffer.left -= read_ahead; - - if (read_ahead == total_need) { - return maxnum; - } - ptr = (char *) ptr + read_ahead; - total_need -= read_ahead; - total_read += read_ahead; - } - - if (total_need < READAHEAD_BUFFER_SIZE) { - if (!ReadFile - (context->hidden.windowsio.h, context->hidden.windowsio.buffer.data, - READAHEAD_BUFFER_SIZE, &byte_read, NULL)) { - SDL_Error(SDL_EFREAD); - return 0; - } - read_ahead = SDL_min(total_need, (int) byte_read); - SDL_memcpy(ptr, context->hidden.windowsio.buffer.data, read_ahead); - context->hidden.windowsio.buffer.size = byte_read; - context->hidden.windowsio.buffer.left = byte_read - read_ahead; - total_read += read_ahead; - } else { - if (!ReadFile - (context->hidden.windowsio.h, ptr, (DWORD)total_need, &byte_read, NULL)) { - SDL_Error(SDL_EFREAD); - return 0; - } - total_read += byte_read; - } - return (total_read / size); -} - -static size_t SDLCALL -windows_file_write(SDL_RWops * context, const void *ptr, size_t size, - size_t num) -{ - - size_t total_bytes; - DWORD byte_written; - size_t nwritten; - - total_bytes = size * num; - - if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE - || total_bytes <= 0 || !size) - return 0; - - if (context->hidden.windowsio.buffer.left) { - SetFilePointer(context->hidden.windowsio.h, - -(LONG)context->hidden.windowsio.buffer.left, NULL, - FILE_CURRENT); - context->hidden.windowsio.buffer.left = 0; - } - - /* if in append mode, we must go to the EOF before write */ - if (context->hidden.windowsio.append) { - if (SetFilePointer(context->hidden.windowsio.h, 0L, NULL, FILE_END) == - INVALID_SET_FILE_POINTER) { - SDL_Error(SDL_EFWRITE); - return 0; - } - } - - if (!WriteFile - (context->hidden.windowsio.h, ptr, (DWORD)total_bytes, &byte_written, NULL)) { - SDL_Error(SDL_EFWRITE); - return 0; - } - - nwritten = byte_written / size; - return nwritten; -} - -static int SDLCALL -windows_file_close(SDL_RWops * context) -{ - - if (context) { - if (context->hidden.windowsio.h != INVALID_HANDLE_VALUE) { - CloseHandle(context->hidden.windowsio.h); - context->hidden.windowsio.h = INVALID_HANDLE_VALUE; /* to be sure */ - } - if (context->hidden.windowsio.buffer.data) { - SDL_free(context->hidden.windowsio.buffer.data); - context->hidden.windowsio.buffer.data = NULL; - } - SDL_FreeRW(context); - } - return (0); -} -#endif /* __WIN32__ */ - -#ifdef HAVE_STDIO_H - -/* Functions to read/write stdio file pointers */ - -static Sint64 SDLCALL -stdio_size(SDL_RWops * context) -{ - Sint64 pos, size; - - pos = SDL_RWseek(context, 0, RW_SEEK_CUR); - if (pos < 0) { - return -1; - } - size = SDL_RWseek(context, 0, RW_SEEK_END); - - SDL_RWseek(context, pos, RW_SEEK_SET); - return size; -} - -static Sint64 SDLCALL -stdio_seek(SDL_RWops * context, Sint64 offset, int whence) -{ -#ifdef HAVE_FSEEKO64 - if (fseeko64(context->hidden.stdio.fp, (off64_t)offset, whence) == 0) { - return ftello64(context->hidden.stdio.fp); - } -#elif defined(HAVE_FSEEKO) - if (fseeko(context->hidden.stdio.fp, (off_t)offset, whence) == 0) { - return ftello(context->hidden.stdio.fp); - } -#else - if (fseek(context->hidden.stdio.fp, offset, whence) == 0) { - return (ftell(context->hidden.stdio.fp)); - } -#endif - return SDL_Error(SDL_EFSEEK); -} - -static size_t SDLCALL -stdio_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum) -{ - size_t nread; - - nread = fread(ptr, size, maxnum, context->hidden.stdio.fp); - if (nread == 0 && ferror(context->hidden.stdio.fp)) { - SDL_Error(SDL_EFREAD); - } - return (nread); -} - -static size_t SDLCALL -stdio_write(SDL_RWops * context, const void *ptr, size_t size, size_t num) -{ - size_t nwrote; - - nwrote = fwrite(ptr, size, num, context->hidden.stdio.fp); - if (nwrote == 0 && ferror(context->hidden.stdio.fp)) { - SDL_Error(SDL_EFWRITE); - } - return (nwrote); -} - -static int SDLCALL -stdio_close(SDL_RWops * context) -{ - int status = 0; - if (context) { - if (context->hidden.stdio.autoclose) { - /* WARNING: Check the return value here! */ - if (fclose(context->hidden.stdio.fp) != 0) { - status = SDL_Error(SDL_EFWRITE); - } - } - SDL_FreeRW(context); - } - return status; -} -#endif /* !HAVE_STDIO_H */ - -/* Functions to read/write memory pointers */ - -static Sint64 SDLCALL -mem_size(SDL_RWops * context) -{ - return (Sint64)(context->hidden.mem.stop - context->hidden.mem.base); -} - -static Sint64 SDLCALL -mem_seek(SDL_RWops * context, Sint64 offset, int whence) -{ - Uint8 *newpos; - - switch (whence) { - case RW_SEEK_SET: - newpos = context->hidden.mem.base + offset; - break; - case RW_SEEK_CUR: - newpos = context->hidden.mem.here + offset; - break; - case RW_SEEK_END: - newpos = context->hidden.mem.stop + offset; - break; - default: - return SDL_SetError("Unknown value for 'whence'"); - } - if (newpos < context->hidden.mem.base) { - newpos = context->hidden.mem.base; - } - if (newpos > context->hidden.mem.stop) { - newpos = context->hidden.mem.stop; - } - context->hidden.mem.here = newpos; - return (Sint64)(context->hidden.mem.here - context->hidden.mem.base); -} - -static size_t SDLCALL -mem_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum) -{ - size_t total_bytes; - size_t mem_available; - - total_bytes = (maxnum * size); - if ((maxnum <= 0) || (size <= 0) - || ((total_bytes / maxnum) != (size_t) size)) { - return 0; - } - - mem_available = (context->hidden.mem.stop - context->hidden.mem.here); - if (total_bytes > mem_available) { - total_bytes = mem_available; - } - - SDL_memcpy(ptr, context->hidden.mem.here, total_bytes); - context->hidden.mem.here += total_bytes; - - return (total_bytes / size); -} - -static size_t SDLCALL -mem_write(SDL_RWops * context, const void *ptr, size_t size, size_t num) -{ - if ((context->hidden.mem.here + (num * size)) > context->hidden.mem.stop) { - num = (context->hidden.mem.stop - context->hidden.mem.here) / size; - } - SDL_memcpy(context->hidden.mem.here, ptr, num * size); - context->hidden.mem.here += num * size; - return (num); -} - -static size_t SDLCALL -mem_writeconst(SDL_RWops * context, const void *ptr, size_t size, size_t num) -{ - SDL_SetError("Can't write to read-only memory"); - return (0); -} - -static int SDLCALL -mem_close(SDL_RWops * context) -{ - if (context) { - SDL_FreeRW(context); - } - return (0); -} - - -/* Functions to create SDL_RWops structures from various data sources */ - -SDL_RWops * -SDL_RWFromFile(const char *file, const char *mode) -{ - SDL_RWops *rwops = NULL; - if (!file || !*file || !mode || !*mode) { - SDL_SetError("SDL_RWFromFile(): No file or no mode specified"); - return NULL; - } -#if defined(ANDROID) -#ifdef HAVE_STDIO_H - /* Try to open the file on the filesystem first */ - if (*file == '/') { - FILE *fp = fopen(file, mode); - if (fp) { - return SDL_RWFromFP(fp, 1); - } - } else { - /* Try opening it from internal storage if it's a relative path */ - char *path; - FILE *fp; - - path = SDL_stack_alloc(char, PATH_MAX); - if (path) { - SDL_snprintf(path, PATH_MAX, "%s/%s", - SDL_AndroidGetInternalStoragePath(), file); - fp = fopen(path, mode); - SDL_stack_free(path); - if (fp) { - return SDL_RWFromFP(fp, 1); - } - } - } -#endif /* HAVE_STDIO_H */ - - /* Try to open the file from the asset system */ - rwops = SDL_AllocRW(); - if (!rwops) - return NULL; /* SDL_SetError already setup by SDL_AllocRW() */ - if (Android_JNI_FileOpen(rwops, file, mode) < 0) { - SDL_FreeRW(rwops); - return NULL; - } - rwops->size = Android_JNI_FileSize; - rwops->seek = Android_JNI_FileSeek; - rwops->read = Android_JNI_FileRead; - rwops->write = Android_JNI_FileWrite; - rwops->close = Android_JNI_FileClose; - rwops->type = SDL_RWOPS_JNIFILE; - -#elif defined(__WIN32__) - rwops = SDL_AllocRW(); - if (!rwops) - return NULL; /* SDL_SetError already setup by SDL_AllocRW() */ - if (windows_file_open(rwops, file, mode) < 0) { - SDL_FreeRW(rwops); - return NULL; - } - rwops->size = windows_file_size; - rwops->seek = windows_file_seek; - rwops->read = windows_file_read; - rwops->write = windows_file_write; - rwops->close = windows_file_close; - rwops->type = SDL_RWOPS_WINFILE; - -#elif HAVE_STDIO_H - { - #ifdef __APPLE__ - FILE *fp = SDL_OpenFPFromBundleOrFallback(file, mode); - #else - FILE *fp = fopen(file, mode); - #endif - if (fp == NULL) { - SDL_SetError("Couldn't open %s", file); - } else { - rwops = SDL_RWFromFP(fp, 1); - } - } -#else - SDL_SetError("SDL not compiled with stdio support"); -#endif /* !HAVE_STDIO_H */ - - return (rwops); -} - -#ifdef HAVE_STDIO_H -SDL_RWops * -SDL_RWFromFP(FILE * fp, SDL_bool autoclose) -{ - SDL_RWops *rwops = NULL; - - rwops = SDL_AllocRW(); - if (rwops != NULL) { - rwops->size = stdio_size; - rwops->seek = stdio_seek; - rwops->read = stdio_read; - rwops->write = stdio_write; - rwops->close = stdio_close; - rwops->hidden.stdio.fp = fp; - rwops->hidden.stdio.autoclose = autoclose; - rwops->type = SDL_RWOPS_STDFILE; - } - return (rwops); -} -#else -SDL_RWops * -SDL_RWFromFP(void * fp, SDL_bool autoclose) -{ - SDL_SetError("SDL not compiled with stdio support"); - return NULL; -} -#endif /* HAVE_STDIO_H */ - -SDL_RWops * -SDL_RWFromMem(void *mem, int size) -{ - SDL_RWops *rwops = NULL; - if (!mem) { - SDL_InvalidParamError("mem"); - return (rwops); - } - if (!size) { - SDL_InvalidParamError("size"); - return (rwops); - } - - rwops = SDL_AllocRW(); - if (rwops != NULL) { - rwops->size = mem_size; - rwops->seek = mem_seek; - rwops->read = mem_read; - rwops->write = mem_write; - rwops->close = mem_close; - rwops->hidden.mem.base = (Uint8 *) mem; - rwops->hidden.mem.here = rwops->hidden.mem.base; - rwops->hidden.mem.stop = rwops->hidden.mem.base + size; - rwops->type = SDL_RWOPS_MEMORY; - } - return (rwops); -} - -SDL_RWops * -SDL_RWFromConstMem(const void *mem, int size) -{ - SDL_RWops *rwops = NULL; - if (!mem) { - SDL_InvalidParamError("mem"); - return (rwops); - } - if (!size) { - SDL_InvalidParamError("size"); - return (rwops); - } - - rwops = SDL_AllocRW(); - if (rwops != NULL) { - rwops->size = mem_size; - rwops->seek = mem_seek; - rwops->read = mem_read; - rwops->write = mem_writeconst; - rwops->close = mem_close; - rwops->hidden.mem.base = (Uint8 *) mem; - rwops->hidden.mem.here = rwops->hidden.mem.base; - rwops->hidden.mem.stop = rwops->hidden.mem.base + size; - rwops->type = SDL_RWOPS_MEMORY_RO; - } - return (rwops); -} - -SDL_RWops * -SDL_AllocRW(void) -{ - SDL_RWops *area; - - area = (SDL_RWops *) SDL_malloc(sizeof *area); - if (area == NULL) { - SDL_OutOfMemory(); - } else { - area->type = SDL_RWOPS_UNKNOWN; - } - return (area); -} - -void -SDL_FreeRW(SDL_RWops * area) -{ - SDL_free(area); -} - -/* Functions for dynamically reading and writing endian-specific values */ - -Uint8 -SDL_ReadU8(SDL_RWops * src) -{ - Uint8 value = 0; - - SDL_RWread(src, &value, (sizeof value), 1); - return value; -} - -Uint16 -SDL_ReadLE16(SDL_RWops * src) -{ - Uint16 value = 0; - - SDL_RWread(src, &value, (sizeof value), 1); - return (SDL_SwapLE16(value)); -} - -Uint16 -SDL_ReadBE16(SDL_RWops * src) -{ - Uint16 value = 0; - - SDL_RWread(src, &value, (sizeof value), 1); - return (SDL_SwapBE16(value)); -} - -Uint32 -SDL_ReadLE32(SDL_RWops * src) -{ - Uint32 value = 0; - - SDL_RWread(src, &value, (sizeof value), 1); - return (SDL_SwapLE32(value)); -} - -Uint32 -SDL_ReadBE32(SDL_RWops * src) -{ - Uint32 value = 0; - - SDL_RWread(src, &value, (sizeof value), 1); - return (SDL_SwapBE32(value)); -} - -Uint64 -SDL_ReadLE64(SDL_RWops * src) -{ - Uint64 value = 0; - - SDL_RWread(src, &value, (sizeof value), 1); - return (SDL_SwapLE64(value)); -} - -Uint64 -SDL_ReadBE64(SDL_RWops * src) -{ - Uint64 value = 0; - - SDL_RWread(src, &value, (sizeof value), 1); - return (SDL_SwapBE64(value)); -} - -size_t -SDL_WriteU8(SDL_RWops * dst, Uint8 value) -{ - return (SDL_RWwrite(dst, &value, (sizeof value), 1)); -} - -size_t -SDL_WriteLE16(SDL_RWops * dst, Uint16 value) -{ - value = SDL_SwapLE16(value); - return (SDL_RWwrite(dst, &value, (sizeof value), 1)); -} - -size_t -SDL_WriteBE16(SDL_RWops * dst, Uint16 value) -{ - value = SDL_SwapBE16(value); - return (SDL_RWwrite(dst, &value, (sizeof value), 1)); -} - -size_t -SDL_WriteLE32(SDL_RWops * dst, Uint32 value) -{ - value = SDL_SwapLE32(value); - return (SDL_RWwrite(dst, &value, (sizeof value), 1)); -} - -size_t -SDL_WriteBE32(SDL_RWops * dst, Uint32 value) -{ - value = SDL_SwapBE32(value); - return (SDL_RWwrite(dst, &value, (sizeof value), 1)); -} - -size_t -SDL_WriteLE64(SDL_RWops * dst, Uint64 value) -{ - value = SDL_SwapLE64(value); - return (SDL_RWwrite(dst, &value, (sizeof value), 1)); -} - -size_t -SDL_WriteBE64(SDL_RWops * dst, Uint64 value) -{ - value = SDL_SwapBE64(value); - return (SDL_RWwrite(dst, &value, (sizeof value), 1)); -} - -/* vi: set ts=4 sw=4 expandtab: */ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2013 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +/* Need this so Linux systems define fseek64o, ftell64o and off64_t */ +#define _LARGEFILE64_SOURCE +#include "SDL_config.h" + +/* This file provides a general interface for SDL to read and write + data sources. It can easily be extended to files, memory, etc. +*/ + +#include "SDL_endian.h" +#include "SDL_rwops.h" + +#ifdef __APPLE__ +#include "cocoa/SDL_rwopsbundlesupport.h" +#endif /* __APPLE__ */ + +#ifdef ANDROID +#include "../core/android/SDL_android.h" +#include "SDL_system.h" +#endif + +#ifdef __WIN32__ + +/* Functions to read/write Win32 API file pointers */ + +#include "../core/windows/SDL_windows.h" + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER 0xFFFFFFFF +#endif + +#define READAHEAD_BUFFER_SIZE 1024 + +static int SDLCALL +windows_file_open(SDL_RWops * context, const char *filename, const char *mode) +{ + UINT old_error_mode; + HANDLE h; + DWORD r_right, w_right; + DWORD must_exist, truncate; + int a_mode; + + if (!context) + return -1; /* failed (invalid call) */ + + context->hidden.windowsio.h = INVALID_HANDLE_VALUE; /* mark this as unusable */ + context->hidden.windowsio.buffer.data = NULL; + context->hidden.windowsio.buffer.size = 0; + context->hidden.windowsio.buffer.left = 0; + + /* "r" = reading, file must exist */ + /* "w" = writing, truncate existing, file may not exist */ + /* "r+"= reading or writing, file must exist */ + /* "a" = writing, append file may not exist */ + /* "a+"= append + read, file may not exist */ + /* "w+" = read, write, truncate. file may not exist */ + + must_exist = (SDL_strchr(mode, 'r') != NULL) ? OPEN_EXISTING : 0; + truncate = (SDL_strchr(mode, 'w') != NULL) ? CREATE_ALWAYS : 0; + r_right = (SDL_strchr(mode, '+') != NULL + || must_exist) ? GENERIC_READ : 0; + a_mode = (SDL_strchr(mode, 'a') != NULL) ? OPEN_ALWAYS : 0; + w_right = (a_mode || SDL_strchr(mode, '+') + || truncate) ? GENERIC_WRITE : 0; + + if (!r_right && !w_right) /* inconsistent mode */ + return -1; /* failed (invalid call) */ + + context->hidden.windowsio.buffer.data = + (char *) SDL_malloc(READAHEAD_BUFFER_SIZE); + if (!context->hidden.windowsio.buffer.data) { + return SDL_OutOfMemory(); + } + /* Do not open a dialog box if failure */ + old_error_mode = + SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); + + { + LPTSTR tstr = WIN_UTF8ToString(filename); + h = CreateFile(tstr, (w_right | r_right), + (w_right) ? 0 : FILE_SHARE_READ, NULL, + (must_exist | truncate | a_mode), + FILE_ATTRIBUTE_NORMAL, NULL); + SDL_free(tstr); + } + + /* restore old behavior */ + SetErrorMode(old_error_mode); + + if (h == INVALID_HANDLE_VALUE) { + SDL_free(context->hidden.windowsio.buffer.data); + context->hidden.windowsio.buffer.data = NULL; + SDL_SetError("Couldn't open %s", filename); + return -2; /* failed (CreateFile) */ + } + context->hidden.windowsio.h = h; + context->hidden.windowsio.append = a_mode ? SDL_TRUE : SDL_FALSE; + + return 0; /* ok */ +} + +static Sint64 SDLCALL +windows_file_size(SDL_RWops * context) +{ + LARGE_INTEGER size; + + if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) { + return SDL_SetError("windows_file_size: invalid context/file not opened"); + } + + if (!GetFileSizeEx(context->hidden.windowsio.h, &size)) { + return WIN_SetError("windows_file_size"); + } + + return size.QuadPart; +} + +static Sint64 SDLCALL +windows_file_seek(SDL_RWops * context, Sint64 offset, int whence) +{ + DWORD windowswhence; + LARGE_INTEGER windowsoffset; + + if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) { + return SDL_SetError("windows_file_seek: invalid context/file not opened"); + } + + /* FIXME: We may be able to satisfy the seek within buffered data */ + if (whence == RW_SEEK_CUR && context->hidden.windowsio.buffer.left) { + offset -= (long)context->hidden.windowsio.buffer.left; + } + context->hidden.windowsio.buffer.left = 0; + + switch (whence) { + case RW_SEEK_SET: + windowswhence = FILE_BEGIN; + break; + case RW_SEEK_CUR: + windowswhence = FILE_CURRENT; + break; + case RW_SEEK_END: + windowswhence = FILE_END; + break; + default: + return SDL_SetError("windows_file_seek: Unknown value for 'whence'"); + } + + windowsoffset.QuadPart = offset; + if (!SetFilePointerEx(context->hidden.windowsio.h, windowsoffset, &windowsoffset, windowswhence)) { + return WIN_SetError("windows_file_seek"); + } + return windowsoffset.QuadPart; +} + +static size_t SDLCALL +windows_file_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum) +{ + size_t total_need; + size_t total_read = 0; + size_t read_ahead; + DWORD byte_read; + + total_need = size * maxnum; + + if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE + || !total_need) + return 0; + + if (context->hidden.windowsio.buffer.left > 0) { + void *data = (char *) context->hidden.windowsio.buffer.data + + context->hidden.windowsio.buffer.size - + context->hidden.windowsio.buffer.left; + read_ahead = + SDL_min(total_need, context->hidden.windowsio.buffer.left); + SDL_memcpy(ptr, data, read_ahead); + context->hidden.windowsio.buffer.left -= read_ahead; + + if (read_ahead == total_need) { + return maxnum; + } + ptr = (char *) ptr + read_ahead; + total_need -= read_ahead; + total_read += read_ahead; + } + + if (total_need < READAHEAD_BUFFER_SIZE) { + if (!ReadFile + (context->hidden.windowsio.h, context->hidden.windowsio.buffer.data, + READAHEAD_BUFFER_SIZE, &byte_read, NULL)) { + SDL_Error(SDL_EFREAD); + return 0; + } + read_ahead = SDL_min(total_need, (int) byte_read); + SDL_memcpy(ptr, context->hidden.windowsio.buffer.data, read_ahead); + context->hidden.windowsio.buffer.size = byte_read; + context->hidden.windowsio.buffer.left = byte_read - read_ahead; + total_read += read_ahead; + } else { + if (!ReadFile + (context->hidden.windowsio.h, ptr, (DWORD)total_need, &byte_read, NULL)) { + SDL_Error(SDL_EFREAD); + return 0; + } + total_read += byte_read; + } + return (total_read / size); +} + +static size_t SDLCALL +windows_file_write(SDL_RWops * context, const void *ptr, size_t size, + size_t num) +{ + + size_t total_bytes; + DWORD byte_written; + size_t nwritten; + + total_bytes = size * num; + + if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE + || total_bytes <= 0 || !size) + return 0; + + if (context->hidden.windowsio.buffer.left) { + SetFilePointer(context->hidden.windowsio.h, + -(LONG)context->hidden.windowsio.buffer.left, NULL, + FILE_CURRENT); + context->hidden.windowsio.buffer.left = 0; + } + + /* if in append mode, we must go to the EOF before write */ + if (context->hidden.windowsio.append) { + if (SetFilePointer(context->hidden.windowsio.h, 0L, NULL, FILE_END) == + INVALID_SET_FILE_POINTER) { + SDL_Error(SDL_EFWRITE); + return 0; + } + } + + if (!WriteFile + (context->hidden.windowsio.h, ptr, (DWORD)total_bytes, &byte_written, NULL)) { + SDL_Error(SDL_EFWRITE); + return 0; + } + + nwritten = byte_written / size; + return nwritten; +} + +static int SDLCALL +windows_file_close(SDL_RWops * context) +{ + + if (context) { + if (context->hidden.windowsio.h != INVALID_HANDLE_VALUE) { + CloseHandle(context->hidden.windowsio.h); + context->hidden.windowsio.h = INVALID_HANDLE_VALUE; /* to be sure */ + } + if (context->hidden.windowsio.buffer.data) { + SDL_free(context->hidden.windowsio.buffer.data); + context->hidden.windowsio.buffer.data = NULL; + } + SDL_FreeRW(context); + } + return (0); +} +#endif /* __WIN32__ */ + +#ifdef HAVE_STDIO_H + +/* Functions to read/write stdio file pointers */ + +static Sint64 SDLCALL +stdio_size(SDL_RWops * context) +{ + Sint64 pos, size; + + pos = SDL_RWseek(context, 0, RW_SEEK_CUR); + if (pos < 0) { + return -1; + } + size = SDL_RWseek(context, 0, RW_SEEK_END); + + SDL_RWseek(context, pos, RW_SEEK_SET); + return size; +} + +static Sint64 SDLCALL +stdio_seek(SDL_RWops * context, Sint64 offset, int whence) +{ +#ifdef HAVE_FSEEKO64 + if (fseeko64(context->hidden.stdio.fp, (off64_t)offset, whence) == 0) { + return ftello64(context->hidden.stdio.fp); + } +#elif defined(HAVE_FSEEKO) + if (fseeko(context->hidden.stdio.fp, (off_t)offset, whence) == 0) { + return ftello(context->hidden.stdio.fp); + } +#elif defined(HAVE__FSEEKI64) + if (_fseeki64(context->hidden.stdio.fp, offset, whence) == 0) { + return _ftelli64(context->hidden.stdio.fp); + } +#else + if (fseek(context->hidden.stdio.fp, offset, whence) == 0) { + return (ftell(context->hidden.stdio.fp)); + } +#endif + return SDL_Error(SDL_EFSEEK); +} + +static size_t SDLCALL +stdio_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum) +{ + size_t nread; + + nread = fread(ptr, size, maxnum, context->hidden.stdio.fp); + if (nread == 0 && ferror(context->hidden.stdio.fp)) { + SDL_Error(SDL_EFREAD); + } + return (nread); +} + +static size_t SDLCALL +stdio_write(SDL_RWops * context, const void *ptr, size_t size, size_t num) +{ + size_t nwrote; + + nwrote = fwrite(ptr, size, num, context->hidden.stdio.fp); + if (nwrote == 0 && ferror(context->hidden.stdio.fp)) { + SDL_Error(SDL_EFWRITE); + } + return (nwrote); +} + +static int SDLCALL +stdio_close(SDL_RWops * context) +{ + int status = 0; + if (context) { + if (context->hidden.stdio.autoclose) { + /* WARNING: Check the return value here! */ + if (fclose(context->hidden.stdio.fp) != 0) { + status = SDL_Error(SDL_EFWRITE); + } + } + SDL_FreeRW(context); + } + return status; +} +#endif /* !HAVE_STDIO_H */ + +/* Functions to read/write memory pointers */ + +static Sint64 SDLCALL +mem_size(SDL_RWops * context) +{ + return (Sint64)(context->hidden.mem.stop - context->hidden.mem.base); +} + +static Sint64 SDLCALL +mem_seek(SDL_RWops * context, Sint64 offset, int whence) +{ + Uint8 *newpos; + + switch (whence) { + case RW_SEEK_SET: + newpos = context->hidden.mem.base + offset; + break; + case RW_SEEK_CUR: + newpos = context->hidden.mem.here + offset; + break; + case RW_SEEK_END: + newpos = context->hidden.mem.stop + offset; + break; + default: + return SDL_SetError("Unknown value for 'whence'"); + } + if (newpos < context->hidden.mem.base) { + newpos = context->hidden.mem.base; + } + if (newpos > context->hidden.mem.stop) { + newpos = context->hidden.mem.stop; + } + context->hidden.mem.here = newpos; + return (Sint64)(context->hidden.mem.here - context->hidden.mem.base); +} + +static size_t SDLCALL +mem_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum) +{ + size_t total_bytes; + size_t mem_available; + + total_bytes = (maxnum * size); + if ((maxnum <= 0) || (size <= 0) + || ((total_bytes / maxnum) != (size_t) size)) { + return 0; + } + + mem_available = (context->hidden.mem.stop - context->hidden.mem.here); + if (total_bytes > mem_available) { + total_bytes = mem_available; + } + + SDL_memcpy(ptr, context->hidden.mem.here, total_bytes); + context->hidden.mem.here += total_bytes; + + return (total_bytes / size); +} + +static size_t SDLCALL +mem_write(SDL_RWops * context, const void *ptr, size_t size, size_t num) +{ + if ((context->hidden.mem.here + (num * size)) > context->hidden.mem.stop) { + num = (context->hidden.mem.stop - context->hidden.mem.here) / size; + } + SDL_memcpy(context->hidden.mem.here, ptr, num * size); + context->hidden.mem.here += num * size; + return (num); +} + +static size_t SDLCALL +mem_writeconst(SDL_RWops * context, const void *ptr, size_t size, size_t num) +{ + SDL_SetError("Can't write to read-only memory"); + return (0); +} + +static int SDLCALL +mem_close(SDL_RWops * context) +{ + if (context) { + SDL_FreeRW(context); + } + return (0); +} + + +/* Functions to create SDL_RWops structures from various data sources */ + +SDL_RWops * +SDL_RWFromFile(const char *file, const char *mode) +{ + SDL_RWops *rwops = NULL; + if (!file || !*file || !mode || !*mode) { + SDL_SetError("SDL_RWFromFile(): No file or no mode specified"); + return NULL; + } +#if defined(ANDROID) +#ifdef HAVE_STDIO_H + /* Try to open the file on the filesystem first */ + if (*file == '/') { + FILE *fp = fopen(file, mode); + if (fp) { + return SDL_RWFromFP(fp, 1); + } + } else { + /* Try opening it from internal storage if it's a relative path */ + char *path; + FILE *fp; + + path = SDL_stack_alloc(char, PATH_MAX); + if (path) { + SDL_snprintf(path, PATH_MAX, "%s/%s", + SDL_AndroidGetInternalStoragePath(), file); + fp = fopen(path, mode); + SDL_stack_free(path); + if (fp) { + return SDL_RWFromFP(fp, 1); + } + } + } +#endif /* HAVE_STDIO_H */ + + /* Try to open the file from the asset system */ + rwops = SDL_AllocRW(); + if (!rwops) + return NULL; /* SDL_SetError already setup by SDL_AllocRW() */ + if (Android_JNI_FileOpen(rwops, file, mode) < 0) { + SDL_FreeRW(rwops); + return NULL; + } + rwops->size = Android_JNI_FileSize; + rwops->seek = Android_JNI_FileSeek; + rwops->read = Android_JNI_FileRead; + rwops->write = Android_JNI_FileWrite; + rwops->close = Android_JNI_FileClose; + rwops->type = SDL_RWOPS_JNIFILE; + +#elif defined(__WIN32__) + rwops = SDL_AllocRW(); + if (!rwops) + return NULL; /* SDL_SetError already setup by SDL_AllocRW() */ + if (windows_file_open(rwops, file, mode) < 0) { + SDL_FreeRW(rwops); + return NULL; + } + rwops->size = windows_file_size; + rwops->seek = windows_file_seek; + rwops->read = windows_file_read; + rwops->write = windows_file_write; + rwops->close = windows_file_close; + rwops->type = SDL_RWOPS_WINFILE; + +#elif HAVE_STDIO_H + { + #ifdef __APPLE__ + FILE *fp = SDL_OpenFPFromBundleOrFallback(file, mode); + #elif __WINRT__ + FILE *fp = NULL; + fopen_s(&fp, file, mode); + #else + FILE *fp = fopen(file, mode); + #endif + if (fp == NULL) { + SDL_SetError("Couldn't open %s", file); + } else { + rwops = SDL_RWFromFP(fp, 1); + } + } +#else + SDL_SetError("SDL not compiled with stdio support"); +#endif /* !HAVE_STDIO_H */ + + return (rwops); +} + +#ifdef HAVE_STDIO_H +SDL_RWops * +SDL_RWFromFP(FILE * fp, SDL_bool autoclose) +{ + SDL_RWops *rwops = NULL; + + rwops = SDL_AllocRW(); + if (rwops != NULL) { + rwops->size = stdio_size; + rwops->seek = stdio_seek; + rwops->read = stdio_read; + rwops->write = stdio_write; + rwops->close = stdio_close; + rwops->hidden.stdio.fp = fp; + rwops->hidden.stdio.autoclose = autoclose; + rwops->type = SDL_RWOPS_STDFILE; + } + return (rwops); +} +#else +SDL_RWops * +SDL_RWFromFP(void * fp, SDL_bool autoclose) +{ + SDL_SetError("SDL not compiled with stdio support"); + return NULL; +} +#endif /* HAVE_STDIO_H */ + +SDL_RWops * +SDL_RWFromMem(void *mem, int size) +{ + SDL_RWops *rwops = NULL; + if (!mem) { + SDL_InvalidParamError("mem"); + return (rwops); + } + if (!size) { + SDL_InvalidParamError("size"); + return (rwops); + } + + rwops = SDL_AllocRW(); + if (rwops != NULL) { + rwops->size = mem_size; + rwops->seek = mem_seek; + rwops->read = mem_read; + rwops->write = mem_write; + rwops->close = mem_close; + rwops->hidden.mem.base = (Uint8 *) mem; + rwops->hidden.mem.here = rwops->hidden.mem.base; + rwops->hidden.mem.stop = rwops->hidden.mem.base + size; + rwops->type = SDL_RWOPS_MEMORY; + } + return (rwops); +} + +SDL_RWops * +SDL_RWFromConstMem(const void *mem, int size) +{ + SDL_RWops *rwops = NULL; + if (!mem) { + SDL_InvalidParamError("mem"); + return (rwops); + } + if (!size) { + SDL_InvalidParamError("size"); + return (rwops); + } + + rwops = SDL_AllocRW(); + if (rwops != NULL) { + rwops->size = mem_size; + rwops->seek = mem_seek; + rwops->read = mem_read; + rwops->write = mem_writeconst; + rwops->close = mem_close; + rwops->hidden.mem.base = (Uint8 *) mem; + rwops->hidden.mem.here = rwops->hidden.mem.base; + rwops->hidden.mem.stop = rwops->hidden.mem.base + size; + rwops->type = SDL_RWOPS_MEMORY_RO; + } + return (rwops); +} + +SDL_RWops * +SDL_AllocRW(void) +{ + SDL_RWops *area; + + area = (SDL_RWops *) SDL_malloc(sizeof *area); + if (area == NULL) { + SDL_OutOfMemory(); + } else { + area->type = SDL_RWOPS_UNKNOWN; + } + return (area); +} + +void +SDL_FreeRW(SDL_RWops * area) +{ + SDL_free(area); +} + +/* Functions for dynamically reading and writing endian-specific values */ + +Uint8 +SDL_ReadU8(SDL_RWops * src) +{ + Uint8 value = 0; + + SDL_RWread(src, &value, (sizeof value), 1); + return value; +} + +Uint16 +SDL_ReadLE16(SDL_RWops * src) +{ + Uint16 value = 0; + + SDL_RWread(src, &value, (sizeof value), 1); + return (SDL_SwapLE16(value)); +} + +Uint16 +SDL_ReadBE16(SDL_RWops * src) +{ + Uint16 value = 0; + + SDL_RWread(src, &value, (sizeof value), 1); + return (SDL_SwapBE16(value)); +} + +Uint32 +SDL_ReadLE32(SDL_RWops * src) +{ + Uint32 value = 0; + + SDL_RWread(src, &value, (sizeof value), 1); + return (SDL_SwapLE32(value)); +} + +Uint32 +SDL_ReadBE32(SDL_RWops * src) +{ + Uint32 value = 0; + + SDL_RWread(src, &value, (sizeof value), 1); + return (SDL_SwapBE32(value)); +} + +Uint64 +SDL_ReadLE64(SDL_RWops * src) +{ + Uint64 value = 0; + + SDL_RWread(src, &value, (sizeof value), 1); + return (SDL_SwapLE64(value)); +} + +Uint64 +SDL_ReadBE64(SDL_RWops * src) +{ + Uint64 value = 0; + + SDL_RWread(src, &value, (sizeof value), 1); + return (SDL_SwapBE64(value)); +} + +size_t +SDL_WriteU8(SDL_RWops * dst, Uint8 value) +{ + return (SDL_RWwrite(dst, &value, (sizeof value), 1)); +} + +size_t +SDL_WriteLE16(SDL_RWops * dst, Uint16 value) +{ + value = SDL_SwapLE16(value); + return (SDL_RWwrite(dst, &value, (sizeof value), 1)); +} + +size_t +SDL_WriteBE16(SDL_RWops * dst, Uint16 value) +{ + value = SDL_SwapBE16(value); + return (SDL_RWwrite(dst, &value, (sizeof value), 1)); +} + +size_t +SDL_WriteLE32(SDL_RWops * dst, Uint32 value) +{ + value = SDL_SwapLE32(value); + return (SDL_RWwrite(dst, &value, (sizeof value), 1)); +} + +size_t +SDL_WriteBE32(SDL_RWops * dst, Uint32 value) +{ + value = SDL_SwapBE32(value); + return (SDL_RWwrite(dst, &value, (sizeof value), 1)); +} + +size_t +SDL_WriteLE64(SDL_RWops * dst, Uint64 value) +{ + value = SDL_SwapLE64(value); + return (SDL_RWwrite(dst, &value, (sizeof value), 1)); +} + +size_t +SDL_WriteBE64(SDL_RWops * dst, Uint64 value) +{ + value = SDL_SwapBE64(value); + return (SDL_RWwrite(dst, &value, (sizeof value), 1)); +} + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c index 912be0524..414e59135 100644 --- a/src/joystick/SDL_gamecontroller.c +++ b/src/joystick/SDL_gamecontroller.c @@ -1,1249 +1,1249 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2013 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_config.h" - -/* This is the game controller API for Simple DirectMedia Layer */ - -#include "SDL_events.h" -#include "SDL_assert.h" -#include "SDL_sysjoystick.h" -#include "SDL_hints.h" -#include "SDL_gamecontrollerdb.h" - -#if !SDL_EVENTS_DISABLED -#include "../events/SDL_events_c.h" -#endif -#define ABS(_x) ((_x) < 0 ? -(_x) : (_x)) - - -/* a list of currently opened game controllers */ -static SDL_GameController *SDL_gamecontrollers = NULL; - -/* keep track of the hat and mask value that transforms this hat movement into a button/axis press */ -struct _SDL_HatMapping -{ - int hat; - Uint8 mask; -}; - -#define k_nMaxReverseEntries 20 - -/** - * We are encoding the "HAT" as 0xhm. where h == hat ID and m == mask - * MAX 4 hats supported - */ -#define k_nMaxHatEntries 0x3f + 1 - -/* our in memory mapping db between joystick objects and controller mappings*/ -struct _SDL_ControllerMapping -{ - SDL_JoystickGUID guid; - const char *name; - - /* mapping of axis/button id to controller version */ - int axes[SDL_CONTROLLER_AXIS_MAX]; - int buttonasaxis[SDL_CONTROLLER_AXIS_MAX]; - - int buttons[SDL_CONTROLLER_BUTTON_MAX]; - int axesasbutton[SDL_CONTROLLER_BUTTON_MAX]; - struct _SDL_HatMapping hatasbutton[SDL_CONTROLLER_BUTTON_MAX]; - - /* reverse mapping, joystick indices to buttons */ - SDL_GameControllerAxis raxes[k_nMaxReverseEntries]; - SDL_GameControllerAxis rbuttonasaxis[k_nMaxReverseEntries]; - - SDL_GameControllerButton rbuttons[k_nMaxReverseEntries]; - SDL_GameControllerButton raxesasbutton[k_nMaxReverseEntries]; - SDL_GameControllerButton rhatasbutton[k_nMaxHatEntries]; - -}; - - -/* our hard coded list of mapping support */ -typedef struct _ControllerMapping_t -{ - SDL_JoystickGUID guid; - char *name; - char *mapping; - struct _ControllerMapping_t *next; -} ControllerMapping_t; - -static ControllerMapping_t *s_pSupportedControllers = NULL; -#ifdef SDL_JOYSTICK_DINPUT -static ControllerMapping_t *s_pXInputMapping = NULL; -#endif - -/* The SDL game controller structure */ -struct _SDL_GameController -{ - SDL_Joystick *joystick; /* underlying joystick device */ - int ref_count; - Uint8 hatState[4]; /* the current hat state for this controller */ - struct _SDL_ControllerMapping mapping; /* the mapping object for this controller */ - struct _SDL_GameController *next; /* pointer to next game controller we have allocated */ -}; - - -int SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value); -int SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state); - -/* - * Event filter to fire controller events from joystick ones - */ -int SDL_GameControllerEventWatcher(void *userdata, SDL_Event * event) -{ - switch( event->type ) - { - case SDL_JOYAXISMOTION: - { - SDL_GameController *controllerlist; - - if ( event->jaxis.axis >= k_nMaxReverseEntries ) break; - - controllerlist = SDL_gamecontrollers; - while ( controllerlist ) - { - if ( controllerlist->joystick->instance_id == event->jaxis.which ) - { - if ( controllerlist->mapping.raxes[event->jaxis.axis] >= 0 ) /* simple axis to axis, send it through */ - { - SDL_GameControllerAxis axis = controllerlist->mapping.raxes[event->jaxis.axis]; - Sint16 value = event->jaxis.value; - switch (axis) - { - case SDL_CONTROLLER_AXIS_TRIGGERLEFT: - case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: - /* Shift it to be 0 - 32767. */ - value = value / 2 + 16384; - default: - break; - } - SDL_PrivateGameControllerAxis( controllerlist, axis, value ); - } - else if ( controllerlist->mapping.raxesasbutton[event->jaxis.axis] >= 0 ) /* simulate an axis as a button */ - { - SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.raxesasbutton[event->jaxis.axis], ABS(event->jaxis.value) > 32768/2 ? SDL_PRESSED : SDL_RELEASED ); - } - break; - } - controllerlist = controllerlist->next; - } - } - break; - case SDL_JOYBUTTONDOWN: - case SDL_JOYBUTTONUP: - { - SDL_GameController *controllerlist; - - if ( event->jbutton.button >= k_nMaxReverseEntries ) break; - - controllerlist = SDL_gamecontrollers; - while ( controllerlist ) - { - if ( controllerlist->joystick->instance_id == event->jbutton.which ) - { - if ( controllerlist->mapping.rbuttons[event->jbutton.button] >= 0 ) /* simple button as button */ - { - SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rbuttons[event->jbutton.button], event->jbutton.state ); - } - else if ( controllerlist->mapping.rbuttonasaxis[event->jbutton.button] >= 0 ) /* an button pretending to be an axis */ - { - SDL_PrivateGameControllerAxis( controllerlist, controllerlist->mapping.rbuttonasaxis[event->jbutton.button], event->jbutton.state > 0 ? 32767 : 0 ); - } - break; - } - controllerlist = controllerlist->next; - } - } - break; - case SDL_JOYHATMOTION: - { - SDL_GameController *controllerlist; - - if ( event->jhat.hat >= 4 ) break; - - controllerlist = SDL_gamecontrollers; - while ( controllerlist ) - { - if ( controllerlist->joystick->instance_id == event->jhat.which ) - { - Uint8 bSame = controllerlist->hatState[event->jhat.hat] & event->jhat.value; - /* Get list of removed bits (button release) */ - Uint8 bChanged = controllerlist->hatState[event->jhat.hat] ^ bSame; - /* the hat idx in the high nibble */ - int bHighHat = event->jhat.hat << 4; - - if ( bChanged & SDL_HAT_DOWN ) - SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_DOWN], SDL_RELEASED ); - if ( bChanged & SDL_HAT_UP ) - SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_UP], SDL_RELEASED ); - if ( bChanged & SDL_HAT_LEFT ) - SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_LEFT], SDL_RELEASED ); - if ( bChanged & SDL_HAT_RIGHT ) - SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_RIGHT], SDL_RELEASED ); - - /* Get list of added bits (button press) */ - bChanged = event->jhat.value ^ bSame; - - if ( bChanged & SDL_HAT_DOWN ) - SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_DOWN], SDL_PRESSED ); - if ( bChanged & SDL_HAT_UP ) - SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_UP], SDL_PRESSED ); - if ( bChanged & SDL_HAT_LEFT ) - SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_LEFT], SDL_PRESSED ); - if ( bChanged & SDL_HAT_RIGHT ) - SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_RIGHT], SDL_PRESSED ); - - /* update our state cache */ - controllerlist->hatState[event->jhat.hat] = event->jhat.value; - - break; - } - controllerlist = controllerlist->next; - } - } - break; - case SDL_JOYDEVICEADDED: - { - if ( SDL_IsGameController(event->jdevice.which ) ) - { - SDL_Event deviceevent; - deviceevent.type = SDL_CONTROLLERDEVICEADDED; - deviceevent.cdevice.which = event->jdevice.which; - SDL_PushEvent(&deviceevent); - } - } - break; - case SDL_JOYDEVICEREMOVED: - { - SDL_GameController *controllerlist = SDL_gamecontrollers; - while ( controllerlist ) - { - if ( controllerlist->joystick->instance_id == event->jdevice.which ) - { - SDL_Event deviceevent; - deviceevent.type = SDL_CONTROLLERDEVICEREMOVED; - deviceevent.cdevice.which = event->jdevice.which; - SDL_PushEvent(&deviceevent); - break; - } - controllerlist = controllerlist->next; - } - } - break; - default: - break; - } - - return 1; -} - -/* - * Helper function to scan the mappings database for a controller with the specified GUID - */ -ControllerMapping_t *SDL_PrivateGetControllerMappingForGUID(SDL_JoystickGUID *guid) -{ - ControllerMapping_t *pSupportedController = s_pSupportedControllers; - while ( pSupportedController ) - { - if ( !SDL_memcmp( guid, &pSupportedController->guid, sizeof(*guid) ) ) - { - return pSupportedController; - } - pSupportedController = pSupportedController->next; - } - return NULL; -} - -/* - * Helper function to determine pre-calculated offset to certain joystick mappings - */ -ControllerMapping_t *SDL_PrivateGetControllerMapping(int device_index) -{ -#ifdef SDL_JOYSTICK_DINPUT - if ( SDL_SYS_IsXInputDeviceIndex(device_index) && s_pXInputMapping ) - { - return s_pXInputMapping; - } - else -#endif - { - SDL_JoystickGUID jGUID = SDL_JoystickGetDeviceGUID( device_index ); - return SDL_PrivateGetControllerMappingForGUID(&jGUID); - } - - return NULL; -} - -static const char* map_StringForControllerAxis[] = { - "leftx", - "lefty", - "rightx", - "righty", - "lefttrigger", - "righttrigger", - NULL -}; - -/* - * convert a string to its enum equivalent - */ -SDL_GameControllerAxis SDL_GameControllerGetAxisFromString( const char *pchString ) -{ - int entry; - if ( !pchString || !pchString[0] ) - return SDL_CONTROLLER_AXIS_INVALID; - - for ( entry = 0; map_StringForControllerAxis[entry]; ++entry) - { - if ( !SDL_strcasecmp( pchString, map_StringForControllerAxis[entry] ) ) - return entry; - } - return SDL_CONTROLLER_AXIS_INVALID; -} - -/* - * convert an enum to its string equivalent - */ -const char* SDL_GameControllerGetStringForAxis( SDL_GameControllerAxis axis ) -{ - if (axis > SDL_CONTROLLER_AXIS_INVALID && axis < SDL_CONTROLLER_AXIS_MAX) - { - return map_StringForControllerAxis[axis]; - } - return NULL; -} - -static const char* map_StringForControllerButton[] = { - "a", - "b", - "x", - "y", - "back", - "guide", - "start", - "leftstick", - "rightstick", - "leftshoulder", - "rightshoulder", - "dpup", - "dpdown", - "dpleft", - "dpright", - NULL -}; - -/* - * convert a string to its enum equivalent - */ -SDL_GameControllerButton SDL_GameControllerGetButtonFromString( const char *pchString ) -{ - int entry; - if ( !pchString || !pchString[0] ) - return SDL_CONTROLLER_BUTTON_INVALID; - - for ( entry = 0; map_StringForControllerButton[entry]; ++entry) - { - if ( !SDL_strcasecmp( pchString, map_StringForControllerButton[entry] ) ) - return entry; - } - return SDL_CONTROLLER_BUTTON_INVALID; -} - -/* - * convert an enum to its string equivalent - */ -const char* SDL_GameControllerGetStringForButton( SDL_GameControllerButton axis ) -{ - if (axis > SDL_CONTROLLER_BUTTON_INVALID && axis < SDL_CONTROLLER_BUTTON_MAX) - { - return map_StringForControllerButton[axis]; - } - return NULL; -} - -/* - * given a controller button name and a joystick name update our mapping structure with it - */ -void SDL_PrivateGameControllerParseButton( const char *szGameButton, const char *szJoystickButton, struct _SDL_ControllerMapping *pMapping ) -{ - int iSDLButton = 0; - SDL_GameControllerButton button; - SDL_GameControllerAxis axis; - button = SDL_GameControllerGetButtonFromString( szGameButton ); - axis = SDL_GameControllerGetAxisFromString( szGameButton ); - iSDLButton = SDL_atoi( &szJoystickButton[1] ); - - if ( szJoystickButton[0] == 'a' ) - { - if ( iSDLButton >= k_nMaxReverseEntries ) - { - SDL_SetError("Axis index too large: %d", iSDLButton ); - return; - } - if ( axis != SDL_CONTROLLER_AXIS_INVALID ) - { - pMapping->axes[ axis ] = iSDLButton; - pMapping->raxes[ iSDLButton ] = axis; - } - else if ( button != SDL_CONTROLLER_BUTTON_INVALID ) - { - pMapping->axesasbutton[ button ] = iSDLButton; - pMapping->raxesasbutton[ iSDLButton ] = button; - } - else - { - SDL_assert( !"How did we get here?" ); - } - - } - else if ( szJoystickButton[0] == 'b' ) - { - if ( iSDLButton >= k_nMaxReverseEntries ) - { - SDL_SetError("Button index too large: %d", iSDLButton ); - return; - } - if ( button != SDL_CONTROLLER_BUTTON_INVALID ) - { - pMapping->buttons[ button ] = iSDLButton; - pMapping->rbuttons[ iSDLButton ] = button; - } - else if ( axis != SDL_CONTROLLER_AXIS_INVALID ) - { - pMapping->buttonasaxis[ axis ] = iSDLButton; - pMapping->rbuttonasaxis[ iSDLButton ] = axis; - } - else - { - SDL_assert( !"How did we get here?" ); - } - } - else if ( szJoystickButton[0] == 'h' ) - { - int hat = SDL_atoi( &szJoystickButton[1] ); - int mask = SDL_atoi( &szJoystickButton[3] ); - if (hat >= 4) { - SDL_SetError("Hat index too large: %d", iSDLButton ); - } - - if ( button != SDL_CONTROLLER_BUTTON_INVALID ) - { - int ridx; - pMapping->hatasbutton[ button ].hat = hat; - pMapping->hatasbutton[ button ].mask = mask; - ridx = (hat << 4) | mask; - pMapping->rhatasbutton[ ridx ] = button; - } - else if ( axis != SDL_CONTROLLER_AXIS_INVALID ) - { - SDL_assert( !"Support hat as axis" ); - } - else - { - SDL_assert( !"How did we get here?" ); - } - } -} - - -/* - * given a controller mapping string update our mapping object - */ -static void -SDL_PrivateGameControllerParseControllerConfigString( struct _SDL_ControllerMapping *pMapping, const char *pchString ) -{ - char szGameButton[20]; - char szJoystickButton[20]; - SDL_bool bGameButton = SDL_TRUE; - int i = 0; - const char *pchPos = pchString; - - SDL_memset( szGameButton, 0x0, sizeof(szGameButton) ); - SDL_memset( szJoystickButton, 0x0, sizeof(szJoystickButton) ); - - while ( pchPos && *pchPos ) - { - if ( *pchPos == ':' ) - { - i = 0; - bGameButton = SDL_FALSE; - } - else if ( *pchPos == ' ' ) - { - - } - else if ( *pchPos == ',' ) - { - i = 0; - bGameButton = SDL_TRUE; - SDL_PrivateGameControllerParseButton( szGameButton, szJoystickButton, pMapping ); - SDL_memset( szGameButton, 0x0, sizeof(szGameButton) ); - SDL_memset( szJoystickButton, 0x0, sizeof(szJoystickButton) ); - - } - else if ( bGameButton ) - { - if ( i >= sizeof(szGameButton)) - { - SDL_SetError( "Button name too large: %s", szGameButton ); - return; - } - szGameButton[i] = *pchPos; - i++; - } - else - { - if ( i >= sizeof(szJoystickButton)) - { - SDL_SetError( "Joystick button name too large: %s", szJoystickButton ); - return; - } - szJoystickButton[i] = *pchPos; - i++; - } - pchPos++; - } - - SDL_PrivateGameControllerParseButton( szGameButton, szJoystickButton, pMapping ); - -} - -/* - * Make a new button mapping struct - */ -void SDL_PrivateLoadButtonMapping( struct _SDL_ControllerMapping *pMapping, SDL_JoystickGUID guid, const char *pchName, const char *pchMapping ) -{ - int j; - - pMapping->guid = guid; - pMapping->name = pchName; - - /* set all the button mappings to non defaults */ - for ( j = 0; j < SDL_CONTROLLER_AXIS_MAX; j++ ) - { - pMapping->axes[j] = -1; - pMapping->buttonasaxis[j] = -1; - } - for ( j = 0; j < SDL_CONTROLLER_BUTTON_MAX; j++ ) - { - pMapping->buttons[j] = -1; - pMapping->axesasbutton[j] = -1; - pMapping->hatasbutton[j].hat = -1; - } - - for ( j = 0; j < k_nMaxReverseEntries; j++ ) - { - pMapping->raxes[j] = SDL_CONTROLLER_AXIS_INVALID; - pMapping->rbuttonasaxis[j] = SDL_CONTROLLER_AXIS_INVALID; - pMapping->rbuttons[j] = SDL_CONTROLLER_BUTTON_INVALID; - pMapping->raxesasbutton[j] = SDL_CONTROLLER_BUTTON_INVALID; - } - - for (j = 0; j < k_nMaxHatEntries; j++) - { - pMapping->rhatasbutton[j] = SDL_CONTROLLER_BUTTON_INVALID; - } - - SDL_PrivateGameControllerParseControllerConfigString( pMapping, pchMapping ); -} - - -/* - * grab the guid string from a mapping string - */ -char *SDL_PrivateGetControllerGUIDFromMappingString( const char *pMapping ) -{ - const char *pFirstComma = SDL_strchr( pMapping, ',' ); - if ( pFirstComma ) - { - char *pchGUID = SDL_malloc( pFirstComma - pMapping + 1 ); - if ( !pchGUID ) - { - SDL_OutOfMemory(); - return NULL; - } - SDL_memcpy( pchGUID, pMapping, pFirstComma - pMapping ); - pchGUID[ pFirstComma - pMapping ] = 0; - return pchGUID; - } - return NULL; -} - - -/* - * grab the name string from a mapping string - */ -char *SDL_PrivateGetControllerNameFromMappingString( const char *pMapping ) -{ - const char *pFirstComma, *pSecondComma; - char *pchName; - - pFirstComma = SDL_strchr( pMapping, ',' ); - if ( !pFirstComma ) - return NULL; - - pSecondComma = SDL_strchr( pFirstComma + 1, ',' ); - if ( !pSecondComma ) - return NULL; - - pchName = SDL_malloc( pSecondComma - pFirstComma ); - if ( !pchName ) - { - SDL_OutOfMemory(); - return NULL; - } - SDL_memcpy( pchName, pFirstComma + 1, pSecondComma - pFirstComma ); - pchName[ pSecondComma - pFirstComma - 1 ] = 0; - return pchName; -} - - -/* - * grab the button mapping string from a mapping string - */ -char *SDL_PrivateGetControllerMappingFromMappingString( const char *pMapping ) -{ - const char *pFirstComma, *pSecondComma; - - pFirstComma = SDL_strchr( pMapping, ',' ); - if ( !pFirstComma ) - return NULL; - - pSecondComma = SDL_strchr( pFirstComma + 1, ',' ); - if ( !pSecondComma ) - return NULL; - - return SDL_strdup(pSecondComma + 1); /* mapping is everything after the 3rd comma */ -} - -void SDL_PrivateGameControllerRefreshMapping( ControllerMapping_t *pControllerMapping ) -{ - SDL_GameController *gamecontrollerlist = SDL_gamecontrollers; - while ( gamecontrollerlist ) - { - if ( !SDL_memcmp( &gamecontrollerlist->mapping.guid, &pControllerMapping->guid, sizeof(pControllerMapping->guid) ) ) - { - SDL_Event event; - event.type = SDL_CONTROLLERDEVICEREMAPPED; - event.cdevice.which = gamecontrollerlist->joystick->instance_id; - SDL_PushEvent(&event); - - /* Not really threadsafe. Should this lock access within SDL_GameControllerEventWatcher? */ - SDL_PrivateLoadButtonMapping(&gamecontrollerlist->mapping, pControllerMapping->guid, pControllerMapping->name, pControllerMapping->mapping); - } - - gamecontrollerlist = gamecontrollerlist->next; - } -} - -/* - * Add or update an entry into the Mappings Database - */ -int -SDL_GameControllerAddMapping( const char *mappingString ) -{ - char *pchGUID; - char *pchName; - char *pchMapping; - SDL_JoystickGUID jGUID; - ControllerMapping_t *pControllerMapping; -#ifdef SDL_JOYSTICK_DINPUT - SDL_bool is_xinput_mapping = SDL_FALSE; -#endif - - pchGUID = SDL_PrivateGetControllerGUIDFromMappingString( mappingString ); - if (!pchGUID) { - return -1; - } -#ifdef SDL_JOYSTICK_DINPUT - if ( !SDL_strcasecmp( pchGUID, "xinput" ) ) { - is_xinput_mapping = SDL_TRUE; - } -#endif - jGUID = SDL_JoystickGetGUIDFromString(pchGUID); - SDL_free(pchGUID); - - pControllerMapping = SDL_PrivateGetControllerMappingForGUID(&jGUID); - - pchName = SDL_PrivateGetControllerNameFromMappingString( mappingString ); - if (!pchName) return -1; - - pchMapping = SDL_PrivateGetControllerMappingFromMappingString( mappingString ); - if (!pchMapping) { - SDL_free( pchName ); - return -1; - } - - if (pControllerMapping) { - /* Update existing mapping */ - SDL_free( pControllerMapping->name ); - pControllerMapping->name = pchName; - SDL_free( pControllerMapping->mapping ); - pControllerMapping->mapping = pchMapping; - /* refresh open controllers */ - SDL_PrivateGameControllerRefreshMapping( pControllerMapping ); - return 0; - } else { - pControllerMapping = SDL_malloc( sizeof(*pControllerMapping) ); - if (!pControllerMapping) { - SDL_free( pchName ); - SDL_free( pchMapping ); - return SDL_OutOfMemory(); - } -#ifdef SDL_JOYSTICK_DINPUT - if ( is_xinput_mapping ) - { - s_pXInputMapping = pControllerMapping; - } -#endif - pControllerMapping->guid = jGUID; - pControllerMapping->name = pchName; - pControllerMapping->mapping = pchMapping; - pControllerMapping->next = s_pSupportedControllers; - s_pSupportedControllers = pControllerMapping; - return 1; - } -} - -/* - * Get the mapping string for this GUID - */ -char * -SDL_GameControllerMappingForGUID( SDL_JoystickGUID guid ) -{ - char *pMappingString = NULL; - ControllerMapping_t *mapping = SDL_PrivateGetControllerMappingForGUID(&guid); - if (mapping) { - char pchGUID[33]; - size_t needed; - SDL_JoystickGetGUIDString(guid, pchGUID, sizeof(pchGUID)); - /* allocate enough memory for GUID + ',' + name + ',' + mapping + \0 */ - needed = SDL_strlen(pchGUID) + 1 + SDL_strlen(mapping->name) + 1 + SDL_strlen(mapping->mapping) + 1; - pMappingString = SDL_malloc( needed ); - SDL_snprintf( pMappingString, needed, "%s,%s,%s", pchGUID, mapping->name, mapping->mapping ); - } - return pMappingString; -} - -/* - * Get the mapping string for this device - */ -char * -SDL_GameControllerMapping( SDL_GameController * gamecontroller ) -{ - return SDL_GameControllerMappingForGUID( gamecontroller->mapping.guid ); -} - -static void -SDL_GameControllerLoadHints() -{ - const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG); - if ( hint && hint[0] ) { - int nchHints = SDL_strlen( hint ); - char *pUserMappings = SDL_malloc( nchHints + 1 ); - char *pTempMappings = pUserMappings; - SDL_memcpy( pUserMappings, hint, nchHints ); - while ( pUserMappings ) { - char *pchNewLine = NULL; - - pchNewLine = SDL_strchr( pUserMappings, '\n' ); - if ( pchNewLine ) - *pchNewLine = '\0'; - - SDL_GameControllerAddMapping( pUserMappings ); - - if ( pchNewLine ) - pUserMappings = pchNewLine + 1; - else - pUserMappings = NULL; - } - SDL_free(pTempMappings); - } -} - -/* - * Initialize the game controller system, mostly load our DB of controller config mappings - */ -int -SDL_GameControllerInit(void) -{ - int i = 0; - const char *pMappingString = NULL; - s_pSupportedControllers = NULL; - pMappingString = s_ControllerMappings[i]; - while ( pMappingString ) - { - SDL_GameControllerAddMapping( pMappingString ); - - i++; - pMappingString = s_ControllerMappings[i]; - } - - /* load in any user supplied config */ - SDL_GameControllerLoadHints(); - - /* watch for joy events and fire controller ones if needed */ - SDL_AddEventWatch( SDL_GameControllerEventWatcher, NULL ); - - return (0); -} - - -/* - * Get the implementation dependent name of a controller - */ -const char * -SDL_GameControllerNameForIndex(int device_index) -{ - ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index); - if ( pSupportedController ) - { - return pSupportedController->name; - } - return NULL; -} - - -/* - * Return 1 if the joystick at this device index is a supported controller - */ -SDL_bool -SDL_IsGameController(int device_index) -{ - ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index); - if ( pSupportedController ) - { - return SDL_TRUE; - } - - return SDL_FALSE; -} - -/* - * Open a controller for use - the index passed as an argument refers to - * the N'th controller on the system. This index is the value which will - * identify this controller in future controller events. - * - * This function returns a controller identifier, or NULL if an error occurred. - */ -SDL_GameController * -SDL_GameControllerOpen(int device_index) -{ - SDL_GameController *gamecontroller; - SDL_GameController *gamecontrollerlist; - ControllerMapping_t *pSupportedController = NULL; - - if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) { - SDL_SetError("There are %d joysticks available", SDL_NumJoysticks()); - return (NULL); - } - - gamecontrollerlist = SDL_gamecontrollers; - /* If the controller is already open, return it */ - while ( gamecontrollerlist ) - { - if ( SDL_SYS_GetInstanceIdOfDeviceIndex(device_index) == gamecontrollerlist->joystick->instance_id ) { - gamecontroller = gamecontrollerlist; - ++gamecontroller->ref_count; - return (gamecontroller); - } - gamecontrollerlist = gamecontrollerlist->next; - } - - /* Find a controller mapping */ - pSupportedController = SDL_PrivateGetControllerMapping(device_index); - if ( !pSupportedController ) { - SDL_SetError("Couldn't find mapping for device (%d)", device_index ); - return (NULL); - } - - /* Create and initialize the joystick */ - gamecontroller = (SDL_GameController *) SDL_malloc((sizeof *gamecontroller)); - if (gamecontroller == NULL) { - SDL_OutOfMemory(); - return NULL; - } - - SDL_memset(gamecontroller, 0, (sizeof *gamecontroller)); - gamecontroller->joystick = SDL_JoystickOpen(device_index); - if ( !gamecontroller->joystick ) { - SDL_free(gamecontroller); - return NULL; - } - - SDL_PrivateLoadButtonMapping( &gamecontroller->mapping, pSupportedController->guid, pSupportedController->name, pSupportedController->mapping ); - - /* Add joystick to list */ - ++gamecontroller->ref_count; - /* Link the joystick in the list */ - gamecontroller->next = SDL_gamecontrollers; - SDL_gamecontrollers = gamecontroller; - - SDL_SYS_JoystickUpdate( gamecontroller->joystick ); - - return (gamecontroller); -} - -/* - * Manually pump for controller updates. - */ -void -SDL_GameControllerUpdate(void) -{ - /* Just for API completeness; the joystick API does all the work. */ - SDL_JoystickUpdate(); -} - - -/* - * Get the current state of an axis control on a controller - */ -Sint16 -SDL_GameControllerGetAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis) -{ - if ( !gamecontroller ) - return 0; - - if (gamecontroller->mapping.axes[axis] >= 0 ) - { - Sint16 value = ( SDL_JoystickGetAxis( gamecontroller->joystick, gamecontroller->mapping.axes[axis]) ); - switch (axis) - { - case SDL_CONTROLLER_AXIS_TRIGGERLEFT: - case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: - /* Shift it to be 0 - 32767. */ - value = value / 2 + 16384; - default: - break; - } - return value; - } - else if (gamecontroller->mapping.buttonasaxis[axis] >= 0 ) - { - Uint8 value; - value = SDL_JoystickGetButton( gamecontroller->joystick, gamecontroller->mapping.buttonasaxis[axis] ); - if ( value > 0 ) - return 32767; - return 0; - } - return 0; -} - - -/* - * Get the current state of a button on a controller - */ -Uint8 -SDL_GameControllerGetButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button) -{ - if ( !gamecontroller ) - return 0; - - if ( gamecontroller->mapping.buttons[button] >= 0 ) - { - return ( SDL_JoystickGetButton( gamecontroller->joystick, gamecontroller->mapping.buttons[button] ) ); - } - else if ( gamecontroller->mapping.axesasbutton[button] >= 0 ) - { - Sint16 value; - value = SDL_JoystickGetAxis( gamecontroller->joystick, gamecontroller->mapping.axesasbutton[button] ); - if ( ABS(value) > 32768/2 ) - return 1; - return 0; - } - else if ( gamecontroller->mapping.hatasbutton[button].hat >= 0 ) - { - Uint8 value; - value = SDL_JoystickGetHat( gamecontroller->joystick, gamecontroller->mapping.hatasbutton[button].hat ); - - if ( value & gamecontroller->mapping.hatasbutton[button].mask ) - return 1; - return 0; - } - - return 0; -} - -/* - * Return if the joystick in question is currently attached to the system, - * \return 0 if not plugged in, 1 if still present. - */ -SDL_bool -SDL_GameControllerGetAttached( SDL_GameController * gamecontroller ) -{ - if ( !gamecontroller ) - return SDL_FALSE; - - return SDL_JoystickGetAttached(gamecontroller->joystick); -} - - -/* - * Get the number of multi-dimensional axis controls on a joystick - */ -const char * -SDL_GameControllerName(SDL_GameController * gamecontroller) -{ - if ( !gamecontroller ) - return NULL; - - return (gamecontroller->mapping.name); -} - - -/* - * Get the joystick for this controller - */ -SDL_Joystick *SDL_GameControllerGetJoystick(SDL_GameController * gamecontroller) -{ - if ( !gamecontroller ) - return NULL; - - return gamecontroller->joystick; -} - -/** - * Get the SDL joystick layer binding for this controller axis mapping - */ -SDL_GameControllerButtonBind SDL_GameControllerGetBindForAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis) -{ - SDL_GameControllerButtonBind bind; - SDL_memset( &bind, 0x0, sizeof(bind) ); - - if ( !gamecontroller || axis == SDL_CONTROLLER_AXIS_INVALID ) - return bind; - - if (gamecontroller->mapping.axes[axis] >= 0 ) - { - bind.bindType = SDL_CONTROLLER_BINDTYPE_AXIS; - bind.value.button = gamecontroller->mapping.axes[axis]; - } - else if (gamecontroller->mapping.buttonasaxis[axis] >= 0 ) - { - bind.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON; - bind.value.button = gamecontroller->mapping.buttonasaxis[axis]; - } - - return bind; -} - - -/** - * Get the SDL joystick layer binding for this controller button mapping - */ -SDL_GameControllerButtonBind SDL_GameControllerGetBindForButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button) -{ - SDL_GameControllerButtonBind bind; - SDL_memset( &bind, 0x0, sizeof(bind) ); - - if ( !gamecontroller || button == SDL_CONTROLLER_BUTTON_INVALID ) - return bind; - - if ( gamecontroller->mapping.buttons[button] >= 0 ) - { - bind.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON; - bind.value.button = gamecontroller->mapping.buttons[button]; - } - else if ( gamecontroller->mapping.axesasbutton[button] >= 0 ) - { - bind.bindType = SDL_CONTROLLER_BINDTYPE_AXIS; - bind.value.axis = gamecontroller->mapping.axesasbutton[button]; - } - else if ( gamecontroller->mapping.hatasbutton[button].hat >= 0 ) - { - bind.bindType = SDL_CONTROLLER_BINDTYPE_HAT; - bind.value.hat.hat = gamecontroller->mapping.hatasbutton[button].hat; - bind.value.hat.hat_mask = gamecontroller->mapping.hatasbutton[button].mask; - } - - return bind; -} - - -/* - * Close a joystick previously opened with SDL_JoystickOpen() - */ -void -SDL_GameControllerClose(SDL_GameController * gamecontroller) -{ - SDL_GameController *gamecontrollerlist, *gamecontrollerlistprev; - - if ( !gamecontroller ) - return; - - /* First decrement ref count */ - if (--gamecontroller->ref_count > 0) { - return; - } - - SDL_JoystickClose( gamecontroller->joystick ); - - gamecontrollerlist = SDL_gamecontrollers; - gamecontrollerlistprev = NULL; - while ( gamecontrollerlist ) - { - if (gamecontroller == gamecontrollerlist) - { - if ( gamecontrollerlistprev ) - { - /* unlink this entry */ - gamecontrollerlistprev->next = gamecontrollerlist->next; - } - else - { - SDL_gamecontrollers = gamecontroller->next; - } - - break; - } - gamecontrollerlistprev = gamecontrollerlist; - gamecontrollerlist = gamecontrollerlist->next; - } - - SDL_free(gamecontroller); -} - - -/* - * Quit the controller subsystem - */ -void -SDL_GameControllerQuit(void) -{ - ControllerMapping_t *pControllerMap; - while ( SDL_gamecontrollers ) - { - SDL_gamecontrollers->ref_count = 1; - SDL_GameControllerClose(SDL_gamecontrollers); - } - - while ( s_pSupportedControllers ) - { - pControllerMap = s_pSupportedControllers; - s_pSupportedControllers = s_pSupportedControllers->next; - SDL_free( pControllerMap->name ); - SDL_free( pControllerMap ); - } - - SDL_DelEventWatch( SDL_GameControllerEventWatcher, NULL ); - -} - -/* - * Event filter to transform joystick events into appropriate game controller ones - */ -int -SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value) -{ - int posted; - - /* translate the event, if desired */ - posted = 0; -#if !SDL_EVENTS_DISABLED - if (SDL_GetEventState(SDL_CONTROLLERAXISMOTION) == SDL_ENABLE) { - SDL_Event event; - event.type = SDL_CONTROLLERAXISMOTION; - event.caxis.which = gamecontroller->joystick->instance_id; - event.caxis.axis = axis; - event.caxis.value = value; - posted = SDL_PushEvent(&event) == 1; - } -#endif /* !SDL_EVENTS_DISABLED */ - return (posted); -} - - -/* - * Event filter to transform joystick events into appropriate game controller ones - */ -int -SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state) -{ - int posted; -#if !SDL_EVENTS_DISABLED - SDL_Event event; - - if ( button == SDL_CONTROLLER_BUTTON_INVALID ) - return (0); - - switch (state) { - case SDL_PRESSED: - event.type = SDL_CONTROLLERBUTTONDOWN; - break; - case SDL_RELEASED: - event.type = SDL_CONTROLLERBUTTONUP; - break; - default: - /* Invalid state -- bail */ - return (0); - } -#endif /* !SDL_EVENTS_DISABLED */ - - /* translate the event, if desired */ - posted = 0; -#if !SDL_EVENTS_DISABLED - if (SDL_GetEventState(event.type) == SDL_ENABLE) { - event.cbutton.which = gamecontroller->joystick->instance_id; - event.cbutton.button = button; - event.cbutton.state = state; - posted = SDL_PushEvent(&event) == 1; - } -#endif /* !SDL_EVENTS_DISABLED */ - return (posted); -} - -/* - * Turn off controller events - */ -int -SDL_GameControllerEventState(int state) -{ -#if SDL_EVENTS_DISABLED - return SDL_IGNORE; -#else - const Uint32 event_list[] = { - SDL_CONTROLLERAXISMOTION, SDL_CONTROLLERBUTTONDOWN, SDL_CONTROLLERBUTTONUP, - SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEREMOVED, SDL_CONTROLLERDEVICEREMAPPED, - }; - unsigned int i; - - switch (state) { - case SDL_QUERY: - state = SDL_IGNORE; - for (i = 0; i < SDL_arraysize(event_list); ++i) { - state = SDL_EventState(event_list[i], SDL_QUERY); - if (state == SDL_ENABLE) { - break; - } - } - break; - default: - for (i = 0; i < SDL_arraysize(event_list); ++i) { - SDL_EventState(event_list[i], state); - } - break; - } - return (state); -#endif /* SDL_EVENTS_DISABLED */ -} - -/* vi: set ts=4 sw=4 expandtab: */ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2013 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +/* This is the game controller API for Simple DirectMedia Layer */ + +#include "SDL_events.h" +#include "SDL_assert.h" +#include "SDL_sysjoystick.h" +#include "SDL_hints.h" +#include "SDL_gamecontrollerdb.h" + +#if !SDL_EVENTS_DISABLED +#include "../events/SDL_events_c.h" +#endif +#define ABS(_x) ((_x) < 0 ? -(_x) : (_x)) + + +/* a list of currently opened game controllers */ +static SDL_GameController *SDL_gamecontrollers = NULL; + +/* keep track of the hat and mask value that transforms this hat movement into a button/axis press */ +struct _SDL_HatMapping +{ + int hat; + Uint8 mask; +}; + +#define k_nMaxReverseEntries 20 + +/** + * We are encoding the "HAT" as 0xhm. where h == hat ID and m == mask + * MAX 4 hats supported + */ +#define k_nMaxHatEntries 0x3f + 1 + +/* our in memory mapping db between joystick objects and controller mappings*/ +struct _SDL_ControllerMapping +{ + SDL_JoystickGUID guid; + const char *name; + + /* mapping of axis/button id to controller version */ + int axes[SDL_CONTROLLER_AXIS_MAX]; + int buttonasaxis[SDL_CONTROLLER_AXIS_MAX]; + + int buttons[SDL_CONTROLLER_BUTTON_MAX]; + int axesasbutton[SDL_CONTROLLER_BUTTON_MAX]; + struct _SDL_HatMapping hatasbutton[SDL_CONTROLLER_BUTTON_MAX]; + + /* reverse mapping, joystick indices to buttons */ + SDL_GameControllerAxis raxes[k_nMaxReverseEntries]; + SDL_GameControllerAxis rbuttonasaxis[k_nMaxReverseEntries]; + + SDL_GameControllerButton rbuttons[k_nMaxReverseEntries]; + SDL_GameControllerButton raxesasbutton[k_nMaxReverseEntries]; + SDL_GameControllerButton rhatasbutton[k_nMaxHatEntries]; + +}; + + +/* our hard coded list of mapping support */ +typedef struct _ControllerMapping_t +{ + SDL_JoystickGUID guid; + char *name; + char *mapping; + struct _ControllerMapping_t *next; +} ControllerMapping_t; + +static ControllerMapping_t *s_pSupportedControllers = NULL; +#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) +static ControllerMapping_t *s_pXInputMapping = NULL; +#endif + +/* The SDL game controller structure */ +struct _SDL_GameController +{ + SDL_Joystick *joystick; /* underlying joystick device */ + int ref_count; + Uint8 hatState[4]; /* the current hat state for this controller */ + struct _SDL_ControllerMapping mapping; /* the mapping object for this controller */ + struct _SDL_GameController *next; /* pointer to next game controller we have allocated */ +}; + + +int SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value); +int SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state); + +/* + * Event filter to fire controller events from joystick ones + */ +int SDL_GameControllerEventWatcher(void *userdata, SDL_Event * event) +{ + switch( event->type ) + { + case SDL_JOYAXISMOTION: + { + SDL_GameController *controllerlist; + + if ( event->jaxis.axis >= k_nMaxReverseEntries ) break; + + controllerlist = SDL_gamecontrollers; + while ( controllerlist ) + { + if ( controllerlist->joystick->instance_id == event->jaxis.which ) + { + if ( controllerlist->mapping.raxes[event->jaxis.axis] >= 0 ) /* simple axis to axis, send it through */ + { + SDL_GameControllerAxis axis = controllerlist->mapping.raxes[event->jaxis.axis]; + Sint16 value = event->jaxis.value; + switch (axis) + { + case SDL_CONTROLLER_AXIS_TRIGGERLEFT: + case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + /* Shift it to be 0 - 32767. */ + value = value / 2 + 16384; + default: + break; + } + SDL_PrivateGameControllerAxis( controllerlist, axis, value ); + } + else if ( controllerlist->mapping.raxesasbutton[event->jaxis.axis] >= 0 ) /* simulate an axis as a button */ + { + SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.raxesasbutton[event->jaxis.axis], ABS(event->jaxis.value) > 32768/2 ? SDL_PRESSED : SDL_RELEASED ); + } + break; + } + controllerlist = controllerlist->next; + } + } + break; + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + { + SDL_GameController *controllerlist; + + if ( event->jbutton.button >= k_nMaxReverseEntries ) break; + + controllerlist = SDL_gamecontrollers; + while ( controllerlist ) + { + if ( controllerlist->joystick->instance_id == event->jbutton.which ) + { + if ( controllerlist->mapping.rbuttons[event->jbutton.button] >= 0 ) /* simple button as button */ + { + SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rbuttons[event->jbutton.button], event->jbutton.state ); + } + else if ( controllerlist->mapping.rbuttonasaxis[event->jbutton.button] >= 0 ) /* an button pretending to be an axis */ + { + SDL_PrivateGameControllerAxis( controllerlist, controllerlist->mapping.rbuttonasaxis[event->jbutton.button], event->jbutton.state > 0 ? 32767 : 0 ); + } + break; + } + controllerlist = controllerlist->next; + } + } + break; + case SDL_JOYHATMOTION: + { + SDL_GameController *controllerlist; + + if ( event->jhat.hat >= 4 ) break; + + controllerlist = SDL_gamecontrollers; + while ( controllerlist ) + { + if ( controllerlist->joystick->instance_id == event->jhat.which ) + { + Uint8 bSame = controllerlist->hatState[event->jhat.hat] & event->jhat.value; + /* Get list of removed bits (button release) */ + Uint8 bChanged = controllerlist->hatState[event->jhat.hat] ^ bSame; + /* the hat idx in the high nibble */ + int bHighHat = event->jhat.hat << 4; + + if ( bChanged & SDL_HAT_DOWN ) + SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_DOWN], SDL_RELEASED ); + if ( bChanged & SDL_HAT_UP ) + SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_UP], SDL_RELEASED ); + if ( bChanged & SDL_HAT_LEFT ) + SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_LEFT], SDL_RELEASED ); + if ( bChanged & SDL_HAT_RIGHT ) + SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_RIGHT], SDL_RELEASED ); + + /* Get list of added bits (button press) */ + bChanged = event->jhat.value ^ bSame; + + if ( bChanged & SDL_HAT_DOWN ) + SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_DOWN], SDL_PRESSED ); + if ( bChanged & SDL_HAT_UP ) + SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_UP], SDL_PRESSED ); + if ( bChanged & SDL_HAT_LEFT ) + SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_LEFT], SDL_PRESSED ); + if ( bChanged & SDL_HAT_RIGHT ) + SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_RIGHT], SDL_PRESSED ); + + /* update our state cache */ + controllerlist->hatState[event->jhat.hat] = event->jhat.value; + + break; + } + controllerlist = controllerlist->next; + } + } + break; + case SDL_JOYDEVICEADDED: + { + if ( SDL_IsGameController(event->jdevice.which ) ) + { + SDL_Event deviceevent; + deviceevent.type = SDL_CONTROLLERDEVICEADDED; + deviceevent.cdevice.which = event->jdevice.which; + SDL_PushEvent(&deviceevent); + } + } + break; + case SDL_JOYDEVICEREMOVED: + { + SDL_GameController *controllerlist = SDL_gamecontrollers; + while ( controllerlist ) + { + if ( controllerlist->joystick->instance_id == event->jdevice.which ) + { + SDL_Event deviceevent; + deviceevent.type = SDL_CONTROLLERDEVICEREMOVED; + deviceevent.cdevice.which = event->jdevice.which; + SDL_PushEvent(&deviceevent); + break; + } + controllerlist = controllerlist->next; + } + } + break; + default: + break; + } + + return 1; +} + +/* + * Helper function to scan the mappings database for a controller with the specified GUID + */ +ControllerMapping_t *SDL_PrivateGetControllerMappingForGUID(SDL_JoystickGUID *guid) +{ + ControllerMapping_t *pSupportedController = s_pSupportedControllers; + while ( pSupportedController ) + { + if ( !SDL_memcmp( guid, &pSupportedController->guid, sizeof(*guid) ) ) + { + return pSupportedController; + } + pSupportedController = pSupportedController->next; + } + return NULL; +} + +/* + * Helper function to determine pre-calculated offset to certain joystick mappings + */ +ControllerMapping_t *SDL_PrivateGetControllerMapping(int device_index) +{ +#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) + if ( SDL_SYS_IsXInputDeviceIndex(device_index) && s_pXInputMapping ) + { + return s_pXInputMapping; + } + else +#endif + { + SDL_JoystickGUID jGUID = SDL_JoystickGetDeviceGUID( device_index ); + return SDL_PrivateGetControllerMappingForGUID(&jGUID); + } + + return NULL; +} + +static const char* map_StringForControllerAxis[] = { + "leftx", + "lefty", + "rightx", + "righty", + "lefttrigger", + "righttrigger", + NULL +}; + +/* + * convert a string to its enum equivalent + */ +SDL_GameControllerAxis SDL_GameControllerGetAxisFromString( const char *pchString ) +{ + int entry; + if ( !pchString || !pchString[0] ) + return SDL_CONTROLLER_AXIS_INVALID; + + for ( entry = 0; map_StringForControllerAxis[entry]; ++entry) + { + if ( !SDL_strcasecmp( pchString, map_StringForControllerAxis[entry] ) ) + return entry; + } + return SDL_CONTROLLER_AXIS_INVALID; +} + +/* + * convert an enum to its string equivalent + */ +const char* SDL_GameControllerGetStringForAxis( SDL_GameControllerAxis axis ) +{ + if (axis > SDL_CONTROLLER_AXIS_INVALID && axis < SDL_CONTROLLER_AXIS_MAX) + { + return map_StringForControllerAxis[axis]; + } + return NULL; +} + +static const char* map_StringForControllerButton[] = { + "a", + "b", + "x", + "y", + "back", + "guide", + "start", + "leftstick", + "rightstick", + "leftshoulder", + "rightshoulder", + "dpup", + "dpdown", + "dpleft", + "dpright", + NULL +}; + +/* + * convert a string to its enum equivalent + */ +SDL_GameControllerButton SDL_GameControllerGetButtonFromString( const char *pchString ) +{ + int entry; + if ( !pchString || !pchString[0] ) + return SDL_CONTROLLER_BUTTON_INVALID; + + for ( entry = 0; map_StringForControllerButton[entry]; ++entry) + { + if ( !SDL_strcasecmp( pchString, map_StringForControllerButton[entry] ) ) + return entry; + } + return SDL_CONTROLLER_BUTTON_INVALID; +} + +/* + * convert an enum to its string equivalent + */ +const char* SDL_GameControllerGetStringForButton( SDL_GameControllerButton axis ) +{ + if (axis > SDL_CONTROLLER_BUTTON_INVALID && axis < SDL_CONTROLLER_BUTTON_MAX) + { + return map_StringForControllerButton[axis]; + } + return NULL; +} + +/* + * given a controller button name and a joystick name update our mapping structure with it + */ +void SDL_PrivateGameControllerParseButton( const char *szGameButton, const char *szJoystickButton, struct _SDL_ControllerMapping *pMapping ) +{ + int iSDLButton = 0; + SDL_GameControllerButton button; + SDL_GameControllerAxis axis; + button = SDL_GameControllerGetButtonFromString( szGameButton ); + axis = SDL_GameControllerGetAxisFromString( szGameButton ); + iSDLButton = SDL_atoi( &szJoystickButton[1] ); + + if ( szJoystickButton[0] == 'a' ) + { + if ( iSDLButton >= k_nMaxReverseEntries ) + { + SDL_SetError("Axis index too large: %d", iSDLButton ); + return; + } + if ( axis != SDL_CONTROLLER_AXIS_INVALID ) + { + pMapping->axes[ axis ] = iSDLButton; + pMapping->raxes[ iSDLButton ] = axis; + } + else if ( button != SDL_CONTROLLER_BUTTON_INVALID ) + { + pMapping->axesasbutton[ button ] = iSDLButton; + pMapping->raxesasbutton[ iSDLButton ] = button; + } + else + { + SDL_assert( !"How did we get here?" ); + } + + } + else if ( szJoystickButton[0] == 'b' ) + { + if ( iSDLButton >= k_nMaxReverseEntries ) + { + SDL_SetError("Button index too large: %d", iSDLButton ); + return; + } + if ( button != SDL_CONTROLLER_BUTTON_INVALID ) + { + pMapping->buttons[ button ] = iSDLButton; + pMapping->rbuttons[ iSDLButton ] = button; + } + else if ( axis != SDL_CONTROLLER_AXIS_INVALID ) + { + pMapping->buttonasaxis[ axis ] = iSDLButton; + pMapping->rbuttonasaxis[ iSDLButton ] = axis; + } + else + { + SDL_assert( !"How did we get here?" ); + } + } + else if ( szJoystickButton[0] == 'h' ) + { + int hat = SDL_atoi( &szJoystickButton[1] ); + int mask = SDL_atoi( &szJoystickButton[3] ); + if (hat >= 4) { + SDL_SetError("Hat index too large: %d", iSDLButton ); + } + + if ( button != SDL_CONTROLLER_BUTTON_INVALID ) + { + int ridx; + pMapping->hatasbutton[ button ].hat = hat; + pMapping->hatasbutton[ button ].mask = mask; + ridx = (hat << 4) | mask; + pMapping->rhatasbutton[ ridx ] = button; + } + else if ( axis != SDL_CONTROLLER_AXIS_INVALID ) + { + SDL_assert( !"Support hat as axis" ); + } + else + { + SDL_assert( !"How did we get here?" ); + } + } +} + + +/* + * given a controller mapping string update our mapping object + */ +static void +SDL_PrivateGameControllerParseControllerConfigString( struct _SDL_ControllerMapping *pMapping, const char *pchString ) +{ + char szGameButton[20]; + char szJoystickButton[20]; + SDL_bool bGameButton = SDL_TRUE; + int i = 0; + const char *pchPos = pchString; + + SDL_memset( szGameButton, 0x0, sizeof(szGameButton) ); + SDL_memset( szJoystickButton, 0x0, sizeof(szJoystickButton) ); + + while ( pchPos && *pchPos ) + { + if ( *pchPos == ':' ) + { + i = 0; + bGameButton = SDL_FALSE; + } + else if ( *pchPos == ' ' ) + { + + } + else if ( *pchPos == ',' ) + { + i = 0; + bGameButton = SDL_TRUE; + SDL_PrivateGameControllerParseButton( szGameButton, szJoystickButton, pMapping ); + SDL_memset( szGameButton, 0x0, sizeof(szGameButton) ); + SDL_memset( szJoystickButton, 0x0, sizeof(szJoystickButton) ); + + } + else if ( bGameButton ) + { + if ( i >= sizeof(szGameButton)) + { + SDL_SetError( "Button name too large: %s", szGameButton ); + return; + } + szGameButton[i] = *pchPos; + i++; + } + else + { + if ( i >= sizeof(szJoystickButton)) + { + SDL_SetError( "Joystick button name too large: %s", szJoystickButton ); + return; + } + szJoystickButton[i] = *pchPos; + i++; + } + pchPos++; + } + + SDL_PrivateGameControllerParseButton( szGameButton, szJoystickButton, pMapping ); + +} + +/* + * Make a new button mapping struct + */ +void SDL_PrivateLoadButtonMapping( struct _SDL_ControllerMapping *pMapping, SDL_JoystickGUID guid, const char *pchName, const char *pchMapping ) +{ + int j; + + pMapping->guid = guid; + pMapping->name = pchName; + + /* set all the button mappings to non defaults */ + for ( j = 0; j < SDL_CONTROLLER_AXIS_MAX; j++ ) + { + pMapping->axes[j] = -1; + pMapping->buttonasaxis[j] = -1; + } + for ( j = 0; j < SDL_CONTROLLER_BUTTON_MAX; j++ ) + { + pMapping->buttons[j] = -1; + pMapping->axesasbutton[j] = -1; + pMapping->hatasbutton[j].hat = -1; + } + + for ( j = 0; j < k_nMaxReverseEntries; j++ ) + { + pMapping->raxes[j] = SDL_CONTROLLER_AXIS_INVALID; + pMapping->rbuttonasaxis[j] = SDL_CONTROLLER_AXIS_INVALID; + pMapping->rbuttons[j] = SDL_CONTROLLER_BUTTON_INVALID; + pMapping->raxesasbutton[j] = SDL_CONTROLLER_BUTTON_INVALID; + } + + for (j = 0; j < k_nMaxHatEntries; j++) + { + pMapping->rhatasbutton[j] = SDL_CONTROLLER_BUTTON_INVALID; + } + + SDL_PrivateGameControllerParseControllerConfigString( pMapping, pchMapping ); +} + + +/* + * grab the guid string from a mapping string + */ +char *SDL_PrivateGetControllerGUIDFromMappingString( const char *pMapping ) +{ + const char *pFirstComma = SDL_strchr( pMapping, ',' ); + if ( pFirstComma ) + { + char *pchGUID = SDL_malloc( pFirstComma - pMapping + 1 ); + if ( !pchGUID ) + { + SDL_OutOfMemory(); + return NULL; + } + SDL_memcpy( pchGUID, pMapping, pFirstComma - pMapping ); + pchGUID[ pFirstComma - pMapping ] = 0; + return pchGUID; + } + return NULL; +} + + +/* + * grab the name string from a mapping string + */ +char *SDL_PrivateGetControllerNameFromMappingString( const char *pMapping ) +{ + const char *pFirstComma, *pSecondComma; + char *pchName; + + pFirstComma = SDL_strchr( pMapping, ',' ); + if ( !pFirstComma ) + return NULL; + + pSecondComma = SDL_strchr( pFirstComma + 1, ',' ); + if ( !pSecondComma ) + return NULL; + + pchName = SDL_malloc( pSecondComma - pFirstComma ); + if ( !pchName ) + { + SDL_OutOfMemory(); + return NULL; + } + SDL_memcpy( pchName, pFirstComma + 1, pSecondComma - pFirstComma ); + pchName[ pSecondComma - pFirstComma - 1 ] = 0; + return pchName; +} + + +/* + * grab the button mapping string from a mapping string + */ +char *SDL_PrivateGetControllerMappingFromMappingString( const char *pMapping ) +{ + const char *pFirstComma, *pSecondComma; + + pFirstComma = SDL_strchr( pMapping, ',' ); + if ( !pFirstComma ) + return NULL; + + pSecondComma = SDL_strchr( pFirstComma + 1, ',' ); + if ( !pSecondComma ) + return NULL; + + return SDL_strdup(pSecondComma + 1); /* mapping is everything after the 3rd comma */ +} + +void SDL_PrivateGameControllerRefreshMapping( ControllerMapping_t *pControllerMapping ) +{ + SDL_GameController *gamecontrollerlist = SDL_gamecontrollers; + while ( gamecontrollerlist ) + { + if ( !SDL_memcmp( &gamecontrollerlist->mapping.guid, &pControllerMapping->guid, sizeof(pControllerMapping->guid) ) ) + { + SDL_Event event; + event.type = SDL_CONTROLLERDEVICEREMAPPED; + event.cdevice.which = gamecontrollerlist->joystick->instance_id; + SDL_PushEvent(&event); + + /* Not really threadsafe. Should this lock access within SDL_GameControllerEventWatcher? */ + SDL_PrivateLoadButtonMapping(&gamecontrollerlist->mapping, pControllerMapping->guid, pControllerMapping->name, pControllerMapping->mapping); + } + + gamecontrollerlist = gamecontrollerlist->next; + } +} + +/* + * Add or update an entry into the Mappings Database + */ +int +SDL_GameControllerAddMapping( const char *mappingString ) +{ + char *pchGUID; + char *pchName; + char *pchMapping; + SDL_JoystickGUID jGUID; + ControllerMapping_t *pControllerMapping; +#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) + SDL_bool is_xinput_mapping = SDL_FALSE; +#endif + + pchGUID = SDL_PrivateGetControllerGUIDFromMappingString( mappingString ); + if (!pchGUID) { + return -1; + } +#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) + if ( !SDL_strcasecmp( pchGUID, "xinput" ) ) { + is_xinput_mapping = SDL_TRUE; + } +#endif + jGUID = SDL_JoystickGetGUIDFromString(pchGUID); + SDL_free(pchGUID); + + pControllerMapping = SDL_PrivateGetControllerMappingForGUID(&jGUID); + + pchName = SDL_PrivateGetControllerNameFromMappingString( mappingString ); + if (!pchName) return -1; + + pchMapping = SDL_PrivateGetControllerMappingFromMappingString( mappingString ); + if (!pchMapping) { + SDL_free( pchName ); + return -1; + } + + if (pControllerMapping) { + /* Update existing mapping */ + SDL_free( pControllerMapping->name ); + pControllerMapping->name = pchName; + SDL_free( pControllerMapping->mapping ); + pControllerMapping->mapping = pchMapping; + /* refresh open controllers */ + SDL_PrivateGameControllerRefreshMapping( pControllerMapping ); + return 0; + } else { + pControllerMapping = SDL_malloc( sizeof(*pControllerMapping) ); + if (!pControllerMapping) { + SDL_free( pchName ); + SDL_free( pchMapping ); + return SDL_OutOfMemory(); + } +#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) + if ( is_xinput_mapping ) + { + s_pXInputMapping = pControllerMapping; + } +#endif + pControllerMapping->guid = jGUID; + pControllerMapping->name = pchName; + pControllerMapping->mapping = pchMapping; + pControllerMapping->next = s_pSupportedControllers; + s_pSupportedControllers = pControllerMapping; + return 1; + } +} + +/* + * Get the mapping string for this GUID + */ +char * +SDL_GameControllerMappingForGUID( SDL_JoystickGUID guid ) +{ + char *pMappingString = NULL; + ControllerMapping_t *mapping = SDL_PrivateGetControllerMappingForGUID(&guid); + if (mapping) { + char pchGUID[33]; + size_t needed; + SDL_JoystickGetGUIDString(guid, pchGUID, sizeof(pchGUID)); + /* allocate enough memory for GUID + ',' + name + ',' + mapping + \0 */ + needed = SDL_strlen(pchGUID) + 1 + SDL_strlen(mapping->name) + 1 + SDL_strlen(mapping->mapping) + 1; + pMappingString = SDL_malloc( needed ); + SDL_snprintf( pMappingString, needed, "%s,%s,%s", pchGUID, mapping->name, mapping->mapping ); + } + return pMappingString; +} + +/* + * Get the mapping string for this device + */ +char * +SDL_GameControllerMapping( SDL_GameController * gamecontroller ) +{ + return SDL_GameControllerMappingForGUID( gamecontroller->mapping.guid ); +} + +static void +SDL_GameControllerLoadHints() +{ + const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG); + if ( hint && hint[0] ) { + int nchHints = SDL_strlen( hint ); + char *pUserMappings = SDL_malloc( nchHints + 1 ); + char *pTempMappings = pUserMappings; + SDL_memcpy( pUserMappings, hint, nchHints ); + while ( pUserMappings ) { + char *pchNewLine = NULL; + + pchNewLine = SDL_strchr( pUserMappings, '\n' ); + if ( pchNewLine ) + *pchNewLine = '\0'; + + SDL_GameControllerAddMapping( pUserMappings ); + + if ( pchNewLine ) + pUserMappings = pchNewLine + 1; + else + pUserMappings = NULL; + } + SDL_free(pTempMappings); + } +} + +/* + * Initialize the game controller system, mostly load our DB of controller config mappings + */ +int +SDL_GameControllerInit(void) +{ + int i = 0; + const char *pMappingString = NULL; + s_pSupportedControllers = NULL; + pMappingString = s_ControllerMappings[i]; + while ( pMappingString ) + { + SDL_GameControllerAddMapping( pMappingString ); + + i++; + pMappingString = s_ControllerMappings[i]; + } + + /* load in any user supplied config */ + SDL_GameControllerLoadHints(); + + /* watch for joy events and fire controller ones if needed */ + SDL_AddEventWatch( SDL_GameControllerEventWatcher, NULL ); + + return (0); +} + + +/* + * Get the implementation dependent name of a controller + */ +const char * +SDL_GameControllerNameForIndex(int device_index) +{ + ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index); + if ( pSupportedController ) + { + return pSupportedController->name; + } + return NULL; +} + + +/* + * Return 1 if the joystick at this device index is a supported controller + */ +SDL_bool +SDL_IsGameController(int device_index) +{ + ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index); + if ( pSupportedController ) + { + return SDL_TRUE; + } + + return SDL_FALSE; +} + +/* + * Open a controller for use - the index passed as an argument refers to + * the N'th controller on the system. This index is the value which will + * identify this controller in future controller events. + * + * This function returns a controller identifier, or NULL if an error occurred. + */ +SDL_GameController * +SDL_GameControllerOpen(int device_index) +{ + SDL_GameController *gamecontroller; + SDL_GameController *gamecontrollerlist; + ControllerMapping_t *pSupportedController = NULL; + + if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) { + SDL_SetError("There are %d joysticks available", SDL_NumJoysticks()); + return (NULL); + } + + gamecontrollerlist = SDL_gamecontrollers; + /* If the controller is already open, return it */ + while ( gamecontrollerlist ) + { + if ( SDL_SYS_GetInstanceIdOfDeviceIndex(device_index) == gamecontrollerlist->joystick->instance_id ) { + gamecontroller = gamecontrollerlist; + ++gamecontroller->ref_count; + return (gamecontroller); + } + gamecontrollerlist = gamecontrollerlist->next; + } + + /* Find a controller mapping */ + pSupportedController = SDL_PrivateGetControllerMapping(device_index); + if ( !pSupportedController ) { + SDL_SetError("Couldn't find mapping for device (%d)", device_index ); + return (NULL); + } + + /* Create and initialize the joystick */ + gamecontroller = (SDL_GameController *) SDL_malloc((sizeof *gamecontroller)); + if (gamecontroller == NULL) { + SDL_OutOfMemory(); + return NULL; + } + + SDL_memset(gamecontroller, 0, (sizeof *gamecontroller)); + gamecontroller->joystick = SDL_JoystickOpen(device_index); + if ( !gamecontroller->joystick ) { + SDL_free(gamecontroller); + return NULL; + } + + SDL_PrivateLoadButtonMapping( &gamecontroller->mapping, pSupportedController->guid, pSupportedController->name, pSupportedController->mapping ); + + /* Add joystick to list */ + ++gamecontroller->ref_count; + /* Link the joystick in the list */ + gamecontroller->next = SDL_gamecontrollers; + SDL_gamecontrollers = gamecontroller; + + SDL_SYS_JoystickUpdate( gamecontroller->joystick ); + + return (gamecontroller); +} + +/* + * Manually pump for controller updates. + */ +void +SDL_GameControllerUpdate(void) +{ + /* Just for API completeness; the joystick API does all the work. */ + SDL_JoystickUpdate(); +} + + +/* + * Get the current state of an axis control on a controller + */ +Sint16 +SDL_GameControllerGetAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis) +{ + if ( !gamecontroller ) + return 0; + + if (gamecontroller->mapping.axes[axis] >= 0 ) + { + Sint16 value = ( SDL_JoystickGetAxis( gamecontroller->joystick, gamecontroller->mapping.axes[axis]) ); + switch (axis) + { + case SDL_CONTROLLER_AXIS_TRIGGERLEFT: + case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + /* Shift it to be 0 - 32767. */ + value = value / 2 + 16384; + default: + break; + } + return value; + } + else if (gamecontroller->mapping.buttonasaxis[axis] >= 0 ) + { + Uint8 value; + value = SDL_JoystickGetButton( gamecontroller->joystick, gamecontroller->mapping.buttonasaxis[axis] ); + if ( value > 0 ) + return 32767; + return 0; + } + return 0; +} + + +/* + * Get the current state of a button on a controller + */ +Uint8 +SDL_GameControllerGetButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button) +{ + if ( !gamecontroller ) + return 0; + + if ( gamecontroller->mapping.buttons[button] >= 0 ) + { + return ( SDL_JoystickGetButton( gamecontroller->joystick, gamecontroller->mapping.buttons[button] ) ); + } + else if ( gamecontroller->mapping.axesasbutton[button] >= 0 ) + { + Sint16 value; + value = SDL_JoystickGetAxis( gamecontroller->joystick, gamecontroller->mapping.axesasbutton[button] ); + if ( ABS(value) > 32768/2 ) + return 1; + return 0; + } + else if ( gamecontroller->mapping.hatasbutton[button].hat >= 0 ) + { + Uint8 value; + value = SDL_JoystickGetHat( gamecontroller->joystick, gamecontroller->mapping.hatasbutton[button].hat ); + + if ( value & gamecontroller->mapping.hatasbutton[button].mask ) + return 1; + return 0; + } + + return 0; +} + +/* + * Return if the joystick in question is currently attached to the system, + * \return 0 if not plugged in, 1 if still present. + */ +SDL_bool +SDL_GameControllerGetAttached( SDL_GameController * gamecontroller ) +{ + if ( !gamecontroller ) + return SDL_FALSE; + + return SDL_JoystickGetAttached(gamecontroller->joystick); +} + + +/* + * Get the number of multi-dimensional axis controls on a joystick + */ +const char * +SDL_GameControllerName(SDL_GameController * gamecontroller) +{ + if ( !gamecontroller ) + return NULL; + + return (gamecontroller->mapping.name); +} + + +/* + * Get the joystick for this controller + */ +SDL_Joystick *SDL_GameControllerGetJoystick(SDL_GameController * gamecontroller) +{ + if ( !gamecontroller ) + return NULL; + + return gamecontroller->joystick; +} + +/** + * Get the SDL joystick layer binding for this controller axis mapping + */ +SDL_GameControllerButtonBind SDL_GameControllerGetBindForAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis) +{ + SDL_GameControllerButtonBind bind; + SDL_memset( &bind, 0x0, sizeof(bind) ); + + if ( !gamecontroller || axis == SDL_CONTROLLER_AXIS_INVALID ) + return bind; + + if (gamecontroller->mapping.axes[axis] >= 0 ) + { + bind.bindType = SDL_CONTROLLER_BINDTYPE_AXIS; + bind.value.button = gamecontroller->mapping.axes[axis]; + } + else if (gamecontroller->mapping.buttonasaxis[axis] >= 0 ) + { + bind.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON; + bind.value.button = gamecontroller->mapping.buttonasaxis[axis]; + } + + return bind; +} + + +/** + * Get the SDL joystick layer binding for this controller button mapping + */ +SDL_GameControllerButtonBind SDL_GameControllerGetBindForButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button) +{ + SDL_GameControllerButtonBind bind; + SDL_memset( &bind, 0x0, sizeof(bind) ); + + if ( !gamecontroller || button == SDL_CONTROLLER_BUTTON_INVALID ) + return bind; + + if ( gamecontroller->mapping.buttons[button] >= 0 ) + { + bind.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON; + bind.value.button = gamecontroller->mapping.buttons[button]; + } + else if ( gamecontroller->mapping.axesasbutton[button] >= 0 ) + { + bind.bindType = SDL_CONTROLLER_BINDTYPE_AXIS; + bind.value.axis = gamecontroller->mapping.axesasbutton[button]; + } + else if ( gamecontroller->mapping.hatasbutton[button].hat >= 0 ) + { + bind.bindType = SDL_CONTROLLER_BINDTYPE_HAT; + bind.value.hat.hat = gamecontroller->mapping.hatasbutton[button].hat; + bind.value.hat.hat_mask = gamecontroller->mapping.hatasbutton[button].mask; + } + + return bind; +} + + +/* + * Close a joystick previously opened with SDL_JoystickOpen() + */ +void +SDL_GameControllerClose(SDL_GameController * gamecontroller) +{ + SDL_GameController *gamecontrollerlist, *gamecontrollerlistprev; + + if ( !gamecontroller ) + return; + + /* First decrement ref count */ + if (--gamecontroller->ref_count > 0) { + return; + } + + SDL_JoystickClose( gamecontroller->joystick ); + + gamecontrollerlist = SDL_gamecontrollers; + gamecontrollerlistprev = NULL; + while ( gamecontrollerlist ) + { + if (gamecontroller == gamecontrollerlist) + { + if ( gamecontrollerlistprev ) + { + /* unlink this entry */ + gamecontrollerlistprev->next = gamecontrollerlist->next; + } + else + { + SDL_gamecontrollers = gamecontroller->next; + } + + break; + } + gamecontrollerlistprev = gamecontrollerlist; + gamecontrollerlist = gamecontrollerlist->next; + } + + SDL_free(gamecontroller); +} + + +/* + * Quit the controller subsystem + */ +void +SDL_GameControllerQuit(void) +{ + ControllerMapping_t *pControllerMap; + while ( SDL_gamecontrollers ) + { + SDL_gamecontrollers->ref_count = 1; + SDL_GameControllerClose(SDL_gamecontrollers); + } + + while ( s_pSupportedControllers ) + { + pControllerMap = s_pSupportedControllers; + s_pSupportedControllers = s_pSupportedControllers->next; + SDL_free( pControllerMap->name ); + SDL_free( pControllerMap ); + } + + SDL_DelEventWatch( SDL_GameControllerEventWatcher, NULL ); + +} + +/* + * Event filter to transform joystick events into appropriate game controller ones + */ +int +SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value) +{ + int posted; + + /* translate the event, if desired */ + posted = 0; +#if !SDL_EVENTS_DISABLED + if (SDL_GetEventState(SDL_CONTROLLERAXISMOTION) == SDL_ENABLE) { + SDL_Event event; + event.type = SDL_CONTROLLERAXISMOTION; + event.caxis.which = gamecontroller->joystick->instance_id; + event.caxis.axis = axis; + event.caxis.value = value; + posted = SDL_PushEvent(&event) == 1; + } +#endif /* !SDL_EVENTS_DISABLED */ + return (posted); +} + + +/* + * Event filter to transform joystick events into appropriate game controller ones + */ +int +SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state) +{ + int posted; +#if !SDL_EVENTS_DISABLED + SDL_Event event; + + if ( button == SDL_CONTROLLER_BUTTON_INVALID ) + return (0); + + switch (state) { + case SDL_PRESSED: + event.type = SDL_CONTROLLERBUTTONDOWN; + break; + case SDL_RELEASED: + event.type = SDL_CONTROLLERBUTTONUP; + break; + default: + /* Invalid state -- bail */ + return (0); + } +#endif /* !SDL_EVENTS_DISABLED */ + + /* translate the event, if desired */ + posted = 0; +#if !SDL_EVENTS_DISABLED + if (SDL_GetEventState(event.type) == SDL_ENABLE) { + event.cbutton.which = gamecontroller->joystick->instance_id; + event.cbutton.button = button; + event.cbutton.state = state; + posted = SDL_PushEvent(&event) == 1; + } +#endif /* !SDL_EVENTS_DISABLED */ + return (posted); +} + +/* + * Turn off controller events + */ +int +SDL_GameControllerEventState(int state) +{ +#if SDL_EVENTS_DISABLED + return SDL_IGNORE; +#else + const Uint32 event_list[] = { + SDL_CONTROLLERAXISMOTION, SDL_CONTROLLERBUTTONDOWN, SDL_CONTROLLERBUTTONUP, + SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEREMOVED, SDL_CONTROLLERDEVICEREMAPPED, + }; + unsigned int i; + + switch (state) { + case SDL_QUERY: + state = SDL_IGNORE; + for (i = 0; i < SDL_arraysize(event_list); ++i) { + state = SDL_EventState(event_list[i], SDL_QUERY); + if (state == SDL_ENABLE) { + break; + } + } + break; + default: + for (i = 0; i < SDL_arraysize(event_list); ++i) { + SDL_EventState(event_list[i], state); + } + break; + } + return (state); +#endif /* SDL_EVENTS_DISABLED */ +} + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h index 4a5019e73..f75aabc07 100644 --- a/src/joystick/SDL_sysjoystick.h +++ b/src/joystick/SDL_sysjoystick.h @@ -108,7 +108,7 @@ extern SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index); /* Function to return the stable GUID for a opened joystick */ extern SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick); -#ifdef SDL_JOYSTICK_DINPUT +#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) /* Function to get the current instance id of the joystick located at device_index */ extern SDL_bool SDL_SYS_IsXInputDeviceIndex( int device_index ); #endif diff --git a/src/joystick/windowsrt/SDL_xinputjoystick.c b/src/joystick/windowsrt/SDL_xinputjoystick.c new file mode 100644 index 000000000..6b3e59f0e --- /dev/null +++ b/src/joystick/windowsrt/SDL_xinputjoystick.c @@ -0,0 +1,435 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2013 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +#if SDL_JOYSTICK_XINPUT + +/* SDL_xinputjoystick.c implements an XInput-only joystick and game controller + backend that is suitable for use on WinRT. SDL's DirectInput backend, also + XInput-capable, was not used as DirectInput is not available on WinRT (or, + at least, it isn't a public API). Some portions of this XInput backend + may copy parts of the XInput-using code from the DirectInput backend. + Refactoring the common parts into one location may be good to-do at some + point. + + TODO, WinRT: add hotplug support for XInput based game controllers +*/ + +#include "SDL_joystick.h" +#include "../SDL_sysjoystick.h" +#include "../SDL_joystick_c.h" +#include "SDL_events.h" +#include "../../events/SDL_events_c.h" + +#include +#include + +struct joystick_hwdata { + //Uint8 bXInputHaptic; // Supports force feedback via XInput. + DWORD userIndex; // The XInput device index, in the range [0, XUSER_MAX_COUNT-1] (probably [0,3]). + XINPUT_STATE XInputState; // the last-read in XInputState, kept around to compare old and new values + SDL_bool isDeviceConnected; // was the device connected (on the last polling, or during backend-initialization)? + SDL_bool isDeviceRemovalEventPending; // was the device removed, and is the associated removal event pending? +}; + +/* Keep track of data on all XInput devices, regardless of whether or not + they've been opened (via SDL_JoystickOpen). + */ +static struct joystick_hwdata g_XInputData[XUSER_MAX_COUNT]; + +/* Function to scan the system for joysticks. + * It should return 0, or -1 on an unrecoverable fatal error. + */ +int +SDL_SYS_JoystickInit(void) +{ + HRESULT result = S_OK; + XINPUT_STATE tempXInputState; + int i; + + SDL_zero(g_XInputData); + + /* Make initial notes on whether or not devices are connected (or not). + */ + for (i = 0; i < XUSER_MAX_COUNT; ++i) { + result = XInputGetState(i, &tempXInputState); + if (result == ERROR_SUCCESS) { + g_XInputData[i].isDeviceConnected = SDL_TRUE; + } + } + + return (0); +} + +int SDL_SYS_NumJoysticks() +{ + int joystickCount = 0; + DWORD i; + + /* Iterate through each possible XInput device and see if something + was connected (at joystick init, or during the last polling). + */ + for (i = 0; i < XUSER_MAX_COUNT; ++i) { + if (g_XInputData[i].isDeviceConnected) { + ++joystickCount; + } + } + + return joystickCount; +} + +void SDL_SYS_JoystickDetect() +{ + DWORD i; + XINPUT_STATE tempXInputState; + HRESULT result; + SDL_Event event; + + /* Iterate through each possible XInput device, seeing if any devices + have been connected, or if they were removed. + */ + for (i = 0; i < XUSER_MAX_COUNT; ++i) { + /* See if any new devices are connected. */ + if (!g_XInputData[i].isDeviceConnected && !g_XInputData[i].isDeviceRemovalEventPending) { + result = XInputGetState(i, &tempXInputState); + if (result == ERROR_SUCCESS) { + /* Yup, a device is connected. Mark the device as connected, + then tell others about it (via an SDL_JOYDEVICEADDED event.) + */ + g_XInputData[i].isDeviceConnected = SDL_TRUE; + +#if !SDL_EVENTS_DISABLED + SDL_zero(event); + event.type = SDL_JOYDEVICEADDED; + + if (SDL_GetEventState(event.type) == SDL_ENABLE) { + event.jdevice.which = i; + if ((SDL_EventOK == NULL) + || (*SDL_EventOK) (SDL_EventOKParam, &event)) { + SDL_PushEvent(&event); + } + } +#endif + } + } else if (g_XInputData[i].isDeviceRemovalEventPending) { + /* A device was previously marked as removed (by + SDL_SYS_JoystickUpdate). Tell others about the device removal. + */ + + g_XInputData[i].isDeviceRemovalEventPending = SDL_FALSE; + +#if !SDL_EVENTS_DISABLED + SDL_zero(event); + event.type = SDL_JOYDEVICEREMOVED; + + if (SDL_GetEventState(event.type) == SDL_ENABLE) { + event.jdevice.which = i; //joystick->hwdata->userIndex; + if ((SDL_EventOK == NULL) + || (*SDL_EventOK) (SDL_EventOKParam, &event)) { + SDL_PushEvent(&event); + } + } +#endif + } + } +} + +SDL_bool SDL_SYS_JoystickNeedsPolling() +{ + /* Since XInput, or WinRT, provides any events to indicate when a game + controller gets connected, and instead indicates device availability + solely through polling, we'll poll (for new devices). + */ + return SDL_TRUE; +} + +/* Internal function to retreive device capabilities. + This function will return an SDL-standard value of 0 on success + (a device is connected, and data on it was retrieved), or -1 + on failure (no device was connected, or some other error + occurred. SDL_SetError() will be invoked to set an appropriate + error message. + */ +static int +SDL_XInput_GetDeviceCapabilities(int device_index, XINPUT_CAPABILITIES * pDeviceCaps) +{ + HRESULT dwResult; + + /* Make sure that the device index is a valid one. If not, return to the + caller with an error. + */ + if (device_index < 0 || device_index >= XUSER_MAX_COUNT) { + return SDL_SetError("invalid/unavailable device index"); + } + + /* See if a device exists, and if so, what its capabilities are. If a + device is not available, return to the caller with an error. + */ + switch ((dwResult = XInputGetCapabilities(device_index, 0, pDeviceCaps))) { + case ERROR_SUCCESS: + /* A device is available, and its capabilities were retrieved! */ + return 0; + case ERROR_DEVICE_NOT_CONNECTED: + return SDL_SetError("no device is connected at joystick index, %d", device_index); + default: + return SDL_SetError("an unknown error occurred when retrieving info on a device at joystick index, %d", device_index); + } +} + +/* Function to get the device-dependent name of a joystick */ +const char * +SDL_SYS_JoystickNameForDeviceIndex(int device_index) +{ + XINPUT_CAPABILITIES deviceCaps; + + if (SDL_XInput_GetDeviceCapabilities(device_index, &deviceCaps) != 0) { + /* Uh oh. Device capabilities couldn't be retrieved. Return to the + caller. SDL_SetError() has already been invoked (with relevant + information). + */ + return NULL; + } + + switch (deviceCaps.SubType) { + default: + if (deviceCaps.Type == XINPUT_DEVTYPE_GAMEPAD) { + return "Undefined game controller"; + } else { + return "Undefined controller"; + } + case XINPUT_DEVSUBTYPE_UNKNOWN: + if (deviceCaps.Type == XINPUT_DEVTYPE_GAMEPAD) { + return "Unknown game controller"; + } else { + return "Unknown controller"; + } + case XINPUT_DEVSUBTYPE_GAMEPAD: + return "Gamepad controller"; + case XINPUT_DEVSUBTYPE_WHEEL: + return "Racing wheel controller"; + case XINPUT_DEVSUBTYPE_ARCADE_STICK: + return "Arcade stick controller"; + case XINPUT_DEVSUBTYPE_FLIGHT_STICK: + return "Flight stick controller"; + case XINPUT_DEVSUBTYPE_DANCE_PAD: + return "Dance pad controller"; + case XINPUT_DEVSUBTYPE_GUITAR: + return "Guitar controller"; + case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE: + return "Guitar controller, Alternate"; + case XINPUT_DEVSUBTYPE_GUITAR_BASS: + return "Guitar controller, Bass"; + case XINPUT_DEVSUBTYPE_DRUM_KIT: + return "Drum controller"; + case XINPUT_DEVSUBTYPE_ARCADE_PAD: + return "Arcade pad controller"; + } +} + +/* Function to perform the mapping from device index to the instance id for this index */ +SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index) +{ + return device_index; +} + +/* Function to open a joystick for use. + The joystick to open is specified by the index field of the joystick. + This should fill the nbuttons and naxes fields of the joystick structure. + It returns 0, or -1 if there is an error. + */ +int +SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) +{ + XINPUT_CAPABILITIES deviceCaps; + + if (SDL_XInput_GetDeviceCapabilities(device_index, &deviceCaps) != 0) { + /* Uh oh. Device capabilities couldn't be retrieved. Return to the + caller. SDL_SetError() has already been invoked (with relevant + information). + */ + return -1; + } + + /* For now, only game pads are supported. If the device is something other + than that, return an error to the caller. + */ + if (deviceCaps.Type != XINPUT_DEVTYPE_GAMEPAD) { + return SDL_SetError("a device is connected (at joystick index, %d), but it is of an unknown device type (deviceCaps.Flags=%ul)", + device_index, (unsigned int)deviceCaps.Flags); + } + + /* Create the joystick data structure */ + joystick->instance_id = device_index; + joystick->hwdata = &g_XInputData[device_index]; + + // The XInput API has a hard coded button/axis mapping, so we just match it + joystick->naxes = 6; + joystick->nbuttons = 15; + joystick->nballs = 0; + joystick->nhats = 0; + + /* We're done! */ + return (0); +} + +/* Function to determine is this joystick is attached to the system right now */ +SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick) +{ + return joystick->hwdata->isDeviceConnected; +} + +/* Function to return > 0 if a bit array of buttons differs after applying a mask +*/ +static int ButtonChanged( int ButtonsNow, int ButtonsPrev, int ButtonMask ) +{ + return ( ButtonsNow & ButtonMask ) != ( ButtonsPrev & ButtonMask ); +} + +/* Function to update the state of a joystick - called as a device poll. + * This function shouldn't update the joystick structure directly, + * but instead should call SDL_PrivateJoystick*() to deliver events + * and update joystick device state. + */ +void +SDL_SYS_JoystickUpdate(SDL_Joystick * joystick) +{ + HRESULT result; + + /* Before polling for new data, make note of the old data */ + XINPUT_STATE prevXInputState = joystick->hwdata->XInputState; + + /* Poll for new data */ + result = XInputGetState(joystick->hwdata->userIndex, &joystick->hwdata->XInputState); + if (result == ERROR_DEVICE_NOT_CONNECTED) { + if (joystick->hwdata->isDeviceConnected) { + joystick->hwdata->isDeviceConnected = SDL_FALSE; + joystick->hwdata->isDeviceRemovalEventPending = SDL_TRUE; + /* TODO, WinRT: make sure isDeviceRemovalEventPending gets cleared as appropriate, and that quick re-plugs don't cause trouble */ + } + return; + } + + /* Make sure the device is marked as connected */ + joystick->hwdata->isDeviceConnected = SDL_TRUE; + + // only fire events if the data changed from last time + if ( joystick->hwdata->XInputState.dwPacketNumber != 0 + && joystick->hwdata->XInputState.dwPacketNumber != prevXInputState.dwPacketNumber ) + { + XINPUT_STATE *pXInputState = &joystick->hwdata->XInputState; + XINPUT_STATE *pXInputStatePrev = &prevXInputState; + + SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX ); + SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-1*pXInputState->Gamepad.sThumbLY-1) ); + SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX ); + SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-1*pXInputState->Gamepad.sThumbRY-1) ); + SDL_PrivateJoystickAxis(joystick, 4, (Sint16)((int)pXInputState->Gamepad.bLeftTrigger*32767/255) ); + SDL_PrivateJoystickAxis(joystick, 5, (Sint16)((int)pXInputState->Gamepad.bRightTrigger*32767/255) ); + + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP ) ) + SDL_PrivateJoystickButton(joystick, 0, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN ) ) + SDL_PrivateJoystickButton(joystick, 1, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT ) ) + SDL_PrivateJoystickButton(joystick, 2, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ) ) + SDL_PrivateJoystickButton(joystick, 3, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_START ) ) + SDL_PrivateJoystickButton(joystick, 4, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_START ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_BACK ) ) + SDL_PrivateJoystickButton(joystick, 5, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_BACK ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB ) ) + SDL_PrivateJoystickButton(joystick, 6, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB ) ) + SDL_PrivateJoystickButton(joystick, 7, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ) ) + SDL_PrivateJoystickButton(joystick, 8, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER ) ) + SDL_PrivateJoystickButton(joystick, 9, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_A ) ) + SDL_PrivateJoystickButton(joystick, 10, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_A ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_B ) ) + SDL_PrivateJoystickButton(joystick, 11, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_B ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_X ) ) + SDL_PrivateJoystickButton(joystick, 12, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_X ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_Y ) ) + SDL_PrivateJoystickButton(joystick, 13, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_Y ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, 0x400 ) ) + SDL_PrivateJoystickButton(joystick, 14, pXInputState->Gamepad.wButtons & 0x400 ? SDL_PRESSED : SDL_RELEASED ); // 0x400 is the undocumented code for the guide button + } +} + +/* Function to close a joystick after use */ +void +SDL_SYS_JoystickClose(SDL_Joystick * joystick) +{ + /* Clear cached button data on the joystick */ + SDL_zero(joystick->hwdata->XInputState); + + /* There's need to free 'hwdata', as it's a pointer to a global array. + The field will be cleared anyways, just to indicate that it's not + currently needed. + */ + joystick->hwdata = NULL; +} + +/* Function to perform any system-specific joystick related cleanup */ +void +SDL_SYS_JoystickQuit(void) +{ + return; +} + +SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index ) +{ + SDL_JoystickGUID guid; + // the GUID is just the first 16 chars of the name for now + const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index ); + SDL_zero( guid ); + SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) ); + return guid; +} + + +SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick) +{ + SDL_JoystickGUID guid; + // the GUID is just the first 16 chars of the name for now + const char *name = joystick->name; + SDL_zero( guid ); + SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) ); + return guid; +} + +SDL_bool SDL_SYS_IsXInputDeviceIndex(int device_index) +{ + /* The XInput-capable DirectInput joystick backend implements the same + function (SDL_SYS_IsXInputDeviceIndex), however in that case, not all + joystick devices are XInput devices. In this case, with the + WinRT-enabled XInput-only backend, all "joystick" devices are XInput + devices. + */ + return SDL_TRUE; +} + +#endif /* SDL_JOYSTICK_XINPUT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/loadso/windows/SDL_sysloadso.c b/src/loadso/windows/SDL_sysloadso.c index 21e7a6c54..44506ecd0 100644 --- a/src/loadso/windows/SDL_sysloadso.c +++ b/src/loadso/windows/SDL_sysloadso.c @@ -33,7 +33,15 @@ void * SDL_LoadObject(const char *sofile) { LPTSTR tstr = WIN_UTF8ToString(sofile); +#ifdef __WINRT__ + /* WinRT only publically supports LoadPackagedLibrary() for loading .dll + files. LoadLibrary() is a private API, and not available for apps + (that can be published to MS' Windows Store.) + */ + void *handle = (void *) LoadPackagedLibrary(tstr, 0); +#else void *handle = (void *) LoadLibrary(tstr); +#endif SDL_free(tstr); /* Generate an error message if all loads failed */ diff --git a/src/main/windowsrt/SDL_winrt_main.cpp b/src/main/windowsrt/SDL_winrt_main.cpp new file mode 100644 index 000000000..9cc046de5 --- /dev/null +++ b/src/main/windowsrt/SDL_winrt_main.cpp @@ -0,0 +1,14 @@ + +//#include "pch.h" + +// The app's C-style main will be passed into SDL.dll as a function +// pointer, and called at the appropriate time. +typedef int (*SDLmain_MainFunction)(int, char **); +extern __declspec(dllimport) int SDL_WinRT_RunApplication(SDLmain_MainFunction mainFunction); +extern "C" int SDL_main(int, char **); + +[Platform::MTAThread] +int main(Platform::Array^) +{ + return SDL_WinRT_RunApplication(SDL_main); +} diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index f06ee1d80..10504654f 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -166,6 +166,9 @@ struct SDL_RenderDriver #if SDL_VIDEO_RENDER_D3D extern SDL_RenderDriver D3D_RenderDriver; #endif +#if SDL_VIDEO_RENDER_D3D11 +extern SDL_RenderDriver D3D11_RenderDriver; +#endif #if SDL_VIDEO_RENDER_OGL extern SDL_RenderDriver GL_RenderDriver; #endif diff --git a/src/render/direct3d11/SDL_D3D11_PixelShader_FixedColor.hlsl b/src/render/direct3d11/SDL_D3D11_PixelShader_FixedColor.hlsl new file mode 100644 index 000000000..84de709f5 --- /dev/null +++ b/src/render/direct3d11/SDL_D3D11_PixelShader_FixedColor.hlsl @@ -0,0 +1,12 @@ + +struct PixelShaderInput +{ + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; +}; + +float4 main(PixelShaderInput input) : SV_TARGET +{ + return input.color; +} diff --git a/src/render/direct3d11/SDL_D3D11_PixelShader_TextureColored.hlsl b/src/render/direct3d11/SDL_D3D11_PixelShader_TextureColored.hlsl new file mode 100644 index 000000000..503b4fdcb --- /dev/null +++ b/src/render/direct3d11/SDL_D3D11_PixelShader_TextureColored.hlsl @@ -0,0 +1,14 @@ +Texture2D theTexture : register(t0); +SamplerState theSampler : register(s0); + +struct PixelShaderInput +{ + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; +}; + +float4 main(PixelShaderInput input) : SV_TARGET +{ + return theTexture.Sample(theSampler, input.tex) * input.color; +} diff --git a/src/render/direct3d11/SDL_D3D11_VertexShader_Default.hlsl b/src/render/direct3d11/SDL_D3D11_VertexShader_Default.hlsl new file mode 100644 index 000000000..b5cac4fa9 --- /dev/null +++ b/src/render/direct3d11/SDL_D3D11_VertexShader_Default.hlsl @@ -0,0 +1,41 @@ + +#pragma pack_matrix( row_major ) + +cbuffer SDL_VertexShaderConstants : register(b0) +{ + matrix model; + matrix view; + matrix projection; +}; + +struct VertexShaderInput +{ + float3 pos : POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; +}; + +struct VertexShaderOutput +{ + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; +}; + +VertexShaderOutput main(VertexShaderInput input) +{ + VertexShaderOutput output; + float4 pos = float4(input.pos, 1.0f); + + // Transform the vertex position into projected space. + pos = mul(pos, model); + pos = mul(pos, view); + pos = mul(pos, projection); + output.pos = pos; + + // Pass through texture coordinates and color values without transformation + output.tex = input.tex; + output.color = input.color; + + return output; +} diff --git a/src/render/direct3d11/SDL_render_d3d11.cpp b/src/render/direct3d11/SDL_render_d3d11.cpp new file mode 100644 index 000000000..ed741c278 --- /dev/null +++ b/src/render/direct3d11/SDL_render_d3d11.cpp @@ -0,0 +1,1807 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_config.h" + +#if SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED + +#ifdef __WINRT__ +#include +#include +#endif + +extern "C" { +#include "../../core/windows/SDL_windows.h" +//#include "SDL_hints.h" +//#include "SDL_loadso.h" +#include "SDL_system.h" +#include "SDL_syswm.h" +#include "../SDL_sysrender.h" +#include "SDL_log.h" +#include "../../video/SDL_sysvideo.h" +//#include "stdio.h" +} + +#include +#include +#include + +#include "SDL_render_d3d11_cpp.h" + +using namespace DirectX; +using namespace Microsoft::WRL; +using namespace std; + +#ifdef __WINRT__ +using namespace Windows::Graphics::Display; +using namespace Windows::UI::Core; +#endif + +/* Direct3D 11.1 renderer implementation */ + +static SDL_Renderer *D3D11_CreateRenderer(SDL_Window * window, Uint32 flags); +static void D3D11_WindowEvent(SDL_Renderer * renderer, + const SDL_WindowEvent *event); +static int D3D11_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture); +static int D3D11_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * rect, const void *srcPixels, + int srcPitch); +static int D3D11_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * rect, void **pixels, int *pitch); +static void D3D11_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture); +static int D3D11_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture); +static int D3D11_UpdateViewport(SDL_Renderer * renderer); +static int D3D11_RenderClear(SDL_Renderer * renderer); +static int D3D11_RenderDrawPoints(SDL_Renderer * renderer, + const SDL_FPoint * points, int count); +static int D3D11_RenderDrawLines(SDL_Renderer * renderer, + const SDL_FPoint * points, int count); +static int D3D11_RenderFillRects(SDL_Renderer * renderer, + const SDL_FRect * rects, int count); +static int D3D11_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect); +static int D3D11_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect, + const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip); +static int D3D11_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, + Uint32 format, void * pixels, int pitch); +static void D3D11_RenderPresent(SDL_Renderer * renderer); +static void D3D11_DestroyTexture(SDL_Renderer * renderer, + SDL_Texture * texture); +static void D3D11_DestroyRenderer(SDL_Renderer * renderer); + +/* Direct3D 11.1 Internal Functions */ +HRESULT D3D11_CreateDeviceResources(SDL_Renderer * renderer); +HRESULT D3D11_CreateWindowSizeDependentResources(SDL_Renderer * renderer); +HRESULT D3D11_UpdateForWindowSizeChange(SDL_Renderer * renderer); +HRESULT D3D11_HandleDeviceLost(SDL_Renderer * renderer); + +extern "C" SDL_RenderDriver D3D11_RenderDriver = { + D3D11_CreateRenderer, + { + "direct3d 11.1", + ( + SDL_RENDERER_ACCELERATED | + SDL_RENDERER_PRESENTVSYNC | + SDL_RENDERER_TARGETTEXTURE + ), // flags. see SDL_RendererFlags + 2, // num_texture_formats + { // texture_formats + SDL_PIXELFORMAT_RGB888, + SDL_PIXELFORMAT_ARGB8888 + }, + 0, // max_texture_width: will be filled in later + 0 // max_texture_height: will be filled in later + } +}; + + +static Uint32 +DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat) { + switch (dxgiFormat) { + case DXGI_FORMAT_B8G8R8A8_UNORM: + return SDL_PIXELFORMAT_ARGB8888; + case DXGI_FORMAT_B8G8R8X8_UNORM: + return SDL_PIXELFORMAT_RGB888; + default: + return SDL_PIXELFORMAT_UNKNOWN; + } +} + +static DXGI_FORMAT +SDLPixelFormatToDXGIFormat(Uint32 sdlFormat) +{ + switch (sdlFormat) { + case SDL_PIXELFORMAT_ARGB8888: + return DXGI_FORMAT_B8G8R8A8_UNORM; + case SDL_PIXELFORMAT_RGB888: + return DXGI_FORMAT_B8G8R8X8_UNORM; + default: + return DXGI_FORMAT_UNKNOWN; + } +} + + +//typedef struct +//{ +// float x, y, z; +// DWORD color; +// float u, v; +//} Vertex; + +SDL_Renderer * +D3D11_CreateRenderer(SDL_Window * window, Uint32 flags) +{ + SDL_Renderer *renderer; + D3D11_RenderData *data; + + renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); + if (!renderer) { + SDL_OutOfMemory(); + return NULL; + } + SDL_zerop(renderer); + + data = new D3D11_RenderData; // Use the C++ 'new' operator to make sure the struct's members initialize using C++ rules + if (!data) { + SDL_OutOfMemory(); + return NULL; + } + data->featureLevel = (D3D_FEATURE_LEVEL) 0; + data->windowSizeInDIPs = XMFLOAT2(0, 0); + data->renderTargetSize = XMFLOAT2(0, 0); + + renderer->WindowEvent = D3D11_WindowEvent; + renderer->CreateTexture = D3D11_CreateTexture; + renderer->UpdateTexture = D3D11_UpdateTexture; + renderer->LockTexture = D3D11_LockTexture; + renderer->UnlockTexture = D3D11_UnlockTexture; + renderer->SetRenderTarget = D3D11_SetRenderTarget; + renderer->UpdateViewport = D3D11_UpdateViewport; + renderer->RenderClear = D3D11_RenderClear; + renderer->RenderDrawPoints = D3D11_RenderDrawPoints; + renderer->RenderDrawLines = D3D11_RenderDrawLines; + renderer->RenderFillRects = D3D11_RenderFillRects; + renderer->RenderCopy = D3D11_RenderCopy; + renderer->RenderCopyEx = D3D11_RenderCopyEx; + renderer->RenderReadPixels = D3D11_RenderReadPixels; + renderer->RenderPresent = D3D11_RenderPresent; + renderer->DestroyTexture = D3D11_DestroyTexture; + renderer->DestroyRenderer = D3D11_DestroyRenderer; + renderer->info = D3D11_RenderDriver.info; + renderer->driverdata = data; + + // HACK: make sure the SDL_Renderer references the SDL_Window data now, in + // order to give init functions access to the underlying window handle: + renderer->window = window; + + /* Initialize Direct3D resources */ + if (FAILED(D3D11_CreateDeviceResources(renderer))) { + D3D11_DestroyRenderer(renderer); + return NULL; + } + if (FAILED(D3D11_CreateWindowSizeDependentResources(renderer))) { + D3D11_DestroyRenderer(renderer); + return NULL; + } + + // TODO, WinRT: fill in renderer->info.texture_formats where appropriate + + return renderer; +} + +static void +D3D11_DestroyRenderer(SDL_Renderer * renderer) +{ + D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; + if (data) { + delete data; + data = NULL; + } +} + +static bool +D3D11_ReadFileContents(const wstring & fileName, vector & out) +{ + ifstream in(fileName, ios::in | ios::binary); + if (!in) { + return false; + } + + in.seekg(0, ios::end); + out.resize((size_t) in.tellg()); + in.seekg(0, ios::beg); + in.read(&out[0], out.size()); + return in.good(); +} + +static bool +D3D11_ReadShaderContents(const wstring & shaderName, vector & out) +{ + wstring fileName; + +#if WINAPI_FAMILY == WINAPI_FAMILY_APP + fileName = SDL_WinRTGetFSPathUNICODE(SDL_WINRT_PATH_INSTALLED_LOCATION); + fileName += L"\\SDL_VS2012_WinRT\\"; +#elif WINAPI_FAMILY == WINAPI_PHONE_APP + fileName = SDL_WinRTGetFSPathUNICODE(SDL_WINRT_PATH_INSTALLED_LOCATION); + fileName += L"\\"; +#endif + // WinRT, TODO: test Direct3D 11.1 shader loading on Win32 + fileName += shaderName; + return D3D11_ReadFileContents(fileName, out); +} + +static HRESULT +D3D11_LoadPixelShader(SDL_Renderer * renderer, + const wstring & shaderName, + ID3D11PixelShader ** shaderOutput) +{ + D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; + HRESULT result = S_OK; + vector fileData; + + if (!D3D11_ReadShaderContents(shaderName, fileData)) { + SDL_SetError("Unable to open SDL's pixel shader file."); + return E_FAIL; + } + + result = data->d3dDevice->CreatePixelShader( + &fileData[0], + fileData.size(), + nullptr, + shaderOutput + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + return S_OK; +} + +static HRESULT +D3D11_CreateBlendMode(SDL_Renderer * renderer, + BOOL enableBlending, + D3D11_BLEND srcBlend, + D3D11_BLEND destBlend, + ID3D11BlendState ** blendStateOutput) +{ + D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; + HRESULT result = S_OK; + + D3D11_BLEND_DESC blendDesc; + memset(&blendDesc, 0, sizeof(blendDesc)); + blendDesc.AlphaToCoverageEnable = FALSE; + blendDesc.IndependentBlendEnable = FALSE; + blendDesc.RenderTarget[0].BlendEnable = enableBlending; + blendDesc.RenderTarget[0].SrcBlend = srcBlend; + blendDesc.RenderTarget[0].DestBlend = destBlend; + blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + result = data->d3dDevice->CreateBlendState(&blendDesc, blendStateOutput); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + return S_OK; +} + +// Create resources that depend on the device. +HRESULT +D3D11_CreateDeviceResources(SDL_Renderer * renderer) +{ + D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; + + // This flag adds support for surfaces with a different color channel ordering + // than the API default. It is required for compatibility with Direct2D. + UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + +#if defined(_DEBUG) + // If the project is in a debug build, enable debugging via SDK Layers with this flag. + creationFlags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + + // This array defines the set of DirectX hardware feature levels this app will support. + // Note the ordering should be preserved. + // Don't forget to declare your application's minimum required feature level in its + // description. All applications are assumed to support 9.1 unless otherwise stated. + D3D_FEATURE_LEVEL featureLevels[] = + { + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1 + }; + + // Create the Direct3D 11 API device object and a corresponding context. + ComPtr device; + ComPtr context; + HRESULT result = S_OK; + result = D3D11CreateDevice( + nullptr, // Specify nullptr to use the default adapter. + D3D_DRIVER_TYPE_HARDWARE, + nullptr, + creationFlags, // Set set debug and Direct2D compatibility flags. + featureLevels, // List of feature levels this app can support. + ARRAYSIZE(featureLevels), + D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps. + &device, // Returns the Direct3D device created. + &data->featureLevel, // Returns feature level of device created. + &context // Returns the device immediate context. + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + // Get the Direct3D 11.1 API device and context interfaces. + Microsoft::WRL::ComPtr d3dDevice1; + result = device.As(&(data->d3dDevice)); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + result = context.As(&data->d3dContext); + if (FAILED(result)) { + return result; + } + + // + // Make note of the maximum texture size + // Max texture sizes are documented on MSDN, at: + // http://msdn.microsoft.com/en-us/library/windows/apps/ff476876.aspx + // + switch (data->d3dDevice->GetFeatureLevel()) { + case D3D_FEATURE_LEVEL_11_1: + case D3D_FEATURE_LEVEL_11_0: + renderer->info.max_texture_width = renderer->info.max_texture_height = 16384; + break; + + case D3D_FEATURE_LEVEL_10_1: + case D3D_FEATURE_LEVEL_10_0: + renderer->info.max_texture_width = renderer->info.max_texture_height = 8192; + break; + + case D3D_FEATURE_LEVEL_9_3: + renderer->info.max_texture_width = renderer->info.max_texture_height = 4096; + break; + + case D3D_FEATURE_LEVEL_9_2: + case D3D_FEATURE_LEVEL_9_1: + renderer->info.max_texture_width = renderer->info.max_texture_height = 2048; + break; + } + + // + // Load in SDL's one and only vertex shader: + // + vector fileData; + if (!D3D11_ReadShaderContents(L"SDL_D3D11_VertexShader_Default.cso", fileData)) { + SDL_SetError("Unable to open SDL's vertex shader file."); + return E_FAIL; + } + + result = data->d3dDevice->CreateVertexShader( + &fileData[0], + fileData.size(), + nullptr, + &data->vertexShader + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + // + // Create an input layout for SDL's vertex shader: + // + const D3D11_INPUT_ELEMENT_DESC vertexDesc[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + + result = data->d3dDevice->CreateInputLayout( + vertexDesc, + ARRAYSIZE(vertexDesc), + &fileData[0], + fileData.size(), + &data->inputLayout + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + // + // Load in SDL's pixel shaders + // + result = D3D11_LoadPixelShader(renderer, L"SDL_D3D11_PixelShader_TextureColored.cso", &data->texturePixelShader); + if (FAILED(result)) { + // D3D11_LoadPixelShader will have aleady set the SDL error + return result; + } + + result = D3D11_LoadPixelShader(renderer, L"SDL_D3D11_PixelShader_FixedColor.cso", &data->colorPixelShader); + if (FAILED(result)) { + // D3D11_LoadPixelShader will have aleady set the SDL error + return result; + } + + // + // Setup space to hold vertex shader constants: + // + CD3D11_BUFFER_DESC constantBufferDesc(sizeof(SDL_VertexShaderConstants), D3D11_BIND_CONSTANT_BUFFER); + result = data->d3dDevice->CreateBuffer( + &constantBufferDesc, + nullptr, + &data->vertexShaderConstants + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + // + // Make sure that the vertex buffer, if already created, gets freed. + // It will be recreated later. + // + data->vertexBuffer = nullptr; + + // + // Create a sampler to use when drawing textures: + // + D3D11_SAMPLER_DESC samplerDesc; + samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + samplerDesc.MipLODBias = 0.0f; + samplerDesc.MaxAnisotropy = 1; + samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; + samplerDesc.BorderColor[0] = 0.0f; + samplerDesc.BorderColor[1] = 0.0f; + samplerDesc.BorderColor[2] = 0.0f; + samplerDesc.BorderColor[3] = 0.0f; + samplerDesc.MinLOD = 0.0f; + samplerDesc.MaxLOD = D3D11_FLOAT32_MAX; + result = data->d3dDevice->CreateSamplerState( + &samplerDesc, + &data->mainSampler + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + // + // Setup the Direct3D rasterizer + // + D3D11_RASTERIZER_DESC rasterDesc; + memset(&rasterDesc, 0, sizeof(rasterDesc)); + rasterDesc.AntialiasedLineEnable = false; + rasterDesc.CullMode = D3D11_CULL_NONE; + rasterDesc.DepthBias = 0; + rasterDesc.DepthBiasClamp = 0.0f; + rasterDesc.DepthClipEnable = true; + rasterDesc.FillMode = D3D11_FILL_SOLID; + rasterDesc.FrontCounterClockwise = false; + rasterDesc.MultisampleEnable = false; + rasterDesc.ScissorEnable = false; + rasterDesc.SlopeScaledDepthBias = 0.0f; + result = data->d3dDevice->CreateRasterizerState(&rasterDesc, &data->mainRasterizer); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + // + // Create blending states: + // + result = D3D11_CreateBlendMode( + renderer, + TRUE, + D3D11_BLEND_SRC_ALPHA, + D3D11_BLEND_INV_SRC_ALPHA, + &data->blendModeBlend); + if (FAILED(result)) { + // D3D11_CreateBlendMode will set the SDL error, if it fails + return result; + } + + result = D3D11_CreateBlendMode( + renderer, + TRUE, + D3D11_BLEND_SRC_ALPHA, + D3D11_BLEND_ONE, + &data->blendModeAdd); + if (FAILED(result)) { + // D3D11_CreateBlendMode will set the SDL error, if it fails + return result; + } + + result = D3D11_CreateBlendMode( + renderer, + TRUE, + D3D11_BLEND_ZERO, + D3D11_BLEND_SRC_COLOR, + &data->blendModeMod); + if (FAILED(result)) { + // D3D11_CreateBlendMode will set the SDL error, if it fails + return result; + } + + // + // All done! + // + return S_OK; +} + +#ifdef __WINRT__ + +static ABI::Windows::UI::Core::ICoreWindow * +D3D11_GetCoreWindowFromSDLRenderer(SDL_Renderer * renderer) +{ + SDL_Window * sdlWindow = renderer->window; + if ( ! renderer->window ) { + return nullptr; + } + + SDL_SysWMinfo sdlWindowInfo; + SDL_VERSION(&sdlWindowInfo.version); + if ( ! SDL_GetWindowWMInfo(sdlWindow, &sdlWindowInfo) ) { + return nullptr; + } + + if (sdlWindowInfo.subsystem != SDL_SYSWM_WINDOWSRT) { + return nullptr; + } + + if ( ! sdlWindowInfo.info.winrt.window ) { + return nullptr; + } + + ABI::Windows::UI::Core::ICoreWindow * coreWindow = nullptr; + if (FAILED(sdlWindowInfo.info.winrt.window->QueryInterface(&coreWindow))) { + return nullptr; + } + + return coreWindow; +} + +// Method to convert a length in device-independent pixels (DIPs) to a length in physical pixels. +static float +D3D11_ConvertDipsToPixels(float dips) +{ + static const float dipsPerInch = 96.0f; + return floor(dips * DisplayProperties::LogicalDpi / dipsPerInch + 0.5f); // Round to nearest integer. +} +#endif + +// Initialize all resources that change when the window's size changes. +// WinRT, TODO: get D3D11_CreateWindowSizeDependentResources working on Win32 +HRESULT +D3D11_CreateWindowSizeDependentResources(SDL_Renderer * renderer) +{ + D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; + HRESULT result = S_OK; + ABI::Windows::UI::Core::ICoreWindow * coreWindow = D3D11_GetCoreWindowFromSDLRenderer(renderer); + + // Store the window bounds so the next time we get a SizeChanged event we can + // avoid rebuilding everything if the size is identical. + ABI::Windows::Foundation::Rect coreWindowBounds; + result = coreWindow->get_Bounds(&coreWindowBounds); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__", Get Window Bounds", result); + return result; + } + + data->windowSizeInDIPs.x = coreWindowBounds.Width; + data->windowSizeInDIPs.y = coreWindowBounds.Height; + + // Calculate the necessary swap chain and render target size in pixels. + float windowWidth = D3D11_ConvertDipsToPixels(data->windowSizeInDIPs.x); + float windowHeight = D3D11_ConvertDipsToPixels(data->windowSizeInDIPs.y); + + // The width and height of the swap chain must be based on the window's + // landscape-oriented width and height. If the window is in a portrait + // orientation, the dimensions must be reversed. + data->orientation = DisplayProperties::CurrentOrientation; + const bool swapDimensions = + data->orientation == DisplayOrientations::Portrait || + data->orientation == DisplayOrientations::PortraitFlipped; + data->renderTargetSize.x = swapDimensions ? windowHeight : windowWidth; + data->renderTargetSize.y = swapDimensions ? windowWidth : windowHeight; + + if(data->swapChain != nullptr) + { + // If the swap chain already exists, resize it. + result = data->swapChain->ResizeBuffers( + 2, // Double-buffered swap chain. + static_cast(data->renderTargetSize.x), + static_cast(data->renderTargetSize.y), + DXGI_FORMAT_B8G8R8A8_UNORM, + 0 + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + } + else + { + // Otherwise, create a new one using the same adapter as the existing Direct3D device. + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0}; + swapChainDesc.Width = static_cast(data->renderTargetSize.x); // Match the size of the window. + swapChainDesc.Height = static_cast(data->renderTargetSize.y); + swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format. + swapChainDesc.Stereo = false; + swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling. + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency. +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + swapChainDesc.Scaling = DXGI_SCALING_STRETCH; // On phone, only stretch and aspect-ratio stretch scaling are allowed. + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; // On phone, no swap effects are supported. +#else + swapChainDesc.Scaling = DXGI_SCALING_NONE; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect. +#endif + swapChainDesc.Flags = 0; + + ComPtr dxgiDevice; + result = data->d3dDevice.As(&dxgiDevice); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + ComPtr dxgiAdapter; + result = dxgiDevice->GetAdapter(&dxgiAdapter); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + ComPtr dxgiFactory; + result = dxgiAdapter->GetParent( + __uuidof(IDXGIFactory2), + &dxgiFactory + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + IUnknown * coreWindowAsIUnknown = nullptr; + result = coreWindow->QueryInterface(&coreWindowAsIUnknown); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__ ", CoreWindow to IUnknown", result); + return result; + } + + result = dxgiFactory->CreateSwapChainForCoreWindow( + data->d3dDevice.Get(), + coreWindowAsIUnknown, + &swapChainDesc, + nullptr, // Allow on all displays. + &data->swapChain + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and + // ensures that the application will only render after each VSync, minimizing power consumption. + result = dxgiDevice->SetMaximumFrameLatency(1); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + } + + // Set the proper orientation for the swap chain, and generate the + // 3D matrix transformation for rendering to the rotated swap chain. + DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED; + switch (data->orientation) + { + case DisplayOrientations::Landscape: + rotation = DXGI_MODE_ROTATION_IDENTITY; + break; + + case DisplayOrientations::Portrait: + rotation = DXGI_MODE_ROTATION_ROTATE270; + break; + + case DisplayOrientations::LandscapeFlipped: + rotation = DXGI_MODE_ROTATION_ROTATE180; + break; + + case DisplayOrientations::PortraitFlipped: + rotation = DXGI_MODE_ROTATION_ROTATE90; + break; + + default: + throw ref new Platform::FailureException(); + } + +#if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP + // TODO, WinRT: Windows Phone does not have the IDXGISwapChain1::SetRotation method. Check if an alternative is available, or needed. + result = data->swapChain->SetRotation(rotation); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } +#endif + + // Create a render target view of the swap chain back buffer. + ComPtr backBuffer; + result = data->swapChain->GetBuffer( + 0, + __uuidof(ID3D11Texture2D), + &backBuffer + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + result = data->d3dDevice->CreateRenderTargetView( + backBuffer.Get(), + nullptr, + &data->mainRenderTargetView + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + if (D3D11_UpdateViewport(renderer) != 0) { + // D3D11_UpdateViewport will set the SDL error if it fails. + return E_FAIL; + } + + return S_OK; +} + +// This method is called when the window's size changes. +HRESULT +D3D11_UpdateForWindowSizeChange(SDL_Renderer * renderer) +{ + D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; + HRESULT result = S_OK; + ABI::Windows::UI::Core::ICoreWindow * coreWindow = D3D11_GetCoreWindowFromSDLRenderer(renderer); + ABI::Windows::Foundation::Rect coreWindowBounds; + + result = coreWindow->get_Bounds(&coreWindowBounds); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__ ", Get Window Bounds", result); + return result; + } + + if (coreWindowBounds.Width != data->windowSizeInDIPs.x || + coreWindowBounds.Height != data->windowSizeInDIPs.y || + data->orientation != DisplayProperties::CurrentOrientation) + { + ID3D11RenderTargetView* nullViews[] = {nullptr}; + data->d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr); + data->mainRenderTargetView = nullptr; + data->d3dContext->Flush(); + result = D3D11_CreateWindowSizeDependentResources(renderer); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + } + + return S_OK; +} + +HRESULT +D3D11_HandleDeviceLost(SDL_Renderer * renderer) +{ + D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; + HRESULT result = S_OK; + + // Reset these member variables to ensure that D3D11_UpdateForWindowSizeChange recreates all resources. + data->windowSizeInDIPs.x = 0; + data->windowSizeInDIPs.y = 0; + data->swapChain = nullptr; + + result = D3D11_CreateDeviceResources(renderer); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + result = D3D11_UpdateForWindowSizeChange(renderer); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return result; + } + + return S_OK; +} + +static void +D3D11_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) +{ + //D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; + + if (event->event == SDL_WINDOWEVENT_RESIZED) { + D3D11_UpdateForWindowSizeChange(renderer); + } +} + +static int +D3D11_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + D3D11_TextureData *textureData; + HRESULT result; + DXGI_FORMAT textureFormat = SDLPixelFormatToDXGIFormat(texture->format); + if (textureFormat == SDL_PIXELFORMAT_UNKNOWN) { + SDL_SetError("%s, An unsupported SDL pixel format (0x%x) was specified", + __FUNCTION__, texture->format); + return -1; + } + + textureData = new D3D11_TextureData; + if (!textureData) { + SDL_OutOfMemory(); + return -1; + } + textureData->pixelFormat = SDL_AllocFormat(texture->format); + textureData->lockedTexturePosition = XMINT2(0, 0); + + texture->driverdata = textureData; + + D3D11_TEXTURE2D_DESC textureDesc = {0}; + textureDesc.Width = texture->w; + textureDesc.Height = texture->h; + textureDesc.MipLevels = 1; + textureDesc.ArraySize = 1; + textureDesc.Format = textureFormat; + textureDesc.SampleDesc.Count = 1; + textureDesc.SampleDesc.Quality = 0; + textureDesc.MiscFlags = 0; + + if (texture->access == SDL_TEXTUREACCESS_STREAMING) { + textureDesc.Usage = D3D11_USAGE_DYNAMIC; + textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + } else { + textureDesc.Usage = D3D11_USAGE_DEFAULT; + textureDesc.CPUAccessFlags = 0; + } + + if (texture->access == SDL_TEXTUREACCESS_TARGET) { + textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; + } else { + textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + } + +#if 0 + // Fill the texture with a non-black color, for debugging purposes: + const int numPixels = textureDesc.Width * textureDesc.Height; + const int pixelSizeInBytes = textureData->pixelFormat->BytesPerPixel; + std::vector initialTexturePixels(numPixels * pixelSizeInBytes, 0x00); + for (int i = 0; i < (numPixels * pixelSizeInBytes); i += pixelSizeInBytes) { + initialTexturePixels[i+0] = 0xff; + initialTexturePixels[i+1] = 0xff; + initialTexturePixels[i+2] = 0x00; + initialTexturePixels[i+3] = 0xff; + } + D3D11_SUBRESOURCE_DATA initialTextureData = {0}; + initialTextureData.pSysMem = (void *)&(initialTexturePixels[0]); + initialTextureData.SysMemPitch = textureDesc.Width * pixelSizeInBytes; + initialTextureData.SysMemSlicePitch = numPixels * pixelSizeInBytes; +#endif + + result = rendererData->d3dDevice->CreateTexture2D( + &textureDesc, + NULL, // &initialTextureData, + &textureData->mainTexture + ); + if (FAILED(result)) { + D3D11_DestroyTexture(renderer, texture); + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return -1; + } + + if (texture->access & SDL_TEXTUREACCESS_TARGET) { + D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc; + renderTargetViewDesc.Format = textureDesc.Format; + renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + renderTargetViewDesc.Texture2D.MipSlice = 0; + + result = rendererData->d3dDevice->CreateRenderTargetView( + textureData->mainTexture.Get(), + &renderTargetViewDesc, + &textureData->mainTextureRenderTargetView); + if (FAILED(result)) { + D3D11_DestroyTexture(renderer, texture); + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return -1; + } + } + + D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc; + resourceViewDesc.Format = textureDesc.Format; + resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + resourceViewDesc.Texture2D.MostDetailedMip = 0; + resourceViewDesc.Texture2D.MipLevels = textureDesc.MipLevels; + result = rendererData->d3dDevice->CreateShaderResourceView( + textureData->mainTexture.Get(), + &resourceViewDesc, + &textureData->mainTextureResourceView + ); + if (FAILED(result)) { + D3D11_DestroyTexture(renderer, texture); + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return -1; + } + + return 0; +} + +static void +D3D11_DestroyTexture(SDL_Renderer * renderer, + SDL_Texture * texture) +{ + D3D11_TextureData *textureData = (D3D11_TextureData *) texture->driverdata; + + if (textureData) { + if (textureData->pixelFormat) { + SDL_FreeFormat(textureData->pixelFormat); + textureData->pixelFormat = NULL; + } + + delete textureData; + texture->driverdata = NULL; + } +} + +static int +D3D11_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * rect, const void * srcPixels, + int srcPitch) +{ + // Lock the texture, retrieving a buffer to write pixel data to: + void * destPixels = NULL; + int destPitch = 0; + if (D3D11_LockTexture(renderer, texture, rect, &destPixels, &destPitch) != 0) { + // An error is already set. Attach some info to it, then return to + // the caller. + std::string errorMessage = string(__FUNCTION__ ", Lock Texture Failed: ") + SDL_GetError(); + SDL_SetError(errorMessage.c_str()); + return -1; + } + + // Copy pixel data to the locked texture's memory: + for (int y = 0; y < rect->h; ++y) { + memcpy( + ((Uint8 *)destPixels) + (destPitch * y), + ((Uint8 *)srcPixels) + (srcPitch * y), + srcPitch + ); + } + + // Commit the texture's memory back to Direct3D: + D3D11_UnlockTexture(renderer, texture); + + // Return to the caller: + return 0; +} + +static int +D3D11_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * rect, void **pixels, int *pitch) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + D3D11_TextureData *textureData = (D3D11_TextureData *) texture->driverdata; + HRESULT result = S_OK; + + if (textureData->stagingTexture) { + SDL_SetError("texture is already locked"); + return -1; + } + + // Create a 'staging' texture, which will be used to write to a portion + // of the main texture. This is necessary, as Direct3D 11.1 does not + // have the ability to write a CPU-bound pixel buffer to a rectangular + // subrect of a texture. Direct3D 11.1 can, however, write a pixel + // buffer to an entire texture, hence the use of a staging texture. + D3D11_TEXTURE2D_DESC stagingTextureDesc; + textureData->mainTexture->GetDesc(&stagingTextureDesc); + stagingTextureDesc.Width = rect->w; + stagingTextureDesc.Height = rect->h; + stagingTextureDesc.BindFlags = 0; + stagingTextureDesc.MiscFlags = 0; + stagingTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + stagingTextureDesc.Usage = D3D11_USAGE_STAGING; + result = rendererData->d3dDevice->CreateTexture2D( + &stagingTextureDesc, + NULL, + &textureData->stagingTexture); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__ ", Create Staging Texture", result); + return -1; + } + + // Get a write-only pointer to data in the staging texture: + D3D11_MAPPED_SUBRESOURCE textureMemory = {0}; + result = rendererData->d3dContext->Map( + textureData->stagingTexture.Get(), + D3D11CalcSubresource(0, 0, 0), + D3D11_MAP_WRITE, + 0, + &textureMemory + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__ ", Map Staging Texture", result); + textureData->stagingTexture = nullptr; + return -1; + } + + // Make note of where the staging texture will be written to (on a + // call to SDL_UnlockTexture): + textureData->lockedTexturePosition = XMINT2(rect->x, rect->y); + + // Make sure the caller has information on the texture's pixel buffer, + // then return: + *pixels = textureMemory.pData; + *pitch = textureMemory.RowPitch; + return 0; +} + +static void +D3D11_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + D3D11_TextureData *textureData = (D3D11_TextureData *) texture->driverdata; + + // Commit the pixel buffer's changes back to the staging texture: + rendererData->d3dContext->Unmap( + textureData->stagingTexture.Get(), + 0); + + // Copy the staging texture's contents back to the main texture: + rendererData->d3dContext->CopySubresourceRegion( + textureData->mainTexture.Get(), + D3D11CalcSubresource(0, 0, 0), + textureData->lockedTexturePosition.x, + textureData->lockedTexturePosition.y, + 0, + textureData->stagingTexture.Get(), + D3D11CalcSubresource(0, 0, 0), + NULL); + + // Clean up and return: + textureData->stagingTexture = nullptr; + textureData->lockedTexturePosition = XMINT2(0, 0); +} + +static int +D3D11_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + + if (texture == NULL) { + rendererData->currentOffscreenRenderTargetView = nullptr; + return 0; + } + + D3D11_TextureData *textureData = (D3D11_TextureData *) texture->driverdata; + + if (!textureData->mainTextureRenderTargetView) { + std::string errorMessage = string(__FUNCTION__) + ": specified texture is not a render target"; + SDL_SetError(errorMessage.c_str()); + return -1; + } + + rendererData->currentOffscreenRenderTargetView = textureData->mainTextureRenderTargetView; + + return 0; +} + +static int +D3D11_UpdateViewport(SDL_Renderer * renderer) +{ + D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; + + if (renderer->viewport.w == 0 || renderer->viewport.h == 0) { + // If the viewport is empty, assume that it is because + // SDL_CreateRenderer is calling it, and will call it again later + // with a non-empty viewport. + return 0; + } + + switch (data->orientation) + { + case DisplayOrientations::Landscape: + data->vertexShaderConstantsData.projection = XMFLOAT4X4( // 0-degree Z-rotation + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + break; + + case DisplayOrientations::Portrait: + data->vertexShaderConstantsData.projection = XMFLOAT4X4( // 90-degree Z-rotation + 0.0f, 1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + break; + + case DisplayOrientations::LandscapeFlipped: + data->vertexShaderConstantsData.projection = XMFLOAT4X4( // 180-degree Z-rotation + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + break; + + case DisplayOrientations::PortraitFlipped: + data->vertexShaderConstantsData.projection = XMFLOAT4X4( // 270-degree Z-rotation + 0.0f, -1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + break; + + default: + SDL_SetError("An unknown DisplayOrientation is being used"); + return -1; + } + + // + // Update the view matrix + // + float viewportWidth = (float) renderer->viewport.w; + float viewportHeight = (float) renderer->viewport.h; + XMStoreFloat4x4(&data->vertexShaderConstantsData.view, + XMMatrixMultiply( + XMMatrixScaling(2.0f / viewportWidth, 2.0f / viewportHeight, 1.0f), + XMMatrixMultiply( + XMMatrixTranslation(-1, -1, 0), + XMMatrixRotationX(XM_PI) + ))); +#if 0 + data->vertexShaderConstantsData.view = XMMatrixIdentity(); +#endif + + // + // Reset the model matrix + // + XMStoreFloat4x4(&data->vertexShaderConstantsData.model, XMMatrixIdentity()); + + // + // Update the Direct3D viewport, which seems to be aligned to the + // swap buffer's coordinate space, which is always in landscape: + // + SDL_FRect orientationAlignedViewport; + const bool swapDimensions = + data->orientation == DisplayOrientations::Portrait || + data->orientation == DisplayOrientations::PortraitFlipped; + if (swapDimensions) { + orientationAlignedViewport.x = (float) renderer->viewport.y; + orientationAlignedViewport.y = (float) renderer->viewport.x; + orientationAlignedViewport.w = (float) renderer->viewport.h; + orientationAlignedViewport.h = (float) renderer->viewport.w; + } else { + orientationAlignedViewport.x = (float) renderer->viewport.x; + orientationAlignedViewport.y = (float) renderer->viewport.y; + orientationAlignedViewport.w = (float) renderer->viewport.w; + orientationAlignedViewport.h = (float) renderer->viewport.h; + } + // WinRT, TODO: get custom viewports working with non-Landscape modes (Portrait, PortraitFlipped, and LandscapeFlipped) + + D3D11_VIEWPORT viewport; + memset(&viewport, 0, sizeof(viewport)); + viewport.TopLeftX = orientationAlignedViewport.x; + viewport.TopLeftY = orientationAlignedViewport.y; + viewport.Width = orientationAlignedViewport.w; + viewport.Height = orientationAlignedViewport.h; + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + data->d3dContext->RSSetViewports(1, &viewport); + +#if 0 + SDL_Log("%s, oav={%.0f,%.0f,%.0f,%.0f}, rend={%.0f,%.0f}\n", + __FUNCTION__, + orientationAlignedViewport.x, + orientationAlignedViewport.y, + orientationAlignedViewport.w, + orientationAlignedViewport.h, + data->renderTargetSize.x, + data->renderTargetSize.y); +#endif + + return 0; +} + +static ComPtr & +D3D11_GetCurrentRenderTargetView(SDL_Renderer * renderer) +{ + D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; + if (data->currentOffscreenRenderTargetView) { + return data->currentOffscreenRenderTargetView; + } else { + return data->mainRenderTargetView; + } +} + +static int +D3D11_RenderClear(SDL_Renderer * renderer) +{ + D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; + const float colorRGBA[] = { + (renderer->r / 255.0f), + (renderer->g / 255.0f), + (renderer->b / 255.0f), + (renderer->a / 255.0f) + }; + data->d3dContext->ClearRenderTargetView( + D3D11_GetCurrentRenderTargetView(renderer).Get(), + colorRGBA + ); + return 0; +} + +static int +D3D11_UpdateVertexBuffer(SDL_Renderer *renderer, + const void * vertexData, unsigned int dataSizeInBytes) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + HRESULT result = S_OK; + D3D11_BUFFER_DESC vertexBufferDesc; + + if (rendererData->vertexBuffer) { + rendererData->vertexBuffer->GetDesc(&vertexBufferDesc); + } else { + memset(&vertexBufferDesc, 0, sizeof(vertexBufferDesc)); + } + + if (vertexBufferDesc.ByteWidth >= dataSizeInBytes) { + rendererData->d3dContext->UpdateSubresource(rendererData->vertexBuffer.Get(), 0, NULL, vertexData, dataSizeInBytes, 0); + } else { + vertexBufferDesc.ByteWidth = dataSizeInBytes; + vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + + D3D11_SUBRESOURCE_DATA vertexBufferData = {0}; + vertexBufferData.pSysMem = vertexData; + vertexBufferData.SysMemPitch = 0; + vertexBufferData.SysMemSlicePitch = 0; + + result = rendererData->d3dDevice->CreateBuffer( + &vertexBufferDesc, + &vertexBufferData, + &rendererData->vertexBuffer + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, result); + return -1; + } + } + + UINT stride = sizeof(VertexPositionColor); + UINT offset = 0; + rendererData->d3dContext->IASetVertexBuffers( + 0, + 1, + rendererData->vertexBuffer.GetAddressOf(), + &stride, + &offset + ); + + return 0; +} + +static void +D3D11_RenderStartDrawOp(SDL_Renderer * renderer) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + + rendererData->d3dContext->OMSetRenderTargets( + 1, + D3D11_GetCurrentRenderTargetView(renderer).GetAddressOf(), + nullptr + ); +} + +static void +D3D11_RenderSetBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + switch (blendMode) { + case SDL_BLENDMODE_BLEND: + rendererData->d3dContext->OMSetBlendState(rendererData->blendModeBlend.Get(), 0, 0xFFFFFFFF); + break; + case SDL_BLENDMODE_ADD: + rendererData->d3dContext->OMSetBlendState(rendererData->blendModeAdd.Get(), 0, 0xFFFFFFFF); + break; + case SDL_BLENDMODE_MOD: + rendererData->d3dContext->OMSetBlendState(rendererData->blendModeMod.Get(), 0, 0xFFFFFFFF); + break; + case SDL_BLENDMODE_NONE: + rendererData->d3dContext->OMSetBlendState(NULL, 0, 0xFFFFFFFF); + break; + } +} + +static void +D3D11_SetPixelShader(SDL_Renderer * renderer, + ID3D11PixelShader * shader, + ID3D11ShaderResourceView * shaderResource, + ID3D11SamplerState * sampler) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + rendererData->d3dContext->PSSetShader(shader, nullptr, 0); + rendererData->d3dContext->PSSetShaderResources(0, 1, &shaderResource); + rendererData->d3dContext->PSSetSamplers(0, 1, &sampler); +} + +static void +D3D11_RenderFinishDrawOp(SDL_Renderer * renderer, + D3D11_PRIMITIVE_TOPOLOGY primitiveTopology, + UINT vertexCount) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + + rendererData->d3dContext->UpdateSubresource( + rendererData->vertexShaderConstants.Get(), + 0, + NULL, + &rendererData->vertexShaderConstantsData, + 0, + 0 + ); + + rendererData->d3dContext->IASetPrimitiveTopology(primitiveTopology); + rendererData->d3dContext->IASetInputLayout(rendererData->inputLayout.Get()); + rendererData->d3dContext->VSSetShader(rendererData->vertexShader.Get(), nullptr, 0); + rendererData->d3dContext->VSSetConstantBuffers(0, 1, rendererData->vertexShaderConstants.GetAddressOf()); + rendererData->d3dContext->RSSetState(rendererData->mainRasterizer.Get()); + rendererData->d3dContext->Draw(vertexCount, 0); +} + +static int +D3D11_RenderDrawPoints(SDL_Renderer * renderer, + const SDL_FPoint * points, int count) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + float r, g, b, a; + + r = (float)(renderer->r / 255.0f); + g = (float)(renderer->g / 255.0f); + b = (float)(renderer->b / 255.0f); + a = (float)(renderer->a / 255.0f); + + vector vertices; + vertices.reserve(count); + for (int i = 0; i < count; ++i) { + VertexPositionColor v = {XMFLOAT3(points[i].x, points[i].y, 0.0f), XMFLOAT2(0.0f, 0.0f), XMFLOAT4(r, g, b, a)}; + vertices.push_back(v); + } + + D3D11_RenderStartDrawOp(renderer); + D3D11_RenderSetBlendMode(renderer, renderer->blendMode); + if (D3D11_UpdateVertexBuffer(renderer, &vertices[0], vertices.size() * sizeof(VertexPositionColor)) != 0) { + return -1; + } + + D3D11_SetPixelShader( + renderer, + rendererData->colorPixelShader.Get(), + nullptr, + nullptr); + + D3D11_RenderFinishDrawOp(renderer, D3D11_PRIMITIVE_TOPOLOGY_POINTLIST, vertices.size()); + + return 0; +} + +static int +D3D11_RenderDrawLines(SDL_Renderer * renderer, + const SDL_FPoint * points, int count) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + float r, g, b, a; + + r = (float)(renderer->r / 255.0f); + g = (float)(renderer->g / 255.0f); + b = (float)(renderer->b / 255.0f); + a = (float)(renderer->a / 255.0f); + + vector vertices; + vertices.reserve(count); + for (int i = 0; i < count; ++i) { + VertexPositionColor v = {XMFLOAT3(points[i].x, points[i].y, 0.0f), XMFLOAT2(0.0f, 0.0f), XMFLOAT4(r, g, b, a)}; + vertices.push_back(v); + } + + D3D11_RenderStartDrawOp(renderer); + D3D11_RenderSetBlendMode(renderer, renderer->blendMode); + if (D3D11_UpdateVertexBuffer(renderer, &vertices[0], vertices.size() * sizeof(VertexPositionColor)) != 0) { + return -1; + } + + D3D11_SetPixelShader( + renderer, + rendererData->colorPixelShader.Get(), + nullptr, + nullptr); + + D3D11_RenderFinishDrawOp(renderer, D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP, vertices.size()); + + return 0; +} + +static int +D3D11_RenderFillRects(SDL_Renderer * renderer, + const SDL_FRect * rects, int count) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + float r, g, b, a; + + r = (float)(renderer->r / 255.0f); + g = (float)(renderer->g / 255.0f); + b = (float)(renderer->b / 255.0f); + a = (float)(renderer->a / 255.0f); + +#if 0 + // Set up a test pattern: + SDL_FRect _rects[] = { + {-1.1f, 1.1f, 1.1f, -1.1f}, + {-1.0f, 1.0f, 1.0f, -1.0f}, // red + {0.0f, 1.0f, 1.0f, -1.0f}, // green + {-1.0f, 0.0f, 1.0f, -1.0f}, // blue + {0.0f, 0.0f, 1.0f, -1.0f} // white + }; + count = sizeof(_rects) / sizeof(SDL_FRect); +#define rects _rects +#endif + + for (int i = 0; i < count; ++i) { + D3D11_RenderStartDrawOp(renderer); + D3D11_RenderSetBlendMode(renderer, renderer->blendMode); + +#if 0 + // Set colors for the test pattern: + a = 1.0f; + switch (i) { + case 0: r = 1.0f; g = 1.0f; b = 0.0f; break; + case 1: r = 1.0f; g = 0.0f; b = 0.0f; break; + case 2: r = 0.0f; g = 1.0f; b = 0.0f; break; + case 3: r = 0.0f; g = 0.0f; b = 1.0f; break; + case 4: r = 1.0f; g = 1.0f; b = 1.0f; break; + } +#endif + + VertexPositionColor vertices[] = { + {XMFLOAT3(rects[i].x, rects[i].y, 0.0f), XMFLOAT2(0.0f, 0.0f), XMFLOAT4(r, g, b, a)}, + {XMFLOAT3(rects[i].x, rects[i].y + rects[i].h, 0.0f), XMFLOAT2(0.0f, 0.0f), XMFLOAT4(r, g, b, a)}, + {XMFLOAT3(rects[i].x + rects[i].w, rects[i].y, 0.0f), XMFLOAT2(0.0f, 0.0f), XMFLOAT4(r, g, b, a)}, + {XMFLOAT3(rects[i].x + rects[i].w, rects[i].y + rects[i].h, 0.0f), XMFLOAT2(0.0f, 0.0f), XMFLOAT4(r, g, b, a)}, + }; + if (D3D11_UpdateVertexBuffer(renderer, vertices, sizeof(vertices)) != 0) { + return -1; + } + + D3D11_SetPixelShader( + renderer, + rendererData->colorPixelShader.Get(), + nullptr, + nullptr); + + D3D11_RenderFinishDrawOp(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, sizeof(vertices) / sizeof(VertexPositionColor)); + } + + return 0; +} + +static int +D3D11_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + D3D11_TextureData *textureData = (D3D11_TextureData *) texture->driverdata; + + D3D11_RenderStartDrawOp(renderer); + D3D11_RenderSetBlendMode(renderer, texture->blendMode); + + float minu = (float) srcrect->x / texture->w; + float maxu = (float) (srcrect->x + srcrect->w) / texture->w; + float minv = (float) srcrect->y / texture->h; + float maxv = (float) (srcrect->y + srcrect->h) / texture->h; + + float r = 1.0f; + float g = 1.0f; + float b = 1.0f; + float a = 1.0f; + if (texture->modMode & SDL_TEXTUREMODULATE_COLOR) { + r = (float)(texture->r / 255.0f); + g = (float)(texture->g / 255.0f); + b = (float)(texture->b / 255.0f); + } + if (texture->modMode & SDL_TEXTUREMODULATE_ALPHA) { + a = (float)(texture->a / 255.0f); + } + + VertexPositionColor vertices[] = { + {XMFLOAT3(dstrect->x, dstrect->y, 0.0f), XMFLOAT2(minu, minv), XMFLOAT4(r, g, b, a)}, + {XMFLOAT3(dstrect->x, dstrect->y + dstrect->h, 0.0f), XMFLOAT2(minu, maxv), XMFLOAT4(r, g, b, a)}, + {XMFLOAT3(dstrect->x + dstrect->w, dstrect->y, 0.0f), XMFLOAT2(maxu, minv), XMFLOAT4(r, g, b, a)}, + {XMFLOAT3(dstrect->x + dstrect->w, dstrect->y + dstrect->h, 0.0f), XMFLOAT2(maxu, maxv), XMFLOAT4(r, g, b, a)}, + }; + if (D3D11_UpdateVertexBuffer(renderer, vertices, sizeof(vertices)) != 0) { + return -1; + } + + D3D11_SetPixelShader( + renderer, + rendererData->texturePixelShader.Get(), + textureData->mainTextureResourceView.Get(), + rendererData->mainSampler.Get()); + + D3D11_RenderFinishDrawOp(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, sizeof(vertices) / sizeof(VertexPositionColor)); + + return 0; +} + +static int +D3D11_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect, + const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + D3D11_TextureData *textureData = (D3D11_TextureData *) texture->driverdata; + + D3D11_RenderStartDrawOp(renderer); + D3D11_RenderSetBlendMode(renderer, texture->blendMode); + + float minu = (float) srcrect->x / texture->w; + float maxu = (float) (srcrect->x + srcrect->w) / texture->w; + float minv = (float) srcrect->y / texture->h; + float maxv = (float) (srcrect->y + srcrect->h) / texture->h; + + float r = 1.0f; + float g = 1.0f; + float b = 1.0f; + float a = 1.0f; + if (texture->modMode & SDL_TEXTUREMODULATE_COLOR) { + r = (float)(texture->r / 255.0f); + g = (float)(texture->g / 255.0f); + b = (float)(texture->b / 255.0f); + } + if (texture->modMode & SDL_TEXTUREMODULATE_ALPHA) { + a = (float)(texture->a / 255.0f); + } + + if (flip & SDL_FLIP_HORIZONTAL) { + float tmp = maxu; + maxu = minu; + minu = tmp; + } + if (flip & SDL_FLIP_VERTICAL) { + float tmp = maxv; + maxv = minv; + minv = tmp; + } + + XMFLOAT4X4 oldModelMatrix = rendererData->vertexShaderConstantsData.model; + XMStoreFloat4x4( + &rendererData->vertexShaderConstantsData.model, + XMMatrixMultiply( + XMMatrixRotationZ((float)(XM_PI * (float) angle / 180.0f)), + XMMatrixTranslation(dstrect->x + center->x, dstrect->y + center->y, 0) + )); + + const float minx = -center->x; + const float maxx = dstrect->w - center->x; + const float miny = -center->y; + const float maxy = dstrect->h - center->y; + + VertexPositionColor vertices[] = { + {XMFLOAT3(minx, miny, 0.0f), XMFLOAT2(minu, minv), XMFLOAT4(r, g, b, a)}, + {XMFLOAT3(minx, maxy, 0.0f), XMFLOAT2(minu, maxv), XMFLOAT4(r, g, b, a)}, + {XMFLOAT3(maxx, miny, 0.0f), XMFLOAT2(maxu, minv), XMFLOAT4(r, g, b, a)}, + {XMFLOAT3(maxx, maxy, 0.0f), XMFLOAT2(maxu, maxv), XMFLOAT4(r, g, b, a)}, + }; + if (D3D11_UpdateVertexBuffer(renderer, vertices, sizeof(vertices)) != 0) { + return -1; + } + + D3D11_SetPixelShader( + renderer, + rendererData->texturePixelShader.Get(), + textureData->mainTextureResourceView.Get(), + rendererData->mainSampler.Get()); + + D3D11_RenderFinishDrawOp(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, sizeof(vertices) / sizeof(VertexPositionColor)); + + rendererData->vertexShaderConstantsData.model = oldModelMatrix; + + return 0; +} + +static int +D3D11_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, + Uint32 format, void * pixels, int pitch) +{ + D3D11_RenderData * data = (D3D11_RenderData *) renderer->driverdata; + HRESULT result = S_OK; + + // Retrieve a pointer to the back buffer: + ComPtr backBuffer; + result = data->swapChain->GetBuffer( + 0, + __uuidof(ID3D11Texture2D), + &backBuffer + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__ ", Get Back Buffer", result); + return -1; + } + + // Create a staging texture to copy the screen's data to: + ComPtr stagingTexture; + D3D11_TEXTURE2D_DESC stagingTextureDesc; + backBuffer->GetDesc(&stagingTextureDesc); + stagingTextureDesc.Width = rect->w; + stagingTextureDesc.Height = rect->h; + stagingTextureDesc.BindFlags = 0; + stagingTextureDesc.MiscFlags = 0; + stagingTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + stagingTextureDesc.Usage = D3D11_USAGE_STAGING; + result = data->d3dDevice->CreateTexture2D( + &stagingTextureDesc, + NULL, + &stagingTexture); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__ ", Create Staging Texture", result); + return -1; + } + + // Copy the desired portion of the back buffer to the staging texture: + D3D11_BOX srcBox; + srcBox.left = rect->x; + srcBox.right = rect->x + rect->w; + srcBox.top = rect->y; + srcBox.bottom = rect->y + rect->h; + srcBox.front = 0; + srcBox.back = 1; + data->d3dContext->CopySubresourceRegion( + stagingTexture.Get(), + D3D11CalcSubresource(0, 0, 0), + 0, 0, 0, + backBuffer.Get(), + D3D11CalcSubresource(0, 0, 0), + &srcBox); + + // Map the staging texture's data to CPU-accessible memory: + D3D11_MAPPED_SUBRESOURCE textureMemory = {0}; + result = data->d3dContext->Map( + stagingTexture.Get(), + D3D11CalcSubresource(0, 0, 0), + D3D11_MAP_READ, + 0, + &textureMemory); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(__FUNCTION__ ", Map Staging Texture to CPU Memory", result); + return -1; + } + + // Copy the data into the desired buffer, converting pixels to the + // desired format at the same time: + if (SDL_ConvertPixels( + rect->w, rect->h, + DXGIFormatToSDLPixelFormat(stagingTextureDesc.Format), + textureMemory.pData, + textureMemory.RowPitch, + format, + pixels, + pitch) != 0) + { + // When SDL_ConvertPixels fails, it'll have already set the format. + // Get the error message, and attach some extra data to it. + std::string errorMessage = string(__FUNCTION__ ", Convert Pixels failed: ") + SDL_GetError(); + SDL_SetError(errorMessage.c_str()); + return -1; + } + + // Unmap the texture: + data->d3dContext->Unmap( + stagingTexture.Get(), + D3D11CalcSubresource(0, 0, 0)); + + // All done. The staging texture will be cleaned up in it's container + // ComPtr<>'s destructor. + return 0; +} + +static void +D3D11_RenderPresent(SDL_Renderer * renderer) +{ + D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; + +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + // The first argument instructs DXGI to block until VSync, putting the application + // to sleep until the next VSync. This ensures we don't waste any cycles rendering + // frames that will never be displayed to the screen. + HRESULT hr = data->swapChain->Present(1, 0); +#else + // The application may optionally specify "dirty" or "scroll" + // rects to improve efficiency in certain scenarios. + // This option is not available on Windows Phone 8, to note. + DXGI_PRESENT_PARAMETERS parameters = {0}; + parameters.DirtyRectsCount = 0; + parameters.pDirtyRects = nullptr; + parameters.pScrollRect = nullptr; + parameters.pScrollOffset = nullptr; + + // The first argument instructs DXGI to block until VSync, putting the application + // to sleep until the next VSync. This ensures we don't waste any cycles rendering + // frames that will never be displayed to the screen. + HRESULT hr = data->swapChain->Present1(1, 0, ¶meters); +#endif + + // Discard the contents of the render target. + // This is a valid operation only when the existing contents will be entirely + // overwritten. If dirty or scroll rects are used, this call should be removed. + data->d3dContext->DiscardView(data->mainRenderTargetView.Get()); + + // If the device was removed either by a disconnect or a driver upgrade, we + // must recreate all device resources. + // + // TODO, WinRT: consider throwing an exception if D3D11_RenderPresent fails, especially if there is a way to salvedge debug info from users' machines + if (hr == DXGI_ERROR_DEVICE_REMOVED) + { + hr = D3D11_HandleDeviceLost(renderer); + if (FAILED(hr)) { + WIN_SetErrorFromHRESULT(__FUNCTION__, hr); + } + } + else + { + WIN_SetErrorFromHRESULT(__FUNCTION__, hr); + } +} + +#endif /* SDL_VIDEO_RENDER_D3D && !SDL_RENDER_DISABLED */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/direct3d11/SDL_render_d3d11_cpp.h b/src/render/direct3d11/SDL_render_d3d11_cpp.h new file mode 100644 index 000000000..14fc61270 --- /dev/null +++ b/src/render/direct3d11/SDL_render_d3d11_cpp.h @@ -0,0 +1,84 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +#include +#include +#include +#include + +struct SDL_VertexShaderConstants +{ + DirectX::XMFLOAT4X4 model; + DirectX::XMFLOAT4X4 view; + DirectX::XMFLOAT4X4 projection; +}; + +typedef struct +{ + Microsoft::WRL::ComPtr d3dDevice; + Microsoft::WRL::ComPtr d3dContext; + Microsoft::WRL::ComPtr swapChain; + Microsoft::WRL::ComPtr mainRenderTargetView; + Microsoft::WRL::ComPtr currentOffscreenRenderTargetView; + Microsoft::WRL::ComPtr inputLayout; + Microsoft::WRL::ComPtr vertexBuffer; + Microsoft::WRL::ComPtr vertexShader; + Microsoft::WRL::ComPtr texturePixelShader; + Microsoft::WRL::ComPtr colorPixelShader; + Microsoft::WRL::ComPtr blendModeBlend; + Microsoft::WRL::ComPtr blendModeAdd; + Microsoft::WRL::ComPtr blendModeMod; + Microsoft::WRL::ComPtr mainSampler; + Microsoft::WRL::ComPtr mainRasterizer; + D3D_FEATURE_LEVEL featureLevel; + + // Vertex buffer constants: + SDL_VertexShaderConstants vertexShaderConstantsData; + Microsoft::WRL::ComPtr vertexShaderConstants; + + // Cached renderer properties. + DirectX::XMFLOAT2 windowSizeInDIPs; + DirectX::XMFLOAT2 renderTargetSize; + Windows::Graphics::Display::DisplayOrientations orientation; + + // Transform used for display orientation. + DirectX::XMFLOAT4X4 orientationTransform3D; +} D3D11_RenderData; + +typedef struct +{ + Microsoft::WRL::ComPtr mainTexture; + Microsoft::WRL::ComPtr mainTextureResourceView; + Microsoft::WRL::ComPtr mainTextureRenderTargetView; + SDL_PixelFormat * pixelFormat; + Microsoft::WRL::ComPtr stagingTexture; + DirectX::XMINT2 lockedTexturePosition; +} D3D11_TextureData; + +struct VertexPositionColor +{ + DirectX::XMFLOAT3 pos; + DirectX::XMFLOAT2 tex; + DirectX::XMFLOAT4 color; +}; + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/thread/SDL_thread_c.h b/src/thread/SDL_thread_c.h index 1971ded24..90d9b57ca 100644 --- a/src/thread/SDL_thread_c.h +++ b/src/thread/SDL_thread_c.h @@ -36,6 +36,8 @@ #include "windows/SDL_systhread_c.h" #elif SDL_THREAD_PSP #include "psp/SDL_systhread_c.h" +#elif SDL_THREAD_STDCPP +#include "stdcpp/SDL_systhread_c.h" #else #error Need thread implementation for this platform #include "generic/SDL_systhread_c.h" diff --git a/src/thread/stdcpp/SDL_syscond.cpp b/src/thread/stdcpp/SDL_syscond.cpp new file mode 100644 index 000000000..4ca1d96f7 --- /dev/null +++ b/src/thread/stdcpp/SDL_syscond.cpp @@ -0,0 +1,183 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +extern "C" { +#include "SDL_thread.h" +} + +#include +#include +#include +#include + +#include "SDL_sysmutex_c.h" + +struct SDL_cond +{ + std::condition_variable_any cpp_cond; +}; + +/* Create a condition variable */ +extern "C" +SDL_cond * +SDL_CreateCond(void) +{ + /* Allocate and initialize the condition variable */ + try { + SDL_cond * cond = new SDL_cond; + return cond; + } catch (std::exception & ex) { + SDL_SetError("unable to create C++ condition variable: %s", ex.what()); + return NULL; + } catch (...) { + SDL_SetError("unable to create C++ condition variable due to an unknown exception"); + return NULL; + } +} + +/* Destroy a condition variable */ +extern "C" +void +SDL_DestroyCond(SDL_cond * cond) +{ + if (cond) { + try { + delete cond; + } catch (...) { + // catch any and all exceptions, just in case something happens + } + } +} + +/* Restart one of the threads that are waiting on the condition variable */ +extern "C" +int +SDL_CondSignal(SDL_cond * cond) +{ + if (!cond) { + SDL_SetError("Passed a NULL condition variable"); + return -1; + } + + try { + cond->cpp_cond.notify_one(); + return 0; + } catch (...) { + // catch any and all exceptions, just in case something happens + SDL_SetError("unable to signal C++ condition variable due to an unknown exception"); + return -1; + } +} + +/* Restart all threads that are waiting on the condition variable */ +extern "C" +int +SDL_CondBroadcast(SDL_cond * cond) +{ + if (!cond) { + SDL_SetError("Passed a NULL condition variable"); + return -1; + } + + try { + cond->cpp_cond.notify_all(); + return 0; + } catch (...) { + // catch any and all exceptions, just in case something happens + SDL_SetError("unable to broadcast C++ condition variable due to an unknown exception"); + return -1; + } +} + +/* Wait on the condition variable for at most 'ms' milliseconds. + The mutex must be locked before entering this function! + The mutex is unlocked during the wait, and locked again after the wait. + +Typical use: + +Thread A: + SDL_LockMutex(lock); + while ( ! condition ) { + SDL_CondWait(cond, lock); + } + SDL_UnlockMutex(lock); + +Thread B: + SDL_LockMutex(lock); + ... + condition = true; + ... + SDL_CondSignal(cond); + SDL_UnlockMutex(lock); + */ +extern "C" +int +SDL_CondWaitTimeout(SDL_cond * cond, SDL_mutex * mutex, Uint32 ms) +{ + if (!cond) { + SDL_SetError("Passed a NULL condition variable"); + return -1; + } + + if (!mutex) { + SDL_SetError("Passed a NULL mutex variable"); + return -1; + } + + try { + std::unique_lock cpp_lock(mutex->cpp_mutex, std::defer_lock_t()); + if (ms == SDL_MUTEX_MAXWAIT) { + cond->cpp_cond.wait( + cpp_lock + ); + cpp_lock.release(); + return 0; + } else { + auto wait_result = cond->cpp_cond.wait_for( + cpp_lock, + std::chrono::duration(ms) + ); + cpp_lock.release(); + if (wait_result == std::cv_status::timeout) { + return SDL_MUTEX_TIMEDOUT; + } else { + return 0; + } + } + } catch (std::exception & ex) { + SDL_SetError("unable to wait on C++ condition variable: %s", ex.what()); + return -1; + } catch (...) { + SDL_SetError("unable to lock wait on C++ condition variable due to an unknown exception"); + return -1; + } +} + +/* Wait on the condition variable forever */ +extern "C" +int +SDL_CondWait(SDL_cond * cond, SDL_mutex * mutex) +{ + return SDL_CondWaitTimeout(cond, mutex, SDL_MUTEX_MAXWAIT); +} + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/thread/stdcpp/SDL_sysmutex.cpp b/src/thread/stdcpp/SDL_sysmutex.cpp new file mode 100644 index 000000000..e43b255e8 --- /dev/null +++ b/src/thread/stdcpp/SDL_sysmutex.cpp @@ -0,0 +1,113 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +extern "C" { +#include "SDL_thread.h" +#include "SDL_systhread_c.h" +#include "SDL_log.h" +} + +#include + +#include "SDL_sysmutex_c.h" +#include + + +/* Create a mutex */ +extern "C" +SDL_mutex * +SDL_CreateMutex(void) +{ + /* Allocate and initialize the mutex */ + try { + SDL_mutex * mutex = new SDL_mutex; + return mutex; + } catch (std::exception & ex) { + SDL_SetError("unable to create C++ mutex: %s", ex.what()); + return NULL; + } catch (...) { + SDL_SetError("unable to create C++ mutex due to an unknown exception"); + return NULL; + } +} + +/* Free the mutex */ +extern "C" +void +SDL_DestroyMutex(SDL_mutex * mutex) +{ + if (mutex) { + try { + delete mutex; + } catch (...) { + // catch any and all exceptions, just in case something happens + } + } +} + +/* Lock the semaphore */ +extern "C" +int +SDL_mutexP(SDL_mutex * mutex) +{ + SDL_threadID threadID = SDL_ThreadID(); + DWORD realThreadID = GetCurrentThreadId(); + if (mutex == NULL) { + SDL_SetError("Passed a NULL mutex"); + return -1; + } + + try { + mutex->cpp_mutex.lock(); + return 0; + } catch (std::exception & ex) { + SDL_SetError("unable to lock C++ mutex: %s", ex.what()); + return -1; + } catch (...) { + SDL_SetError("unable to lock C++ mutex due to an unknown exception"); + return -1; + } +} + +/* Unlock the mutex */ +extern "C" +int +SDL_mutexV(SDL_mutex * mutex) +{ + SDL_threadID threadID = SDL_ThreadID(); + DWORD realThreadID = GetCurrentThreadId(); + if (mutex == NULL) { + SDL_SetError("Passed a NULL mutex"); + return -1; + } + + try { + mutex->cpp_mutex.unlock(); + return 0; + } catch (...) { + // catch any and all exceptions, just in case something happens. + SDL_SetError("unable to unlock C++ mutex due to an unknown exception"); + return -1; + } +} + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/thread/stdcpp/SDL_sysmutex_c.h b/src/thread/stdcpp/SDL_sysmutex_c.h new file mode 100644 index 000000000..500e57d40 --- /dev/null +++ b/src/thread/stdcpp/SDL_sysmutex_c.h @@ -0,0 +1,30 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +#include + +struct SDL_mutex +{ + std::recursive_mutex cpp_mutex; +}; + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/thread/stdcpp/SDL_systhread.cpp b/src/thread/stdcpp/SDL_systhread.cpp new file mode 100644 index 000000000..fd4cdafb3 --- /dev/null +++ b/src/thread/stdcpp/SDL_systhread.cpp @@ -0,0 +1,124 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +/* Thread management routines for SDL */ + +extern "C" { +#include "SDL_thread.h" +#include "../SDL_systhread.h" +#include "../SDL_thread_c.h" +#include "SDL_log.h" +} + +#include +#include + +// HACK: Mimic C++11's thread_local keyword on Visual C++ 2012 (aka. VC++ 11) +// TODO: make sure this hack doesn't get used if and when Visual C++ supports +// the official, 'thread_local' keyword. +#ifdef _MSC_VER +#define thread_local __declspec(thread) +// Documentation for __declspec(thread) can be found online at: +// http://msdn.microsoft.com/en-us/library/2s9wt68x.aspx +#endif + +static void +RunThread(void *args) +{ + SDL_RunThread(args); +} + +extern "C" +int +SDL_SYS_CreateThread(SDL_Thread * thread, void *args) +{ + try { + std::thread cpp_thread(RunThread, args); + thread->handle = (void *) new std::thread(std::move(cpp_thread)); + return 0; + } catch (std::exception & ex) { + SDL_SetError("unable to create a C++ thread: %s", ex.what()); + return -1; + } catch (...) { + SDL_SetError("unable to create a C++ thread due to an unknown exception"); + return -1; + } +} + +extern "C" +void +SDL_SYS_SetupThread(const char *name) +{ + // Make sure a thread ID gets assigned ASAP, for debugging purposes: + SDL_ThreadID(); + return; +} + +extern "C" +SDL_threadID +SDL_ThreadID(void) +{ + static thread_local SDL_threadID current_thread_id = 0; + static SDL_threadID next_thread_id = 1; + static std::mutex next_thread_id_mutex; + + if (current_thread_id == 0) { + std::lock_guard lock(next_thread_id_mutex); + current_thread_id = next_thread_id; + ++next_thread_id; + } + + return current_thread_id; +} + +extern "C" +int +SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority) +{ + // Thread priorities do not look to be settable via C++11's thread + // interface, at least as of this writing (Nov 2012). std::thread does + // provide access to the OS' native handle, however, and some form of + // priority-setting could, in theory, be done through this interface. + return (0); +} + +extern "C" +void +SDL_SYS_WaitThread(SDL_Thread * thread) +{ + if ( ! thread) { + return; + } + + try { + std::thread * cpp_thread = (std::thread *) thread->handle; + if (cpp_thread->joinable()) { + cpp_thread->join(); + } + } catch (...) { + // Catch any exceptions, just in case. + // Report nothing, as SDL_WaitThread does not seem to offer a means + // to report errors to its callers. + } +} + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/thread/stdcpp/SDL_systhread_c.h b/src/thread/stdcpp/SDL_systhread_c.h new file mode 100644 index 000000000..4a060c98a --- /dev/null +++ b/src/thread/stdcpp/SDL_systhread_c.h @@ -0,0 +1,26 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +/* For a thread handle, use a void pointer to a std::thread */ +typedef void * SYS_ThreadHandle; + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 4973648c7..e149b8a18 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -343,6 +343,9 @@ extern VideoBootStrap DirectFB_bootstrap; #if SDL_VIDEO_DRIVER_WINDOWS extern VideoBootStrap WINDOWS_bootstrap; #endif +#if SDL_VIDEO_DRIVER_WINRT +extern VideoBootStrap WINRT_bootstrap; +#endif #if SDL_VIDEO_DRIVER_BWINDOW extern VideoBootStrap BWINDOW_bootstrap; #endif diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index bab6c6cb9..1404ba0e8 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -64,6 +64,9 @@ static VideoBootStrap *bootstrap[] = { #if SDL_VIDEO_DRIVER_WINDOWS &WINDOWS_bootstrap, #endif +#if SDL_VIDEO_DRIVER_WINRT + &WINRT_bootstrap, +#endif #if SDL_VIDEO_DRIVER_BWINDOW &BWINDOW_bootstrap, #endif diff --git a/src/video/uikit/SDL_uikitwindow.m b/src/video/uikit/SDL_uikitwindow.m index b3f7ac9f4..0285b6bcc 100644 --- a/src/video/uikit/SDL_uikitwindow.m +++ b/src/video/uikit/SDL_uikitwindow.m @@ -202,7 +202,7 @@ UIKit_CreateWindow(_THIS, SDL_Window *window) if (external) { [uiwindow setScreen:data->uiscreen]; } - + if (SetupWindowData(_this, window, uiwindow, SDL_TRUE) < 0) { [uiwindow release]; return -1; @@ -301,10 +301,12 @@ SDL_bool UIKit_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info) { UIWindow *uiwindow = ((SDL_WindowData *) window->driverdata)->uiwindow; + UIViewController *uiviewcontroller = ((SDL_WindowData *) window->driverdata)->viewcontroller; if (info->version.major <= SDL_MAJOR_VERSION) { info->subsystem = SDL_SYSWM_UIKIT; info->info.uikit.window = uiwindow; + info->info.uikit.viewcontroller = uiviewcontroller; return SDL_TRUE; } else { SDL_SetError("Application not compiled with SDL %d.%d\n", diff --git a/src/video/windowsrt/SDL_WinRTApp.cpp b/src/video/windowsrt/SDL_WinRTApp.cpp new file mode 100644 index 000000000..a807bda7f --- /dev/null +++ b/src/video/windowsrt/SDL_WinRTApp.cpp @@ -0,0 +1,978 @@ + +#include +#include +#include + +#include "ppltasks.h" + +extern "C" { +#include "SDL_assert.h" +#include "SDL_events.h" +#include "SDL_hints.h" +#include "SDL_log.h" +#include "SDL_stdinc.h" +#include "SDL_render.h" +#include "../SDL_sysvideo.h" +#include "../../SDL_hints_c.h" +#include "../../events/scancodes_windows.h" +#include "../../events/SDL_mouse_c.h" +#include "../../events/SDL_keyboard_c.h" +#include "../../events/SDL_windowevents_c.h" +#include "../../render/SDL_sysrender.h" +} + +#include "SDL_winrtvideo.h" +#include "SDL_WinRTApp.h" + +using namespace concurrency; +using namespace std; +using namespace Windows::ApplicationModel; +using namespace Windows::ApplicationModel::Core; +using namespace Windows::ApplicationModel::Activation; +using namespace Windows::Devices::Input; +using namespace Windows::Graphics::Display; +using namespace Windows::Foundation; +using namespace Windows::System; +using namespace Windows::UI::Core; +using namespace Windows::UI::Input; + +// Compile-time debugging options: +// To enable, uncomment; to disable, comment them out. +//#define LOG_POINTER_EVENTS 1 +//#define LOG_WINDOW_EVENTS 1 +//#define LOG_ORIENTATION_EVENTS 1 + + +// HACK, DLudwig: The C-style main() will get loaded via the app's +// WinRT-styled main(), which is part of SDLmain_for_WinRT.cpp. +// This seems wrong on some level, but does seem to work. +typedef int (*SDL_WinRT_MainFunction)(int, char **); +static SDL_WinRT_MainFunction SDL_WinRT_main = nullptr; + +// HACK, DLudwig: record a reference to the global, Windows RT 'app'/view. +// SDL/WinRT will use this throughout its code. +// +// TODO, WinRT: consider replacing SDL_WinRTGlobalApp with something +// non-global, such as something created inside +// SDL_InitSubSystem(SDL_INIT_VIDEO), or something inside +// SDL_CreateWindow(). +SDL_WinRTApp ^ SDL_WinRTGlobalApp = nullptr; + +ref class SDLApplicationSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource +{ +public: + virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView(); +}; + +IFrameworkView^ SDLApplicationSource::CreateView() +{ + // TODO, WinRT: see if this function (CreateView) can ever get called + // more than once. For now, just prevent it from ever assigning + // SDL_WinRTGlobalApp more than once. + SDL_assert(!SDL_WinRTGlobalApp); + SDL_WinRTApp ^ app = ref new SDL_WinRTApp(); + if (!SDL_WinRTGlobalApp) + { + SDL_WinRTGlobalApp = app; + } + return app; +} + +__declspec(dllexport) int SDL_WinRT_RunApplication(SDL_WinRT_MainFunction mainFunction) +{ + SDL_WinRT_main = mainFunction; + auto direct3DApplicationSource = ref new SDLApplicationSource(); + CoreApplication::Run(direct3DApplicationSource); + return 0; +} + +static void WINRT_SetDisplayOrientationsPreference(const char *name, const char *oldValue, const char *newValue) +{ + SDL_assert(SDL_strcmp(name, SDL_HINT_ORIENTATIONS) == 0); + + // Start with no orientation flags, then add each in as they're parsed + // from newValue. + unsigned int orientationFlags = 0; + std::istringstream tokenizer(newValue); + while (!tokenizer.eof()) { + std::string orientationName; + std::getline(tokenizer, orientationName, ' '); + if (orientationName == "LandscapeLeft") { + orientationFlags |= (unsigned int) DisplayOrientations::LandscapeFlipped; + } else if (orientationName == "LandscapeRight") { + orientationFlags |= (unsigned int) DisplayOrientations::Landscape; + } else if (orientationName == "Portrait") { + orientationFlags |= (unsigned int) DisplayOrientations::Portrait; + } else if (orientationName == "PortraitUpsideDown") { + orientationFlags |= (unsigned int) DisplayOrientations::PortraitFlipped; + } + } + + // If no valid orientation flags were specified, use a reasonable set of defaults: + if (!orientationFlags) { + // TODO, WinRT: consider seeing if an app's default orientation flags can be found out via some API call(s). + orientationFlags = (unsigned int) ( \ + DisplayOrientations::Landscape | + DisplayOrientations::LandscapeFlipped | + DisplayOrientations::Portrait | + DisplayOrientations::PortraitFlipped); + } + + // Set the orientation/rotation preferences. Please note that this does + // not constitute a 100%-certain lock of a given set of possible + // orientations. According to Microsoft's documentation on Windows RT [1] + // when a device is not capable of being rotated, Windows may ignore + // the orientation preferences, and stick to what the device is capable of + // displaying. + // + // [1] Documentation on the 'InitialRotationPreference' setting for a + // Windows app's manifest file describes how some orientation/rotation + // preferences may be ignored. See + // http://msdn.microsoft.com/en-us/library/windows/apps/hh700343.aspx + // for details. Microsoft's "Display orientation sample" also gives an + // outline of how Windows treats device rotation + // (http://code.msdn.microsoft.com/Display-Orientation-Sample-19a58e93). + DisplayProperties::AutoRotationPreferences = (DisplayOrientations) orientationFlags; +} + +SDL_WinRTApp::SDL_WinRTApp() : + m_windowClosed(false), + m_windowVisible(true), + m_sdlWindowData(NULL), + m_sdlVideoDevice(NULL), + m_useRelativeMouseMode(false) +{ +} + +void SDL_WinRTApp::Initialize(CoreApplicationView^ applicationView) +{ + applicationView->Activated += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnActivated); + + CoreApplication::Suspending += + ref new EventHandler(this, &SDL_WinRTApp::OnSuspending); + + CoreApplication::Resuming += + ref new EventHandler(this, &SDL_WinRTApp::OnResuming); + + DisplayProperties::OrientationChanged += + ref new DisplayPropertiesEventHandler(this, &SDL_WinRTApp::OnOrientationChanged); + + // Register the hint, SDL_HINT_ORIENTATIONS, with SDL. This needs to be + // done before the hint's callback is registered (as of Feb 22, 2013), + // otherwise the hint callback won't get registered. + // + // WinRT, TODO: see if an app's default orientation can be found out via WinRT API(s), then set the initial value of SDL_HINT_ORIENTATIONS accordingly. + SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight Portrait PortraitUpsideDown"); + SDL_RegisterHintChangedCb(SDL_HINT_ORIENTATIONS, WINRT_SetDisplayOrientationsPreference); +} + +void SDL_WinRTApp::OnOrientationChanged(Object^ sender) +{ +#if LOG_ORIENTATION_EVENTS==1 + CoreWindow^ window = CoreWindow::GetForCurrentThread(); + if (window) { + SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, CoreWindow Size={%f,%f}\n", + __FUNCTION__, + (int)DisplayProperties::CurrentOrientation, + (int)DisplayProperties::NativeOrientation, + (int)DisplayProperties::AutoRotationPreferences, + window->Bounds.Width, + window->Bounds.Height); + } else { + SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d\n", + __FUNCTION__, + (int)DisplayProperties::CurrentOrientation, + (int)DisplayProperties::NativeOrientation, + (int)DisplayProperties::AutoRotationPreferences); + } +#endif +} + +void SDL_WinRTApp::SetWindow(CoreWindow^ window) +{ +#if LOG_WINDOW_EVENTS==1 + SDL_Log("%s, current orientation=%d, native orientation=%d, auto rot. pref=%d, window Size={%f,%f}\n", + __FUNCTION__, + (int)DisplayProperties::CurrentOrientation, + (int)DisplayProperties::NativeOrientation, + (int)DisplayProperties::AutoRotationPreferences, + window->Bounds.Width, + window->Bounds.Height); +#endif + + window->SizeChanged += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnWindowSizeChanged); + + window->VisibilityChanged += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnVisibilityChanged); + + window->Closed += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnWindowClosed); + +#if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP + window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0); +#endif + + window->PointerPressed += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnPointerPressed); + + window->PointerReleased += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnPointerReleased); + + window->PointerWheelChanged += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnPointerWheelChanged); + + window->PointerMoved += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnPointerMoved); + +#if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP + // Retrieves relative-only mouse movements: + Windows::Devices::Input::MouseDevice::GetForCurrentView()->MouseMoved += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnMouseMoved); +#endif + + window->KeyDown += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnKeyDown); + + window->KeyUp += + ref new TypedEventHandler(this, &SDL_WinRTApp::OnKeyUp); +} + +void SDL_WinRTApp::Load(Platform::String^ entryPoint) +{ +} + +void SDL_WinRTApp::Run() +{ + if (SDL_WinRT_main) + { + // TODO, WinRT: pass the C-style main() a reasonably realistic + // representation of command line arguments. + int argc = 0; + char **argv = NULL; + SDL_WinRT_main(argc, argv); + } +} + +void SDL_WinRTApp::PumpEvents() +{ + if (!m_windowClosed) + { + if (m_windowVisible) + { + CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent); + } + else + { + CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending); + } + } +} + +void SDL_WinRTApp::Uninitialize() +{ +} + +void SDL_WinRTApp::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args) +{ +#if LOG_WINDOW_EVENTS==1 + SDL_Log("%s, size={%f,%f}, current orientation=%d, native orientation=%d, auto rot. pref=%d, m_sdlWindowData?=%s\n", + __FUNCTION__, + args->Size.Width, args->Size.Height, + (int)DisplayProperties::CurrentOrientation, + (int)DisplayProperties::NativeOrientation, + (int)DisplayProperties::AutoRotationPreferences, + (m_sdlWindowData ? "yes" : "no")); +#endif + + if (m_sdlWindowData) { + // Make the new window size be the one true fullscreen mode. + // This change was done, in part, to allow the Direct3D 11.1 renderer + // to receive window-resize events as a device rotates. + // Before, rotating a device from landscape, to portrait, and then + // back to landscape would cause the Direct3D 11.1 swap buffer to + // not get resized appropriately. SDL would, on the rotation from + // landscape to portrait, re-resize the SDL window to it's initial + // size (landscape). On the subsequent rotation, SDL would drop the + // window-resize event as it appeared the SDL window didn't change + // size, and the Direct3D 11.1 renderer wouldn't resize its swap + // chain. + // + // TODO, WinRT: consider dropping old display modes after the fullscreen window changes size (from rotations, etc.) + m_sdlWindowData->sdlWindow->fullscreen_mode = SDL_WinRTGlobalApp->GetMainDisplayMode(); + SDL_AddDisplayMode(&m_sdlVideoDevice->displays[0], &m_sdlWindowData->sdlWindow->fullscreen_mode); + + // HACK, Feb 19, 2013: SDL_WINDOWEVENT_RESIZED events, when sent, + // will attempt to fix the values of the main window's renderer's + // viewport. While this can be good, it does appear to be buggy, + // and can cause a fullscreen viewport to become corrupted. This + // behavior was noticed on a Surface RT while rotating the device + // from landscape to portrait. Oddly enough, this did not occur + // in the Windows Simulator. + // + // Backing up, then restoring, the main renderer's 'resized' flag + // seems to fix fullscreen viewport problems when rotating a + // Windows device. + // + // Commencing hack in 3... 2... 1... + SDL_Renderer * rendererForMainWindow = SDL_GetRenderer(m_sdlWindowData->sdlWindow); + // For now, limit the hack to when the Direct3D 11.1 is getting used: + const bool usingD3D11Renderer = \ + (rendererForMainWindow != NULL) && + (SDL_strcmp(rendererForMainWindow->info.name, "direct3d 11.1") == 0); + SDL_bool wasD3D11RendererResized = SDL_FALSE; + if (usingD3D11Renderer) { + wasD3D11RendererResized = rendererForMainWindow->resized; + } + + // Send the window-resize event to the rest of SDL, and to apps: + const int windowWidth = (int) ceil(args->Size.Width); + const int windowHeight = (int) ceil(args->Size.Height); + SDL_SendWindowEvent( + m_sdlWindowData->sdlWindow, + SDL_WINDOWEVENT_RESIZED, + windowWidth, + windowHeight); + + // Viewport hack, part two: + if (usingD3D11Renderer) { + rendererForMainWindow->resized = wasD3D11RendererResized; + } + } +} + +void SDL_WinRTApp::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args) +{ +#if LOG_WINDOW_EVENTS==1 + SDL_Log("%s, visible?=%s, m_sdlWindowData?=%s\n", + __FUNCTION__, + (args->Visible ? "yes" : "no"), + (m_sdlWindowData ? "yes" : "no")); +#endif + + m_windowVisible = args->Visible; + if (m_sdlWindowData) { + SDL_bool wasSDLWindowSurfaceValid = m_sdlWindowData->sdlWindow->surface_valid; + + if (args->Visible) { + SDL_SendWindowEvent(m_sdlWindowData->sdlWindow, SDL_WINDOWEVENT_SHOWN, 0, 0); + } else { + SDL_SendWindowEvent(m_sdlWindowData->sdlWindow, SDL_WINDOWEVENT_HIDDEN, 0, 0); + } + + // HACK: Prevent SDL's window-hide handling code, which currently + // triggers a fake window resize (possibly erronously), from + // marking the SDL window's surface as invalid. + // + // A better solution to this probably involves figuring out if the + // fake window resize can be prevented. + m_sdlWindowData->sdlWindow->surface_valid = wasSDLWindowSurfaceValid; + } +} + +void SDL_WinRTApp::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args) +{ +#if LOG_WINDOW_EVENTS==1 + SDL_Log("%s\n", __FUNCTION__); +#endif + m_windowClosed = true; +} + +static Uint8 +WINRT_GetSDLButtonForPointerPoint(PointerPoint ^ pt) +{ + switch (pt->Properties->PointerUpdateKind) + { + case PointerUpdateKind::LeftButtonPressed: + case PointerUpdateKind::LeftButtonReleased: + return SDL_BUTTON_LEFT; + + case PointerUpdateKind::RightButtonPressed: + case PointerUpdateKind::RightButtonReleased: + return SDL_BUTTON_RIGHT; + + case PointerUpdateKind::MiddleButtonPressed: + case PointerUpdateKind::MiddleButtonReleased: + return SDL_BUTTON_MIDDLE; + + case PointerUpdateKind::XButton1Pressed: + case PointerUpdateKind::XButton1Released: + return SDL_BUTTON_X1; + + case PointerUpdateKind::XButton2Pressed: + case PointerUpdateKind::XButton2Released: + return SDL_BUTTON_X2; + + default: + break; + } + + return 0; +} + +static const char * +WINRT_ConvertPointerUpdateKindToString(PointerUpdateKind kind) +{ + switch (kind) + { + case PointerUpdateKind::Other: + return "Other"; + case PointerUpdateKind::LeftButtonPressed: + return "LeftButtonPressed"; + case PointerUpdateKind::LeftButtonReleased: + return "LeftButtonReleased"; + case PointerUpdateKind::RightButtonPressed: + return "RightButtonPressed"; + case PointerUpdateKind::RightButtonReleased: + return "RightButtonReleased"; + case PointerUpdateKind::MiddleButtonPressed: + return "MiddleButtonPressed"; + case PointerUpdateKind::MiddleButtonReleased: + return "MiddleButtonReleased"; + case PointerUpdateKind::XButton1Pressed: + return "XButton1Pressed"; + case PointerUpdateKind::XButton1Released: + return "XButton1Released"; + case PointerUpdateKind::XButton2Pressed: + return "XButton2Pressed"; + case PointerUpdateKind::XButton2Released: + return "XButton2Released"; + } + + return ""; +} + +static void +WINRT_LogPointerEvent(const string & header, PointerEventArgs ^ args, Point transformedPoint) +{ + PointerPoint ^ pt = args->CurrentPoint; + SDL_Log("%s: Position={%f,%f}, Transformed Pos={%f, %f}, MouseWheelDelta=%d, FrameId=%d, PointerId=%d, PointerUpdateKind=%s\n", + header.c_str(), + pt->Position.X, pt->Position.Y, + transformedPoint.X, transformedPoint.Y, + pt->Properties->MouseWheelDelta, + pt->FrameId, + pt->PointerId, + WINRT_ConvertPointerUpdateKindToString(args->CurrentPoint->Properties->PointerUpdateKind)); +} + +void SDL_WinRTApp::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args) +{ +#if LOG_POINTER_EVENTS + WINRT_LogPointerEvent("mouse down", args, TransformCursor(args->CurrentPoint->Position)); +#endif + + if (m_sdlWindowData) { + Uint8 button = WINRT_GetSDLButtonForPointerPoint(args->CurrentPoint); + if (button) { + SDL_SendMouseButton(m_sdlWindowData->sdlWindow, 0, SDL_PRESSED, button); + } + } +} + +void SDL_WinRTApp::OnPointerReleased(CoreWindow^ sender, PointerEventArgs^ args) +{ +#if LOG_POINTER_EVENTS + WINRT_LogPointerEvent("mouse up", args, TransformCursor(args->CurrentPoint->Position)); +#endif + + if (m_sdlWindowData) { + Uint8 button = WINRT_GetSDLButtonForPointerPoint(args->CurrentPoint); + if (button) { + SDL_SendMouseButton(m_sdlWindowData->sdlWindow, 0, SDL_RELEASED, button); + } + } +} + +void SDL_WinRTApp::OnPointerWheelChanged(CoreWindow^ sender, PointerEventArgs^ args) +{ +#if LOG_POINTER_EVENTS + WINRT_LogPointerEvent("wheel changed", args, TransformCursor(args->CurrentPoint->Position)); +#endif + + if (m_sdlWindowData) { + // FIXME: This may need to accumulate deltas up to WHEEL_DELTA + short motion = args->CurrentPoint->Properties->MouseWheelDelta / WHEEL_DELTA; + SDL_SendMouseWheel(m_sdlWindowData->sdlWindow, 0, 0, motion); + } +} + +static inline int _lround(float arg) { + if (arg >= 0.0f) { + return (int)floor(arg + 0.5f); + } else { + return (int)ceil(arg - 0.5f); + } +} + +void SDL_WinRTApp::OnMouseMoved(MouseDevice^ mouseDevice, MouseEventArgs^ args) +{ + if (m_sdlWindowData && m_useRelativeMouseMode) { + // DLudwig, 2012-12-28: On some systems, namely Visual Studio's Windows + // Simulator, as well as Windows 8 in a Parallels 8 VM, MouseEventArgs' + // MouseDelta field often reports very large values. More information + // on this can be found at the following pages on MSDN: + // - http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/a3c789fa-f1c5-49c4-9c0a-7db88d0f90f8 + // - https://connect.microsoft.com/VisualStudio/Feedback/details/756515 + // + // The values do not appear to be as large when running on some systems, + // most notably a Surface RT. Furthermore, the values returned by + // CoreWindow's PointerMoved event, and sent to this class' OnPointerMoved + // method, do not ever appear to be large, even when MouseEventArgs' + // MouseDelta is reporting to the contrary. + // + // On systems with the large-values behavior, it appears that the values + // get reported as if the screen's size is 65536 units in both the X and Y + // dimensions. This can be viewed by using Windows' now-private, "Raw Input" + // APIs. (GetRawInputData, RegisterRawInputDevices, WM_INPUT, etc.) + // + // MSDN's documentation on MouseEventArgs' MouseDelta field (at + // http://msdn.microsoft.com/en-us/library/windows/apps/windows.devices.input.mouseeventargs.mousedelta ), + // does not seem to indicate (to me) that its values should be so large. It + // says that its values should be a "change in screen location". I could + // be misinterpreting this, however a post on MSDN from a Microsoft engineer (see: + // http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/09a9868e-95bb-4858-ba1a-cb4d2c298d62 ), + // indicates that these values are in DIPs, which is the same unit used + // by CoreWindow's PointerMoved events (via the Position field in its CurrentPoint + // property. See http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.input.pointerpoint.position.aspx + // for details.) + // + // To note, PointerMoved events are sent a 'RawPosition' value (via the + // CurrentPoint property in MouseEventArgs), however these do not seem + // to exhibit the same large-value behavior. + // + // The values passed via PointerMoved events can't always be used for relative + // mouse motion, unfortunately. Its values are bound to the cursor's position, + // which stops when it hits one of the screen's edges. This can be a problem in + // first person shooters, whereby it is normal for mouse motion to travel far + // along any one axis for a period of time. MouseMoved events do not have the + // screen-bounding limitation, and can be used regardless of where the system's + // cursor is. + // + // One possible workaround would be to programmatically set the cursor's + // position to the screen's center (when SDL's relative mouse mode is enabled), + // however Windows RT does not yet seem to have the ability to set the cursor's + // position via a public API. Win32 did this via an API call, SetCursorPos, + // however WinRT makes this function be private. Apps that use it won't get + // approved for distribution in the Windows Store. I've yet to be able to find + // a suitable, store-friendly counterpart for WinRT. + // + // There may be some room for a workaround whereby OnPointerMoved's values + // are compared to the values from OnMouseMoved in order to detect + // when this bug is active. A suitable transformation could then be made to + // OnMouseMoved's values. For now, however, the system-reported values are sent + // to SDL with minimal transformation: from native screen coordinates (in DIPs) + // to SDL window coordinates. + // + const Point mouseDeltaInDIPs((float)args->MouseDelta.X, (float)args->MouseDelta.Y); + const Point mouseDeltaInSDLWindowCoords = TransformCursor(mouseDeltaInDIPs); + SDL_SendMouseMotion( + m_sdlWindowData->sdlWindow, + 0, + 1, + _lround(mouseDeltaInSDLWindowCoords.X), + _lround(mouseDeltaInSDLWindowCoords.Y)); + } +} + +// Applies necessary geometric transformations to raw cursor positions: +Point SDL_WinRTApp::TransformCursor(Point rawPosition) +{ + if ( ! m_sdlWindowData || ! m_sdlWindowData->sdlWindow ) { + return rawPosition; + } + CoreWindow ^ nativeWindow = CoreWindow::GetForCurrentThread(); + Point outputPosition; + outputPosition.X = rawPosition.X * (((float32)m_sdlWindowData->sdlWindow->w) / nativeWindow->Bounds.Width); + outputPosition.Y = rawPosition.Y * (((float32)m_sdlWindowData->sdlWindow->h) / nativeWindow->Bounds.Height); + return outputPosition; +} + +void SDL_WinRTApp::OnPointerMoved(CoreWindow^ sender, PointerEventArgs^ args) +{ +#if LOG_POINTER_EVENTS + WINRT_LogPointerEvent("pointer moved", args, TransformCursor(args->CurrentPoint->Position)); +#endif + + if (m_sdlWindowData && ! m_useRelativeMouseMode) + { + Point transformedPoint = TransformCursor(args->CurrentPoint->Position); + SDL_SendMouseMotion(m_sdlWindowData->sdlWindow, 0, 0, (int)transformedPoint.X, (int)transformedPoint.Y); + } +} + +static SDL_Scancode WinRT_Official_Keycodes[] = { + SDL_SCANCODE_UNKNOWN, // VirtualKey.None -- 0 + SDL_SCANCODE_UNKNOWN, // VirtualKey.LeftButton -- 1 + SDL_SCANCODE_UNKNOWN, // VirtualKey.RightButton -- 2 + SDL_SCANCODE_CANCEL, // VirtualKey.Cancel -- 3 + SDL_SCANCODE_UNKNOWN, // VirtualKey.MiddleButton -- 4 + SDL_SCANCODE_UNKNOWN, // VirtualKey.XButton1 -- 5 + SDL_SCANCODE_UNKNOWN, // VirtualKey.XButton2 -- 6 + SDL_SCANCODE_UNKNOWN, // -- 7 + SDL_SCANCODE_BACKSPACE, // VirtualKey.Back -- 8 + SDL_SCANCODE_TAB, // VirtualKey.Tab -- 9 + SDL_SCANCODE_UNKNOWN, // -- 10 + SDL_SCANCODE_UNKNOWN, // -- 11 + SDL_SCANCODE_CLEAR, // VirtualKey.Clear -- 12 + SDL_SCANCODE_RETURN, // VirtualKey.Enter -- 13 + SDL_SCANCODE_UNKNOWN, // -- 14 + SDL_SCANCODE_UNKNOWN, // -- 15 + SDL_SCANCODE_LSHIFT, // VirtualKey.Shift -- 16 + SDL_SCANCODE_LCTRL, // VirtualKey.Control -- 17 + SDL_SCANCODE_MENU, // VirtualKey.Menu -- 18 + SDL_SCANCODE_PAUSE, // VirtualKey.Pause -- 19 + SDL_SCANCODE_CAPSLOCK, // VirtualKey.CapitalLock -- 20 + SDL_SCANCODE_UNKNOWN, // VirtualKey.Kana or VirtualKey.Hangul -- 21 + SDL_SCANCODE_UNKNOWN, // -- 22 + SDL_SCANCODE_UNKNOWN, // VirtualKey.Junja -- 23 + SDL_SCANCODE_UNKNOWN, // VirtualKey.Final -- 24 + SDL_SCANCODE_UNKNOWN, // VirtualKey.Hanja or VirtualKey.Kanji -- 25 + SDL_SCANCODE_UNKNOWN, // -- 26 + SDL_SCANCODE_ESCAPE, // VirtualKey.Escape -- 27 + SDL_SCANCODE_UNKNOWN, // VirtualKey.Convert -- 28 + SDL_SCANCODE_UNKNOWN, // VirtualKey.NonConvert -- 29 + SDL_SCANCODE_UNKNOWN, // VirtualKey.Accept -- 30 + SDL_SCANCODE_UNKNOWN, // VirtualKey.ModeChange -- 31 (maybe SDL_SCANCODE_MODE ?) + SDL_SCANCODE_SPACE, // VirtualKey.Space -- 32 + SDL_SCANCODE_PAGEUP, // VirtualKey.PageUp -- 33 + SDL_SCANCODE_PAGEDOWN, // VirtualKey.PageDown -- 34 + SDL_SCANCODE_END, // VirtualKey.End -- 35 + SDL_SCANCODE_HOME, // VirtualKey.Home -- 36 + SDL_SCANCODE_LEFT, // VirtualKey.Left -- 37 + SDL_SCANCODE_UP, // VirtualKey.Up -- 38 + SDL_SCANCODE_RIGHT, // VirtualKey.Right -- 39 + SDL_SCANCODE_DOWN, // VirtualKey.Down -- 40 + SDL_SCANCODE_SELECT, // VirtualKey.Select -- 41 + SDL_SCANCODE_UNKNOWN, // VirtualKey.Print -- 42 (maybe SDL_SCANCODE_PRINTSCREEN ?) + SDL_SCANCODE_EXECUTE, // VirtualKey.Execute -- 43 + SDL_SCANCODE_UNKNOWN, // VirtualKey.Snapshot -- 44 + SDL_SCANCODE_INSERT, // VirtualKey.Insert -- 45 + SDL_SCANCODE_DELETE, // VirtualKey.Delete -- 46 + SDL_SCANCODE_HELP, // VirtualKey.Help -- 47 + SDL_SCANCODE_0, // VirtualKey.Number0 -- 48 + SDL_SCANCODE_1, // VirtualKey.Number1 -- 49 + SDL_SCANCODE_2, // VirtualKey.Number2 -- 50 + SDL_SCANCODE_3, // VirtualKey.Number3 -- 51 + SDL_SCANCODE_4, // VirtualKey.Number4 -- 52 + SDL_SCANCODE_5, // VirtualKey.Number5 -- 53 + SDL_SCANCODE_6, // VirtualKey.Number6 -- 54 + SDL_SCANCODE_7, // VirtualKey.Number7 -- 55 + SDL_SCANCODE_8, // VirtualKey.Number8 -- 56 + SDL_SCANCODE_9, // VirtualKey.Number9 -- 57 + SDL_SCANCODE_UNKNOWN, // -- 58 + SDL_SCANCODE_UNKNOWN, // -- 59 + SDL_SCANCODE_UNKNOWN, // -- 60 + SDL_SCANCODE_UNKNOWN, // -- 61 + SDL_SCANCODE_UNKNOWN, // -- 62 + SDL_SCANCODE_UNKNOWN, // -- 63 + SDL_SCANCODE_UNKNOWN, // -- 64 + SDL_SCANCODE_A, // VirtualKey.A -- 65 + SDL_SCANCODE_B, // VirtualKey.B -- 66 + SDL_SCANCODE_C, // VirtualKey.C -- 67 + SDL_SCANCODE_D, // VirtualKey.D -- 68 + SDL_SCANCODE_E, // VirtualKey.E -- 69 + SDL_SCANCODE_F, // VirtualKey.F -- 70 + SDL_SCANCODE_G, // VirtualKey.G -- 71 + SDL_SCANCODE_H, // VirtualKey.H -- 72 + SDL_SCANCODE_I, // VirtualKey.I -- 73 + SDL_SCANCODE_J, // VirtualKey.J -- 74 + SDL_SCANCODE_K, // VirtualKey.K -- 75 + SDL_SCANCODE_L, // VirtualKey.L -- 76 + SDL_SCANCODE_M, // VirtualKey.M -- 77 + SDL_SCANCODE_N, // VirtualKey.N -- 78 + SDL_SCANCODE_O, // VirtualKey.O -- 79 + SDL_SCANCODE_P, // VirtualKey.P -- 80 + SDL_SCANCODE_Q, // VirtualKey.Q -- 81 + SDL_SCANCODE_R, // VirtualKey.R -- 82 + SDL_SCANCODE_S, // VirtualKey.S -- 83 + SDL_SCANCODE_T, // VirtualKey.T -- 84 + SDL_SCANCODE_U, // VirtualKey.U -- 85 + SDL_SCANCODE_V, // VirtualKey.V -- 86 + SDL_SCANCODE_W, // VirtualKey.W -- 87 + SDL_SCANCODE_X, // VirtualKey.X -- 88 + SDL_SCANCODE_Y, // VirtualKey.Y -- 89 + SDL_SCANCODE_Z, // VirtualKey.Z -- 90 + SDL_SCANCODE_UNKNOWN, // VirtualKey.LeftWindows -- 91 (maybe SDL_SCANCODE_APPLICATION or SDL_SCANCODE_LGUI ?) + SDL_SCANCODE_UNKNOWN, // VirtualKey.RightWindows -- 92 (maybe SDL_SCANCODE_APPLICATION or SDL_SCANCODE_RGUI ?) + SDL_SCANCODE_APPLICATION, // VirtualKey.Application -- 93 + SDL_SCANCODE_UNKNOWN, // -- 94 + SDL_SCANCODE_SLEEP, // VirtualKey.Sleep -- 95 + SDL_SCANCODE_KP_0, // VirtualKey.NumberPad0 -- 96 + SDL_SCANCODE_KP_1, // VirtualKey.NumberPad1 -- 97 + SDL_SCANCODE_KP_2, // VirtualKey.NumberPad2 -- 98 + SDL_SCANCODE_KP_3, // VirtualKey.NumberPad3 -- 99 + SDL_SCANCODE_KP_4, // VirtualKey.NumberPad4 -- 100 + SDL_SCANCODE_KP_5, // VirtualKey.NumberPad5 -- 101 + SDL_SCANCODE_KP_6, // VirtualKey.NumberPad6 -- 102 + SDL_SCANCODE_KP_7, // VirtualKey.NumberPad7 -- 103 + SDL_SCANCODE_KP_8, // VirtualKey.NumberPad8 -- 104 + SDL_SCANCODE_KP_9, // VirtualKey.NumberPad9 -- 105 + SDL_SCANCODE_KP_MULTIPLY, // VirtualKey.Multiply -- 106 + SDL_SCANCODE_KP_PLUS, // VirtualKey.Add -- 107 + SDL_SCANCODE_UNKNOWN, // VirtualKey.Separator -- 108 + SDL_SCANCODE_KP_MINUS, // VirtualKey.Subtract -- 109 + SDL_SCANCODE_UNKNOWN, // VirtualKey.Decimal -- 110 (maybe SDL_SCANCODE_DECIMALSEPARATOR, SDL_SCANCODE_KP_DECIMAL, or SDL_SCANCODE_KP_PERIOD ?) + SDL_SCANCODE_KP_DIVIDE, // VirtualKey.Divide -- 111 + SDL_SCANCODE_F1, // VirtualKey.F1 -- 112 + SDL_SCANCODE_F2, // VirtualKey.F2 -- 113 + SDL_SCANCODE_F3, // VirtualKey.F3 -- 114 + SDL_SCANCODE_F4, // VirtualKey.F4 -- 115 + SDL_SCANCODE_F5, // VirtualKey.F5 -- 116 + SDL_SCANCODE_F6, // VirtualKey.F6 -- 117 + SDL_SCANCODE_F7, // VirtualKey.F7 -- 118 + SDL_SCANCODE_F8, // VirtualKey.F8 -- 119 + SDL_SCANCODE_F9, // VirtualKey.F9 -- 120 + SDL_SCANCODE_F10, // VirtualKey.F10 -- 121 + SDL_SCANCODE_F11, // VirtualKey.F11 -- 122 + SDL_SCANCODE_F12, // VirtualKey.F12 -- 123 + SDL_SCANCODE_F13, // VirtualKey.F13 -- 124 + SDL_SCANCODE_F14, // VirtualKey.F14 -- 125 + SDL_SCANCODE_F15, // VirtualKey.F15 -- 126 + SDL_SCANCODE_F16, // VirtualKey.F16 -- 127 + SDL_SCANCODE_F17, // VirtualKey.F17 -- 128 + SDL_SCANCODE_F18, // VirtualKey.F18 -- 129 + SDL_SCANCODE_F19, // VirtualKey.F19 -- 130 + SDL_SCANCODE_F20, // VirtualKey.F20 -- 131 + SDL_SCANCODE_F21, // VirtualKey.F21 -- 132 + SDL_SCANCODE_F22, // VirtualKey.F22 -- 133 + SDL_SCANCODE_F23, // VirtualKey.F23 -- 134 + SDL_SCANCODE_F24, // VirtualKey.F24 -- 135 + SDL_SCANCODE_UNKNOWN, // -- 136 + SDL_SCANCODE_UNKNOWN, // -- 137 + SDL_SCANCODE_UNKNOWN, // -- 138 + SDL_SCANCODE_UNKNOWN, // -- 139 + SDL_SCANCODE_UNKNOWN, // -- 140 + SDL_SCANCODE_UNKNOWN, // -- 141 + SDL_SCANCODE_UNKNOWN, // -- 142 + SDL_SCANCODE_UNKNOWN, // -- 143 + SDL_SCANCODE_NUMLOCKCLEAR, // VirtualKey.NumberKeyLock -- 144 + SDL_SCANCODE_SCROLLLOCK, // VirtualKey.Scroll -- 145 + SDL_SCANCODE_UNKNOWN, // -- 146 + SDL_SCANCODE_UNKNOWN, // -- 147 + SDL_SCANCODE_UNKNOWN, // -- 148 + SDL_SCANCODE_UNKNOWN, // -- 149 + SDL_SCANCODE_UNKNOWN, // -- 150 + SDL_SCANCODE_UNKNOWN, // -- 151 + SDL_SCANCODE_UNKNOWN, // -- 152 + SDL_SCANCODE_UNKNOWN, // -- 153 + SDL_SCANCODE_UNKNOWN, // -- 154 + SDL_SCANCODE_UNKNOWN, // -- 155 + SDL_SCANCODE_UNKNOWN, // -- 156 + SDL_SCANCODE_UNKNOWN, // -- 157 + SDL_SCANCODE_UNKNOWN, // -- 158 + SDL_SCANCODE_UNKNOWN, // -- 159 + SDL_SCANCODE_LSHIFT, // VirtualKey.LeftShift -- 160 + SDL_SCANCODE_RSHIFT, // VirtualKey.RightShift -- 161 + SDL_SCANCODE_LCTRL, // VirtualKey.LeftControl -- 162 + SDL_SCANCODE_RCTRL, // VirtualKey.RightControl -- 163 + SDL_SCANCODE_MENU, // VirtualKey.LeftMenu -- 164 + SDL_SCANCODE_MENU, // VirtualKey.RightMenu -- 165 +}; + +static std::unordered_map WinRT_Unofficial_Keycodes; + +static SDL_Scancode +TranslateKeycode(int keycode) +{ + if (WinRT_Unofficial_Keycodes.empty()) { + /* Set up a table of undocumented (by Microsoft), WinRT-specific, + key codes: */ + // TODO, WinRT: move content declarations of WinRT_Unofficial_Keycodes into a C++11 initializer list, when possible + WinRT_Unofficial_Keycodes[220] = SDL_SCANCODE_GRAVE; + WinRT_Unofficial_Keycodes[222] = SDL_SCANCODE_BACKSLASH; + } + + /* Try to get a documented, WinRT, 'VirtualKey' first (as documented at + http://msdn.microsoft.com/en-us/library/windows/apps/windows.system.virtualkey.aspx ). + If that fails, fall back to a Win32 virtual key. + */ + // TODO, WinRT: try filling out the WinRT keycode table as much as possible, using the Win32 table for interpretation hints + //SDL_Log("WinRT TranslateKeycode, keycode=%d\n", (int)keycode); + SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; + if (keycode < SDL_arraysize(WinRT_Official_Keycodes)) { + scancode = WinRT_Official_Keycodes[keycode]; + } + if (scancode == SDL_SCANCODE_UNKNOWN) { + if (WinRT_Unofficial_Keycodes.find(keycode) != WinRT_Unofficial_Keycodes.end()) { + scancode = WinRT_Unofficial_Keycodes[keycode]; + } + } + if (scancode == SDL_SCANCODE_UNKNOWN) { + if (keycode < SDL_arraysize(windows_scancode_table)) { + scancode = windows_scancode_table[keycode]; + } + } + if (scancode == SDL_SCANCODE_UNKNOWN) { + SDL_Log("WinRT TranslateKeycode, unknown keycode=%d\n", (int)keycode); + } + return scancode; +} + +void SDL_WinRTApp::OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args) +{ + SDL_Scancode sdlScancode = TranslateKeycode((int)args->VirtualKey); +#if 0 + SDL_Keycode keycode = SDL_GetKeyFromScancode(sdlScancode); + SDL_Log("key down, handled=%s, ext?=%s, released?=%s, menu key down?=%s, repeat count=%d, native scan code=%d, was down?=%s, vkey=%d, sdl scan code=%d (%s), sdl key code=%d (%s)\n", + (args->Handled ? "1" : "0"), + (args->KeyStatus.IsExtendedKey ? "1" : "0"), + (args->KeyStatus.IsKeyReleased ? "1" : "0"), + (args->KeyStatus.IsMenuKeyDown ? "1" : "0"), + args->KeyStatus.RepeatCount, + args->KeyStatus.ScanCode, + (args->KeyStatus.WasKeyDown ? "1" : "0"), + args->VirtualKey, + sdlScancode, + SDL_GetScancodeName(sdlScancode), + keycode, + SDL_GetKeyName(keycode)); + //args->Handled = true; + //VirtualKey vkey = args->VirtualKey; +#endif + SDL_SendKeyboardKey(SDL_PRESSED, sdlScancode); +} + +void SDL_WinRTApp::OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args) +{ + SDL_Scancode sdlScancode = TranslateKeycode((int)args->VirtualKey); +#if 0 + SDL_Keycode keycode = SDL_GetKeyFromScancode(sdlScancode); + SDL_Log("key up, handled=%s, ext?=%s, released?=%s, menu key down?=%s, repeat count=%d, native scan code=%d, was down?=%s, vkey=%d, sdl scan code=%d (%s), sdl key code=%d (%s)\n", + (args->Handled ? "1" : "0"), + (args->KeyStatus.IsExtendedKey ? "1" : "0"), + (args->KeyStatus.IsKeyReleased ? "1" : "0"), + (args->KeyStatus.IsMenuKeyDown ? "1" : "0"), + args->KeyStatus.RepeatCount, + args->KeyStatus.ScanCode, + (args->KeyStatus.WasKeyDown ? "1" : "0"), + args->VirtualKey, + sdlScancode, + SDL_GetScancodeName(sdlScancode), + keycode, + SDL_GetKeyName(keycode)); + //args->Handled = true; +#endif + SDL_SendKeyboardKey(SDL_RELEASED, sdlScancode); +} + +void SDL_WinRTApp::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args) +{ + CoreWindow::GetForCurrentThread()->Activate(); +} + +static int SDLCALL RemoveAppSuspendAndResumeEvents(void * userdata, SDL_Event * event) +{ + if (event->type == SDL_WINDOWEVENT) + { + switch (event->window.event) + { + case SDL_WINDOWEVENT_MINIMIZED: + case SDL_WINDOWEVENT_RESTORED: + // Return 0 to indicate that the event should be removed from the + // event queue: + return 0; + default: + break; + } + } + + // Return 1 to indicate that the event should stay in the event queue: + return 1; +} + +void SDL_WinRTApp::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args) +{ + // Save app state asynchronously after requesting a deferral. Holding a deferral + // indicates that the application is busy performing suspending operations. Be + // aware that a deferral may not be held indefinitely. After about five seconds, + // the app will be forced to exit. + SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral(); + create_task([this, deferral]() + { + // Send a window-minimized event immediately to observers. + // CoreDispatcher::ProcessEvents, which is the backbone on which + // SDL_WinRTApp::PumpEvents is built, will not return to its caller + // once it sends out a suspend event. Any events posted to SDL's + // event queue won't get received until the WinRT app is resumed. + // SDL_AddEventWatch() may be used to receive app-suspend events on + // WinRT. + // + // In order to prevent app-suspend events from being received twice: + // first via a callback passed to SDL_AddEventWatch, and second via + // SDL's event queue, the event will be sent to SDL, then immediately + // removed from the queue. + if (m_sdlWindowData) + { + SDL_SendWindowEvent(m_sdlWindowData->sdlWindow, SDL_WINDOWEVENT_MINIMIZED, 0, 0); // TODO: see if SDL_WINDOWEVENT_SIZE_CHANGED should be getting triggered here (it is, currently) + SDL_FilterEvents(RemoveAppSuspendAndResumeEvents, 0); + } + deferral->Complete(); + }); +} + +void SDL_WinRTApp::OnResuming(Platform::Object^ sender, Platform::Object^ args) +{ + // Restore any data or state that was unloaded on suspend. By default, data + // and state are persisted when resuming from suspend. Note that this event + // does not occur if the app was previously terminated. + if (m_sdlWindowData) + { + SDL_SendWindowEvent(m_sdlWindowData->sdlWindow, SDL_WINDOWEVENT_RESTORED, 0, 0); // TODO: see if SDL_WINDOWEVENT_SIZE_CHANGED should be getting triggered here (it is, currently) + + // Remove the app-resume event from the queue, as is done with the + // app-suspend event. + // + // TODO, WinRT: consider posting this event to the queue even though + // its counterpart, the app-suspend event, effectively has to be + // processed immediately. + SDL_FilterEvents(RemoveAppSuspendAndResumeEvents, 0); + } +} + +SDL_DisplayMode SDL_WinRTApp::GetMainDisplayMode() +{ + // Create an empty, zeroed-out display mode: + SDL_DisplayMode mode; + SDL_zero(mode); + + // Fill in most fields: + mode.format = SDL_PIXELFORMAT_RGB888; + mode.refresh_rate = 0; // TODO, WinRT: see if refresh rate data is available, or relevant (for WinRT apps) + mode.driverdata = NULL; + + // Calculate the display size given the window size, taking into account + // the current display's DPI: + const float currentDPI = Windows::Graphics::Display::DisplayProperties::LogicalDpi; + const float dipsPerInch = 96.0f; + mode.w = (int) ((CoreWindow::GetForCurrentThread()->Bounds.Width * currentDPI) / dipsPerInch); + mode.h = (int) ((CoreWindow::GetForCurrentThread()->Bounds.Height * currentDPI) / dipsPerInch); + + return mode; +} + +const SDL_WindowData * SDL_WinRTApp::GetSDLWindowData() const +{ + return m_sdlWindowData; +} + +bool SDL_WinRTApp::HasSDLWindowData() const +{ + return (m_sdlWindowData != NULL); +} + +void SDL_WinRTApp::SetRelativeMouseMode(bool enable) +{ + m_useRelativeMouseMode = enable; +} + +void SDL_WinRTApp::SetSDLWindowData(const SDL_WindowData * windowData) +{ + m_sdlWindowData = windowData; +} + +void SDL_WinRTApp::SetSDLVideoDevice(const SDL_VideoDevice * videoDevice) +{ + m_sdlVideoDevice = videoDevice; +} diff --git a/src/video/windowsrt/SDL_WinRTApp.h b/src/video/windowsrt/SDL_WinRTApp.h new file mode 100644 index 000000000..cd0c379fc --- /dev/null +++ b/src/video/windowsrt/SDL_WinRTApp.h @@ -0,0 +1,52 @@ +#pragma once + +struct SDL_WindowData; + +ref class SDL_WinRTApp sealed : public Windows::ApplicationModel::Core::IFrameworkView +{ +public: + SDL_WinRTApp(); + + // IFrameworkView Methods. + virtual void Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView); + virtual void SetWindow(Windows::UI::Core::CoreWindow^ window); + virtual void Load(Platform::String^ entryPoint); + virtual void Run(); + virtual void Uninitialize(); + +internal: + // SDL-specific methods + SDL_DisplayMode GetMainDisplayMode(); + void PumpEvents(); + const SDL_WindowData * GetSDLWindowData() const; + bool HasSDLWindowData() const; + void SetRelativeMouseMode(bool enable); + void SetSDLWindowData(const SDL_WindowData * windowData); + void SetSDLVideoDevice(const SDL_VideoDevice * videoDevice); + Windows::Foundation::Point TransformCursor(Windows::Foundation::Point rawPosition); + +protected: + // Event Handlers. + void OnOrientationChanged(Platform::Object^ sender); + void OnWindowSizeChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::WindowSizeChangedEventArgs^ args); + void OnLogicalDpiChanged(Platform::Object^ sender); + void OnActivated(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args); + void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ args); + void OnResuming(Platform::Object^ sender, Platform::Object^ args); + void OnWindowClosed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CoreWindowEventArgs^ args); + void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args); + void OnPointerPressed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); + void OnPointerReleased(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); + void OnPointerWheelChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); + void OnPointerMoved(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); + void OnMouseMoved(Windows::Devices::Input::MouseDevice^ mouseDevice, Windows::Devices::Input::MouseEventArgs^ args); + void OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args); + void OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args); + +private: + bool m_windowClosed; + bool m_windowVisible; + const SDL_WindowData* m_sdlWindowData; + const SDL_VideoDevice* m_sdlVideoDevice; + bool m_useRelativeMouseMode; +}; diff --git a/src/video/windowsrt/SDL_winrtevents.cpp b/src/video/windowsrt/SDL_winrtevents.cpp new file mode 100644 index 000000000..f445b8b65 --- /dev/null +++ b/src/video/windowsrt/SDL_winrtevents.cpp @@ -0,0 +1,41 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +#if SDL_VIDEO_DRIVER_WINRT + +#include "../../events/SDL_events_c.h" + +#include "SDL_winrtvideo.h" +#include "SDL_winrtevents_c.h" +#include "SDL_WinRTApp.h" + +extern SDL_WinRTApp ^ SDL_WinRTGlobalApp; + +void +WINRT_PumpEvents(_THIS) +{ + SDL_WinRTGlobalApp->PumpEvents(); +} + +#endif /* SDL_VIDEO_DRIVER_WINRT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/windowsrt/SDL_winrtevents_c.h b/src/video/windowsrt/SDL_winrtevents_c.h new file mode 100644 index 000000000..afbd6862c --- /dev/null +++ b/src/video/windowsrt/SDL_winrtevents_c.h @@ -0,0 +1,27 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +#include "SDL_winrtvideo.h" + +extern void WINRT_PumpEvents(_THIS); + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/windowsrt/SDL_winrtmouse.cpp b/src/video/windowsrt/SDL_winrtmouse.cpp new file mode 100644 index 000000000..e0450c655 --- /dev/null +++ b/src/video/windowsrt/SDL_winrtmouse.cpp @@ -0,0 +1,149 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_config.h" + +#if SDL_VIDEO_DRIVER_WINRT + +extern "C" { +#include "SDL_assert.h" +#include "../../events/SDL_mouse_c.h" +#include "../SDL_sysvideo.h" +} + +#include "SDL_WinRTApp.h" +#include "SDL_winrtmouse.h" + +using namespace Windows::UI::Core; +using Windows::UI::Core::CoreCursor; + +extern SDL_WinRTApp ^ SDL_WinRTGlobalApp; + + +static SDL_Cursor * +WINRT_CreateSystemCursor(SDL_SystemCursor id) +{ + SDL_Cursor *cursor; + CoreCursorType cursorType = CoreCursorType::Arrow; + + switch(id) + { + default: + SDL_assert(0); + return NULL; + case SDL_SYSTEM_CURSOR_ARROW: cursorType = CoreCursorType::Arrow; break; + case SDL_SYSTEM_CURSOR_IBEAM: cursorType = CoreCursorType::IBeam; break; + case SDL_SYSTEM_CURSOR_WAIT: cursorType = CoreCursorType::Wait; break; + case SDL_SYSTEM_CURSOR_CROSSHAIR: cursorType = CoreCursorType::Cross; break; + case SDL_SYSTEM_CURSOR_WAITARROW: cursorType = CoreCursorType::Wait; break; + case SDL_SYSTEM_CURSOR_SIZENWSE: cursorType = CoreCursorType::SizeNorthwestSoutheast; break; + case SDL_SYSTEM_CURSOR_SIZENESW: cursorType = CoreCursorType::SizeNortheastSouthwest; break; + case SDL_SYSTEM_CURSOR_SIZEWE: cursorType = CoreCursorType::SizeWestEast; break; + case SDL_SYSTEM_CURSOR_SIZENS: cursorType = CoreCursorType::SizeNorthSouth; break; + case SDL_SYSTEM_CURSOR_SIZEALL: cursorType = CoreCursorType::SizeAll; break; + case SDL_SYSTEM_CURSOR_NO: cursorType = CoreCursorType::UniversalNo; break; + case SDL_SYSTEM_CURSOR_HAND: cursorType = CoreCursorType::Hand; break; + } + + cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor)); + if (cursor) { + /* Create a pointer to a COM reference to a cursor. The extra + pointer is used (on top of the COM reference) to allow the cursor + to be referenced by the SDL_cursor's driverdata field, which is + a void pointer. + */ + CoreCursor ^* theCursor = new CoreCursor^(nullptr); + *theCursor = ref new CoreCursor(cursorType, 0); + cursor->driverdata = (void *) theCursor; + } else { + SDL_OutOfMemory(); + } + + return cursor; +} + +static SDL_Cursor * +WINRT_CreateDefaultCursor() +{ + return WINRT_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); +} + +static void +WINRT_FreeCursor(SDL_Cursor * cursor) +{ + if (cursor->driverdata) { + CoreCursor ^* theCursor = (CoreCursor ^*) cursor->driverdata; + *theCursor = nullptr; // Release the COM reference to the CoreCursor + delete theCursor; // Delete the pointer to the COM reference + } + SDL_free(cursor); +} + +static int +WINRT_ShowCursor(SDL_Cursor * cursor) +{ + if (cursor) { + CoreCursor ^* theCursor = (CoreCursor ^*) cursor->driverdata; + CoreWindow::GetForCurrentThread()->PointerCursor = *theCursor; + } else { + CoreWindow::GetForCurrentThread()->PointerCursor = nullptr; + } + return 0; +} + +static int +WINRT_SetRelativeMouseMode(SDL_bool enabled) +{ + SDL_WinRTGlobalApp->SetRelativeMouseMode(enabled ? true : false); + return 0; +} + +void +WINRT_InitMouse(_THIS) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + /* DLudwig, Dec 3, 2012: Windows RT does not currently provide APIs for + the following features, AFAIK: + - custom cursors (multiple system cursors are, however, available) + - programmatically moveable cursors + */ + +#if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP + //mouse->CreateCursor = WINRT_CreateCursor; + mouse->CreateSystemCursor = WINRT_CreateSystemCursor; + mouse->ShowCursor = WINRT_ShowCursor; + mouse->FreeCursor = WINRT_FreeCursor; + //mouse->WarpMouse = WINRT_WarpMouse; + mouse->SetRelativeMouseMode = WINRT_SetRelativeMouseMode; + + SDL_SetDefaultCursor(WINRT_CreateDefaultCursor()); +#endif +} + +void +WINRT_QuitMouse(_THIS) +{ +} + +#endif /* SDL_VIDEO_DRIVER_WINRT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/windowsrt/SDL_winrtmouse.h b/src/video/windowsrt/SDL_winrtmouse.h new file mode 100644 index 000000000..50bdbe24d --- /dev/null +++ b/src/video/windowsrt/SDL_winrtmouse.h @@ -0,0 +1,31 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +#ifndef _SDL_windowsmouse_h +#define _SDL_windowsmouse_h + +extern void WINRT_InitMouse(_THIS); +extern void WINRT_QuitMouse(_THIS); + +#endif /* _SDL_windowsmouse_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/windowsrt/SDL_winrtvideo.cpp b/src/video/windowsrt/SDL_winrtvideo.cpp new file mode 100644 index 000000000..7a459fb95 --- /dev/null +++ b/src/video/windowsrt/SDL_winrtvideo.cpp @@ -0,0 +1,262 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +#if SDL_VIDEO_DRIVER_WINRT + +/* WinRT SDL video driver implementation + + Initial work on this was done by David Ludwig (dludwig@pobox.com), and + was based off of SDL's "dummy" video driver. + */ + +extern "C" { +#include "SDL_video.h" +#include "SDL_mouse.h" +#include "../SDL_sysvideo.h" +#include "../SDL_pixels_c.h" +#include "../../events/SDL_events_c.h" +#include "../../render/SDL_sysrender.h" +#include "SDL_syswm.h" +} + +#include "SDL_WinRTApp.h" +#include "SDL_winrtvideo.h" +#include "SDL_winrtevents_c.h" +#include "SDL_winrtmouse.h" + +using namespace Windows::UI::Core; + +/* On Windows, windows.h defines CreateWindow */ +#ifdef CreateWindow +#undef CreateWindow +#endif + +extern SDL_WinRTApp ^ SDL_WinRTGlobalApp; + +#define WINRTVID_DRIVER_NAME "winrt" + +/* Initialization/Query functions */ +static int WINRT_VideoInit(_THIS); +static int WINRT_InitModes(_THIS); +static int WINRT_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode); +static void WINRT_VideoQuit(_THIS); + +/* Window functions */ +static int WINRT_CreateWindow(_THIS, SDL_Window * window); +static void WINRT_DestroyWindow(_THIS, SDL_Window * window); +static SDL_bool WINRT_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info); + +/* WinRT driver bootstrap functions */ + +static int +WINRT_Available(void) +{ + return (1); +} + +static void +WINRT_DeleteDevice(SDL_VideoDevice * device) +{ + SDL_WinRTGlobalApp->SetSDLVideoDevice(NULL); + SDL_free(device); +} + +static SDL_VideoDevice * +WINRT_CreateDevice(int devindex) +{ + SDL_VideoDevice *device; + + /* Initialize all variables that we clean on shutdown */ + device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice)); + if (!device) { + SDL_OutOfMemory(); + if (device) { + SDL_free(device); + } + return (0); + } + + /* Set the function pointers */ + device->VideoInit = WINRT_VideoInit; + device->VideoQuit = WINRT_VideoQuit; + device->CreateWindow = WINRT_CreateWindow; + device->DestroyWindow = WINRT_DestroyWindow; + device->SetDisplayMode = WINRT_SetDisplayMode; + device->PumpEvents = WINRT_PumpEvents; + //device->CreateWindowFramebuffer = SDL_WINRT_CreateWindowFramebuffer; + //device->UpdateWindowFramebuffer = SDL_WINRT_UpdateWindowFramebuffer; + //device->DestroyWindowFramebuffer = SDL_WINRT_DestroyWindowFramebuffer; + device->GetWindowWMInfo = WINRT_GetWindowWMInfo; + device->free = WINRT_DeleteDevice; + + SDL_WinRTGlobalApp->SetSDLVideoDevice(device); + + return device; +} + +VideoBootStrap WINRT_bootstrap = { + WINRTVID_DRIVER_NAME, "SDL Windows RT video driver", + WINRT_Available, WINRT_CreateDevice +}; + +int +WINRT_VideoInit(_THIS) +{ + // TODO, WinRT: consider adding a hack to wait (here) for the app's orientation to finish getting set (before the initial display mode is set up) + + if (WINRT_InitModes(_this) < 0) { + return -1; + } + WINRT_InitMouse(_this); + + return 0; +} + +static int +WINRT_InitModes(_THIS) +{ + SDL_DisplayMode mode = SDL_WinRTGlobalApp->GetMainDisplayMode(); + if (SDL_AddBasicVideoDisplay(&mode) < 0) { + return -1; + } + + SDL_AddDisplayMode(&_this->displays[0], &mode); + return 0; +} + +static int +WINRT_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) +{ + return 0; +} + +void +WINRT_VideoQuit(_THIS) +{ + WINRT_QuitMouse(_this); +} + +int +WINRT_CreateWindow(_THIS, SDL_Window * window) +{ + // Make sure that only one window gets created, at least until multimonitor + // support is added. + if (SDL_WinRTGlobalApp->HasSDLWindowData()) + { + SDL_SetError("WinRT only supports one window"); + return -1; + } + + SDL_WindowData *data = new SDL_WindowData; + if (!data) { + SDL_OutOfMemory(); + return -1; + } + window->driverdata = data; + data->sdlWindow = window; + data->coreWindow = CoreWindow::GetForCurrentThread(); + + /* Make sure the window is considered to be positioned at {0,0}, + and is considered fullscreen, shown, and the like. + */ + window->x = 0; + window->y = 0; + window->flags = + SDL_WINDOW_FULLSCREEN | + SDL_WINDOW_SHOWN | + SDL_WINDOW_BORDERLESS | + SDL_WINDOW_MAXIMIZED | + SDL_WINDOW_INPUT_GRABBED; + + /* HACK from DLudwig: The following line of code prevents + SDL_CreateWindow and SDL_UpdateFullscreenMode from trying to resize + the window after the call to WINRT_CreateWindow returns. + + This hack should allow a window to be created in virtually any size, + and more importantly, it allows a window's framebuffer, as created and + retrieved via SDL_GetWindowSurface, to be in any size. This can be + utilized by apps centered around software rendering, such as ports + of older apps. The app can have SDL create a framebuffer in any size + it chooses. SDL will scale the framebuffer to the native + screen size on the GPU (via SDL_UpdateWindowSurface). + */ + _this->displays[0].fullscreen_window = window; + + /* Further prevent any display resizing, and make sure SDL_GetWindowDisplayMode + can report the correct size of windows, by creating a new display + mode in the requested size. To note, if the window is being created in + the device's native screen size, SDL_AddDisplayMode will do nothing. + */ + window->fullscreen_mode = SDL_WinRTGlobalApp->GetMainDisplayMode(); + window->fullscreen_mode.w = window->w; + window->fullscreen_mode.h = window->h; + SDL_AddDisplayMode(&_this->displays[0], &window->fullscreen_mode); + + /* TODO: Consider removing custom display modes in WINRT_DestroyWindow. */ + + /* Make sure the WinRT app's IFramworkView can post events on + behalf of SDL: + */ + SDL_WinRTGlobalApp->SetSDLWindowData(data); + + /* All done! */ + return 0; +} + +void +WINRT_DestroyWindow(_THIS, SDL_Window * window) +{ + SDL_WindowData * data = (SDL_WindowData *) window->driverdata; + + if (SDL_WinRTGlobalApp->HasSDLWindowData() && + SDL_WinRTGlobalApp->GetSDLWindowData()->sdlWindow == window) + { + SDL_WinRTGlobalApp->SetSDLWindowData(NULL); + } + + if (data) { + // Delete the internal window data: + delete data; + data = NULL; + } +} + +SDL_bool +WINRT_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info) +{ + SDL_WindowData * data = (SDL_WindowData *) window->driverdata; + + if (info->version.major <= SDL_MAJOR_VERSION) { + info->subsystem = SDL_SYSWM_WINDOWSRT; + info->info.winrt.window = reinterpret_cast(data->coreWindow.Get()); + return SDL_TRUE; + } else { + SDL_SetError("Application not compiled with SDL %d.%d\n", + SDL_MAJOR_VERSION, SDL_MINOR_VERSION); + return SDL_FALSE; + } + return SDL_FALSE; +} + +#endif /* SDL_VIDEO_DRIVER_WINRT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/windowsrt/SDL_winrtvideo.h b/src/video/windowsrt/SDL_winrtvideo.h new file mode 100644 index 000000000..4418754c2 --- /dev/null +++ b/src/video/windowsrt/SDL_winrtvideo.h @@ -0,0 +1,40 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +#ifndef _SDL_winrtvideo_h +#define _SDL_winrtvideo_h + +extern "C" { +#include "../SDL_sysvideo.h" +} + +#include + +struct SDL_WindowData +{ + SDL_Window *sdlWindow; + Platform::Agile coreWindow; +}; + +#endif /* _SDL_winrtvideo_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/test/loopwave.c b/test/loopwave.c index 22b0559de..de8329af1 100644 --- a/test/loopwave.c +++ b/test/loopwave.c @@ -79,6 +79,7 @@ int main(int argc, char *argv[]) { int i; + char filename[4096]; /* Load the SDL library */ if (SDL_Init(SDL_INIT_AUDIO) < 0) { @@ -86,12 +87,14 @@ main(int argc, char *argv[]) return (1); } - if (argv[1] == NULL) { - argv[1] = "sample.wav"; + if (argc >= 1) { + SDL_strlcpy(filename, argv[1], sizeof(filename)); + } else { + SDL_strlcpy(filename, "sample.wav", sizeof(filename)); } /* Load the wave file into memory */ - if (SDL_LoadWAV(argv[1], &wave.spec, &wave.sound, &wave.soundlen) == NULL) { - fprintf(stderr, "Couldn't load %s: %s\n", argv[1], SDL_GetError()); + if (SDL_LoadWAV(filename, &wave.spec, &wave.sound, &wave.soundlen) == NULL) { + fprintf(stderr, "Couldn't load %s: %s\n", filename, SDL_GetError()); quit(1); }